dev(UML-981): Переделал для взаимодействия по pipe

This commit is contained in:
svad05 2022-08-11 09:10:48 +03:00
parent a3c62f54e5
commit d1ade982e6
31 changed files with 1675 additions and 106 deletions

1
.gitignore vendored
View File

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

View File

@ -5,11 +5,18 @@ project (PN_ECHO_TEST VERSION 0.0.1)
set(CMAKE_CXX_STANDARD 20)
set(INC_DIRS ${INC_DIRS} ../../src/profinet)
set(INC_DIRS ${INC_DIRS} ../../src/interprocess)
set(INC_DIRS ${INC_DIRS} ../../src/nlohmann_json)
set(INC_DIRS ${INC_DIRS} ../../src/interprocess/shared_memory)
set(INC_DIRS ${INC_DIRS} ./)
set(INC_DIRS ${INC_DIRS} ../../src/shared_data)
set(INC_DIRS ${INC_DIRS} ../../src/nlohmann_json)
set(INC_DIRS ${INC_DIRS} ../../src/file_api)
set(SRC_FILES ${SRC_FILES} ../../src/shared_data/shared_data.cpp)
set(SRC_FILES ${SRC_FILES} ../../src/interprocess/profinet_shared_data.cpp)
#set(SRC_FILES ${SRC_FILES} ../../src/shared_data/shared_data.cpp)
#set(SRC_FILES ${SRC_FILES} ../../src/interprocess/profinet_shared_data.cpp)
set(SRC_FILES ${SRC_FILES} ./CreatePipes.cpp)
set(SRC_FILES ${SRC_FILES} ../../src/file_api/file_api.cpp)
set(SRC_FILES ${SRC_FILES} ./main.cpp)
set(Boost_USE_STATIC_LIBS ON)
@ -25,10 +32,13 @@ endif()
message("Boost dir: " ${Boost_INCLUDE_DIRS})
message("Boost libs:" ${Boost_LIBRARIES})
#Копирование тестового файла конфигурации устройства Profinet
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/sample_app_echo_config.json
DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
add_executable(pn_echo_test ./main.cpp ${SRC_FILES})
target_include_directories(pn_echo_test PRIVATE
./
${INC_DIRS}
)

View File

@ -0,0 +1,137 @@
//
// Created by sozonov on 18.10.2021.
//
#include "CreatePipes.h"
#include <iostream>
#include <memory>
#include "boost/process/pipe.hpp"
/*! Создание в операционной системе именнованого канала, заданого имени и заданого направления.<br>
* Обеспечена кросплатформенность данной функции (Windows/Linux).
* @param path Назавние именновоного канала.
* @param mode Направление работы именнованного канала: <br> std::ios_base::in / std::ios_base::out
* @return Именнованый канал, привязаный к библиотеке boost.
*/
boost::process::pipe build_named_io_pipe( std::filesystem::path path, std::ios_base::openmode mode );
std::tuple< std::unique_ptr< std::istream >,
std::unique_ptr< std::ostream > > itr::create_pipes( std::filesystem::path input_path,
std::filesystem::path output_path,
std::ostream & info ) {
//todo: перед созданием пайпов проверять валидность пути и выводить инфу в поток &info
info << "Create pipes: " << input_path << ", " << output_path << "..." << std::endl;
auto ostream = std::make_unique<::boost::process::opstream>( std::move( build_named_io_pipe( output_path,
std::ios_base::out ) ) );
auto istream = std::make_unique<::boost::process::ipstream>( std::move( build_named_io_pipe( input_path,
std::ios_base::in ) ) );
#if (defined (_WIN32) || defined (_WIN64))
::boost::winapi::ConnectNamedPipe( istream->pipe().native_source(), nullptr );
::boost::winapi::ConnectNamedPipe( ostream->pipe().native_sink(), nullptr );
#endif
info << "Pipes " << input_path << " and " << output_path << " are connected!" << std::endl;
return {
std::move(istream),
std::move(ostream)
};
};
boost::process::pipe build_named_io_pipe( std::filesystem::path path, std::ios_base::openmode mode ) {
#if (defined (_WIN32) || defined (_WIN64))
if( mode == std::ios_base::in ) {
boost::winapi::HANDLE_ input_source = boost::winapi::create_named_pipe(
path.string().c_str(),
boost::winapi::PIPE_ACCESS_INBOUND_, //write flag
boost::winapi::PIPE_TYPE_BYTE_ | boost::winapi::PIPE_READMODE_BYTE_ | boost::winapi::PIPE_WAIT_ ,
4,
8192,
8192,
0,
nullptr );
return boost::process::pipe( input_source, boost::winapi::INVALID_HANDLE_VALUE_ );
}
if( mode == std::ios_base::out ) {
boost::winapi::HANDLE_ output_source = boost::winapi::create_named_pipe(
path.string().c_str(),
boost::winapi::PIPE_ACCESS_OUTBOUND_, //write flag
boost::winapi::PIPE_TYPE_BYTE_ | ::boost::winapi::PIPE_READMODE_BYTE_ |
boost::winapi::PIPE_WAIT_,
4,
8192,
8192,
0,
nullptr);
return boost::process::pipe( boost::winapi::INVALID_HANDLE_VALUE_ , output_source);
}
return boost::process::pipe( boost::winapi::INVALID_HANDLE_VALUE_ ,boost::winapi::INVALID_HANDLE_VALUE_ );
#elif (defined (LINUX) || defined (__linux__))
if( mode == std::ios_base::in ) {
if( not std::filesystem::exists(path) ) {
auto fifo = mkfifo(path.c_str(), 0666 );
if (fifo != 0)
boost::process::detail::throw_last_error("mkfifo() failed");
std::cout << "Input fifo created: " << path << std::endl;
}
else
{
std::cout << "Input fifo exists: " << path << std::endl;
}
int read_fd = open(path.c_str(), O_RDONLY );
if( read_fd == -1 )
boost::process::detail::throw_last_error();
return boost::process::pipe( read_fd, -1 );
}
if( mode == std::ios_base::out ) {
if( not std::filesystem::exists(path) ) {
auto fifo = mkfifo(path.c_str(), 0666 );
if (fifo != 0)
boost::process::detail::throw_last_error("mkfifo() failed");
std::cout << "Output fifo created: " << path << std::endl;
}
else
{
std::cout << "Output fifo exists: " << path << std::endl;
}
int write_fd = open(path.c_str(), O_WRONLY );
if (write_fd == -1)
boost::process::detail::throw_last_error();
return boost::process::pipe( -1, write_fd );
}
return boost::process::pipe( -1, -1 );
#endif
};

View File

@ -0,0 +1,25 @@
//
// Created by sozonov on 18.10.2021.
//
#pragma once
#include <filesystem>
namespace itr {
/*! Создание пары именнованых каналов (на прием и передачу данных). <br>
* Данная функция является блокирующей, до подключения к именованым каналам. <br>
* После успешного подключения, в потоко info будет переденна информация об этом. <br>
* //todo: В функции реализована проверка валидности пути, информация об этом предается в info <br>
*
* @param to_input Название именнованного канала на приём данных.
* @param to_output Название именнованного канала на передачу данных.
* @param info Поток вывода информации.
* @return Кортеж с уникальными указатаелями на потоки ввода/вывода для каждого именнованого канала.
*/
std::tuple<
std::unique_ptr<std::istream>,
std::unique_ptr<std::ostream>
> create_pipes( std::filesystem::path to_input, std::filesystem::path to_output, std::ostream & info );
}

View File

