dev(UML-981): Реализовано межпроцессное взаимодействие.
1. profinet_dev(сервер) выделяет именованую разделяемую память и пишет туда сигналы и данные подмодулей: параметры и данные от ПЛК. Читает оттуда данные от клиента. 2. Разработано тестовое приложение(клиент) которое подключается к именованной разделяемой памяти, получает сигналы от сервера(установление соединения, обновление данных параметров, обновление данных циклического обмена и реализует функционал эхо-модуля. 3. Пока фарш. Нужен небольшой рефакторинг.
This commit is contained in:
parent
d5c127d911
commit
3fdd61b35c
34
profinet_test/sample_app_echo/CMakeLists.txt
Normal file
34
profinet_test/sample_app_echo/CMakeLists.txt
Normal file
@ -0,0 +1,34 @@
|
||||
cmake_minimum_required (VERSION 3.0)
|
||||
|
||||
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/shared_data)
|
||||
|
||||
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} ./main.cpp)
|
||||
|
||||
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})
|
||||
else()
|
||||
message(BOOST NOT FOUND)
|
||||
endif()
|
||||
|
||||
message("Boost dir: " ${Boost_INCLUDE_DIRS})
|
||||
message("Boost libs:" ${Boost_LIBRARIES})
|
||||
|
||||
add_executable(pn_echo_test ./main.cpp ${SRC_FILES})
|
||||
|
||||
target_include_directories(pn_echo_test PRIVATE
|
||||
./
|
||||
${INC_DIRS}
|
||||
)
|
||||
|
119
profinet_test/sample_app_echo/main.cpp
Normal file
119
profinet_test/sample_app_echo/main.cpp
Normal file
@ -0,0 +1,119 @@
|
||||
#include "profinet_shared_data_client.hpp"
|
||||
#include <iostream>
|
||||
|
||||
#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");
|
||||
auto& echo_module = p_profinet_data->p_mods[0];
|
||||
if (echo_module.id != ECHO_MODULE_ID)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto& echo_submod = echo_module.p_submods[0];
|
||||
|
||||
if (echo_submod.id != ECHO_SUBMOD_ID)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto& echo_param_gain = echo_submod.p_params[0];
|
||||
|
||||
if (echo_param_gain.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)
|
||||
{
|
||||
std::cout << "New cyc data" << std::endl;
|
||||
|
||||
p_profinet_data->Events.clear_flags(ProfinetEvent::EVENT_NEW_CYCLIC_DATA);
|
||||
|
||||
///Читаем данные от ПЛК
|
||||
echo_submod.out_data.Read(0, Echo_outCycData.mem, echo_submod.cyc_outdata_len);
|
||||
/// Конвертируем в литл эндиан
|
||||
endian_convert_32((uint8_t*)&Echo_outCycData.data.data_i);
|
||||
endian_convert_32((uint8_t*)&Echo_outCycData.data.data_f);
|
||||
///Подготавливаем данные для ПЛК
|
||||
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.inp_data.Write(0, Echo_inpCycData.mem, echo_submod.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.data.Read(0, (uint8_t*)&Echo_Gain, echo_param_gain.length);
|
||||
/// Конвертируем в литл эндиан
|
||||
endian_convert_32((uint8_t*)&Echo_Gain);
|
||||
std::cout << "Echo_Gain = " << std::to_string(Echo_Gain) << endl;
|
||||
}
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@
|
||||
# Copyright 2017 rt-labs AB, Sweden.
|
||||
# See LICENSE file in the project root for full license information.
|
||||
#*******************************************************************/
|
||||
cmake_minimum_required (VERSION 3.14)
|
||||
cmake_minimum_required (VERSION 3.0)
|
||||
|
||||
project (PN_DEV_TEST VERSION 0.0.1)
|
||||
|
||||
@ -22,6 +22,11 @@ include(./profinet/profinet.cmake)
|
||||
include(./nlohmann_json/nlohmann_json.cmake)
|
||||
include(./file_api/file_api.cmake)
|
||||
include(./configuration/configuration.cmake)
|
||||
include (./interprocess/interprocess.cmake)
|
||||
include (./shared_data/shared_data.cmake)
|
||||
|
||||
set(SRC_FILES ${SRC_FILES} ./app.cpp)
|
||||
|
||||
|
||||
add_executable(pn_dev_test ./main.cpp ${SRC_FILES})
|
||||
|
||||
@ -36,5 +41,4 @@ target_include_directories(pn_dev_test PRIVATE
|
||||
)
|
||||
|
||||
target_link_directories(pn_dev_test PUBLIC ${LIBS_INSTALL_PATH}/lib)
|
||||
|
||||
target_link_libraries (pn_dev_test PUBLIC profinet osal)
|
||||
target_link_libraries (pn_dev_test PUBLIC profinet osal ${Boost_LIBRARIES})
|
||||
|
82
src/app.cpp
Normal file
82
src/app.cpp
Normal file
@ -0,0 +1,82 @@
|
||||
#include "app.hpp"
|
||||
#include "program_config.hpp"
|
||||
|
||||
bool App::Init(std::string profinet_config_file)
|
||||
{
|
||||
ProfinetSettings profinet_settings;
|
||||
ProfinetDeviceSettings profinet_dev_settings;
|
||||
|
||||
/// Читаем настройки из файла
|
||||
if (!programconf_getProfinetSettings("program_configure.json", profinet_settings))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
///Создаем структуру в разделяемой памяти
|
||||
ProfinetData_Map * p_profinetMap = shared_data_.Create("profinet_shared_data");
|
||||
|
||||
/// Читаем настройки из файла, выделяем память под структуру модулей и подмодулей
|
||||
if (!programconf_getProfinetDeviceSettings(profinet_settings.profinet_device_config, profinet_dev_settings, p_profinetMap, shared_data_))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Настройка Profinet: Инициализация pnet, добавление DAP слотов и подслотов и подключение к ним модулей и подмодулей,
|
||||
if (!profinet_.Config(profinet_settings, profinet_dev_settings, p_profinetMap))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Циклические данные устройства передаются с определенным периодом, сообщения от контроллера при этом не требуются.
|
||||
*/
|
||||
/// Запуск потока Profinet
|
||||
profinet_.Start();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void App::Run()
|
||||
{
|
||||
//пауза
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
uint32_t event_mask = (Profinet::EVENT_CONNECTION_ESTABLISHED |
|
||||
Profinet::EVENT_NEW_CYCLIC_DATA |
|
||||
Profinet::EVENT_NEW_PARAM_DATA |
|
||||
Profinet::EVENT_CONNECTION_ABORT);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
//std::cout << "App: Waiting event flag" << std::endl;
|
||||
|
||||
uint32_t events = profinet_.EventWait(event_mask);
|
||||
|
||||
//std::cout << "App: New flag = " << std::to_string(events) << std::endl;
|
||||
|
||||
if (events & Profinet::EVENT_CONNECTION_ESTABLISHED)
|
||||
{
|
||||
std::cout << "App: EVENT_CONNECTION_ESTABLISHED" << std::endl;
|
||||
|
||||
profinet_.EventClear(Profinet::EVENT_CONNECTION_ESTABLISHED);
|
||||
|
||||
shared_data_.p_profinet_data_->Events.set_flag(ProfinetEvent::EVENT_CONNECTION_ESTABLISHED);
|
||||
}
|
||||
|
||||
if (events & Profinet::EVENT_NEW_CYCLIC_DATA)
|
||||
{
|
||||
//std::cout << "App: EVENT_NEW_CYCLIC_DATA" << std::endl;
|
||||
profinet_.EventClear(Profinet::EVENT_NEW_CYCLIC_DATA);
|
||||
|
||||
shared_data_.p_profinet_data_->Events.set_flag(ProfinetEvent::EVENT_NEW_CYCLIC_DATA);
|
||||
}
|
||||
|
||||
if (events & Profinet::EVENT_NEW_PARAM_DATA)
|
||||
{
|
||||
std::cout << "App: Clear: EVENT_NEW_PARAM_DATA" << std::endl;
|
||||
profinet_.EventClear(Profinet::EVENT_NEW_PARAM_DATA);
|
||||
|
||||
shared_data_.p_profinet_data_->Events.set_flag(ProfinetEvent::EVENT_NEW_PARAM_DATA);
|
||||
}
|
||||
}
|
||||
}
|
14
src/app.hpp
Normal file
14
src/app.hpp
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "./profinet/profinet.hpp"
|
||||
#include "./interprocess/profinet_shared_data.hpp"
|
||||
#include <string>
|
||||
|
||||
class App {
|
||||
public:
|
||||
bool Init(std::string profinet_config_file);
|
||||
void Run();
|
||||
private:
|
||||
Profinet profinet_;
|
||||
ProfinetSharedData shared_data_;
|
||||
};
|
@ -1,8 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
struct AppSettings
|
||||
{
|
||||
std::string EthDevName; /// Имя адаптера
|
||||
};
|
@ -7,6 +7,12 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "../profinet/profinet_data_map.hpp"
|
||||
|
||||
#include <boost/interprocess/managed_shared_memory.hpp>
|
||||
|
||||
|
||||
using namespace boost::interprocess;
|
||||
using json = nlohmann::json;
|
||||
using string = std::string;
|
||||
|
||||
@ -131,93 +137,168 @@ static bool programconf_getPnetDeviceSettings(json& j, ProfinetDeviceSettings& p
|
||||
return true;
|
||||
}
|
||||
|
||||
static pnet_submodule_dir_t programconf_dataDirConvert(string dir_str)
|
||||
static uint8_t programconf_dataDirConvert(string dir_str)
|
||||
{
|
||||
pnet_submodule_dir_t out = PNET_DIR_NO_IO;
|
||||
ProfinetSubmoduleDir out = PROFINET_SUBMOD_DIR_NO_IO;
|
||||
|
||||
if (dir_str == "PNET_DIR_IO")
|
||||
{
|
||||
out = PNET_DIR_IO;
|
||||
out = PROFINET_SUBMOD_DIR_IO;
|
||||
}
|
||||
else if (dir_str == "PNET_DIR_INPUT")
|
||||
{
|
||||
out = PNET_DIR_INPUT;
|
||||
out = PROFINET_SUBMOD_DIR_INPUT;
|
||||
}
|
||||
else if (dir_str == "PNET_DIR_OUTPUT")
|
||||
{
|
||||
out = PNET_DIR_OUTPUT;
|
||||
out = PROFINET_SUBMOD_DIR_OUTPUT;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool programconf_getProfinetModulesSettings(json& j, std::vector<std::shared_ptr<ProfinetModule>>& modules_vect)
|
||||
{
|
||||
static bool programconf_getProfinetModulesSettings(json& j, ProfinetData_Map * p_data_map, ProfinetSharedData& shared_data)
|
||||
{
|
||||
if (!j.contains("ProfinetDeviceModulesSettings"))
|
||||
return false;
|
||||
|
||||
|
||||
auto& modules_sett = j["ProfinetDeviceModulesSettings"]["modules"];
|
||||
///Узнаем количество модулей
|
||||
uint32_t mudules_number = modules_sett.size();
|
||||
|
||||
if (mudules_number == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
///Выделяем разделяемую память
|
||||
p_data_map->p_mods = shared_data.allocateData<ProfinetData_Module>(mudules_number);
|
||||
|
||||
if (p_data_map->p_mods == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
p_data_map->mods_nbr = mudules_number;
|
||||
|
||||
uint32_t module_index = 0;
|
||||
|
||||
for (auto& mod_set : modules_sett)
|
||||
{
|
||||
uint32_t mod_id = hexstring_to_int(mod_set["id"].get<string>());
|
||||
string mod_name = mod_set["name"].get<string>();
|
||||
|
||||
///Модуль
|
||||
auto& mod = p_data_map->p_mods[module_index];
|
||||
|
||||
auto module_ptr = ProfinetModule::Create(mod_id, mod_name);
|
||||
if (module_ptr == nullptr)
|
||||
++module_index;
|
||||
|
||||
/// Копируем параметры модуля
|
||||
mod.id = mod_id;
|
||||
mod.name = mod_set["name"].get<string>();
|
||||
|
||||
auto& submodules_sett = mod_set["submodules"];
|
||||
|
||||
uint32_t submudules_number = submodules_sett.size();
|
||||
|
||||
if (submudules_number == 0)
|
||||
continue;
|
||||
|
||||
///Выделяем разделяемую память
|
||||
mod.p_submods = shared_data.allocateData<ProfinetData_Submodule>(submudules_number);
|
||||
|
||||
if (mod.p_submods == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& submodules_sett = mod_set["submodules"];
|
||||
mod.submods_nbr = submudules_number;
|
||||
|
||||
uint32_t submodules_index = 0;
|
||||
|
||||
for (auto& submod_sett : submodules_sett)
|
||||
{
|
||||
uint32_t submod_id = hexstring_to_int(submod_sett["id"].get<string>());
|
||||
string submod_name = submod_sett["name"].get<string>();
|
||||
|
||||
pnet_data_cfg_t data_cfg;
|
||||
|
||||
data_cfg.data_dir = programconf_dataDirConvert(submod_sett["cyc_data_dir"].get<string>());
|
||||
data_cfg.insize = submod_sett["cyc_inp_data_size"].get<int>();
|
||||
data_cfg.outsize = submod_sett["cyc_out_data_size"].get<int>();
|
||||
|
||||
auto submodule_ptr = ProfinetSubmodule::Create(submod_id, submod_name, data_cfg);
|
||||
|
||||
if (submodule_ptr == nullptr)
|
||||
auto& submod = mod.p_submods[submodules_index];
|
||||
|
||||
++submodules_index;
|
||||
|
||||
/// Копируем данные подмодуля
|
||||
submod.id = submod_id;
|
||||
submod.name = submod_sett["name"].get<string>();
|
||||
submod.cyc_data_dir = programconf_dataDirConvert(submod_sett["cyc_data_dir"].get<string>());
|
||||
submod.cyc_indata_len = static_cast<uint16_t>(submod_sett["cyc_inp_data_size"].get<int>());
|
||||
submod.cyc_outdata_len = static_cast<uint16_t>(submod_sett["cyc_out_data_size"].get<int>());
|
||||
|
||||
/// Выделяем память под циклические данные
|
||||
if (submod.cyc_indata_len)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& params_sett = submod_sett["parameters"];
|
||||
|
||||
/// Записываем параметры в подмодуль
|
||||
for (auto& parm_sett : params_sett)
|
||||
{
|
||||
uint32_t index = parm_sett["index"].get<int>();
|
||||
string parm_name = parm_sett["name"].get<string>();
|
||||
uint16_t length = parm_sett["data_size"].get<int>();
|
||||
|
||||
auto submod_param_ptr = ProfinetParameter::Create(index, parm_name, length);
|
||||
|
||||
if (submod_param_ptr == nullptr)
|
||||
uint8_t * p_data = shared_data.allocateData<uint8_t>(submod.cyc_indata_len);
|
||||
if (p_data == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
submodule_ptr->addParameter(submod_param_ptr);
|
||||
submod.inp_data.Init(p_data, submod.cyc_indata_len);
|
||||
}
|
||||
|
||||
module_ptr->addSubmodule(submodule_ptr);
|
||||
}
|
||||
|
||||
modules_vect.push_back(module_ptr);
|
||||
if (submod.cyc_outdata_len)
|
||||
{
|
||||
uint8_t * p_data = shared_data.allocateData<uint8_t>(submod.cyc_outdata_len);
|
||||
if (p_data == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
submod.out_data.Init(p_data, submod.cyc_outdata_len);
|
||||
}
|
||||
|
||||
///Параметры подмодуля
|
||||
auto& params_sett = submod_sett["parameters"];
|
||||
uint32_t params_nbr = params_sett.size();
|
||||
|
||||
///Выделяем разделяемую память
|
||||
submod.p_params = shared_data.allocateData<ProfinetData_Parameter>(params_nbr);
|
||||
|
||||
if (submod.p_params == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
submod.params_nbr = params_nbr;
|
||||
|
||||
uint32_t params_index = 0;
|
||||
|
||||
/// Записываем параметры в подмодуль
|
||||
for (auto& parm_sett : params_sett)
|
||||
{
|
||||
auto& param = submod.p_params[params_index];
|
||||
++params_index;
|
||||
|
||||
param.index = static_cast<uint32_t>(parm_sett["index"].get<int>());
|
||||
param.name = parm_sett["name"].get<string>();
|
||||
param.length = static_cast<uint16_t>(parm_sett["data_size"].get<int>());
|
||||
|
||||
if (param.length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
/// Выделяем память для параметров
|
||||
uint8_t * p_data = shared_data.allocateData<uint8_t>(param.length);
|
||||
if (p_data == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
param.data.Init(p_data, param.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool programconf_getProfinetDeviceSettings(std::string file_path, ProfinetDeviceSettings& pn_sett, std::vector<std::shared_ptr<ProfinetModule>>& modules_vect)
|
||||
bool programconf_getProfinetDeviceSettings(std::string file_path, ProfinetDeviceSettings& pn_sett,
|
||||
ProfinetData_Map * p_data_map, ProfinetSharedData& shared_data)
|
||||
{
|
||||
json j;
|
||||
|
||||
@ -231,7 +312,7 @@ bool programconf_getProfinetDeviceSettings(std::string file_path, ProfinetDevice
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!programconf_getProfinetModulesSettings(j, modules_vect))
|
||||
if (!programconf_getProfinetModulesSettings(j, p_data_map, shared_data))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -5,9 +5,10 @@
|
||||
#include "json.hpp"
|
||||
#include "profinet_settings.hpp"
|
||||
#include "profinet_module.hpp"
|
||||
#include "profinet_data_map.hpp"
|
||||
#include "profinet_shared_data.hpp"
|
||||
|
||||
bool programconf_getProfinetSettings(std::string file_path, ProfinetSettings& pn_sett);
|
||||
|
||||
bool programconf_getProfinetDeviceSettings(std::string file_path, ProfinetDeviceSettings& pn_sett,
|
||||
std::vector<std::shared_ptr<ProfinetModule>>& modules_vect);
|
||||
|
||||
bool programconf_getProfinetDeviceSettings(std::string file_path, ProfinetDeviceSettings& pn_sett,
|
||||
ProfinetData_Map * p_data_map, ProfinetSharedData& shared_data);
|
||||
|
@ -4,24 +4,42 @@
|
||||
#include <cstdint>
|
||||
#include <condition_variable>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
class Flags {
|
||||
public:
|
||||
Flags() : flags_{0} {}
|
||||
Flags() : flags_{0} {
|
||||
///Блокируем мьютекс ожидания
|
||||
mutex_wait_.lock();
|
||||
}
|
||||
|
||||
void set_flag(uint32_t mask) {
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
flags_ |= mask;
|
||||
flag_cond_.notify_one();
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
flags_ |= mask;
|
||||
}
|
||||
/// Разблокируем мьютекс ожидания
|
||||
mutex_wait_.unlock();
|
||||
//flag_cond_.notify_all();
|
||||
}
|
||||
|
||||
uint32_t get_flag(uint32_t mask) {
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
return flags_ & mask;
|
||||
uint32_t out;
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
out = flags_ & mask;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
uint32_t get_flags() {
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
return flags_;
|
||||
uint32_t out;
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
out = flags_;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void clear_flags(uint32_t mask)
|
||||
@ -32,14 +50,32 @@ public:
|
||||
|
||||
uint32_t wait_flags(uint32_t mask)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mutex_);
|
||||
flag_cond_.wait(lk, [&]{return flags_ & mask;});
|
||||
lk.unlock();
|
||||
return flags_;
|
||||
mutex_wait_.lock(); //Ожидаем изменения данных
|
||||
uint32_t out;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
out = flags_ & mask;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Блочит потоки pnet. Нужно разбираться.
|
||||
*
|
||||
*/
|
||||
/*uint32_t wait_condition(uint32_t mask)
|
||||
{
|
||||
uint32_t out;
|
||||
std::unique_lock<std::mutex> lk(mutex_);
|
||||
flag_cond_.wait(lk, [this, mask]{return static_cast<bool>(flags_ & mask);});
|
||||
out = flags_ & mask;
|
||||
lk.unlock();
|
||||
return out;
|
||||
}*/
|
||||
private:
|
||||
uint32_t flags_;
|
||||
mutable std::mutex mutex_;
|
||||
std::condition_variable flag_cond_;
|
||||
std::mutex mutex_;
|
||||
std::mutex mutex_wait_;
|
||||
//std::condition_variable flag_cond_;
|
||||
};
|
78
src/flags/shared_flags.hpp
Normal file
78
src/flags/shared_flags.hpp
Normal file
@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <boost/interprocess/sync/interprocess_mutex.hpp>
|
||||
|
||||
class SharedFlags {
|
||||
public:
|
||||
SharedFlags() : flags_{0} {
|
||||
///Блокируем мьютекс ожидания
|
||||
mutex_wait_.lock();
|
||||
}
|
||||
|
||||
void set_flag(uint32_t mask) {
|
||||
{
|
||||
std::lock_guard guard(mutex_);
|
||||
flags_ |= mask;
|
||||
}
|
||||
/// Разблокируем мьютекс ожидания
|
||||
mutex_wait_.unlock();
|
||||
//flag_cond_.notify_all();
|
||||
}
|
||||
|
||||
uint32_t get_flag(uint32_t mask) {
|
||||
uint32_t out;
|
||||
{
|
||||
std::lock_guard guard(mutex_);
|
||||
out = flags_ & mask;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
uint32_t get_flags() {
|
||||
uint32_t out;
|
||||
{
|
||||
std::lock_guard guard(mutex_);
|
||||
out = flags_;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void clear_flags(uint32_t mask)
|
||||
{
|
||||
std::lock_guard guard(mutex_);
|
||||
flags_ &= ~mask;
|
||||
}
|
||||
|
||||
uint32_t wait_flags(uint32_t mask)
|
||||
{
|
||||
mutex_wait_.lock(); //Ожидаем изменения данных
|
||||
uint32_t out;
|
||||
|
||||
{
|
||||
std::lock_guard guard(mutex_);
|
||||
out = flags_ & mask;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Блочит потоки pnet. Нужно разбираться.
|
||||
*
|
||||
*/
|
||||
/*uint32_t wait_condition(uint32_t mask)
|
||||
{
|
||||
uint32_t out;
|
||||
std::unique_lock<std::mutex> lk(mutex_);
|
||||
flag_cond_.wait(lk, [this, mask]{return static_cast<bool>(flags_ & mask);});
|
||||
out = flags_ & mask;
|
||||
lk.unlock();
|
||||
return out;
|
||||
}*/
|
||||
private:
|
||||
uint32_t flags_;
|
||||
boost::interprocess::interprocess_mutex mutex_;
|
||||
boost::interprocess::interprocess_mutex mutex_wait_;
|
||||
};
|
17
src/interprocess/interprocess.cmake
Normal file
17
src/interprocess/interprocess.cmake
Normal file
@ -0,0 +1,17 @@
|
||||
|
||||
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})
|
||||
else()
|
||||
message(BOOST NOT FOUND)
|
||||
endif()
|
||||
|
||||
message("Boost dir: " ${Boost_INCLUDE_DIRS})
|
||||
message("Boost libs:" ${Boost_LIBRARIES})
|
||||
|
||||
set(SRC_FILES ${SRC_FILES} ./interprocess/profinet_shared_data.cpp)
|
48
src/interprocess/profinet_shared_data.cpp
Normal file
48
src/interprocess/profinet_shared_data.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
#include "../interprocess/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());
|
||||
|
||||
shmem_ = managed_shared_memory{create_only, mem_name.c_str(), 65536};
|
||||
|
||||
///Создаем данные в разделяемой памяти
|
||||
p_profinet_data_ = shmem_.construct<ProfinetData_Map>("ProfinetData_Map")();
|
||||
return p_profinet_data_;
|
||||
}
|
||||
|
31
src/interprocess/profinet_shared_data.hpp
Normal file
31
src/interprocess/profinet_shared_data.hpp
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
|
||||
#include "../profinet/profinet_data_map.hpp"
|
||||
#include <boost/interprocess/managed_shared_memory.hpp>
|
||||
|
||||
class ProfinetSharedData {
|
||||
public:
|
||||
/**
|
||||
* @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]();
|
||||
}
|
||||
|
||||
ProfinetData_Map * p_profinet_data_;
|
||||
|
||||
private:
|
||||
boost::interprocess::managed_shared_memory shmem_;
|
||||
|
||||
};
|
21
src/interprocess/profinet_shared_data_client.hpp
Normal file
21
src/interprocess/profinet_shared_data_client.hpp
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "../profinet/profinet_data_map.hpp"
|
||||
#include <boost/interprocess/managed_shared_memory.hpp>
|
||||
|
||||
class ProfinetSharedDataClient {
|
||||
public:
|
||||
/**
|
||||
* @brief Подключается к выделенной разделяемой памяти
|
||||
*
|
||||
* @param mem_name
|
||||
*/
|
||||
ProfinetData_Map * Connect(std::string mem_name) {
|
||||
shmem_ = boost::interprocess::managed_shared_memory{boost::interprocess::open_only, mem_name.c_str()};
|
||||
auto mem = shmem_.find<ProfinetData_Map>("ProfinetData_Map");
|
||||
return mem.first;
|
||||
}
|
||||
|
||||
private:
|
||||
boost::interprocess::managed_shared_memory shmem_;
|
||||
};
|
134
src/main.cpp
134
src/main.cpp
@ -1,144 +1,20 @@
|
||||
#include "app_settings.hpp"
|
||||
#include "../libs/include/pnet_api.h"
|
||||
#include "profinet.hpp"
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
|
||||
#include "program_config.hpp"
|
||||
#include "app.hpp"
|
||||
|
||||
#define ECHO_MODULE_ID 0x00000040 /// Идентификатор тестового модуля
|
||||
#define ECHO_SUBMOD_ID 0x00000140 /// Идентификатор тестового подмодуля
|
||||
#define ECHO_INPUT_DATA_SIZE 8
|
||||
#define ECHO_OUTPUT_DATA_SIZE 8
|
||||
#define ECHO_PARAMETER_GAIN_IDX 125 /// Индекс параметра Gain для подмодуля ECHO
|
||||
#include "./interprocess/profinet_shared_data.hpp"
|
||||
|
||||
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;
|
||||
}
|
||||
App app;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
Echo_inpCycData.data.data_f = 0.0f; /// Преобразовать в big endian
|
||||
Echo_inpCycData.data.data_i = 0; /// Преобразовать в big endian
|
||||
|
||||
Echo_outCycData.data.data_f = 0.0f;
|
||||
Echo_outCycData.data.data_i = 0;
|
||||
|
||||
ProfinetSettings profinet_settings;
|
||||
ProfinetDeviceSettings profinet_dev_settings;
|
||||
|
||||
/// Читаем настройки из файла
|
||||
if (!programconf_getProfinetSettings("program_configure.json", profinet_settings))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<ProfinetModule>> Modules;
|
||||
|
||||
/// Читаем настройки из файла
|
||||
if (!programconf_getProfinetDeviceSettings(profinet_settings.profinet_device_config, profinet_dev_settings, Modules))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Profinet profinet;
|
||||
/// Настройка Profinet: Инициализация pnet, добавление DAP слотов и подслотов и подключение к ним модулей и подмодулей,
|
||||
if (!profinet.Config(profinet_settings, profinet_dev_settings, Modules))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
app.Init("program_configure.json");
|
||||
|
||||
/**
|
||||
* Циклические данные устройства передаются с определенным периодом, сообщения от контроллера при этом не требуются.
|
||||
*/
|
||||
/// Запуск потока Profinet
|
||||
profinet.Start();
|
||||
|
||||
|
||||
/**
|
||||
* @brief Ниже тестовый функционал
|
||||
*
|
||||
*/
|
||||
auto echo_mod = profinet.getModule(ECHO_MODULE_ID);
|
||||
auto echo_submod = echo_mod->getSubmodulePtr(ECHO_SUBMOD_ID);
|
||||
echo_submod->putDataToPlc(Echo_inpCycData.mem);
|
||||
|
||||
auto param = echo_submod->getParameterPtr(ECHO_PARAMETER_GAIN_IDX);
|
||||
|
||||
|
||||
uint32_t event_mask = (Profinet::EVENT_CONNECTION_ESTABLISHED |
|
||||
Profinet::EVENT_NEW_CYCLIC_DATA |
|
||||
Profinet::EVENT_NEW_PARAM_DATA );
|
||||
for (;;)
|
||||
{
|
||||
uint32_t events = profinet.EventWait(event_mask);
|
||||
//std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
//++ix;
|
||||
//std::cout << "tick: " << ix << endl;
|
||||
|
||||
if (events & Profinet::EVENT_CONNECTION_ESTABLISHED)
|
||||
{
|
||||
profinet.EventClear(Profinet::EVENT_CONNECTION_ESTABLISHED);
|
||||
|
||||
std::cout << "Event: PLC connection established" << endl;
|
||||
}
|
||||
else if (events & Profinet::EVENT_NEW_CYCLIC_DATA)
|
||||
{
|
||||
profinet.EventClear(Profinet::EVENT_NEW_CYCLIC_DATA);
|
||||
|
||||
///Читаем данные от ПЛК
|
||||
echo_submod->getDataFromPlc(Echo_outCycData.mem);
|
||||
/// Конвертируем в литл эндиан
|
||||
endian_convert_32((uint8_t*)&Echo_outCycData.data.data_i);
|
||||
endian_convert_32((uint8_t*)&Echo_outCycData.data.data_f);
|
||||
///Подготавливаем данные для ПЛК
|
||||
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->putDataToPlc(Echo_inpCycData.mem);
|
||||
|
||||
}
|
||||
else if (events & Profinet::EVENT_NEW_PARAM_DATA)
|
||||
{
|
||||
profinet.EventClear(Profinet::EVENT_NEW_PARAM_DATA);
|
||||
|
||||
std::cout << "Event: New parameter data: ";
|
||||
//Читаем параметр записанный ПЛК при установлении связи
|
||||
param->readParameter((uint8_t*)&Echo_Gain);
|
||||
/// Конвертируем в литл эндиан
|
||||
endian_convert_32((uint8_t*)&Echo_Gain);
|
||||
std::cout << "Echo_Gain = " << std::to_string(Echo_Gain) << endl;
|
||||
}
|
||||
}
|
||||
app.Run();
|
||||
|
||||
return 0;
|
||||
}
|
@ -165,9 +165,38 @@ Profinet::Profinet() : m_pnet_data({nullptr, 0, UINT32_MAX, 0}),
|
||||
|
||||
}
|
||||
|
||||
void Profinet::createInternalStucture(ProfinetData_Map * const p_data_map)
|
||||
{
|
||||
for (int i = 0; i < p_data_map->mods_nbr; ++i)
|
||||
{
|
||||
auto& mod = p_data_map->p_mods[i];
|
||||
|
||||
auto module_ptr = ProfinetModule::Create(mod.id, mod.name);
|
||||
|
||||
for (int j = 0; j < mod.submods_nbr; ++j)
|
||||
{
|
||||
auto& submod = mod.p_submods[j];
|
||||
pnet_data_cfg_t dap_data_cfg = { static_cast<pnet_submodule_dir_t>(submod.cyc_data_dir),
|
||||
submod.cyc_indata_len, submod.cyc_outdata_len };
|
||||
auto submodule_ptr = ProfinetSubmodule::Create(submod.id, submod.name, dap_data_cfg, &submod.inp_data, &submod.out_data);
|
||||
|
||||
for (int k = 0; k < submod.params_nbr; ++k)
|
||||
{
|
||||
auto& param = submod.p_params[k];
|
||||
auto Parameter = ProfinetParameter::Create(param.index, param.name, ¶m.data);
|
||||
submodule_ptr->addParameter(Parameter);
|
||||
}
|
||||
|
||||
module_ptr->addSubmodule(submodule_ptr);
|
||||
}
|
||||
addModule(module_ptr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool Profinet::Config(ProfinetSettings& Settings,
|
||||
ProfinetDeviceSettings& DevSettings,
|
||||
std::vector<std::shared_ptr<ProfinetModule>>& Modules)
|
||||
ProfinetData_Map * const p_data_map)
|
||||
{
|
||||
bool ret = true;
|
||||
pnet_cfg_t pnet_cfg = {0};
|
||||
@ -213,15 +242,17 @@ bool Profinet::Config(ProfinetSettings& Settings,
|
||||
/// Создаем обязательный модуль DAP
|
||||
auto dap_module = ProfinetModule::Create(PNET_MOD_DAP_IDENT, "DAP 1");
|
||||
/// Создаем обязательный подмодуль DAP Identity 1
|
||||
auto dap_submodule_indent_1 = ProfinetSubmodule::Create(PNET_SUBMOD_DAP_IDENT, "DAP Identity 1", dap_data_cfg);
|
||||
auto dap_submodule_indent_1 = ProfinetSubmodule::Create(PNET_SUBMOD_DAP_IDENT, "DAP Identity 1", dap_data_cfg, nullptr, nullptr);
|
||||
/// Создаем обязательный подмодуль DAP IFACE1 IDENT
|
||||
auto dap_submodule_iface_1_indent_1 = ProfinetSubmodule::Create(PNET_SUBMOD_DAP_INTERFACE_1_IDENT, "DAP IFACE1 IDENT", dap_data_cfg);
|
||||
auto dap_submodule_iface_1_indent_1 = ProfinetSubmodule::Create(PNET_SUBMOD_DAP_INTERFACE_1_IDENT, "DAP IFACE1 IDENT", dap_data_cfg, nullptr, nullptr);
|
||||
/// Создаем обязательный подмодуль DAP Port 1
|
||||
auto dap_subslot_iface_1_port1_ident = ProfinetSubmodule::Create(PNET_SUBMOD_DAP_INTERFACE_1_PORT_1_IDENT, "DAP Port 1", dap_data_cfg);
|
||||
auto dap_subslot_iface_1_port1_ident = ProfinetSubmodule::Create(PNET_SUBMOD_DAP_INTERFACE_1_PORT_1_IDENT, "DAP Port 1", dap_data_cfg, nullptr, nullptr);
|
||||
/// Добавляем подмодули к модулю
|
||||
ret&= dap_module->addSubmodule(dap_submodule_indent_1);
|
||||
ret&= dap_module->addSubmodule(dap_submodule_iface_1_indent_1);
|
||||
ret&= dap_module->addSubmodule(dap_subslot_iface_1_port1_ident);
|
||||
/// Добавляем модуль в конфигурацию
|
||||
addModule(dap_module);
|
||||
|
||||
/// Создаем слот для модуля DAP и доключаем к нему модуль dap_module
|
||||
ret&= addSlotAndPlugModule(PNET_SLOT_DAP_IDENT, dap_module);
|
||||
@ -233,21 +264,8 @@ bool Profinet::Config(ProfinetSettings& Settings,
|
||||
/// 3. Подмодуль "DAP Port 1"
|
||||
ret&= addSubslotAndPlugSubmodule(PNET_SLOT_DAP_IDENT, PNET_SUBSLOT_DAP_INTERFACE_1_PORT_1_IDENT, dap_subslot_iface_1_port1_ident);
|
||||
|
||||
///Копируем конфигурацию модулей
|
||||
for (auto module_ptr : Modules)
|
||||
{
|
||||
if (module_ptr == nullptr)
|
||||
{
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!addModule(module_ptr))
|
||||
{
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
///Создаем внутреннию структуру модулей
|
||||
createInternalStucture(p_data_map);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -558,13 +576,19 @@ void Profinet::PeriodicOperations()
|
||||
{
|
||||
pnet_application_ready (m_pnet_data.pnet_ptr, m_pnet_data.arep_for_appl_ready);
|
||||
flags_.clear_flags(FLAGS_AREP_APP_READY);
|
||||
LOG("FLAGS_AREP_APP_READY");
|
||||
LOG("PeriodicOperations: FLAGS_AREP_APP_READY");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Profinet::cyclicIoData()
|
||||
{
|
||||
///Циклический обмен только когда установлено соединение
|
||||
if (!isConnectedToController())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto slot : m_slots)
|
||||
{
|
||||
shared_ptr<ProfinetSlot>& slot_ptr = slot.second;
|
||||
@ -598,12 +622,12 @@ void Profinet::cyclicIoData()
|
||||
events_.set_flag(EVENT_NEW_CYCLIC_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (submodule_ptr->m_data_cfg.insize > 0)
|
||||
{
|
||||
uint8_t indata_iocs;
|
||||
uint8_t indata_iops = PNET_IOXS_GOOD;
|
||||
|
||||
//LOG("CYC: Put data to PLC");
|
||||
/**
|
||||
* @brief Отправляем данные подмодуля в ПЛК
|
||||
*
|
||||
@ -679,7 +703,7 @@ int Profinet::callbackStateInd ( uint32_t arep, pnet_event_values_t event)
|
||||
/// Ошибок нет
|
||||
}
|
||||
|
||||
events_.clear_flags(EVENT_CONNECTION_ESTABLISHED);
|
||||
events_.set_flag(EVENT_CONNECTION_ABORT);
|
||||
|
||||
m_pnet_data.arep = UINT32_MAX;
|
||||
|
||||
@ -813,11 +837,11 @@ int Profinet::callbackReadInd ( uint32_t arep,
|
||||
|
||||
///3. Передаем данные параметра
|
||||
/**
|
||||
* По идее тут нужно утановить мьютекс lock, а unlock сделать потом, когда данные будет причитаны стеком pnet
|
||||
* Проблема в том, что нет ни одного события которое бы сообщало об окончании процесса чтения данных стеком pnet.
|
||||
* Нужно поисследовать.
|
||||
* В функции getReadBufPtr данные копируются во временный буфер и уже затем передаются в стек pnet.
|
||||
* Это нужно из-за отсутствия события которое бы сообщало об окончании процесса чтения данных стеком pnet.
|
||||
*
|
||||
*/
|
||||
*pp_read_data = param_ptr->getDataPointer();
|
||||
*pp_read_data = param_ptr->getReadBufPtr();
|
||||
*p_read_length = param_ptr->length;
|
||||
|
||||
return 0;
|
||||
|
@ -15,6 +15,8 @@
|
||||
#include "../log/log.hpp"
|
||||
#include "../flags/flags.hpp"
|
||||
|
||||
#include "profinet_data_map.hpp"
|
||||
|
||||
|
||||
class Profinet {
|
||||
public:
|
||||
@ -23,13 +25,14 @@ public:
|
||||
EVENT_CONNECTION_ESTABLISHED = 0x00000001, /// Установлено соединение с ПЛК
|
||||
EVENT_NEW_CYCLIC_DATA = 0x00000002, /// Получение новых циклических данных от ПЛК
|
||||
EVENT_NEW_PARAM_DATA = 0x00000004, /// Новая запись параметра от ПЛК
|
||||
EVENT_CONNECTION_ABORT = 0x00000008
|
||||
};
|
||||
|
||||
Profinet();
|
||||
|
||||
bool Config(ProfinetSettings& Settings,
|
||||
ProfinetDeviceSettings& DevSettings,
|
||||
std::vector<std::shared_ptr<ProfinetModule>>& Modules);
|
||||
ProfinetData_Map * const p_data_map);
|
||||
|
||||
/**
|
||||
* @brief Добавляет ранее созданный слот slot_ptr
|
||||
@ -154,7 +157,6 @@ public:
|
||||
|
||||
void EventClear(uint32_t mask) { events_.clear_flags(mask); }
|
||||
|
||||
|
||||
~Profinet();
|
||||
|
||||
private:
|
||||
@ -216,6 +218,8 @@ private:
|
||||
|
||||
void cyclicIoData();
|
||||
|
||||
void createInternalStucture(ProfinetData_Map * const p_data_map);
|
||||
|
||||
public:
|
||||
/**
|
||||
*
|
||||
|
60
src/profinet/profinet_data_map.hpp
Normal file
60
src/profinet/profinet_data_map.hpp
Normal file
@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "../flags/shared_flags.hpp"
|
||||
#include "../shared_data/shared_data.hpp"
|
||||
|
||||
|
||||
enum ProfinetSubmoduleDir
|
||||
{
|
||||
PROFINET_SUBMOD_DIR_NO_IO = 0x00,
|
||||
PROFINET_SUBMOD_DIR_INPUT = 0x01,
|
||||
PROFINET_SUBMOD_DIR_OUTPUT = 0x02,
|
||||
PROFINET_SUBMOD_DIR_IO = 0x03
|
||||
};
|
||||
|
||||
enum ProfinetEvent
|
||||
{
|
||||
EVENT_CONNECTION_ESTABLISHED = 0x00000001, /// Установлено соединение с ПЛК
|
||||
EVENT_NEW_CYCLIC_DATA = 0x00000002, /// Получение новых циклических данных от ПЛК
|
||||
EVENT_NEW_PARAM_DATA = 0x00000004, /// Новая запись параметра от ПЛК
|
||||
EVENT_CONNECTION_ABORT = 0x00000008; /// Разрыв соединения
|
||||
};
|
||||
|
||||
struct ProfinetData_Parameter {
|
||||
uint32_t index; /// Идентфикатор параметра
|
||||
std::string name; /// Наименование
|
||||
uint16_t length; /// длина параметра в байтах
|
||||
SharedData data; /// Указатель на данные параметра
|
||||
};
|
||||
|
||||
struct ProfinetData_Submodule{
|
||||
uint32_t id; /// Идентификатор подмодуля
|
||||
std::string name; /// Наименование
|
||||
uint8_t cyc_data_dir; /// Направление данных
|
||||
uint16_t cyc_indata_len; /// Длина входных для ПЛК данных
|
||||
uint16_t cyc_outdata_len; /// Длина выходных для ПЛК данных
|
||||
SharedData inp_data; /// Входные данные(данные от контроллера к устройству)
|
||||
SharedData out_data; /// Выходные данные(данные от устройства к контроллеру)
|
||||
|
||||
ProfinetData_Parameter * p_params; /// Параметры подмодуля
|
||||
uint32_t params_nbr; /// Количество параметров
|
||||
};
|
||||
|
||||
struct ProfinetData_Module {
|
||||
uint32_t id;
|
||||
std::string name;
|
||||
ProfinetData_Submodule * p_submods; /// Подмодули
|
||||
uint32_t submods_nbr; /// Количество подмодулей
|
||||
};
|
||||
|
||||
struct ProfinetData_Map {
|
||||
SharedFlags Events; /// События profinet
|
||||
ProfinetData_Module * p_mods; /// Модули
|
||||
uint32_t mods_nbr; /// Количество модулей
|
||||
};
|
||||
|
@ -1,50 +1,58 @@
|
||||
#include "profinet_parameter.hpp"
|
||||
#include <cstring>
|
||||
|
||||
ProfinetParameter::ProfinetParameter(uint32_t Index, std::string Name, uint16_t Length) :
|
||||
ProfinetParameter::ProfinetParameter(uint32_t Index, std::string Name, SharedData * p_data) :
|
||||
index(Index),
|
||||
name(Name),
|
||||
length(Length),
|
||||
data_ptr_(new uint8_t[Length])
|
||||
length(p_data->getSize()),
|
||||
p_data_(p_data),
|
||||
read_buf_(nullptr)
|
||||
{
|
||||
if (length != 0)
|
||||
{
|
||||
read_buf_ = std::shared_ptr<uint8_t[]>(new uint8_t[length]);
|
||||
}
|
||||
};
|
||||
|
||||
bool ProfinetParameter::readParameter(uint8_t * const data_ptr)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(data_mutex_); /// Синхронизация чтения и записи
|
||||
|
||||
if (data_ptr == nullptr)
|
||||
if ((data_ptr == nullptr) || (p_data_ == nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(data_ptr, data_ptr_.get(), length);
|
||||
p_data_->Read(0, (void*)data_ptr, length);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProfinetParameter::writeParameter(const uint8_t * data_ptr)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(data_mutex_); /// Синхронизация чтения и записи
|
||||
|
||||
if (data_ptr == nullptr)
|
||||
if ((data_ptr == nullptr) || (p_data_ == nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(data_ptr_.get(), data_ptr, length);
|
||||
p_data_->Write(0, (void*)data_ptr, length);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t * ProfinetParameter::getDataPointer()
|
||||
uint8_t * ProfinetParameter::getReadBufPtr()
|
||||
{
|
||||
/**
|
||||
* @brief Слабое утешение от ситуации когда приложение записывает данные в параметр, а контроллер хочет этот параметр
|
||||
* @brief Cитуации когда приложение записывает данные в параметр, а контроллер хочет этот параметр
|
||||
* прочитать и вызвался коллбэк profinet_cb_read. В этом случае будем тут висеть пока приложение не запишет параметр.
|
||||
*
|
||||
*/
|
||||
std::lock_guard<std::mutex> guard(data_mutex_);
|
||||
|
||||
return data_ptr_.get();
|
||||
if (p_data_ == nullptr)
|
||||
return nullptr;
|
||||
|
||||
uint8_t * p_read_buf = read_buf_.get();
|
||||
|
||||
/// Копируем данные в буфер
|
||||
p_data_->Read(0, p_read_buf, length);
|
||||
|
||||
|
||||
return p_read_buf;
|
||||
}
|
@ -1,20 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "../shared_data/shared_data.hpp"
|
||||
|
||||
|
||||
class ProfinetParameter
|
||||
{
|
||||
public:
|
||||
static std::shared_ptr<ProfinetParameter> Create(uint32_t Index, std::string Name, uint16_t Length)
|
||||
static std::shared_ptr<ProfinetParameter> Create(uint32_t Index, std::string Name, SharedData * p_data)
|
||||
{
|
||||
return std::shared_ptr<ProfinetParameter>(new ProfinetParameter(Index, Name, Length));
|
||||
if (p_data == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::shared_ptr<ProfinetParameter>(new ProfinetParameter(Index, Name, p_data));
|
||||
}
|
||||
|
||||
ProfinetParameter(uint32_t Index, std::string Name, uint16_t Length);
|
||||
ProfinetParameter(uint32_t Index, std::string Name, SharedData * p_data);
|
||||
|
||||
/**
|
||||
* @brief Защищенная запись данных параметра
|
||||
@ -39,17 +45,18 @@ public:
|
||||
* Для этого ему в колбэке нужно предоставить указатель на данные параметра и длину данных.
|
||||
* Подразумевается, что эта функция будет использоваться только внутри колбэка стека pnet.
|
||||
* см. profinet_cb_read.cpp
|
||||
* Не использовать эту функцию в приложении!!!
|
||||
* @return uint8_t*
|
||||
*/
|
||||
uint8_t * getDataPointer();
|
||||
uint8_t * getReadBufPtr();
|
||||
private:
|
||||
|
||||
|
||||
|
||||
public:
|
||||
const std::string name; /// Имя параметра
|
||||
const uint32_t index; /// Идентфикатор параметра
|
||||
const uint16_t length; /// длина параметра в байтах
|
||||
|
||||
private:
|
||||
std::shared_ptr<uint8_t[]> data_ptr_; /// указатель на данные параметра
|
||||
mutable std::mutex data_mutex_; /// Доступ к данным
|
||||
};
|
||||
std::shared_ptr<uint8_t[]> read_buf_; ///Буфер данных для передачи его в стек pnet для операции чтения
|
||||
SharedData * const p_data_; ///Указатель на данные параметра(в разделяемой области)
|
||||
};
|
@ -3,25 +3,34 @@
|
||||
|
||||
ProfinetSubmodule::ProfinetSubmodule(uint32_t submodule_id,
|
||||
std::string submodule_name,
|
||||
pnet_data_cfg_t& submodule_data_cfg) :
|
||||
pnet_data_cfg_t& submodule_data_cfg,
|
||||
SharedData * inp_data_ptr,
|
||||
SharedData * out_data_ptr) :
|
||||
m_id(submodule_id),
|
||||
m_name(submodule_name),
|
||||
m_data_cfg(submodule_data_cfg),
|
||||
inp_data_ptr_(nullptr),
|
||||
out_data_ptr_(nullptr),
|
||||
inp_data_ptr_(inp_data_ptr),
|
||||
out_data_ptr_(out_data_ptr),
|
||||
m_indata_iocs(0),
|
||||
m_outdata_iops(0),
|
||||
data_updated_(false)
|
||||
data_updated_(false),
|
||||
out_data_buf_(nullptr),
|
||||
inp_data_buf_(nullptr)
|
||||
{
|
||||
/// Могут быть подмодули без данных, например DAP
|
||||
if (m_data_cfg.insize > 0)
|
||||
if (m_data_cfg.outsize && (out_data_ptr_!=nullptr))
|
||||
{
|
||||
inp_data_ptr_ = std::shared_ptr<uint8_t[]>(new uint8_t[m_data_cfg.insize]);
|
||||
if (m_data_cfg.outsize == out_data_ptr_->getSize())
|
||||
{
|
||||
out_data_buf_ = std::shared_ptr<uint8_t[]>(new uint8_t[m_data_cfg.outsize]);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_data_cfg.outsize > 0)
|
||||
if (m_data_cfg.insize && (inp_data_ptr_ != nullptr))
|
||||
{
|
||||
out_data_ptr_ = std::shared_ptr<uint8_t[]>(new uint8_t[m_data_cfg.outsize]);
|
||||
if (m_data_cfg.insize == inp_data_ptr_->getSize())
|
||||
{
|
||||
inp_data_buf_ = std::shared_ptr<uint8_t[]>(new uint8_t[m_data_cfg.insize]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,10 +57,8 @@ bool ProfinetSubmodule::inputSetData( pnet_t * pnet_ptr,
|
||||
uint16_t subslot_nbr,
|
||||
uint8_t iops)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(inp_data_mutex_);
|
||||
|
||||
if ((pnet_ptr == nullptr) ||
|
||||
((inp_data_ptr_ == nullptr) && m_data_cfg.insize > 0))
|
||||
((inp_data_buf_ == nullptr) && (m_data_cfg.insize > 0)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -61,12 +68,19 @@ bool ProfinetSubmodule::inputSetData( pnet_t * pnet_ptr,
|
||||
* нужен для корректного установления соединения
|
||||
*
|
||||
*/
|
||||
|
||||
///Копируем данные в буфер
|
||||
if (inp_data_ptr_ != nullptr)
|
||||
{
|
||||
inp_data_ptr_->Read(0, inp_data_buf_.get(), m_data_cfg.insize);
|
||||
}
|
||||
|
||||
/// Копируем данные для передачи в ПЛК
|
||||
pnet_input_set_data_and_iops( pnet_ptr,
|
||||
api,
|
||||
slot_nbr,
|
||||
subslot_nbr,
|
||||
inp_data_ptr_.get(),
|
||||
inp_data_buf_.get(),
|
||||
m_data_cfg.insize,
|
||||
iops);
|
||||
|
||||
@ -81,27 +95,33 @@ bool ProfinetSubmodule::outputGetData( pnet_t * pnet_ptr,
|
||||
bool out = true;
|
||||
|
||||
/// Существуют подмодули без данных, например DAP
|
||||
if ((pnet_ptr == nullptr) || (out_data_ptr_ == nullptr))
|
||||
if ((pnet_ptr == nullptr) || ((out_data_ptr_ == nullptr) && (m_data_cfg.outsize > 0)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> guard(out_data_mutex_);
|
||||
|
||||
uint16_t outdata_length = m_data_cfg.outsize;
|
||||
uint8_t outdata_iops;
|
||||
bool updated;
|
||||
bool updated{false};
|
||||
|
||||
/// Копируем данные от полученные от контроллера
|
||||
|
||||
/// Копируем данные полученные от контроллера
|
||||
pnet_output_get_data_and_iops (pnet_ptr,
|
||||
api,
|
||||
slot_nbr,
|
||||
subslot_nbr,
|
||||
&updated,
|
||||
out_data_ptr_.get(),
|
||||
out_data_buf_.get(),
|
||||
&outdata_length,
|
||||
&outdata_iops);
|
||||
|
||||
///out_data_ptr_ данные из буфера
|
||||
///Когда новых данных нет, то outdata_length == 0
|
||||
if ((out_data_ptr_ != nullptr) && (outdata_length == m_data_cfg.outsize))
|
||||
{
|
||||
out_data_ptr_->Write(0, out_data_buf_.get(), m_data_cfg.outsize);
|
||||
}
|
||||
|
||||
m_outdata_iops = outdata_iops;
|
||||
|
||||
if ((!data_updated_) && (updated == true))
|
||||
@ -125,34 +145,4 @@ bool ProfinetSubmodule::outputGetData( pnet_t * pnet_ptr,
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
bool ProfinetSubmodule::getDataFromPlc(uint8_t * data_ptr)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(out_data_mutex_);
|
||||
|
||||
if ((data_ptr == nullptr) || (out_data_ptr_ == nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(data_ptr, out_data_ptr_.get(), m_data_cfg.outsize);
|
||||
|
||||
data_updated_ = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProfinetSubmodule::putDataToPlc(uint8_t * data_ptr)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(inp_data_mutex_);
|
||||
|
||||
if ((data_ptr == nullptr) || (inp_data_ptr_ == nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(inp_data_ptr_.get(), data_ptr, m_data_cfg.insize);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@
|
||||
#include <mutex>
|
||||
|
||||
#include "profinet_parameter.hpp"
|
||||
#include "../shared_data/shared_data.hpp"
|
||||
|
||||
#include "../../libs/include/pnet_api.h"
|
||||
|
||||
@ -15,21 +16,27 @@ class ProfinetSubmodule {
|
||||
public:
|
||||
static std::shared_ptr<ProfinetSubmodule> Create(uint32_t submodule_id,
|
||||
std::string submodule_name,
|
||||
pnet_data_cfg_t& submodule_data_cfg)
|
||||
pnet_data_cfg_t& submodule_data_cfg,
|
||||
SharedData * inp_data_ptr,
|
||||
SharedData * out_data_ptr)
|
||||
{
|
||||
return std::shared_ptr<ProfinetSubmodule>(new ProfinetSubmodule(submodule_id,
|
||||
submodule_name,
|
||||
submodule_data_cfg));
|
||||
submodule_data_cfg,
|
||||
inp_data_ptr,
|
||||
out_data_ptr));
|
||||
}
|
||||
|
||||
ProfinetSubmodule(uint32_t submodule_id,
|
||||
std::string submodule_name,
|
||||
pnet_data_cfg_t& submodule_data_cfg);
|
||||
pnet_data_cfg_t& submodule_data_cfg,
|
||||
SharedData * inp_data_ptr,
|
||||
SharedData * out_data_ptr);
|
||||
|
||||
bool addParameter(std::shared_ptr<ProfinetParameter>& param);
|
||||
|
||||
std::shared_ptr<ProfinetParameter> getParameterPtr(uint32_t index);
|
||||
|
||||
|
||||
bool inputSetData( pnet_t * pnet_ptr,
|
||||
uint32_t api,
|
||||
uint16_t slot_nbr,
|
||||
@ -41,11 +48,13 @@ public:
|
||||
uint16_t slot_nbr,
|
||||
uint16_t subslot_nbr);
|
||||
|
||||
bool getDataFromPlc(uint8_t * data_ptr);
|
||||
bool isDataUpdated() {
|
||||
bool out{data_updated_};
|
||||
|
||||
bool putDataToPlc (uint8_t * data_ptr);
|
||||
|
||||
bool isDataUpdated() { return data_updated_; };
|
||||
data_updated_ = false;
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
public:
|
||||
const uint32_t m_id; /// Идентификатор подмодуля
|
||||
@ -62,12 +71,13 @@ public:
|
||||
private:
|
||||
/// Набор параметров подмодуля
|
||||
std::map<uint32_t, std::shared_ptr<ProfinetParameter>> m_params;
|
||||
|
||||
/// Буферы для взаимодействия с pnet и решения вопросов синхронизации
|
||||
std::shared_ptr<uint8_t[]> out_data_buf_;
|
||||
std::shared_ptr<uint8_t[]> inp_data_buf_;
|
||||
|
||||
std::shared_ptr<uint8_t[]> inp_data_ptr_; /// Входные циклические данные (DEV->PLC)
|
||||
std::mutex inp_data_mutex_;
|
||||
|
||||
std::shared_ptr<uint8_t[]> out_data_ptr_; /// Выходные циклические данные (PLC->DEV)
|
||||
std::mutex out_data_mutex_;
|
||||
SharedData * const inp_data_ptr_; /// Входные циклические данные (DEV->PLC)
|
||||
SharedData * const out_data_ptr_; /// Выходные циклические данные (PLC->DEV)
|
||||
|
||||
bool data_updated_;
|
||||
};
|
2
src/shared_data/shared_data.cmake
Normal file
2
src/shared_data/shared_data.cmake
Normal file
@ -0,0 +1,2 @@
|
||||
set(SRC_FILES ${SRC_FILES} ./shared_data/shared_data.cpp)
|
||||
set(INC_DIRS ${INC_DIRS} ./shared_data)
|
46
src/shared_data/shared_data.cpp
Normal file
46
src/shared_data/shared_data.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
#include "shared_data.hpp"
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
bool SharedData::Init(uint8_t * p_data, uint32_t size)
|
||||
{
|
||||
if ((p_data == nullptr) || (size == 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
p_data_ = p_data;
|
||||
size_ = size;
|
||||
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
std::memset(p_data_, 0, size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SharedData::Write(uint32_t offset_bytes, void * p_data, uint32_t length)
|
||||
{
|
||||
if ((p_data == nullptr) || ((offset_bytes + length) > size_))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
std::memcpy(p_data_ + offset_bytes, p_data, length);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SharedData::Read(uint32_t offset_bytes, void * p_data, uint32_t length)
|
||||
{
|
||||
if ((p_data == nullptr) || ((offset_bytes + length) > size_))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
std::memcpy(p_data, p_data_ + offset_bytes, length);
|
||||
|
||||
return true;
|
||||
}
|
23
src/shared_data/shared_data.hpp
Normal file
23
src/shared_data/shared_data.hpp
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
|
||||
class SharedData {
|
||||
public:
|
||||
SharedData() : p_data_{nullptr}, size_{0} {};
|
||||
|
||||
bool Init(uint8_t * p_data, uint32_t size);
|
||||
|
||||
bool Write(uint32_t offset_bytes, void * p_data, uint32_t length);
|
||||
|
||||
bool Read(uint32_t offset_bytes, void * p_data, uint32_t length);
|
||||
|
||||
uint8_t * get() { return p_data_; }
|
||||
|
||||
uint32_t getSize() {return size_; };
|
||||
|
||||
private:
|
||||
std::mutex mutex_;
|
||||
uint8_t * p_data_;
|
||||
uint32_t size_;
|
||||
};
|
Loading…
Reference in New Issue
Block a user