dev(UML-981): Переделал для взаимодействия по pipe
This commit is contained in:
parent
a3c62f54e5
commit
d1ade982e6
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,4 +1,5 @@
|
|||||||
# игнорирование папок build
|
# игнорирование папок build
|
||||||
/src/build
|
/src/build
|
||||||
/profinet_test/sample_app/build
|
/profinet_test/sample_app/build
|
||||||
|
/profinet_test/sample_app_echo/build
|
||||||
*.bin
|
*.bin
|
||||||
|
|||||||
@ -5,11 +5,18 @@ project (PN_ECHO_TEST VERSION 0.0.1)
|
|||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
|
||||||
set(INC_DIRS ${INC_DIRS} ../../src/profinet)
|
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/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/shared_data/shared_data.cpp)
|
||||||
set(SRC_FILES ${SRC_FILES} ../../src/interprocess/profinet_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(SRC_FILES ${SRC_FILES} ./main.cpp)
|
||||||
|
|
||||||
set(Boost_USE_STATIC_LIBS ON)
|
set(Boost_USE_STATIC_LIBS ON)
|
||||||
@ -25,10 +32,13 @@ endif()
|
|||||||
message("Boost dir: " ${Boost_INCLUDE_DIRS})
|
message("Boost dir: " ${Boost_INCLUDE_DIRS})
|
||||||
message("Boost libs:" ${Boost_LIBRARIES})
|
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})
|
add_executable(pn_echo_test ./main.cpp ${SRC_FILES})
|
||||||
|
|
||||||
target_include_directories(pn_echo_test PRIVATE
|
target_include_directories(pn_echo_test PRIVATE
|
||||||
./
|
./
|
||||||
${INC_DIRS}
|
${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 <iostream>
|
||||||
|
#include "json.hpp"
|
||||||
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include "file_api.hpp"
|
||||||
|
|
||||||
#define ECHO_MODULE_ID 0x00000040 /// Идентификатор тестового модуля
|
#define ECHO_MODULE_ID 0x00000040 /// Идентификатор тестового модуля
|
||||||
#define ECHO_SUBMOD_ID 0x00000140 /// Идентификатор тестового подмодуля
|
#define ECHO_SUBMOD_ID 0x00000140 /// Идентификатор тестового подмодуля
|
||||||
@ -8,7 +12,7 @@
|
|||||||
#define ECHO_OUTPUT_DATA_SIZE 8
|
#define ECHO_OUTPUT_DATA_SIZE 8
|
||||||
#define ECHO_PARAMETER_GAIN_IDX 125 /// Индекс параметра Gain для подмодуля ECHO
|
#define ECHO_PARAMETER_GAIN_IDX 125 /// Индекс параметра Gain для подмодуля ECHO
|
||||||
|
|
||||||
using namespace std;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
uint32_t Echo_Gain = 0;
|
uint32_t Echo_Gain = 0;
|
||||||
|
|
||||||
@ -38,106 +42,220 @@ void endian_convert_32(uint8_t * p_data)
|
|||||||
p_data[3] = tmp;
|
p_data[3] = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProfinetSharedDataClient shared_data;
|
/** Поток обработки входящих данных от именнованого канала. */
|
||||||
ProfinetData_Map * p_profinet_data;
|
std::shared_ptr< std::istream > p_input_stream_;
|
||||||
|
|
||||||
uint32_t event_mask = ( ProfinetEvent::EVENT_CONNECTION_ESTABLISHED |
|
/** Поток для отправки данных в именнованй канал. */
|
||||||
ProfinetEvent::EVENT_NEW_CYCLIC_DATA |
|
std::shared_ptr< std::ostream > p_output_stream_;
|
||||||
ProfinetEvent::EVENT_NEW_PARAM_DATA |
|
|
||||||
ProfinetEvent::EVENT_CONNECTION_ABORT );
|
|
||||||
|
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
int main(int argc, char * argv[])
|
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)
|
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>()}; /// Входной канал профинет, входной канал апп
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Берем указатель на данные подмодуля в разделяемой памяти
|
auto streams = itr::create_pipes( _input_path, _output_path, std::cout );
|
||||||
auto echo_submod_ptr = shared_data.getSubmodule(ECHO_MODULE_ID, ECHO_SUBMOD_ID);
|
|
||||||
|
|
||||||
if (echo_submod_ptr->id != ECHO_SUBMOD_ID)
|
p_input_stream_ = std::move( std::get<0>( streams ) ) ;
|
||||||
{
|
p_output_stream_ = std::move( std::get<1>( streams ) );
|
||||||
return 0;
|
|
||||||
}
|
json j_get_flags;
|
||||||
/// Берем указатель на данные параметра подмодуля в разделяемолй памяти
|
j_get_flags["jsonrpc"] = "2.0";
|
||||||
auto echo_param_gain_ptr = shared_data.getSubmoduleParameter(ECHO_MODULE_ID, ECHO_SUBMOD_ID, ECHO_PARAMETER_GAIN_IDX);
|
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)
|
json j_set_echo_data;
|
||||||
{
|
|
||||||
return 0;
|
j_set_echo_data["jsonrpc"] = "2.0";
|
||||||
}
|
j_set_echo_data["method"] = "writeCyclicIoData";
|
||||||
|
j_set_echo_data["params"] = json::array();
|
||||||
std::cout << "Strarting..." << std::endl;
|
j_set_echo_data["id"] = 0;
|
||||||
|
|
||||||
|
std::cout << "Starting...." << std::endl;
|
||||||
|
std::string answer_str;
|
||||||
for(;;)
|
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 uint32_t value_i = 0;
|
||||||
static float value_f = 0.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;
|
||||||
|
|
||||||
///Читаем данные от ПЛК
|
for (auto& jx : j_answ_data["result"])
|
||||||
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;
|
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)
|
j_set_echo_data["params"].clear();
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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;
|
ProfinetSettings profinet_settings;
|
||||||
ProfinetDeviceSettings profinet_dev_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;
|
return false;
|
||||||
}
|
}
|
||||||
@ -27,11 +28,16 @@ bool App::Init(std::string profinet_config_file)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Создаем именованые каналы
|
||||||
|
pipes_.Init(profinet_pipe_settings);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Циклические данные устройства передаются с определенным периодом, сообщения от контроллера при этом не требуются.
|
* Циклические данные устройства передаются с определенным периодом, сообщения от контроллера при этом не требуются.
|
||||||
*/
|
*/
|
||||||
/// Запуск потока Profinet
|
/// Запуск потока Profinet
|
||||||
profinet_.Start();
|
profinet_.Start();
|
||||||
|
/// Запуск потока чтения запросов по pipe
|
||||||
|
pipes_.Start(&profinet_);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "./profinet/profinet.hpp"
|
#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>
|
#include <string>
|
||||||
|
|
||||||
class App {
|
class App {
|
||||||
@ -9,6 +10,7 @@ public:
|
|||||||
bool Init(std::string profinet_config_file);
|
bool Init(std::string profinet_config_file);
|
||||||
void Run();
|
void Run();
|
||||||
private:
|
private:
|
||||||
|
ProfinetPipes pipes_;
|
||||||
ProfinetSharedData shared_data_;
|
ProfinetSharedData shared_data_;
|
||||||
Profinet profinet_;
|
Profinet profinet_;
|
||||||
};
|
};
|
||||||
@ -12,6 +12,8 @@
|
|||||||
#include <boost/interprocess/managed_shared_memory.hpp>
|
#include <boost/interprocess/managed_shared_memory.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
using namespace boost::interprocess;
|
using namespace boost::interprocess;
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
using string = std::string;
|
using string = std::string;
|
||||||
@ -34,7 +36,7 @@ static bool programconf_readfile(json& j, std::string& file_path)
|
|||||||
return true;
|
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;
|
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.cyclic_ms = j["ProfinetSettings"]["cyclic_ms"].get<int>();
|
||||||
pn_sett.eth_dev_name = j["ProfinetSettings"]["eth_dev_name"].get<string>();
|
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_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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,8 +7,9 @@
|
|||||||
#include "profinet_module.hpp"
|
#include "profinet_module.hpp"
|
||||||
#include "profinet_data_map.hpp"
|
#include "profinet_data_map.hpp"
|
||||||
#include "profinet_shared_data.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,
|
bool programconf_getProfinetDeviceSettings(std::string file_path, ProfinetDeviceSettings& pn_sett,
|
||||||
ProfinetData_Map * p_data_map, ProfinetSharedData& shared_data);
|
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)
|
find_package(Boost 1.74.0)
|
||||||
if(Boost_FOUND)
|
if(Boost_FOUND)
|
||||||
set(INC_DIRS ${INC_DIRS} ${Boost_INCLUDE_DIRS})
|
set(INC_DIRS ${INC_DIRS} ${Boost_INCLUDE_DIRS})
|
||||||
@ -14,4 +8,5 @@ endif()
|
|||||||
message("Boost dir: " ${Boost_INCLUDE_DIRS})
|
message("Boost dir: " ${Boost_INCLUDE_DIRS})
|
||||||
message("Boost libs:" ${Boost_LIBRARIES})
|
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 "program_config.hpp"
|
||||||
#include "app.hpp"
|
#include "app.hpp"
|
||||||
|
|
||||||
#include "./interprocess/profinet_shared_data.hpp"
|
|
||||||
|
|
||||||
App app;
|
App app;
|
||||||
|
|
||||||
int main(int argc, char * argv[])
|
int main(int argc, char * argv[])
|
||||||
|
|||||||
@ -659,6 +659,70 @@ void Profinet::Start()
|
|||||||
m_periodic_thread = std::move(std::thread(&Profinet::PeriodicOperations, this));
|
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()
|
Profinet::~Profinet()
|
||||||
{
|
{
|
||||||
///Остановка потока
|
///Остановка потока
|
||||||
|
|||||||
@ -18,17 +18,11 @@
|
|||||||
#include "../flags/flags_iface.hpp"
|
#include "../flags/flags_iface.hpp"
|
||||||
|
|
||||||
#include "profinet_data_map.hpp"
|
#include "profinet_data_map.hpp"
|
||||||
|
#include "profinet_iface.hpp"
|
||||||
|
|
||||||
class Profinet {
|
class Profinet : public ProfinetIface {
|
||||||
public:
|
public:
|
||||||
enum Events
|
|
||||||
{
|
|
||||||
EVENT_CONNECTION_ESTABLISHED = 0x00000001, /// Установлено соединение с ПЛК
|
|
||||||
EVENT_NEW_CYCLIC_DATA = 0x00000002, /// Получение новых циклических данных от ПЛК
|
|
||||||
EVENT_NEW_PARAM_DATA = 0x00000004, /// Новая запись параметра от ПЛК
|
|
||||||
EVENT_CONNECTION_ABORT = 0x00000008
|
|
||||||
};
|
|
||||||
|
|
||||||
Profinet();
|
Profinet();
|
||||||
|
|
||||||
~Profinet();
|
~Profinet();
|
||||||
@ -158,8 +152,23 @@ public:
|
|||||||
|
|
||||||
uint32_t EventWait(uint32_t mask) { return p_events_->wait_flags(mask); }
|
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:
|
private:
|
||||||
ProfinetServiceData m_pnet_data;
|
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;
|
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 <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "profinet_parameter.hpp"
|
#include "profinet_parameter.hpp"
|
||||||
#include "../shared_data/shared_data_iface.hpp"
|
#include "../shared_data/shared_data_iface.hpp"
|
||||||
@ -48,6 +49,10 @@ public:
|
|||||||
uint16_t slot_nbr,
|
uint16_t slot_nbr,
|
||||||
uint16_t subslot_nbr, bool& data_update);
|
uint16_t subslot_nbr, bool& data_update);
|
||||||
|
|
||||||
|
void readOutputData(std::vector<uint8_t>& out);
|
||||||
|
|
||||||
|
bool writeInputData(std::vector<uint8_t>& in);
|
||||||
|
|
||||||
bool isDataUpdated() {
|
bool isDataUpdated() {
|
||||||
return data_updated_;
|
return data_updated_;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4,6 +4,9 @@
|
|||||||
"ticks_us": 1000,
|
"ticks_us": 1000,
|
||||||
"cyclic_ms": 100,
|
"cyclic_ms": 100,
|
||||||
"eth_dev_name": "enp6s1",
|
"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