@ -1,6 +1,10 @@
#include "profinet_shared_data_client.hpp"
#include "CreatePipes.h"
#include <memory>
#include <iostream>
#include "json.hpp"
#include <chrono>
#include <thread>
#include "file_api.hpp"
#define ECHO_MODULE_ID 0x00000040 /// Идентификатор тестового модуля
#define ECHO_SUBMOD_ID 0x00000140 /// Идентификатор тестового подмодуля
@ -8,7 +12,7 @@
#define ECHO_OUTPUT_DATA_SIZE 8
#define ECHO_PARAMETER_GAIN_IDX 125 /// Индекс параметра Gain для подмодуля ECHO
using namespace std;
using json = nlohmann::json;
uint32_t Echo_Gain = 0;
@ -38,106 +42,220 @@ void endian_convert_32(uint8_t * p_data)
p_data[3] = tmp;
}
ProfinetSharedDataClient shared_data;
ProfinetData_Map * p_profinet_data;
uint32_t event_mask = ( ProfinetEvent::EVENT_CONNECTION_ESTABLISHED |
ProfinetEvent::EVENT_NEW_CYCLIC_DATA |
ProfinetEvent::EVENT_NEW_PARAM_DATA |
ProfinetEvent::EVENT_CONNECTION_ABORT );
/** Поток обработки входящих данных от именнованого канала. */
std::shared_ptr< std::istream > p_input_stream_;
/** Поток для отправки данных в именнованй канал. */
std::shared_ptr< std::ostream > p_output_stream_;
using json = nlohmann::json;
int main(int argc, char * argv[])
{
p_profinet_data = shared_data.Connect("profinet_shared_data");
json j_cfg = json::parse(fileapi_read_file("sample_app_echo_config.json"));
if (p_profinet_data == nullptr)
{
return 0;
}
std::filesystem::path _input_path{j_cfg["app_settings"]["app_inp_pipe_name"].get<std::string>()}; /// Выходной канал профинет, входной канал апп
std::filesystem::path _output_path{j_cfg["app_settings"]["app_out_pipe_name"].get<std::string>()}; /// Входной канал профинет, входной канал апп
/// Берем указатель на данные подмодуля в разделяемой памяти
auto echo_submod_ptr = shared_data.getSubmodule(ECHO_MODULE_ID, ECHO_SUBMOD_ID);
auto streams = itr::create_pipes( _input_path, _output_path, std::cout );
if (echo_submod_ptr->id != ECHO_SUBMOD_ID)
{
return 0;
}
/// Берем указатель на данные параметра подмодуля в разделяемолй памяти
auto echo_param_gain_ptr = shared_data.getSubmoduleParameter(ECHO_MODULE_ID, ECHO_SUBMOD_ID, ECHO_PARAMETER_GAIN_IDX);
p_input_stream_ = std::move( std::get<0>( streams ) ) ;
p_output_stream_ = std::move( std::get<1>( streams ) );
json j_get_flags;
j_get_flags["jsonrpc"] = "2.0";
j_get_flags["method"] = "readFlags";
j_get_flags["params"];
j_get_flags["id"] = 0;
json j_get_echo_param_gain;
j_get_echo_param_gain["jsonrpc"] = "2.0";
j_get_echo_param_gain["method"] = "readSubmoduleParameter";
j_get_echo_param_gain["params"] = json::array();
json j_data;
j_data["module_id"] = ECHO_MODULE_ID;
j_data["submodule_id"] = ECHO_SUBMOD_ID;
j_data["param_index"] = ECHO_PARAMETER_GAIN_IDX;
j_get_echo_param_gain["params"].push_back(j_data);
j_get_echo_param_gain["id"] = 0;
json j_get_echo_data;
j_get_echo_data["jsonrpc"] = "2.0";
j_get_echo_data["method"] = "readCyclicIoData";
j_get_echo_data["params"] = json::array();
j_data.clear();
j_data["module_id"] = ECHO_MODULE_ID;
j_data["submodule_id"] = ECHO_SUBMOD_ID;
j_get_echo_data["params"].push_back(j_data);
j_get_echo_data["id"] = 0;
if (echo_param_gain_ptr->index != ECHO_PARAMETER_GAIN_IDX)
{
return 0;
}
std::cout << "Strarting..." << std::endl;
json j_set_echo_data;
j_set_echo_data["jsonrpc"] = "2.0";
j_set_echo_data["method"] = "writeCyclicIoData";
j_set_echo_data["params"] = json::array();
j_set_echo_data["id"] = 0;
std::cout << "Starting...." << std::endl;
std::string answer_str;
for(;;)
{
uint32_t events = p_profinet_data->Events.wait_flags(event_mask);
answer_str.clear();
if (events & ProfinetEvent::EVENT_CONNECTION_ESTABLISHED)
std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::string request{j_get_flags.dump()};
///Запрос:
*p_output_stream_ << request << std::endl;
///Ждем ответ:
std::getline( *p_input_stream_.get( ), answer_str);
json j_answ = json::parse(answer_str);
if (!j_answ.contains("result"))
{
p_profinet_data->Events.clear_flags(ProfinetEvent::EVENT_CONNECTION_ESTABLISHED);
std::cout << "Error: result" << std::endl;
continue;
}
std::cout << "Event: PLC connection established" << std::endl;
json& j_res = j_answ["result"];
if (!j_res.contains("EVENT_CONNECTION_ESTABLISHED"))
{
std::cout << "Error: EVENT_CONNECTION_ESTABLISHED" << std::endl;
continue;
}
if (events & ProfinetEvent::EVENT_NEW_CYCLIC_DATA)
if (j_res["EVENT_CONNECTION_ESTABLISHED"].get<bool>() == true)
{
std::cout << "EVENT_CONNECTION_ESTABLISHED: true" << std::endl;
}
if (j_res["EVENT_CONNECTION_ABORT"].get<bool>() == true)
{
std::cout << "EVENT_CONNECTION_ABORT: true" << std::endl;
}
if (j_res["EVENT_NEW_PARAM_DATA"].get<bool>() == true)
{
answer_str.clear();
std::cout << "EVENT_NEW_PARAM_DATA: true" << std::endl;
*p_output_stream_ << j_get_echo_param_gain.dump() << std::endl;
std::getline( *p_input_stream_.get( ), answer_str);
json j_answ_par = json::parse(answer_str);
std::vector<uint8_t> in;
for (auto& jx : j_answ_par["result"])
{
uint32_t module_id;
uint32_t submodule_id;
uint32_t param_idx;
module_id = jx["module_id"].get<int>();
submodule_id = jx["submodule_id"].get<int>();
param_idx = jx["param_index"].get<int>();
if ((module_id != ECHO_MODULE_ID) || (submodule_id != ECHO_SUBMOD_ID))
{
std::cout << "Error id" << std::endl;
continue;
}
if (param_idx != ECHO_PARAMETER_GAIN_IDX)
{
std::cout << "Error param idx" << std::endl;
continue;
}
in.clear();
in = jx["data"]["bytes"].get<std::vector<uint8_t>>();
if (in.size() == 4)
{
std::copy(in.begin(), in.end(), (uint8_t*)&Echo_Gain);
}
endian_convert_32((uint8_t*)&Echo_Gain);
std::cout << "Echo_Gain = " << Echo_Gain << std::endl;
}
}
if (j_res["EVENT_NEW_CYCLIC_DATA"].get<bool>() == true)
{
static uint32_t value_i = 0;
static float value_f = 0.0;
p_profinet_data->Events.clear_flags(ProfinetEvent::EVENT_NEW_CYCLIC_DATA);
answer_str.clear();
*p_output_stream_ << j_get_echo_data.dump() << std::endl;
std::getline( *p_input_stream_.get( ), answer_str);
json j_answ_data = json::parse(answer_str);
std::vector<uint8_t> in;
///Читаем данные от ПЛК
echo_submod_ptr->out_data.Read(0, Echo_outCycData.mem, echo_submod_ptr->cyc_outdata_len);
/// Конвертируем в литл эндиан
endian_convert_32((uint8_t*)&Echo_outCycData.data.data_i);
endian_convert_32((uint8_t*)&Echo_outCycData.data.data_f);
if (value_i != Echo_outCycData.data.data_i)
for (auto& jx : j_answ_data["result"])
{
value_i = Echo_outCycData.data.data_i;
uint32_t module_id;
uint32_t submodule_id;
std::cout << "New data_i from PLC: " << value_i << std::endl;
module_id = jx["module_id"].get<int>();
submodule_id = jx["submodule_id"].get<int>();
if ((module_id != ECHO_MODULE_ID) || (submodule_id != ECHO_SUBMOD_ID))
{
std::cout << "Error id" << std::endl;
continue;
}
in = jx["data"]["bytes"].get<std::vector<uint8_t>>();
if (in.size() == 8)
{
std::copy(in.begin(), in.end(), Echo_outCycData.mem);
}
/// Конвертируем в литл эндиан
endian_convert_32((uint8_t*)&Echo_outCycData.data.data_i);
endian_convert_32((uint8_t*)&Echo_outCycData.data.data_f);
if (value_i != Echo_outCycData.data.data_i)
{
value_i = Echo_outCycData.data.data_i;
std::cout << "New data_i from PLC: " << value_i << std::endl;
}
if (value_f != Echo_outCycData.data.data_f)
{
value_f = Echo_outCycData.data.data_f;
std::cout << "New data_f from PLC: " << value_f << std::endl;
}
///Подготавливаем данные для ПЛК
Echo_inpCycData.data.data_i = Echo_Gain * Echo_outCycData.data.data_i;
Echo_inpCycData.data.data_f = Echo_Gain * Echo_outCycData.data.data_f;
///Конвертируем в биг эндиан
endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_i);
endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_f);
json j_set_data;
std::vector<uint8_t> in_data;
in_data.insert(in_data.end(), &Echo_inpCycData.mem[0], &Echo_inpCycData.mem[8]);
j_set_data["module_id"] = module_id;
j_set_data["submodule_id"] = submodule_id;
j_set_data["data"] = json::binary(in_data);
j_set_echo_data["params"].push_back(j_set_data);
///Записываем данные
*p_output_stream_ << j_set_echo_data.dump() << std::endl;
answer_str.clear();
///Ждем ответ
std::getline( *p_input_stream_.get( ), answer_str);
}
if (value_f != Echo_outCycData.data.data_f)
{
value_f = Echo_outCycData.data.data_f;
std::cout << "New data_f from PLC: " << value_f << std::endl;
}
///Подготавливаем данные для ПЛК
Echo_inpCycData.data.data_i = Echo_Gain * Echo_outCycData.data.data_i;
Echo_inpCycData.data.data_f = Echo_Gain * Echo_outCycData.data.data_f;
///Конвертируем в биг эндиан
endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_i);
endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_f);
///Отправляем данные для ПЛК
echo_submod_ptr->inp_data.Write(0, Echo_inpCycData.mem, echo_submod_ptr->cyc_indata_len);
}
if (events & ProfinetEvent::EVENT_NEW_PARAM_DATA)
{
p_profinet_data->Events.clear_flags(ProfinetEvent::EVENT_NEW_PARAM_DATA);
std::cout << "Event: New parameter data: " << std::endl;
//Читаем параметр записанный ПЛК при установлении связи
echo_param_gain_ptr->data.Read(0, (uint8_t*)&Echo_Gain, echo_param_gain_ptr->length);
/// Конвертируем в литл эндиан
endian_convert_32((uint8_t*)&Echo_Gain);
std::cout << "Echo_Gain = " << std::to_string(Echo_Gain) << endl;
}
if (events & ProfinetEvent::EVENT_CONNECTION_ABORT)
{
p_profinet_data->Events.clear_flags(ProfinetEvent::EVENT_CONNECTION_ABORT);
std::cout << "Connection Aborted" << std::endl;
j_set_echo_data["params"].clear();
}
}
return 0;
}

