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.
|
# Copyright 2017 rt-labs AB, Sweden.
|
||||||
# See LICENSE file in the project root for full license information.
|
# 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)
|
project (PN_DEV_TEST VERSION 0.0.1)
|
||||||
|
|
||||||
@ -22,6 +22,11 @@ include(./profinet/profinet.cmake)
|
|||||||
include(./nlohmann_json/nlohmann_json.cmake)
|
include(./nlohmann_json/nlohmann_json.cmake)
|
||||||
include(./file_api/file_api.cmake)
|
include(./file_api/file_api.cmake)
|
||||||
include(./configuration/configuration.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})
|
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_directories(pn_dev_test PUBLIC ${LIBS_INSTALL_PATH}/lib)
|
||||||
|
target_link_libraries (pn_dev_test PUBLIC profinet osal ${Boost_LIBRARIES})
|
||||||
target_link_libraries (pn_dev_test PUBLIC profinet osal)
|
|
||||||
|
|||||||
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 <cstdint>
|
||||||
|
|
||||||
|
#include "../profinet/profinet_data_map.hpp"
|
||||||
|
|
||||||
|
#include <boost/interprocess/managed_shared_memory.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
using namespace boost::interprocess;
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
using string = std::string;
|
using string = std::string;
|
||||||
|
|
||||||
@ -131,93 +137,168 @@ static bool programconf_getPnetDeviceSettings(json& j, ProfinetDeviceSettings& p
|
|||||||
return true;
|
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")
|
if (dir_str == "PNET_DIR_IO")
|
||||||
{
|
{
|
||||||
out = PNET_DIR_IO;
|
out = PROFINET_SUBMOD_DIR_IO;
|
||||||
}
|
}
|
||||||
else if (dir_str == "PNET_DIR_INPUT")
|
else if (dir_str == "PNET_DIR_INPUT")
|
||||||
{
|
{
|
||||||
out = PNET_DIR_INPUT;
|
out = PROFINET_SUBMOD_DIR_INPUT;
|
||||||
}
|
}
|
||||||
else if (dir_str == "PNET_DIR_OUTPUT")
|
else if (dir_str == "PNET_DIR_OUTPUT")
|
||||||
{
|
{
|
||||||
out = PNET_DIR_OUTPUT;
|
out = PROFINET_SUBMOD_DIR_OUTPUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
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"))
|
if (!j.contains("ProfinetDeviceModulesSettings"))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
||||||
auto& modules_sett = j["ProfinetDeviceModulesSettings"]["modules"];
|
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)
|
for (auto& mod_set : modules_sett)
|
||||||
{
|
{
|
||||||
uint32_t mod_id = hexstring_to_int(mod_set["id"].get<string>());
|
uint32_t mod_id = hexstring_to_int(mod_set["id"].get<string>());
|
||||||
string mod_name = mod_set["name"].get<string>();
|
|
||||||
|
|
||||||
auto module_ptr = ProfinetModule::Create(mod_id, mod_name);
|
///Модуль
|
||||||
if (module_ptr == nullptr)
|
auto& mod = p_data_map->p_mods[module_index];
|
||||||
|
|
||||||
|
++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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& submodules_sett = mod_set["submodules"];
|
mod.submods_nbr = submudules_number;
|
||||||
|
|
||||||
|
uint32_t submodules_index = 0;
|
||||||
|
|
||||||
for (auto& submod_sett : submodules_sett)
|
for (auto& submod_sett : submodules_sett)
|
||||||
{
|
{
|
||||||
uint32_t submod_id = hexstring_to_int(submod_sett["id"].get<string>());
|
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;
|
auto& submod = mod.p_submods[submodules_index];
|
||||||
|
|
||||||
data_cfg.data_dir = programconf_dataDirConvert(submod_sett["cyc_data_dir"].get<string>());
|
++submodules_index;
|
||||||
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);
|
/// Копируем данные подмодуля
|
||||||
|
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 (submodule_ptr == nullptr)
|
/// Выделяем память под циклические данные
|
||||||
|
if (submod.cyc_indata_len)
|
||||||
|
{
|
||||||
|
uint8_t * p_data = shared_data.allocateData<uint8_t>(submod.cyc_indata_len);
|
||||||
|
if (p_data == nullptr)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
submod.inp_data.Init(p_data, submod.cyc_indata_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
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"];
|
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)
|
for (auto& parm_sett : params_sett)
|
||||||
{
|
{
|
||||||
uint32_t index = parm_sett["index"].get<int>();
|
auto& param = submod.p_params[params_index];
|
||||||
string parm_name = parm_sett["name"].get<string>();
|
++params_index;
|
||||||
uint16_t length = parm_sett["data_size"].get<int>();
|
|
||||||
|
|
||||||
auto submod_param_ptr = ProfinetParameter::Create(index, parm_name, length);
|
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 (submod_param_ptr == nullptr)
|
if (param.length == 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
/// Выделяем память для параметров
|
||||||
submodule_ptr->addParameter(submod_param_ptr);
|
uint8_t * p_data = shared_data.allocateData<uint8_t>(param.length);
|
||||||
|
if (p_data == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
param.data.Init(p_data, param.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
module_ptr->addSubmodule(submodule_ptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
modules_vect.push_back(module_ptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
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;
|
json j;
|
||||||
|
|
||||||
@ -231,7 +312,7 @@ bool programconf_getProfinetDeviceSettings(std::string file_path, ProfinetDevice
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!programconf_getProfinetModulesSettings(j, modules_vect))
|
if (!programconf_getProfinetModulesSettings(j, p_data_map, shared_data))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,9 +5,10 @@
|
|||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
#include "profinet_settings.hpp"
|
#include "profinet_settings.hpp"
|
||||||
#include "profinet_module.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_getProfinetSettings(std::string file_path, ProfinetSettings& pn_sett);
|
||||||
|
|
||||||
bool programconf_getProfinetDeviceSettings(std::string file_path, ProfinetDeviceSettings& pn_sett,
|
bool programconf_getProfinetDeviceSettings(std::string file_path, ProfinetDeviceSettings& pn_sett,
|
||||||
std::vector<std::shared_ptr<ProfinetModule>>& modules_vect);
|
ProfinetData_Map * p_data_map, ProfinetSharedData& shared_data);
|
||||||
|
|
||||||
|
|||||||
@ -4,24 +4,42 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
class Flags {
|
class Flags {
|
||||||
public:
|
public:
|
||||||
Flags() : flags_{0} {}
|
Flags() : flags_{0} {
|
||||||
|
///Блокируем мьютекс ожидания
|
||||||
|
mutex_wait_.lock();
|
||||||
|
}
|
||||||
|
|
||||||
void set_flag(uint32_t mask) {
|
void set_flag(uint32_t mask) {
|
||||||
|
{
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
flags_ |= mask;
|
flags_ |= mask;
|
||||||
flag_cond_.notify_one();
|
}
|
||||||
|
/// Разблокируем мьютекс ожидания
|
||||||
|
mutex_wait_.unlock();
|
||||||
|
//flag_cond_.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t get_flag(uint32_t mask) {
|
uint32_t get_flag(uint32_t mask) {
|
||||||
|
uint32_t out;
|
||||||
|
{
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
return flags_ & mask;
|
out = flags_ & mask;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t get_flags() {
|
uint32_t get_flags() {
|
||||||
|
uint32_t out;
|
||||||
|
{
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
return flags_;
|
out = flags_;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear_flags(uint32_t mask)
|
void clear_flags(uint32_t mask)
|
||||||
@ -32,14 +50,32 @@ public:
|
|||||||
|
|
||||||
uint32_t wait_flags(uint32_t mask)
|
uint32_t wait_flags(uint32_t mask)
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lk(mutex_);
|
mutex_wait_.lock(); //Ожидаем изменения данных
|
||||||
flag_cond_.wait(lk, [&]{return flags_ & mask;});
|
uint32_t out;
|
||||||
lk.unlock();
|
|
||||||
return flags_;
|
{
|
||||||
|
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:
|
private:
|
||||||
uint32_t flags_;
|
uint32_t flags_;
|
||||||
mutable std::mutex mutex_;
|
std::mutex mutex_;
|
||||||
std::condition_variable flag_cond_;
|
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 "../libs/include/pnet_api.h"
|
||||||
#include "profinet.hpp"
|
#include "profinet.hpp"
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "program_config.hpp"
|
#include "program_config.hpp"
|
||||||
|
#include "app.hpp"
|
||||||
|
|
||||||
#define ECHO_MODULE_ID 0x00000040 /// Идентификатор тестового модуля
|
#include "./interprocess/profinet_shared_data.hpp"
|
||||||
#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;
|
App app;
|
||||||
|
|
||||||
uint32_t Echo_Gain = 0;
|
|
||||||
|
|
||||||
struct EchoData {
|
|
||||||
float data_f;
|
|
||||||
uint32_t data_i;
|
|
||||||
};
|
|
||||||
|
|
||||||
union EchoDataMem
|
|
||||||
{
|
|
||||||
EchoData data;
|
|
||||||
uint8_t mem[sizeof(EchoData)];
|
|
||||||
};
|
|
||||||
|
|
||||||
EchoDataMem Echo_inpCycData;
|
|
||||||
EchoDataMem Echo_outCycData;
|
|
||||||
|
|
||||||
uint8_t be_data_inp[sizeof(EchoData)];
|
|
||||||
|
|
||||||
void endian_convert_32(uint8_t * p_data)
|
|
||||||
{
|
|
||||||
uint8_t tmp = p_data[1];
|
|
||||||
p_data[1] = p_data[2];
|
|
||||||
p_data[2] = tmp;
|
|
||||||
tmp = p_data[0];
|
|
||||||
p_data[0] = p_data[3];
|
|
||||||
p_data[3] = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char * argv[])
|
int main(int argc, char * argv[])
|
||||||
{
|
{
|
||||||
Echo_inpCycData.data.data_f = 0.0f; /// Преобразовать в big endian
|
app.Init("program_configure.json");
|
||||||
Echo_inpCycData.data.data_i = 0; /// Преобразовать в big endian
|
|
||||||
|
|
||||||
Echo_outCycData.data.data_f = 0.0f;
|
app.Run();
|
||||||
Echo_outCycData.data.data_i = 0;
|
|
||||||
|
|
||||||
ProfinetSettings profinet_settings;
|
|
||||||
ProfinetDeviceSettings profinet_dev_settings;
|
|
||||||
|
|
||||||
/// Читаем настройки из файла
|
|
||||||
if (!programconf_getProfinetSettings("program_configure.json", profinet_settings))
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<ProfinetModule>> Modules;
|
|
||||||
|
|
||||||
/// Читаем настройки из файла
|
|
||||||
if (!programconf_getProfinetDeviceSettings(profinet_settings.profinet_device_config, profinet_dev_settings, Modules))
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Profinet profinet;
|
|
||||||
/// Настройка Profinet: Инициализация pnet, добавление DAP слотов и подслотов и подключение к ним модулей и подмодулей,
|
|
||||||
if (!profinet.Config(profinet_settings, profinet_dev_settings, Modules))
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Циклические данные устройства передаются с определенным периодом, сообщения от контроллера при этом не требуются.
|
|
||||||
*/
|
|
||||||
/// Запуск потока Profinet
|
|
||||||
profinet.Start();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
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,
|
bool Profinet::Config(ProfinetSettings& Settings,
|
||||||
ProfinetDeviceSettings& DevSettings,
|
ProfinetDeviceSettings& DevSettings,
|
||||||
std::vector<std::shared_ptr<ProfinetModule>>& Modules)
|
ProfinetData_Map * const p_data_map)
|
||||||
{
|
{
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
pnet_cfg_t pnet_cfg = {0};
|
pnet_cfg_t pnet_cfg = {0};
|
||||||
@ -213,15 +242,17 @@ bool Profinet::Config(ProfinetSettings& Settings,
|
|||||||
/// Создаем обязательный модуль DAP
|
/// Создаем обязательный модуль DAP
|
||||||
auto dap_module = ProfinetModule::Create(PNET_MOD_DAP_IDENT, "DAP 1");
|
auto dap_module = ProfinetModule::Create(PNET_MOD_DAP_IDENT, "DAP 1");
|
||||||
/// Создаем обязательный подмодуль DAP Identity 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
|
/// Создаем обязательный подмодуль 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
|
/// Создаем обязательный подмодуль 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_indent_1);
|
||||||
ret&= dap_module->addSubmodule(dap_submodule_iface_1_indent_1);
|
ret&= dap_module->addSubmodule(dap_submodule_iface_1_indent_1);
|
||||||
ret&= dap_module->addSubmodule(dap_subslot_iface_1_port1_ident);
|
ret&= dap_module->addSubmodule(dap_subslot_iface_1_port1_ident);
|
||||||
|
/// Добавляем модуль в конфигурацию
|
||||||
|
addModule(dap_module);
|
||||||
|
|
||||||
/// Создаем слот для модуля DAP и доключаем к нему модуль dap_module
|
/// Создаем слот для модуля DAP и доключаем к нему модуль dap_module
|
||||||
ret&= addSlotAndPlugModule(PNET_SLOT_DAP_IDENT, dap_module);
|
ret&= addSlotAndPlugModule(PNET_SLOT_DAP_IDENT, dap_module);
|
||||||
@ -233,21 +264,8 @@ bool Profinet::Config(ProfinetSettings& Settings,
|
|||||||
/// 3. Подмодуль "DAP Port 1"
|
/// 3. Подмодуль "DAP Port 1"
|
||||||
ret&= addSubslotAndPlugSubmodule(PNET_SLOT_DAP_IDENT, PNET_SUBSLOT_DAP_INTERFACE_1_PORT_1_IDENT, dap_subslot_iface_1_port1_ident);
|
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)
|
createInternalStucture(p_data_map);
|
||||||
{
|
|
||||||
if (module_ptr == nullptr)
|
|
||||||
{
|
|
||||||
ret = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!addModule(module_ptr))
|
|
||||||
{
|
|
||||||
ret = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -558,13 +576,19 @@ void Profinet::PeriodicOperations()
|
|||||||
{
|
{
|
||||||
pnet_application_ready (m_pnet_data.pnet_ptr, m_pnet_data.arep_for_appl_ready);
|
pnet_application_ready (m_pnet_data.pnet_ptr, m_pnet_data.arep_for_appl_ready);
|
||||||
flags_.clear_flags(FLAGS_AREP_APP_READY);
|
flags_.clear_flags(FLAGS_AREP_APP_READY);
|
||||||
LOG("FLAGS_AREP_APP_READY");
|
LOG("PeriodicOperations: FLAGS_AREP_APP_READY");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Profinet::cyclicIoData()
|
void Profinet::cyclicIoData()
|
||||||
{
|
{
|
||||||
|
///Циклический обмен только когда установлено соединение
|
||||||
|
if (!isConnectedToController())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (auto slot : m_slots)
|
for (auto slot : m_slots)
|
||||||
{
|
{
|
||||||
shared_ptr<ProfinetSlot>& slot_ptr = slot.second;
|
shared_ptr<ProfinetSlot>& slot_ptr = slot.second;
|
||||||
@ -603,7 +627,7 @@ void Profinet::cyclicIoData()
|
|||||||
{
|
{
|
||||||
uint8_t indata_iocs;
|
uint8_t indata_iocs;
|
||||||
uint8_t indata_iops = PNET_IOXS_GOOD;
|
uint8_t indata_iops = PNET_IOXS_GOOD;
|
||||||
|
//LOG("CYC: Put data to PLC");
|
||||||
/**
|
/**
|
||||||
* @brief Отправляем данные подмодуля в ПЛК
|
* @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;
|
m_pnet_data.arep = UINT32_MAX;
|
||||||
|
|
||||||
@ -813,11 +837,11 @@ int Profinet::callbackReadInd ( uint32_t arep,
|
|||||||
|
|
||||||
///3. Передаем данные параметра
|
///3. Передаем данные параметра
|
||||||
/**
|
/**
|
||||||
* По идее тут нужно утановить мьютекс lock, а unlock сделать потом, когда данные будет причитаны стеком pnet
|
* В функции getReadBufPtr данные копируются во временный буфер и уже затем передаются в стек pnet.
|
||||||
* Проблема в том, что нет ни одного события которое бы сообщало об окончании процесса чтения данных стеком pnet.
|
* Это нужно из-за отсутствия события которое бы сообщало об окончании процесса чтения данных стеком pnet.
|
||||||
* Нужно поисследовать.
|
*
|
||||||
*/
|
*/
|
||||||
*pp_read_data = param_ptr->getDataPointer();
|
*pp_read_data = param_ptr->getReadBufPtr();
|
||||||
*p_read_length = param_ptr->length;
|
*p_read_length = param_ptr->length;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -15,6 +15,8 @@
|
|||||||
#include "../log/log.hpp"
|
#include "../log/log.hpp"
|
||||||
#include "../flags/flags.hpp"
|
#include "../flags/flags.hpp"
|
||||||
|
|
||||||
|
#include "profinet_data_map.hpp"
|
||||||
|
|
||||||
|
|
||||||
class Profinet {
|
class Profinet {
|
||||||
public:
|
public:
|
||||||
@ -23,13 +25,14 @@ public:
|
|||||||
EVENT_CONNECTION_ESTABLISHED = 0x00000001, /// Установлено соединение с ПЛК
|
EVENT_CONNECTION_ESTABLISHED = 0x00000001, /// Установлено соединение с ПЛК
|
||||||
EVENT_NEW_CYCLIC_DATA = 0x00000002, /// Получение новых циклических данных от ПЛК
|
EVENT_NEW_CYCLIC_DATA = 0x00000002, /// Получение новых циклических данных от ПЛК
|
||||||
EVENT_NEW_PARAM_DATA = 0x00000004, /// Новая запись параметра от ПЛК
|
EVENT_NEW_PARAM_DATA = 0x00000004, /// Новая запись параметра от ПЛК
|
||||||
|
EVENT_CONNECTION_ABORT = 0x00000008
|
||||||
};
|
};
|
||||||
|
|
||||||
Profinet();
|
Profinet();
|
||||||
|
|
||||||
bool Config(ProfinetSettings& Settings,
|
bool Config(ProfinetSettings& Settings,
|
||||||
ProfinetDeviceSettings& DevSettings,
|
ProfinetDeviceSettings& DevSettings,
|
||||||
std::vector<std::shared_ptr<ProfinetModule>>& Modules);
|
ProfinetData_Map * const p_data_map);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Добавляет ранее созданный слот slot_ptr
|
* @brief Добавляет ранее созданный слот slot_ptr
|
||||||
@ -154,7 +157,6 @@ public:
|
|||||||
|
|
||||||
void EventClear(uint32_t mask) { events_.clear_flags(mask); }
|
void EventClear(uint32_t mask) { events_.clear_flags(mask); }
|
||||||
|
|
||||||
|
|
||||||
~Profinet();
|
~Profinet();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -216,6 +218,8 @@ private:
|
|||||||
|
|
||||||
void cyclicIoData();
|
void cyclicIoData();
|
||||||
|
|
||||||
|
void createInternalStucture(ProfinetData_Map * const p_data_map);
|
||||||
|
|
||||||
public:
|
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 "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),
|
index(Index),
|
||||||
name(Name),
|
name(Name),
|
||||||
length(Length),
|
length(p_data->getSize()),
|
||||||
data_ptr_(new uint8_t[Length])
|
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)
|
bool ProfinetParameter::readParameter(uint8_t * const data_ptr)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> guard(data_mutex_); /// Синхронизация чтения и записи
|
if ((data_ptr == nullptr) || (p_data_ == nullptr))
|
||||||
|
|
||||||
if (data_ptr == nullptr)
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(data_ptr, data_ptr_.get(), length);
|
p_data_->Read(0, (void*)data_ptr, length);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProfinetParameter::writeParameter(const uint8_t * data_ptr)
|
bool ProfinetParameter::writeParameter(const uint8_t * data_ptr)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> guard(data_mutex_); /// Синхронизация чтения и записи
|
if ((data_ptr == nullptr) || (p_data_ == nullptr))
|
||||||
|
|
||||||
if (data_ptr == nullptr)
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(data_ptr_.get(), data_ptr, length);
|
p_data_->Write(0, (void*)data_ptr, length);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t * ProfinetParameter::getDataPointer()
|
uint8_t * ProfinetParameter::getReadBufPtr()
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @brief Слабое утешение от ситуации когда приложение записывает данные в параметр, а контроллер хочет этот параметр
|
* @brief Cитуации когда приложение записывает данные в параметр, а контроллер хочет этот параметр
|
||||||
* прочитать и вызвался коллбэк profinet_cb_read. В этом случае будем тут висеть пока приложение не запишет параметр.
|
* прочитать и вызвался коллбэк 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
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
#include "../shared_data/shared_data.hpp"
|
||||||
|
|
||||||
|
|
||||||
class ProfinetParameter
|
class ProfinetParameter
|
||||||
{
|
{
|
||||||
public:
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProfinetParameter(uint32_t Index, std::string Name, uint16_t Length);
|
return std::shared_ptr<ProfinetParameter>(new ProfinetParameter(Index, Name, p_data));
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfinetParameter(uint32_t Index, std::string Name, SharedData * p_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Защищенная запись данных параметра
|
* @brief Защищенная запись данных параметра
|
||||||
@ -39,17 +45,18 @@ public:
|
|||||||
* Для этого ему в колбэке нужно предоставить указатель на данные параметра и длину данных.
|
* Для этого ему в колбэке нужно предоставить указатель на данные параметра и длину данных.
|
||||||
* Подразумевается, что эта функция будет использоваться только внутри колбэка стека pnet.
|
* Подразумевается, что эта функция будет использоваться только внутри колбэка стека pnet.
|
||||||
* см. profinet_cb_read.cpp
|
* см. profinet_cb_read.cpp
|
||||||
* Не использовать эту функцию в приложении!!!
|
|
||||||
* @return uint8_t*
|
* @return uint8_t*
|
||||||
*/
|
*/
|
||||||
uint8_t * getDataPointer();
|
uint8_t * getReadBufPtr();
|
||||||
|
private:
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
const std::string name; /// Имя параметра
|
const std::string name; /// Имя параметра
|
||||||
const uint32_t index; /// Идентфикатор параметра
|
const uint32_t index; /// Идентфикатор параметра
|
||||||
const uint16_t length; /// длина параметра в байтах
|
const uint16_t length; /// длина параметра в байтах
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<uint8_t[]> data_ptr_; /// указатель на данные параметра
|
std::shared_ptr<uint8_t[]> read_buf_; ///Буфер данных для передачи его в стек pnet для операции чтения
|
||||||
mutable std::mutex data_mutex_; /// Доступ к данным
|
SharedData * const p_data_; ///Указатель на данные параметра(в разделяемой области)
|
||||||
};
|
};
|
||||||
@ -3,25 +3,34 @@
|
|||||||
|
|
||||||
ProfinetSubmodule::ProfinetSubmodule(uint32_t submodule_id,
|
ProfinetSubmodule::ProfinetSubmodule(uint32_t submodule_id,
|
||||||
std::string submodule_name,
|
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_id(submodule_id),
|
||||||
m_name(submodule_name),
|
m_name(submodule_name),
|
||||||
m_data_cfg(submodule_data_cfg),
|
m_data_cfg(submodule_data_cfg),
|
||||||
inp_data_ptr_(nullptr),
|
inp_data_ptr_(inp_data_ptr),
|
||||||
out_data_ptr_(nullptr),
|
out_data_ptr_(out_data_ptr),
|
||||||
m_indata_iocs(0),
|
m_indata_iocs(0),
|
||||||
m_outdata_iops(0),
|
m_outdata_iops(0),
|
||||||
data_updated_(false)
|
data_updated_(false),
|
||||||
|
out_data_buf_(nullptr),
|
||||||
|
inp_data_buf_(nullptr)
|
||||||
{
|
{
|
||||||
/// Могут быть подмодули без данных, например DAP
|
if (m_data_cfg.outsize && (out_data_ptr_!=nullptr))
|
||||||
if (m_data_cfg.insize > 0)
|
|
||||||
{
|
{
|
||||||
inp_data_ptr_ = std::shared_ptr<uint8_t[]>(new uint8_t[m_data_cfg.insize]);
|
if (m_data_cfg.outsize == 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,
|
uint16_t subslot_nbr,
|
||||||
uint8_t iops)
|
uint8_t iops)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> guard(inp_data_mutex_);
|
|
||||||
|
|
||||||
if ((pnet_ptr == nullptr) ||
|
if ((pnet_ptr == nullptr) ||
|
||||||
((inp_data_ptr_ == nullptr) && m_data_cfg.insize > 0))
|
((inp_data_buf_ == nullptr) && (m_data_cfg.insize > 0)))
|
||||||
{
|
{
|
||||||
return false;
|
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,
|
pnet_input_set_data_and_iops( pnet_ptr,
|
||||||
api,
|
api,
|
||||||
slot_nbr,
|
slot_nbr,
|
||||||
subslot_nbr,
|
subslot_nbr,
|
||||||
inp_data_ptr_.get(),
|
inp_data_buf_.get(),
|
||||||
m_data_cfg.insize,
|
m_data_cfg.insize,
|
||||||
iops);
|
iops);
|
||||||
|
|
||||||
@ -81,27 +95,33 @@ bool ProfinetSubmodule::outputGetData( pnet_t * pnet_ptr,
|
|||||||
bool out = true;
|
bool out = true;
|
||||||
|
|
||||||
/// Существуют подмодули без данных, например DAP
|
/// Существуют подмодули без данных, например DAP
|
||||||
if ((pnet_ptr == nullptr) || (out_data_ptr_ == nullptr))
|
if ((pnet_ptr == nullptr) || ((out_data_ptr_ == nullptr) && (m_data_cfg.outsize > 0)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> guard(out_data_mutex_);
|
|
||||||
|
|
||||||
uint16_t outdata_length = m_data_cfg.outsize;
|
uint16_t outdata_length = m_data_cfg.outsize;
|
||||||
uint8_t outdata_iops;
|
uint8_t outdata_iops;
|
||||||
bool updated;
|
bool updated{false};
|
||||||
|
|
||||||
/// Копируем данные от полученные от контроллера
|
|
||||||
|
/// Копируем данные полученные от контроллера
|
||||||
pnet_output_get_data_and_iops (pnet_ptr,
|
pnet_output_get_data_and_iops (pnet_ptr,
|
||||||
api,
|
api,
|
||||||
slot_nbr,
|
slot_nbr,
|
||||||
subslot_nbr,
|
subslot_nbr,
|
||||||
&updated,
|
&updated,
|
||||||
out_data_ptr_.get(),
|
out_data_buf_.get(),
|
||||||
&outdata_length,
|
&outdata_length,
|
||||||
&outdata_iops);
|
&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;
|
m_outdata_iops = outdata_iops;
|
||||||
|
|
||||||
if ((!data_updated_) && (updated == true))
|
if ((!data_updated_) && (updated == true))
|
||||||
@ -126,33 +146,3 @@ bool ProfinetSubmodule::outputGetData( pnet_t * pnet_ptr,
|
|||||||
|
|
||||||
return out;
|
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 <mutex>
|
||||||
|
|
||||||
#include "profinet_parameter.hpp"
|
#include "profinet_parameter.hpp"
|
||||||
|
#include "../shared_data/shared_data.hpp"
|
||||||
|
|
||||||
#include "../../libs/include/pnet_api.h"
|
#include "../../libs/include/pnet_api.h"
|
||||||
|
|
||||||
@ -15,16 +16,22 @@ class ProfinetSubmodule {
|
|||||||
public:
|
public:
|
||||||
static std::shared_ptr<ProfinetSubmodule> Create(uint32_t submodule_id,
|
static std::shared_ptr<ProfinetSubmodule> Create(uint32_t submodule_id,
|
||||||
std::string submodule_name,
|
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,
|
return std::shared_ptr<ProfinetSubmodule>(new ProfinetSubmodule(submodule_id,
|
||||||
submodule_name,
|
submodule_name,
|
||||||
submodule_data_cfg));
|
submodule_data_cfg,
|
||||||
|
inp_data_ptr,
|
||||||
|
out_data_ptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
ProfinetSubmodule(uint32_t submodule_id,
|
ProfinetSubmodule(uint32_t submodule_id,
|
||||||
std::string submodule_name,
|
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);
|
bool addParameter(std::shared_ptr<ProfinetParameter>& param);
|
||||||
|
|
||||||
@ -41,11 +48,13 @@ public:
|
|||||||
uint16_t slot_nbr,
|
uint16_t slot_nbr,
|
||||||
uint16_t subslot_nbr);
|
uint16_t subslot_nbr);
|
||||||
|
|
||||||
bool getDataFromPlc(uint8_t * data_ptr);
|
bool isDataUpdated() {
|
||||||
|
bool out{data_updated_};
|
||||||
|
|
||||||
bool putDataToPlc (uint8_t * data_ptr);
|
data_updated_ = false;
|
||||||
|
|
||||||
bool isDataUpdated() { return data_updated_; };
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const uint32_t m_id; /// Идентификатор подмодуля
|
const uint32_t m_id; /// Идентификатор подмодуля
|
||||||
@ -63,11 +72,12 @@ private:
|
|||||||
/// Набор параметров подмодуля
|
/// Набор параметров подмодуля
|
||||||
std::map<uint32_t, std::shared_ptr<ProfinetParameter>> m_params;
|
std::map<uint32_t, std::shared_ptr<ProfinetParameter>> m_params;
|
||||||
|
|
||||||
std::shared_ptr<uint8_t[]> inp_data_ptr_; /// Входные циклические данные (DEV->PLC)
|
/// Буферы для взаимодействия с pnet и решения вопросов синхронизации
|
||||||
std::mutex inp_data_mutex_;
|
std::shared_ptr<uint8_t[]> out_data_buf_;
|
||||||
|
std::shared_ptr<uint8_t[]> inp_data_buf_;
|
||||||
|
|
||||||
std::shared_ptr<uint8_t[]> out_data_ptr_; /// Выходные циклические данные (PLC->DEV)
|
SharedData * const inp_data_ptr_; /// Входные циклические данные (DEV->PLC)
|
||||||
std::mutex out_data_mutex_;
|
SharedData * const out_data_ptr_; /// Выходные циклические данные (PLC->DEV)
|
||||||
|
|
||||||
bool data_updated_;
|
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