dev(UML-981): Переделал для взаимодействия по pipe
This commit is contained in:
parent
a3c62f54e5
commit
d1ade982e6
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
# игнорирование папок build
|
||||
/src/build
|
||||
/profinet_test/sample_app/build
|
||||
/profinet_test/sample_app_echo/build
|
||||
*.bin
|
||||
|
||||
@ -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}
|
||||
)
|
||||
|
||||
|
||||
137
profinet_test/sample_app_echo/CreatePipes.cpp
Normal file
137
profinet_test/sample_app_echo/CreatePipes.cpp
Normal 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
|
||||
};
|
||||
25
profinet_test/sample_app_echo/CreatePipes.h
Normal file
25
profinet_test/sample_app_echo/CreatePipes.h
Normal 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 );
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
143
profinet_test/sample_app_echo/main.cpp.backup
Normal file
143
profinet_test/sample_app_echo/main.cpp.backup
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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_;
|
||||
};
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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)
|
||||
135
src/interprocess/pipes/CreatePipes.cpp
Normal file
135
src/interprocess/pipes/CreatePipes.cpp
Normal 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
|
||||
};
|
||||
25
src/interprocess/pipes/CreatePipes.h
Normal file
25
src/interprocess/pipes/CreatePipes.h
Normal 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 );
|
||||
}
|
||||
|
||||
211
src/interprocess/pipes/json_rpc_handler.cpp
Normal file
211
src/interprocess/pipes/json_rpc_handler.cpp
Normal 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;
|
||||
}
|
||||
11
src/interprocess/pipes/json_rpc_handler.hpp
Normal file
11
src/interprocess/pipes/json_rpc_handler.hpp
Normal 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);
|
||||
|
||||
|
||||
|
||||
219
src/interprocess/pipes/json_rpc_protocol.txt
Normal file
219
src/interprocess/pipes/json_rpc_protocol.txt
Normal 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
|
||||
}
|
||||
4
src/interprocess/pipes/pipes.cmake
Normal file
4
src/interprocess/pipes/pipes.cmake
Normal 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)
|
||||
69
src/interprocess/pipes/profinet_pipes.cpp
Normal file
69
src/interprocess/pipes/profinet_pipes.cpp
Normal 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));
|
||||
}
|
||||
40
src/interprocess/pipes/profinet_pipes.hpp
Normal file
40
src/interprocess/pipes/profinet_pipes.hpp
Normal 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_;
|
||||
};
|
||||
65
src/interprocess/shared_memory/profinet_shared_data.cpp
Normal file
65
src/interprocess/shared_memory/profinet_shared_data.cpp
Normal 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")();
|
||||
}
|
||||
|
||||
47
src/interprocess/shared_memory/profinet_shared_data.hpp
Normal file
47
src/interprocess/shared_memory/profinet_shared_data.hpp
Normal 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_;
|
||||
};
|
||||
113
src/interprocess/shared_memory/profinet_shared_data_client.hpp
Normal file
113
src/interprocess/shared_memory/profinet_shared_data_client.hpp
Normal 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; /// Упорядоченная структура указателей на разделяемую память
|
||||
};
|
||||
2
src/interprocess/shared_memory/shared_memory.cmake
Normal file
2
src/interprocess/shared_memory/shared_memory.cmake
Normal 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)
|
||||
@ -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[])
|
||||
|
||||
@ -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()
|
||||
{
|
||||
///Остановка потока
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
73
src/profinet/profinet_iface.hpp
Normal file
73
src/profinet/profinet_iface.hpp
Normal 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;
|
||||
};
|
||||
@ -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;
|
||||
}
|
||||
@ -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_;
|
||||
};
|
||||
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user