View File

@ -0,0 +1,143 @@
#include "profinet_shared_data_client.hpp"
#include <iostream>
#include <thread>
#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;
}
ProfinetSharedDataClient shared_data;
ProfinetData_Map * p_profinet_data;
uint32_t event_mask = ( ProfinetEvent::EVENT_CONNECTION_ESTABLISHED |
ProfinetEvent::EVENT_NEW_CYCLIC_DATA |
ProfinetEvent::EVENT_NEW_PARAM_DATA |
ProfinetEvent::EVENT_CONNECTION_ABORT );
int main(int argc, char * argv[])
{
p_profinet_data = shared_data.Connect("profinet_shared_data");
if (p_profinet_data == nullptr)
{
return 0;
}
/// Берем указатель на данные подмодуля в разделяемой памяти
auto echo_submod_ptr = shared_data.getSubmodule(ECHO_MODULE_ID, ECHO_SUBMOD_ID);
if (echo_submod_ptr->id != ECHO_SUBMOD_ID)
{
return 0;
}
/// Берем указатель на данные параметра подмодуля в разделяемолй памяти
auto echo_param_gain_ptr = shared_data.getSubmoduleParameter(ECHO_MODULE_ID, ECHO_SUBMOD_ID, ECHO_PARAMETER_GAIN_IDX);
if (echo_param_gain_ptr->index != ECHO_PARAMETER_GAIN_IDX)
{
return 0;
}
std::cout << "Strarting..." << std::endl;
for(;;)
{
uint32_t events = p_profinet_data->Events.wait_flags(event_mask);
if (events & ProfinetEvent::EVENT_CONNECTION_ESTABLISHED)
{
p_profinet_data->Events.clear_flags(ProfinetEvent::EVENT_CONNECTION_ESTABLISHED);
std::cout << "Event: PLC connection established" << std::endl;
}
if (events & ProfinetEvent::EVENT_NEW_CYCLIC_DATA)
{
static uint32_t value_i = 0;
static float value_f = 0.0;
p_profinet_data->Events.clear_flags(ProfinetEvent::EVENT_NEW_CYCLIC_DATA);
///Читаем данные от ПЛК
echo_submod_ptr->out_data.Read(0, Echo_outCycData.mem, echo_submod_ptr->cyc_outdata_len);
/// Конвертируем в литл эндиан
endian_convert_32((uint8_t*)&Echo_outCycData.data.data_i);
endian_convert_32((uint8_t*)&Echo_outCycData.data.data_f);
if (value_i != Echo_outCycData.data.data_i)
{
value_i = Echo_outCycData.data.data_i;
std::cout << "New data_i from PLC: " << value_i << std::endl;
}
if (value_f != Echo_outCycData.data.data_f)
{
value_f = Echo_outCycData.data.data_f;
std::cout << "New data_f from PLC: " << value_f << std::endl;
}
///Подготавливаем данные для ПЛК
Echo_inpCycData.data.data_i = Echo_Gain * Echo_outCycData.data.data_i;
Echo_inpCycData.data.data_f = Echo_Gain * Echo_outCycData.data.data_f;
///Конвертируем в биг эндиан
endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_i);
endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_f);
///Отправляем данные для ПЛК
echo_submod_ptr->inp_data.Write(0, Echo_inpCycData.mem, echo_submod_ptr->cyc_indata_len);
}
if (events & ProfinetEvent::EVENT_NEW_PARAM_DATA)
{
p_profinet_data->Events.clear_flags(ProfinetEvent::EVENT_NEW_PARAM_DATA);
std::cout << "Event: New parameter data: " << std::endl;
//Читаем параметр записанный ПЛК при установлении связи
echo_param_gain_ptr->data.Read(0, (uint8_t*)&Echo_Gain, echo_param_gain_ptr->length);
/// Конвертируем в литл эндиан
endian_convert_32((uint8_t*)&Echo_Gain);
std::cout << "Echo_Gain = " << std::to_string(Echo_Gain) << endl;
}
if (events & ProfinetEvent::EVENT_CONNECTION_ABORT)
{
p_profinet_data->Events.clear_flags(ProfinetEvent::EVENT_CONNECTION_ABORT);
std::cout << "Connection Aborted" << std::endl;
}
}
}

View File

@ -0,0 +1,7 @@
{
"app_settings":
{
"app_out_pipe_name": "/home/svad/Projects/profinet_io_dev/profinet_io_dev_inp",
"app_inp_pipe_name": "/home/svad/Projects/profinet_io_dev/profinet_io_dev_out"
}
}

View File

@ -5,9 +5,10 @@ bool App::Init(std::string profinet_config_file)
{
ProfinetSettings profinet_settings;
ProfinetDeviceSettings profinet_dev_settings;
ProfinetPipesSettings profinet_pipe_settings;
/// Читаем настройки из файла
if (!programconf_getProfinetSettings("program_configure.json", profinet_settings))
if (!programconf_getProfinetSettings("program_configure.json", profinet_settings, profinet_pipe_settings))
{
return false;
}
@ -27,11 +28,16 @@ bool App::Init(std::string profinet_config_file)
return false;
}
/// Создаем именованые каналы
pipes_.Init(profinet_pipe_settings);
/**
* Циклические данные устройства передаются с определенным периодом, сообщения от контроллера при этом не требуются.
*/
/// Запуск потока Profinet
profinet_.Start();
/// Запуск потока чтения запросов по pipe
pipes_.Start(&profinet_);
return true;
}

View File

@ -1,7 +1,8 @@
#pragma once
#include "./profinet/profinet.hpp"
#include "./interprocess/profinet_shared_data.hpp"
#include "./interprocess/shared_memory/profinet_shared_data.hpp"
#include "./interprocess/pipes/profinet_pipes.hpp"
#include <string>
class App {
@ -9,6 +10,7 @@ public:
bool Init(std::string profinet_config_file);
void Run();
private:
ProfinetPipes pipes_;
ProfinetSharedData shared_data_;
Profinet profinet_;
};

View File

@ -12,6 +12,8 @@
#include <boost/interprocess/managed_shared_memory.hpp>
using namespace boost::interprocess;
using json = nlohmann::json;
using string = std::string;
@ -34,7 +36,7 @@ static bool programconf_readfile(json& j, std::string& file_path)
return true;
}
bool programconf_getProfinetSettings(std::string file_path, ProfinetSettings& pn_sett)
bool programconf_getProfinetSettings(std::string file_path, ProfinetSettings& pn_sett, ProfinetPipesSettings& pn_pipes_sett)
{
json j;
@ -47,6 +49,10 @@ bool programconf_getProfinetSettings(std::string file_path, ProfinetSettings& pn
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>();
pn_pipes_sett.enable = j["ProfinetSettings"]["enable_pipes"].get<bool>();
pn_pipes_sett.out_pipe_name = j["ProfinetSettings"]["out_pipe_name"].get<string>();
pn_pipes_sett.inp_pipe_name = j["ProfinetSettings"]["inp_pipe_name"].get<string>();
return true;
}

View File

@ -7,8 +7,9 @@
#include "profinet_module.hpp"
#include "profinet_data_map.hpp"
#include "profinet_shared_data.hpp"
#include "profinet_pipes.hpp"
bool programconf_getProfinetSettings(std::string file_path, ProfinetSettings& pn_sett);
bool programconf_getProfinetSettings(std::string file_path, ProfinetSettings& pn_sett, ProfinetPipesSettings& pn_pipes_sett);
bool programconf_getProfinetDeviceSettings(std::string file_path, ProfinetDeviceSettings& pn_sett,
ProfinetData_Map * p_data_map, ProfinetSharedData& shared_data);

View File

@ -1,9 +1,3 @@
set(INC_DIRS ${INC_DIRS} ./interprocess)
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_STATIC_RUNTIME ON)
find_package(Boost 1.74.0)
if(Boost_FOUND)
set(INC_DIRS ${INC_DIRS} ${Boost_INCLUDE_DIRS})
@ -14,4 +8,5 @@ endif()
message("Boost dir: " ${Boost_INCLUDE_DIRS})
message("Boost libs:" ${Boost_LIBRARIES})
set(SRC_FILES ${SRC_FILES} ./interprocess/profinet_shared_data.cpp)
include(./interprocess/shared_memory/shared_memory.cmake)
include(./interprocess/pipes/pipes.cmake)

View File

@ -0,0 +1,135 @@
//
// Created by sozonov on 18.10.2021.
//
#include "CreatePipes.h"
#include <iostream>
#include <memory>
#include "boost/process/pipe.hpp"
/*! Создание в операционной системе именнованого канала, заданого имени и заданого направления.<br>
* Обеспечена кросплатформенность данной функции (Windows/Linux).
* @param path Назавние именновоного канала.
* @param mode Направление работы именнованного канала: <br> std::ios_base::in / std::ios_base::out
* @return Именнованый канал, привязаный к библиотеке boost.
*/
boost::process::pipe build_named_io_pipe( std::filesystem::path path, std::ios_base::openmode mode );
std::tuple< std::unique_ptr< std::istream >,
std::unique_ptr< std::ostream > > itr::create_pipes( std::filesystem::path input_path,
std::filesystem::path output_path,
std::ostream & info ) {
//todo: перед созданием пайпов проверять валидность пути и выводить инфу в поток &info
info << "Create pipes: " << input_path << ", " << output_path << "..." << std::endl;
auto istream = std::make_unique<::boost::process::ipstream>( std::move( build_named_io_pipe( input_path,
std::ios_base::in ) ) );
auto ostream = std::make_unique<::boost::process::opstream>( std::move( build_named_io_pipe( output_path,
std::ios_base::out ) ) );
#if (defined (_WIN32) || defined (_WIN64))
::boost::winapi::ConnectNamedPipe( istream->pipe().native_source(), nullptr );
::boost::winapi::ConnectNamedPipe( ostream->pipe().native_sink(), nullptr );
#endif
info << "Pipes " << input_path << " and " << output_path << " are connected!" << std::endl;
return {
std::move(istream),
std::move(ostream)
};
};
boost::process::pipe build_named_io_pipe( std::filesystem::path path, std::ios_base::openmode mode ) {
#if (defined (_WIN32) || defined (_WIN64))
if( mode == std::ios_base::in ) {
boost::winapi::HANDLE_ input_source = boost::winapi::create_named_pipe(
path.string().c_str(),
boost::winapi::PIPE_ACCESS_INBOUND_, //write flag
boost::winapi::PIPE_TYPE_BYTE_ | boost::winapi::PIPE_READMODE_BYTE_ | boost::winapi::PIPE_WAIT_ ,
4,
8192,
8192,
0,
nullptr );
return boost::process::pipe( input_source, boost::winapi::INVALID_HANDLE_VALUE_ );
}
if( mode == std::ios_base::out ) {
boost::winapi::HANDLE_ output_source = boost::winapi::create_named_pipe(
path.string().c_str(),
boost::winapi::PIPE_ACCESS_OUTBOUND_, //write flag
boost::winapi::PIPE_TYPE_BYTE_ | ::boost::winapi::PIPE_READMODE_BYTE_ |
boost::winapi::PIPE_WAIT_,
4,
8192,
8192,
0,
nullptr);
return boost::process::pipe( boost::winapi::INVALID_HANDLE_VALUE_ , output_source);
}
return boost::process::pipe( boost::winapi::INVALID_HANDLE_VALUE_ ,boost::winapi::INVALID_HANDLE_VALUE_ );
#elif (defined (LINUX) || defined (__linux__))
if( mode == std::ios_base::in ) {
if( not std::filesystem::exists(path) ) {
auto fifo = mkfifo(path.c_str(), 0666 );
if (fifo != 0)
boost::process::detail::throw_last_error("mkfifo() failed");
std::cout << "Input fifo created: " << path << std::endl;
}
else
{
std::cout << "Input fifo exists: " << path << std::endl;
}
int read_fd = open(path.c_str(), O_RDONLY );
if( read_fd == -1 )
boost::process::detail::throw_last_error();
return boost::process::pipe( read_fd, -1 );
}
if( mode == std::ios_base::out ) {
if( not std::filesystem::exists(path) ) {
auto fifo = mkfifo(path.c_str(), 0666 );
if (fifo != 0)
boost::process::detail::throw_last_error("mkfifo() failed");
std::cout << "Output fifo created: " << path << std::endl;
}
else
{
std::cout << "Output fifo exists: " << path << std::endl;
}
int write_fd = open(path.c_str(), O_WRONLY );
if (write_fd == -1)
boost::process::detail::throw_last_error();
return boost::process::pipe( -1, write_fd );
}
return boost::process::pipe( -1, -1 );
#endif
};

View File

@ -0,0 +1,25 @@
//
// Created by sozonov on 18.10.2021.
//
#pragma once
#include <filesystem>
namespace itr {
/*! Создание пары именнованых каналов (на прием и передачу данных). <br>
* Данная функция является блокирующей, до подключения к именованым каналам. <br>
* После успешного подключения, в потоко info будет переденна информация об этом. <br>
* //todo: В функции реализована проверка валидности пути, информация об этом предается в info <br>
*
* @param to_input Название именнованного канала на приём данных.
* @param to_output Название именнованного канала на передачу данных.
* @param info Поток вывода информации.
* @return Кортеж с уникальными указатаелями на потоки ввода/вывода для каждого именнованого канала.
*/
std::tuple<
std::unique_ptr<std::istream>,
std::unique_ptr<std::ostream>
> create_pipes( std::filesystem::path to_input, std::filesystem::path to_output, std::ostream & info );
}

View File

@ -0,0 +1,211 @@
#include "json_rpc_handler.hpp"
#include <vector>
#include <cstdint>
using json = nlohmann::json;
using string = std::string;
void json_rpc_set_status(ProfinetIface::ErrorCode result, string& out)
{
switch(result)
{
case ProfinetIface::ERR_SUCCESS:
{
out = "OK";
}
break;
case ProfinetIface::ERR_MODULE_DOES_NOT_EXIST:
{
out = "WRONG_MODULE_ID";
}
break;
case ProfinetIface::ERR_SUBMODULE_DOES_NOT_EXIST:
{
out = "WRONG_SUBMODULE_ID";
}
break;
case ProfinetIface::ERR_PARAM_DOES_NOT_EXIST:
{
out = "WRONG_PARAM_INDEX";
}
break;
case ProfinetIface::ERR_WRONG_DATA_LENGTH:
{
out = "WRONG_DATA_LENGTH";
}
break;
}
}
static void json_readFlags(json& j_in, json& j_out, ProfinetIface * p_profinet)
{
uint32_t events = p_profinet->getEvents();
j_out["result" ]["EVENT_CONNECTION_ESTABLISHED"] = static_cast<bool>(events & ProfinetIface::EVENT_CONNECTION_ESTABLISHED);
j_out["result" ]["EVENT_NEW_CYCLIC_DATA"] = static_cast<bool>(events & ProfinetIface::EVENT_NEW_CYCLIC_DATA);
j_out["result" ]["EVENT_NEW_PARAM_DATA"] = static_cast<bool>(events & ProfinetIface::EVENT_NEW_PARAM_DATA);
j_out["result" ]["EVENT_CONNECTION_ABORT"] = static_cast<bool>(events & ProfinetIface::EVENT_CONNECTION_ABORT);
p_profinet->clearEvents(events);
}
static bool json_writeCyclicIoData(json& j_in, json& j_out, ProfinetIface * p_profinet)
{
if (!j_in.contains("params"))
return false;
/// Запись
j_out["result"] = json::array();
for (auto& param : j_in["params"])
{
json j_arr;
uint32_t module_id = param["module_id"].get<int>();
uint32_t submodule_id = param["submodule_id"].get<int>();
std::vector<uint8_t> data = param["data"]["bytes"].get<std::vector<uint8_t>>();
auto result = p_profinet->putInputCyclicData(module_id, submodule_id, data);
///
j_arr["module_id"] = module_id;
j_arr["submodule_id"] = submodule_id;
string status;
json_rpc_set_status(static_cast<ProfinetIface::ErrorCode>(result), status);
j_arr["status"] = status;
j_out["result"].push_back(j_arr);
}
return true;
}
static bool json_readCyclicIoData(json& j_in, json& j_out, ProfinetIface * p_profinet)
{
if (!j_in.contains("params"))
return false;
/// Запись
j_out["result"] = json::array();
for (auto& param : j_in["params"])
{
json j_arr;
uint32_t module_id = param["module_id"].get<int>();
uint32_t submodule_id = param["submodule_id"].get<int>();
std::vector<uint8_t> data;
auto result = p_profinet->getOutputCyclicData(module_id, submodule_id, data);
///
j_arr["module_id"] = module_id;
j_arr["submodule_id"] = submodule_id;
string status;
json_rpc_set_status(static_cast<ProfinetIface::ErrorCode>(result), status);
j_arr["status"] = status;
if (result == ProfinetIface::ERR_SUCCESS)
{
j_arr["data"] = json::binary(data);
}
else
{
j_arr["data"];
}
j_out["result"].push_back(j_arr);
}
return true;
}
static bool json_readSubmoduleParameter(json& j_in, json& j_out, ProfinetIface * p_profinet)
{
if (!j_in.contains("params"))
return false;
/// Запись
j_out["result"] = json::array();
for (auto& param : j_in["params"])
{
json j_arr;
uint32_t module_id = param["module_id"].get<int>();
uint32_t submodule_id = param["submodule_id"].get<int>();
uint32_t param_idx = param["param_index"].get<int>();
std::vector<uint8_t> data;
auto result = p_profinet->getSubmoduleParam(module_id, submodule_id, param_idx, data);
///
j_arr["module_id"] = module_id;
j_arr["submodule_id"] = submodule_id;
j_arr["param_index"] = param_idx;
string status;
json_rpc_set_status(static_cast<ProfinetIface::ErrorCode>(result), status);
j_arr["status"] = status;
if (result == ProfinetIface::ERR_SUCCESS)
{
j_arr["data"] = json::binary(data);
}
else
{
j_arr["data"];
}
j_out["result"].push_back(j_arr);
}
return true;
}
bool json_rpc_handler(std::string& input, std::string& output, ProfinetIface * p_profinet)
{
json j_in = json::parse(input);
json j_out;
if (j_in.is_null())
return false;
if (!j_in.contains("jsonrpc"))
return false;
if (!j_in.contains("id"))
return false;
int rpc_id = j_in["id"].get<int>();
if (!j_in.contains("method"))
return false;
string method = j_in["method"].get<string>();
j_out["jsonrpc"] = "2.0";
if (method == "readFlags")
{
json_readFlags(j_in, j_out, p_profinet);
}
else if (method == "writeCyclicIoData")
{
json_writeCyclicIoData(j_in, j_out, p_profinet);
}
else if (method == "readCyclicIoData")
{
json_readCyclicIoData(j_in, j_out, p_profinet);
}
else if (method == "readSubmoduleParameter")
{
json_readSubmoduleParameter(j_in, j_out, p_profinet);
}
j_out["method"] = method;
j_out["id"] = rpc_id;
output = j_out.dump();
return true;
}

View File

@ -0,0 +1,11 @@
#pragma once
#include "../../nlohmann_json/json.hpp"
#include "../../profinet/profinet_iface.hpp"
#include <string>
bool json_rpc_handler(std::string& input, std::string& output, ProfinetIface * p_profinet);

View File

@ -0,0 +1,219 @@
1. Метод записи входных циклических данных(данные к ПЛК):
-->
{
"jsonrpc": "2.0",
"method": "writeCyclicIoData",
"params":
[
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"data":
{
bytes: [1, 2, 3...]
}
},
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"data":
{
bytes: [1, 2, 3...]
}
},ProfinetIface::
:
:
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"data":
{
bytes: [1, 2, 3...]
}
},
]
"id": 0
}
<--
{
"jsonrpc": "2.0",
"method": "writeCyclicIoData"
"result":
[
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"status": "OK", "WRONG_MODULE_ID", "WRONG_SUBMODULE_ID", "WRONG_DATA_LENGTH"
},
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"status": "OK", "WRONG_MODULE_ID", "WRONG_SUBMODULE_ID", "WRONG_DATA_LENGTH"
},
:
:
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"status": "OK", "WRONG_MODULE_ID", "WRONG_SUBMODULE_ID", "WRONG_DATA_LENGTH"
},
]
"id": 0
}
2. Метод чтения выходных циклических данных(данные от ПЛК):
-->
{
"jsonrpc": "2.0",
"method": "readCyclicIoData",
"params":
[
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
},
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
},
:
:
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
},
]
"id": 0
}
<--
{
"jsonrpc": "2.0",
"method": "readCyclicIoData"
"result":
[
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"status": "OK", "WRONG_MODULE_ID", "WRONG_SUBMODULE_ID"
"data":
{
bytes: [1, 2, 3...]
} или null в, если status!=ОК
},
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"status": "OK", "WRONG_MODULE_ID", "WRONG_SUBMODULE_ID"
"data":
{
bytes: [1, 2, 3...]
} или null в, если status!=ОК
},
:
:
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"status": "OK", "WRONG_MODULE_ID", "WRONG_SUBMODULE_ID"
"data":
{
bytes: [1, 2, 3...]
} или null в, если status!=ОК
},
]
"id": 0
}
3. Метод чтения параметров подмодуля:
Устройство может только читать параметры. Записывает параметры только Profinet контроллер(ПЛК)
после установления связи и перед началом циклического обмена.
-->
{
"jsonrpc": "2.0",
"method": "readSubmoduleParameter",
"params":
[
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"param_index": Индекс парамтера
},
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"param_index": Индекс парамтера
},
:
:
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"param_index": Индекс парамтера
},
]
"id": 0
}
<--
{
"jsonrpc": "2.0",
"method": "readSubmoduleParameter",
"result":
[
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"param_index": Индекс парамтера
"status": "OK", "WRONG_MODULE_ID", "WRONG_SUBMODULE_ID", "WRONG_PARAM_INDEX"
"data":
{
bytes: [1, 2, 3...]
} или null в, если status!=ОК
},
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"param_index": Индекс парамтера
"status": "OK", "WRONG_MODULE_ID", "WRONG_SUBMODULE_ID", "WRONG_PARAM_INDEX"
"data":
{
bytes: [1, 2, 3...]
} или null в, если status!=ОК
},
:
:
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"param_index": Индекс парамтера
"status": "OK", "WRONG_MODULE_ID", "WRONG_SUBMODULE_ID", "WRONG_PARAM_INDEX"
"data":
{
bytes: [1, 2, 3...]
} или null в, если status!=ОК
},
]
"id": 0
}
4. Метод чтения флагов:
-->
{
"jsonrpc": "2.0",
"method": "readFlags",
"params": null
"id": 0
}
<--
{
"jsonrpc": "2.0",
"method": "readFlags",
"result":
{
"EVENT_CONNECTION_ESTABLISHED": true/false,
"EVENT_NEW_CYCLIC_DATA": true/false,
"EVENT_NEW_PARAM_DATA": true/false,
"EVENT_CONNECTION_ABORT": true/false
}
0-й бит -
"id": 0
}

View File

@ -0,0 +1,4 @@
set(INC_DIRS ${INC_DIRS} ./interprocess/pipes)
set(SRC_FILES ${SRC_FILES} ./interprocess/pipes/json_rpc_handler.cpp)
set(SRC_FILES ${SRC_FILES} ./interprocess/pipes/CreatePipes.cpp)
set(SRC_FILES ${SRC_FILES} ./interprocess/pipes/profinet_pipes.cpp)

View File

@ -0,0 +1,69 @@
#include "profinet_pipes.hpp"
#include "CreatePipes.h"
#include "json_rpc_handler.hpp"
#include <iostream>
#include <string>
ProfinetPipes::ProfinetPipes() : p_input_stream_{nullptr},
p_output_stream_{nullptr},
listen_thread_stop_{false}
{
}
ProfinetPipes::~ProfinetPipes()
{
///Остановка потока
listen_thread_stop_.store(true, std::memory_order_relaxed);
if (listen_thread_.joinable())
{
listen_thread_.join();
}
}
void ProfinetPipes::Init( ProfinetPipesSettings& sett )
{
enable_ = sett.enable;
if (!enable_)
return;
std::filesystem::path _input_path{sett.inp_pipe_name};
std::filesystem::path _output_path{sett.out_pipe_name};
auto streams = itr::create_pipes( _input_path, _output_path, std::cout );
p_input_stream_ = std::move( std::get<0>( streams ) ) ;
p_output_stream_ = std::move( std::get<1>( streams ) );
}
void ProfinetPipes::listen_pipe(ProfinetIface * p_profinet)
{
std::string request_str;
try{
while(!listen_thread_stop_.load(std::memory_order_relaxed))
{
std::string answer_str;
std::getline( *p_input_stream_.get(), request_str);
//std::cout << "Request: " << request_str << std::endl;
json_rpc_handler(request_str, answer_str, p_profinet);
//std::cout << "Answer: " << answer_str << std::endl;
*p_output_stream_ << answer_str << std::endl;
}
}
catch ( const std::exception &ex ){
auto answer_str = ex.what();
std::cout << "Answer: " << answer_str << std::endl;
*p_output_stream_ << answer_str << std::endl;
listen_thread_stop_.store(true, std::memory_order_relaxed);
}
}
void ProfinetPipes::Start(ProfinetIface * p_profinet)
{
if (!enable_)
return;
listen_thread_ = std::move(std::thread(&ProfinetPipes::listen_pipe, this, p_profinet));
}

View File

@ -0,0 +1,40 @@
#pragma once
#include <memory>
#include <filesystem>
#include <thread>
#include <atomic>
#include <string>
#include "../../profinet/profinet_iface.hpp"
struct ProfinetPipesSettings {
bool enable;
std::string out_pipe_name;
std::string inp_pipe_name;
};
class ProfinetPipes {
public:
ProfinetPipes();
~ProfinetPipes();
void Init( ProfinetPipesSettings& sett );
void listen_pipe(ProfinetIface * p_profinet);
void Start(ProfinetIface * p_profinet);
private:
/** Поток обработки входящих данных от именнованого канала. */
std::shared_ptr< std::istream > p_input_stream_;
/** Поток для отправки данных в именнованй канал. */
std::shared_ptr< std::ostream > p_output_stream_;
std::thread listen_thread_; /// Поток в котором исполняется PeriodicOperations
std::atomic<bool> listen_thread_stop_; /// Флаг завершения потока
bool enable_;
};

View File

@ -0,0 +1,65 @@
#include "../interprocess/shared_memory/profinet_shared_data.hpp"
#include <cstring>
using namespace boost::interprocess;
/**
* @brief Расчет размера данных для выделения памяти
*
* @param data_settings
* @return uint32_t
*/
static uint32_t calc_mem_size(ProfinetData_Map& data_settings)
{
uint32_t size = 0;
for (int i = 0; i < data_settings.mods_nbr; ++i)
{
auto& submods = data_settings.p_mods[i].p_submods;
for (int j = 0; j < data_settings.p_mods[i].submods_nbr; ++j)
{
auto& submod = submods[j];
size+= submod.cyc_indata_len + submod.cyc_outdata_len;
auto& params = submod.p_params;
for (int k = 0; k < submod.params_nbr; ++k)
{
size+= params[i].length;
}
}
}
return size + sizeof(ProfinetData_Map);
}
ProfinetData_Map * ProfinetSharedData::Create(std::string mem_name)
{
/// Если память уже выделена, удаляем
shared_memory_object::remove(mem_name.c_str());
/// Для начала выделяем 65к байт. Потом вызывается функция checkMemorySize и размер моджет быть увеличен
shmem_ = managed_shared_memory{create_only, mem_name.c_str(), 65536};
mem_name_ = mem_name;
///Создаем данные в разделяемой памяти
p_profinet_data_ = shmem_.construct<ProfinetData_Map>("ProfinetData_Map")();
return p_profinet_data_;
}
void ProfinetSharedData::checkMemorySize(uint32_t required_mem_size)
{
uint32_t free_size = shmem_.get_size();
if (required_mem_size < free_size)
return;
/// Увеличиваем память
managed_shared_memory::grow(mem_name_.c_str(), required_mem_size - free_size);
/// Заново открываем память(может измениться указатель)
shmem_ = managed_shared_memory{open_only, mem_name_.c_str()};
/// Заново находим или создаем структуру ProfinetData_Map
p_profinet_data_ = shmem_.find_or_construct<ProfinetData_Map>("ProfinetData_Map")();
}

View File

@ -0,0 +1,47 @@
#pragma once
#include <string>
#include <mutex>
#include "../profinet/profinet_data_map.hpp"
#include <boost/interprocess/managed_shared_memory.hpp>
#include <iostream>
class ProfinetSharedData {
public:
~ProfinetSharedData() {
///Удаляем память
boost::interprocess::shared_memory_object::remove(mem_name_.c_str());
//std::cout << "Destruct: ProfinetSharedData: Shared data deleted" << std::endl;
}
/**
* @brief Выделяыет раздеяемую память под структуру ProfinetData_Map
*
* @param mem_name
* @return ProfinetData_Map*
*/
ProfinetData_Map * Create(std::string mem_name);
template<class T>
T * allocateData(uint32_t nbr) {
return shmem_.construct<T>(boost::interprocess::anonymous_instance)[nbr]();
}
/**
* @brief Проверяет, что размер свободной памяти не меньше чем значение required_mem_size
* Если меньше, то увеличиват память.
*
* @param required_mem_size
* @return uint32_t
*/
void checkMemorySize(uint32_t required_mem_size);
ProfinetData_Map * p_profinet_data_;
private:
boost::interprocess::managed_shared_memory shmem_;
std::string mem_name_;
};

View File

@ -0,0 +1,113 @@
#pragma once
#include "../../profinet/profinet_data_map.hpp"
#include <boost/interprocess/managed_shared_memory.hpp>
#include <iostream>
#include <map>
#include <memory>
class ProfinetSharedDataClient {
public:
/**
* @brief Подключается к выделенной разделяемой памяти
*
* @param mem_name
*/
ProfinetData_Map * Connect(std::string mem_name) {
ProfinetData_Map * out{nullptr};
try
{
shmem_ = boost::interprocess::managed_shared_memory{boost::interprocess::open_only, mem_name.c_str()};
try
{
auto mem = shmem_.find<ProfinetData_Map>("ProfinetData_Map");
out = mem.first;
///Упорядочиваем память для удобства обращения по индентификатору Profinet
collected_data = collect(out);
}
catch(const std::exception& e)
{
std::cerr << e.what() << '\n';
return nullptr;
}
}
catch(const std::exception& e)
{
std::cerr << e.what() << '\n';
return out;
}
return out;
}
/**
* @brief Возвращает указатель на данные подмодуля в разделяемой памяти
*
* @param module_id идентификатор модуля
* @param submodule_id идентификатор подмодуля
* @return ProfinetData_Submodule*
*/
ProfinetData_Submodule * getSubmodule(uint32_t module_id, uint32_t submodule_id)
{
return (*collected_data)[module_id].submods[submodule_id].p_submod;
}
/**
* @brief Возвращает указатель на данные параметра подмодуля в разделяемой памяти
*
* @param module_id идентификатор модуля
* @param submodule_id идентификатор подмодуля
* @param param_index индекс параметра
* @return ProfinetData_Parameter*
*/
ProfinetData_Parameter * getSubmoduleParameter(uint32_t module_id, uint32_t submodule_id, uint32_t param_index)
{
return (*collected_data)[module_id].submods[submodule_id].params[param_index];
}
private:
struct ProfinetSharedDataSubmodule {
ProfinetData_Submodule* p_submod;
std::map<uint32_t, ProfinetData_Parameter*> params;
};
struct ProfinetSharedDataModule {
ProfinetData_Module* p_mod;
std::map<uint32_t, ProfinetSharedDataSubmodule> submods;
};
/**
* @brief Собирает данные в словарь, можно использовать для удобства обращения к данным по идентификатору
*
* @param p_data_map
* @return std::shared_ptr<std::map<uint32_t, ProfinetSharedDataModule>>
*/
static std::shared_ptr<std::map<uint32_t, ProfinetSharedDataModule>> collect(ProfinetData_Map * p_data_map) {
std::shared_ptr<std::map<uint32_t, ProfinetSharedDataModule>> p_map(new std::map<uint32_t, ProfinetSharedDataModule>);
for (int i = 0; i < p_data_map->mods_nbr; ++i)
{
auto& mod = (*p_map)[p_data_map->p_mods[i].id];
mod.p_mod = &p_data_map->p_mods[i];
for (int j = 0; j < p_data_map->p_mods[i].submods_nbr; ++j)
{
auto& submod = mod.submods[p_data_map->p_mods[i].p_submods[j].id];
submod.p_submod = &p_data_map->p_mods[i].p_submods[j];
for (int k = 0; k < p_data_map->p_mods[i].p_submods[j].params_nbr; ++k)
{
submod.params[p_data_map->p_mods[i].p_submods[j].p_params[k].index] = &p_data_map->p_mods[i].p_submods[j].p_params[k];
}
}
}
return p_map;
}
private:
boost::interprocess::managed_shared_memory shmem_;
std::shared_ptr<std::map<uint32_t, ProfinetSharedDataModule>> collected_data; /// Упорядоченная структура указателей на разделяемую память
};

View File

@ -0,0 +1,2 @@
set(INC_DIRS ${INC_DIRS} ./interprocess/shared_memory)
set(SRC_FILES ${SRC_FILES} ./interprocess/shared_memory/profinet_shared_data.cpp)

View File

@ -6,8 +6,6 @@
#include "program_config.hpp"
#include "app.hpp"
#include "./interprocess/profinet_shared_data.hpp"
App app;
int main(int argc, char * argv[])

View File

@ -659,6 +659,70 @@ void Profinet::Start()
m_periodic_thread = std::move(std::thread(&Profinet::PeriodicOperations, this));
}
int Profinet::getOutputCyclicData(uint32_t module_id,
uint32_t submodule_id,
std::vector<uint8_t>& data)
{
if (!m_modules.contains(module_id))
return ErrorCode::ERR_MODULE_DOES_NOT_EXIST;
auto submodule = m_modules[module_id]->getSubmodulePtr(submodule_id);
if (submodule == nullptr)
return ErrorCode::ERR_SUBMODULE_DOES_NOT_EXIST;
submodule->readOutputData(data);
return ErrorCode::ERR_SUCCESS;
}
int Profinet::putInputCyclicData(uint32_t module_id,
uint32_t submodule_id,
std::vector<uint8_t>& data)
{
if (!m_modules.contains(module_id))
return ErrorCode::ERR_MODULE_DOES_NOT_EXIST;
auto submodule = m_modules[module_id]->getSubmodulePtr(submodule_id);
if (submodule == nullptr)
return ErrorCode::ERR_SUBMODULE_DOES_NOT_EXIST;
if (data.size() != submodule->m_data_cfg.insize)
return ErrorCode::ERR_WRONG_DATA_LENGTH;
submodule->writeInputData(data);
return ErrorCode::ERR_SUCCESS;
}
int Profinet::getSubmoduleParam(uint32_t module_id,
uint32_t submodule_id,
uint32_t param_id,
std::vector<uint8_t>& data)
{
if (!m_modules.contains(module_id))
return ErrorCode::ERR_MODULE_DOES_NOT_EXIST;
auto submodule = m_modules[module_id]->getSubmodulePtr(submodule_id);
if (submodule == nullptr)
return ErrorCode::ERR_SUBMODULE_DOES_NOT_EXIST;
auto param = submodule->getParameterPtr(param_id);
if (param == nullptr)
return ErrorCode::ERR_PARAM_DOES_NOT_EXIST;
uint8_t buff[param->length];
param->readParameter(buff);
data.insert(data.end(), &buff[0], &buff[param->length]);
return ErrorCode::ERR_SUCCESS;
}
Profinet::~Profinet()
{
///Остановка потока

View File

@ -18,17 +18,11 @@
#include "../flags/flags_iface.hpp"
#include "profinet_data_map.hpp"
#include "profinet_iface.hpp"
class Profinet {
class Profinet : public ProfinetIface {
public:
enum Events
{
EVENT_CONNECTION_ESTABLISHED = 0x00000001, /// Установлено соединение с ПЛК
EVENT_NEW_CYCLIC_DATA = 0x00000002, /// Получение новых циклических данных от ПЛК
EVENT_NEW_PARAM_DATA = 0x00000004, /// Новая запись параметра от ПЛК
EVENT_CONNECTION_ABORT = 0x00000008
};
Profinet();
~Profinet();
@ -158,8 +152,23 @@ public:
uint32_t EventWait(uint32_t mask) { return p_events_->wait_flags(mask); }
void EventClear(uint32_t mask) { p_events_->clear_flags(mask); }
virtual void clearEvents(uint32_t mask) override { p_events_->clear_flags(mask); }
virtual uint32_t getEvents() const override { return p_events_->get_flags(); }
virtual int getOutputCyclicData(uint32_t module_id,
uint32_t submodule_id,
std::vector<uint8_t>& data) override;
virtual int putInputCyclicData(uint32_t module_id,
uint32_t submodule_id,
std::vector<uint8_t>& data) override;
virtual int getSubmoduleParam(uint32_t module_id,
uint32_t submodule_id,
uint32_t param_id,
std::vector<uint8_t>& data) override;
private:
ProfinetServiceData m_pnet_data;

View File

@ -0,0 +1,73 @@
#pragma once
#include <cstdint>
#include <vector>
class ProfinetIface {
public:
enum ErrorCode {
ERR_SUCCESS = 0,
ERR_MODULE_DOES_NOT_EXIST = -1,
ERR_SUBMODULE_DOES_NOT_EXIST = -2,
ERR_PARAM_DOES_NOT_EXIST = -3,
ERR_WRONG_DATA_LENGTH = -4
};
enum Events
{
EVENT_CONNECTION_ESTABLISHED = 0x00000001, /// Установлено соединение с ПЛК
EVENT_NEW_CYCLIC_DATA = 0x00000002, /// Получение новых циклических данных от ПЛК
EVENT_NEW_PARAM_DATA = 0x00000004, /// Новая запись параметра от ПЛК
EVENT_CONNECTION_ABORT = 0x00000008
};
/**
* @brief Прочитать циклические данные от ПЛК
*
* @param module_id
* @param submodule_id
* @param p_data
* @return true
* @return false
*/
virtual int getOutputCyclicData(uint32_t module_id,
uint32_t submodule_id,
std::vector<uint8_t>& data) = 0;
/**
* @brief Записать циклические данные к ПЛК
*
* @param module_id
* @param submodule_id
* @param data
* @return int
*/
virtual int putInputCyclicData(uint32_t module_id,
uint32_t submodule_id,
std::vector<uint8_t>& data) = 0;
/**
* @brief Прочитать параметры подмодуля
*
* @param module_id
* @param submodule_id
* @param param_id
* @param data
* @return int
*/
virtual int getSubmoduleParam(uint32_t module_id,
uint32_t submodule_id,
uint32_t param_id,
std::vector<uint8_t>& data) = 0;
/**
* @brief Прочитать флаги событий
*
* @return uint32_t
*/
virtual uint32_t getEvents() const = 0;
/**
* @brief Сбросить флаги событий
*
* @return uint32_t
*/
virtual void clearEvents(uint32_t mask) = 0;
};

View File

@ -142,4 +142,29 @@ bool ProfinetSubmodule::outputGetData( pnet_t * pnet_ptr,
}
return out;
}
void ProfinetSubmodule::readOutputData(std::vector<uint8_t>& out)
{
uint32_t size = out_data_ptr_->Size();
uint8_t buff[size];
out_data_ptr_->Read(0, buff, size);
out.insert(out.end(), &buff[0], &buff[size]);
}
bool ProfinetSubmodule::writeInputData(std::vector<uint8_t>& in)
{
uint32_t size = out_data_ptr_->Size();
uint8_t buff[size];
if (in.size() != size)
return false;
std::copy(in.begin(), in.end(), buff);
inp_data_ptr_->Write(0, buff, size);
return true;
}

View File

@ -5,6 +5,7 @@
#include <map>
#include <memory>
#include <mutex>
#include <vector>
#include "profinet_parameter.hpp"
#include "../shared_data/shared_data_iface.hpp"
@ -48,6 +49,10 @@ public:
uint16_t slot_nbr,
uint16_t subslot_nbr, bool& data_update);
void readOutputData(std::vector<uint8_t>& out);
bool writeInputData(std::vector<uint8_t>& in);
bool isDataUpdated() {
return data_updated_;
};

View File

@ -4,6 +4,9 @@
"ticks_us": 1000,
"cyclic_ms": 100,
"eth_dev_name": "enp6s1",
"profinet_device_config": "profinet_device_configure.json"
"profinet_device_config": "profinet_device_configure.json",
"enable_pipes": true,
"out_pipe_name": "/home/svad/Projects/profinet_io_dev/profinet_io_dev_out",
"inp_pipe_name": "/home/svad/Projects/profinet_io_dev/profinet_io_dev_inp"
}
}