dev(UML-981): Добавил новые rpc json запросы

Эти запросы позволяют получать именованные данные датаграммы profinet
This commit is contained in:
Vadim Sychev 2022-08-26 14:43:30 +03:00
parent 1f86ae7b2a
commit fe46a8b03f
21 changed files with 1489 additions and 164 deletions

View File

@ -8,14 +8,26 @@
#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
#define BIT_MODULE_ID 0x00000032
#define BIT_SUBMOD_ID 0x00000132
#define BIT_INPUT_DATA_SIZE 1
#define BIT_OUTPUT_DATA_SIZE 1
#define BIT_PARAMETER_1_IDX 123
#define BIT_PARAMETER_2_IDX 124
using json = nlohmann::json;
uint32_t Echo_Gain = 0;
uint32_t bit_param_1 = 0;
uint32_t bit_param_2 = 0;
struct EchoData {
float data_f;
uint32_t data_i;
@ -50,6 +62,53 @@ std::shared_ptr< std::ostream > p_output_stream_;
using json = nlohmann::json;
void getParameter(json& jx, std::vector<uint8_t>& in_vect, uint8_t * pParam, uint16_t param_size)
{
in_vect.clear();
in_vect = jx["data"]["bytes"].get<std::vector<uint8_t>>();
if (in_vect.size() == param_size)
{
std::copy(in_vect.begin(), in_vect.end(), pParam);
if (param_size == 4)
{
endian_convert_32(pParam);
}
}
}
void requestParam(json& j_request, uint32_t mod_id, uint32_t submod_id, uint32_t param_idx)
{
json j_data;
j_data["module_id"] = mod_id;
j_data["submodule_id"] = submod_id;
j_data["param_index"] = param_idx;
j_request["params"].push_back(j_data);
}
void requestCyclic(json& j_request, uint32_t mod_id, uint32_t submod_id)
{
json j_data;
j_data["module_id"] = mod_id;
j_data["submodule_id"] = submod_id;
j_request["params"].push_back(j_data);
}
void putCyclicData(json& j_cyc_data, uint32_t mod_id, uint32_t submod_id, uint8_t * pdata, uint16_t data_size)
{
json j_set_data;
std::vector<uint8_t> in_data;
in_data.insert(in_data.end(), &pdata[0], &pdata[data_size]);
j_set_data["module_id"] = mod_id;
j_set_data["submodule_id"] = submod_id;
j_set_data["data"] = json::binary(in_data);
j_cyc_data["params"].push_back(j_set_data);
}
int main(int argc, char * argv[])
{
json j_cfg = json::parse(fileapi_read_file("sample_app_echo_config.json"));
@ -70,29 +129,28 @@ int main(int argc, char * argv[])
json j_get_echo_param_gain;
j_get_echo_param_gain["jsonrpc"] = "2.0";
j_get_echo_param_gain["method"] = "readSubmoduleParameter";
j_get_echo_param_gain["method"] = "readSubmoduleParameterNamed";
j_get_echo_param_gain["params"] = json::array();
json j_data;
j_data["module_id"] = ECHO_MODULE_ID;
j_data["submodule_id"] = ECHO_SUBMOD_ID;
j_data["param_index"] = ECHO_PARAMETER_GAIN_IDX;
j_get_echo_param_gain["params"].push_back(j_data);
requestParam(j_get_echo_param_gain, ECHO_MODULE_ID, ECHO_SUBMOD_ID, ECHO_PARAMETER_GAIN_IDX);
requestParam(j_get_echo_param_gain, BIT_MODULE_ID, BIT_SUBMOD_ID, BIT_PARAMETER_1_IDX);
requestParam(j_get_echo_param_gain, BIT_MODULE_ID, BIT_SUBMOD_ID, BIT_PARAMETER_2_IDX);
j_get_echo_param_gain["id"] = 0;
json j_data;
json j_get_echo_data;
j_get_echo_data["jsonrpc"] = "2.0";
j_get_echo_data["method"] = "readCyclicIoData";
j_get_echo_data["method"] = "readCyclicIoDataNamed";
j_get_echo_data["params"] = json::array();
j_data.clear();
j_data["module_id"] = ECHO_MODULE_ID;
j_data["submodule_id"] = ECHO_SUBMOD_ID;
j_get_echo_data["params"].push_back(j_data);
requestCyclic(j_get_echo_data, ECHO_MODULE_ID, ECHO_SUBMOD_ID);
requestCyclic(j_get_echo_data, BIT_MODULE_ID, BIT_SUBMOD_ID);
j_get_echo_data["id"] = 0;
json j_set_echo_data;
j_set_echo_data["jsonrpc"] = "2.0";
j_set_echo_data["method"] = "writeCyclicIoData";
j_set_echo_data["method"] = "writeCyclicIoDataNamed";
j_set_echo_data["params"] = json::array();
j_set_echo_data["id"] = 0;
@ -103,7 +161,6 @@ int main(int argc, char * argv[])
answer_str.clear();
std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::string request{j_get_flags.dump()};
///Запрос:
*p_output_stream_ << request << std::endl;
@ -141,12 +198,14 @@ int main(int argc, char * argv[])
if (j_res["EVENT_NEW_PARAM_DATA"].get<bool>() == true)
{
answer_str.clear();
std::cout << "EVENT_NEW_PARAM_DATA: true" << std::endl;
std::cout << "EVENT_NEW_PARAM_DATA: true" << std::endl;
*p_output_stream_ << j_get_echo_param_gain.dump() << std::endl;
std::getline( *p_input_stream_.get( ), answer_str);
json j_answ_par = json::parse(answer_str);
std::vector<uint8_t> in;
//std::cout << "EVENT_NEW_PARAM_DATA: " << answer_str << std::endl;
for (auto& jx : j_answ_par["result"])
{
uint32_t module_id;
@ -156,28 +215,81 @@ int main(int argc, char * argv[])
module_id = jx["module_id"].get<int>();
submodule_id = jx["submodule_id"].get<int>();
param_idx = jx["param_index"].get<int>();
if ((module_id != ECHO_MODULE_ID) || (submodule_id != ECHO_SUBMOD_ID))
switch(module_id)
{
std::cout << "Error id" << std::endl;
continue;
}
case ECHO_MODULE_ID:
{
switch(submodule_id)
{
case ECHO_SUBMOD_ID:
{
if (param_idx != ECHO_PARAMETER_GAIN_IDX)
{
std::cout << "Error param idx" << std::endl;
continue;
}
if (param_idx != ECHO_PARAMETER_GAIN_IDX)
{
std::cout << "Error param idx" << std::endl;
continue;
}
in.clear();
in = jx["data"]["bytes"].get<std::vector<uint8_t>>();
Echo_Gain = jx.at("data").at("EchoGain").get<uint32_t>();
if (in.size() == 4)
{
std::copy(in.begin(), in.end(), (uint8_t*)&Echo_Gain);
}
endian_convert_32((uint8_t*)&Echo_Gain);
std::cout << "Echo_Gain = " << Echo_Gain << std::endl;
}
break;
default:
{
std::cout << "Error submodule id" << std::endl;
continue;
}
break;
}
}
break;
case BIT_MODULE_ID:
{
switch(submodule_id)
{
case BIT_SUBMOD_ID:
{
switch(param_idx)
{
case BIT_PARAMETER_1_IDX:
{
bit_param_1 = jx.at("data").at("I8O8Param1").get<uint32_t>();
std::cout << "bit_param_1 = " << bit_param_1 << std::endl;
}
break;
case BIT_PARAMETER_2_IDX:
{
bit_param_2 = jx.at("data").at("I8O8Param2").get<uint32_t>();
std::cout << "Echo_Gain = " << Echo_Gain << std::endl;
std::cout << "bit_param_2 = " << bit_param_2 << std::endl;
}
break;
default:
{
std::cout << "Error param idx" << std::endl;
continue;
}
break;
}
}
break;
default:
{
std::cout << "Error submodule id" << std::endl;
continue;
}
break;
}
}
break;
default:
{
std::cout << "Error module id" << std::endl;
continue;
}
break;
}
}
}
@ -186,6 +298,8 @@ int main(int argc, char * argv[])
static uint32_t value_i = 0;
static float value_f = 0.0;
static uint32_t value_bit = 0;
answer_str.clear();
*p_output_stream_ << j_get_echo_data.dump() << std::endl;
std::getline( *p_input_stream_.get( ), answer_str);
@ -200,61 +314,105 @@ int main(int argc, char * argv[])
module_id = jx["module_id"].get<int>();
submodule_id = jx["submodule_id"].get<int>();
if ((module_id != ECHO_MODULE_ID) || (submodule_id != ECHO_SUBMOD_ID))
switch(module_id)
{
std::cout << "Error id" << std::endl;
continue;
case ECHO_MODULE_ID:
{
switch(submodule_id)
{
case ECHO_SUBMOD_ID:
{
Echo_outCycData.data.data_f = jx.at("data").at("EchoModuleOutFloat").get<float>();
Echo_outCycData.data.data_i = jx.at("data").at("EchoModuleOutInt").get<uint32_t>();
if (value_i != Echo_outCycData.data.data_i)
{
value_i = Echo_outCycData.data.data_i;
std::cout << "New data_i from PLC: " << value_i << std::endl;
}
if (value_f != Echo_outCycData.data.data_f)
{
value_f = Echo_outCycData.data.data_f;
std::cout << "New data_f from PLC: " << value_f << std::endl;
}
///Подготавливаем данные для ПЛК
Echo_inpCycData.data.data_i = Echo_Gain * Echo_outCycData.data.data_i;
Echo_inpCycData.data.data_f = Echo_Gain * Echo_outCycData.data.data_f;
json j_set_data;
j_set_data["module_id"] = module_id;
j_set_data["submodule_id"] = submodule_id;
j_set_data["data"]["EchoModuleInpFloat"] = Echo_inpCycData.data.data_f;
j_set_data["data"]["EchoModuleInpInt"] = Echo_inpCycData.data.data_i;
j_set_echo_data["params"].push_back(j_set_data);
}
break;
default:
{
std::cout << "Error submodule id" << std::endl;
continue;
}
break;
}
}
break;
case BIT_MODULE_ID:
{
switch(submodule_id)
{
case BIT_SUBMOD_ID:
{
uint8_t bits = 0;
/*in = jx["data"]["bytes"].get<std::vector<uint8_t>>();
if (in.size() == 1)
{
std::copy(in.begin(), in.end(), &bits);
}
if (bits != value_bit)
{
value_bit = bits;
std::cout << "New bit data from PLC: " << value_bit << std::endl;
}
bits = ~bits;
putCyclicData(j_set_echo_data, module_id, submodule_id, &bits, 1);*/
}
break;
default:
{
std::cout << "Error submodule id" << std::endl;
continue;
}
break;
}
}
break;
default:
{
std::cout << "Error module id" << std::endl;
continue;
}
break;
}
in = jx["data"]["bytes"].get<std::vector<uint8_t>>();
if (in.size() == 8)
{
std::copy(in.begin(), in.end(), Echo_outCycData.mem);
}
/// Конвертируем в литл эндиан
endian_convert_32((uint8_t*)&Echo_outCycData.data.data_i);
endian_convert_32((uint8_t*)&Echo_outCycData.data.data_f);
if (value_i != Echo_outCycData.data.data_i)
{
value_i = Echo_outCycData.data.data_i;
std::cout << "New data_i from PLC: " << value_i << std::endl;
}
if (value_f != Echo_outCycData.data.data_f)
{
value_f = Echo_outCycData.data.data_f;
std::cout << "New data_f from PLC: " << value_f << std::endl;
}
///Подготавливаем данные для ПЛК
Echo_inpCycData.data.data_i = Echo_Gain * Echo_outCycData.data.data_i;
Echo_inpCycData.data.data_f = Echo_Gain * Echo_outCycData.data.data_f;
///Конвертируем в биг эндиан
endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_i);
endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_f);
json j_set_data;
std::vector<uint8_t> in_data;
in_data.insert(in_data.end(), &Echo_inpCycData.mem[0], &Echo_inpCycData.mem[8]);
j_set_data["module_id"] = module_id;
j_set_data["submodule_id"] = submodule_id;
j_set_data["data"] = json::binary(in_data);
j_set_echo_data["params"].push_back(j_set_data);
///Записываем данные
*p_output_stream_ << j_set_echo_data.dump() << std::endl;
answer_str.clear();
///Ждем ответ
std::getline( *p_input_stream_.get( ), answer_str);
}
j_set_echo_data["params"].clear();
///Записываем данные
*p_output_stream_ << j_set_echo_data.dump() << std::endl;
std::cout << "OUT: " << j_set_echo_data.dump() << std::endl;
j_set_echo_data["params"].array().clear();
answer_str.clear();
///Ждем ответ
std::getline( *p_input_stream_.get( ), answer_str);
}
}
return 0;

View File

@ -14,6 +14,8 @@ include(./file_api/file_api.cmake)
include(./configuration/configuration.cmake)
include (./interprocess/interprocess.cmake)
include (./shared_data/shared_data.cmake)
include (./endian/endian.cmake)
include (./user_data/user_data.cmake)
set(SRC_FILES ${SRC_FILES} ./app.cpp)
@ -48,5 +50,5 @@ target_link_directories(${TARGET_NAME} PUBLIC ${LIBS_INSTALL_PATH}/lib/x86_64)
#Если не собирается с ошибкой линкера undefined reference pthread, то после ${Boost_LIBRARIES} добавить pthread или -lpthread:
#target_link_libraries (${TARGET_NAME} PUBLIC profinet osal ${Boost_LIBRARIES} pthread)
target_link_libraries (${TARGET_NAME} PUBLIC profinet osal ${Boost_LIBRARIES})
target_link_libraries (${TARGET_NAME} PUBLIC profinetd osald ${Boost_LIBRARIES})

View File

@ -17,7 +17,11 @@ bool App::Init(std::string profinet_config_file)
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_))
if (!programconf_getProfinetDeviceSettings(profinet_settings.profinet_device_config,
profinet_dev_settings,
p_profinetMap,
shared_data_,
user_data_map_))
{
return false;
}
@ -37,7 +41,7 @@ bool App::Init(std::string profinet_config_file)
/// Запуск потока Profinet
profinet_.Start();
/// Запуск потока чтения запросов по pipe
pipes_.Start(&profinet_);
pipes_.Start(&profinet_, &user_data_map_);
return true;
}

View File

@ -4,6 +4,7 @@
#include "./interprocess/shared_memory/profinet_shared_data.hpp"
#include "./interprocess/pipes/profinet_pipes.hpp"
#include <string>
#include "user_data.hpp"
class App {
public:
@ -13,4 +14,6 @@ private:
ProfinetPipes pipes_;
ProfinetSharedData shared_data_;
Profinet profinet_;
UserDataMap user_data_map_; /// Для конвертации данных из rpc json и обратно
};

View File

@ -163,6 +163,24 @@ static uint8_t programconf_dataDirConvert(string dir_str)
return out;
}
static int programconf_getDataSize(const json& j_in)
{
if (!j_in.is_array())
return 0;
int size = 0;
for (auto& j : j_in)
{
if (!j.contains("DataType"))
return 0;
size+= UserData::getDataSize(j.at("DataType").get<std::string>());
}
return size;
}
/**
* @brief Считаем общий размер данных требуемый всеми подмодулями: их циклическими данными и параметрами
* Пробегает по файлу конфигурации и считает размер требуемой памяти.
@ -171,47 +189,159 @@ static uint8_t programconf_dataDirConvert(string dir_str)
* @return true
* @return false
*/
static bool programconf_calc_memory_size(json& j, uint32_t& memory_size)
static bool programconf_calc_memory_size(const json& j, uint32_t& memory_size)
{
if (!j.contains("ProfinetDeviceModulesSettings"))
return false;
auto& modules_sett = j["ProfinetDeviceModulesSettings"]["modules"];
auto& modules_sett = j.at("ProfinetDeviceModulesSettings").at("modules");
memory_size = sizeof(ProfinetData_Map) + modules_sett.size() * sizeof(ProfinetData_Module);
for (auto& mod_set : modules_sett)
{
auto& submodules_sett = mod_set["submodules"];
auto& submodules_sett = mod_set.at("submodules");
memory_size += submodules_sett.size() * sizeof(ProfinetData_Submodule);
for (auto& submod_sett : submodules_sett)
{
uint16_t in_data_len = static_cast<uint16_t>(submod_sett["cyc_inp_data_size"].get<int>());
uint16_t out_data_len = static_cast<uint16_t>(submod_sett["cyc_out_data_size"].get<int>());
auto& cyclic_data = submod_sett.at("cyclic_data");
uint16_t in_data_len = programconf_getDataSize(cyclic_data.at("Inputs"));
uint16_t out_data_len = programconf_getDataSize(cyclic_data.at("Outputs"));
memory_size+= in_data_len + out_data_len;
///Параметры подмодуля
auto& params_sett = submod_sett["parameters"];
memory_size += params_sett.size() * sizeof(ProfinetData_Parameter);
///Параметры подмодуля
memory_size += submod_sett.at("parameters").size() * sizeof(ProfinetData_Parameter);
/// Записываем параметры в подмодуль
for (auto& parm_sett : params_sett)
{
uint16_t data_size = static_cast<uint16_t>(parm_sett["data_size"].get<int>());
memory_size += data_size;
}
memory_size += programconf_getDataSize(submod_sett.at("parameters"));
}
}
return true;
}
static bool programconf_getProfinetModulesSettings(json& j, ProfinetData_Map * p_data_map, ProfinetSharedData& shared_data)
static bool programconf_getCyclycDataDir(json& j_cyclic_data, uint8_t& dir)
{
if (!j_cyclic_data.contains("dir"))
{
return false;
}
dir = programconf_dataDirConvert(j_cyclic_data["dir"].get<string>());
return true;
}
static bool programconf_getCyclicDataSettings(const json& j_cyc_data, std::map<std::string, UserData>& user_data, uint16_t& cyc_data_len)
{
uint16_t data_lengh = 0;
if (!j_cyc_data.is_array())
return false;
for (auto& j : j_cyc_data)
{
if (!j.contains("name"))
{
return false;
}
if (!j.contains("DataType"))
{
return false;
}
auto name = j.at("name").get<std::string>();
auto Type = j.at("DataType").get<std::string>();
user_data.emplace(std::make_pair(std::string(name), UserData{name, Type, data_lengh}));
data_lengh+= user_data.at(name).size;
if (j.contains("bits"))
{
auto& j_bits = j.at("bits");
auto& usr_data = user_data.at(name);
for (auto& j_bit : j_bits)
{
if (!j_bit.contains("name") || !j.contains("BitOffset"))
continue;
auto name = j_bit.at("name").get<std::string>();
auto Type = j_bit.at("BitOffset").get<uint32_t>();
usr_data.putBit(name, Type);
}
}
}
cyc_data_len = data_lengh;
return true;
}
static bool programconf_getParamsSettings(const json& j_sett,
ProfinetData_Submodule& submod,
ProfinetSharedData& shared_data,
std::map<std::uint32_t, UserData>& user_data)
{
if (!j_sett.contains("parameters"))
return false;
if (!j_sett.at("parameters").is_array())
return false;
///Параметры подмодуля
auto& params_sett = j_sett.at("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;
std::string data_type = parm_sett.at("DataType").get<std::string>();
param.index = static_cast<uint32_t>(parm_sett.at("index").get<int>());
param.name = parm_sett.at("name").get<string>();
user_data.emplace(std::make_pair(param.index, UserData{param.name, data_type, 0}));
param.length = static_cast<uint16_t>(user_data.at(param.index).size);
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;
}
static bool programconf_getProfinetModulesSettings(json& j, ProfinetData_Map * p_data_map,
ProfinetSharedData& shared_data,
UserDataMap& user_data_map)
{
if (!j.contains("ProfinetDeviceModulesSettings"))
return false;
@ -291,10 +421,43 @@ static bool programconf_getProfinetModulesSettings(json& j, ProfinetData_Map * p
/// Копируем данные подмодуля
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>());
auto& j_cycdata_sett = submod_sett["cyclic_data"];
if (!programconf_getCyclycDataDir(j_cycdata_sett, submod.cyc_data_dir))
{
return false;
}
auto& submod_user_data = UserData_addNewSubmod(mod_id, submod_id, user_data_map);
/// Если есть секция Inputs
if (j_cycdata_sett.contains("Inputs"))
{
/// Конструируем поля пользовательских данных в датаграмме циклических данных
if (!programconf_getCyclicDataSettings(j_cycdata_sett.at("Inputs"), submod_user_data.inp_data_map, submod.cyc_indata_len))
{
return false;
}
}
else if ((submod.cyc_data_dir == PROFINET_SUBMOD_DIR_IO) || (submod.cyc_data_dir == PROFINET_SUBMOD_DIR_INPUT))
{
/// Данные должны быть
return false;
}
if (j_cycdata_sett.contains("Outputs"))
{
if (!programconf_getCyclicDataSettings(j_cycdata_sett.at("Outputs"), submod_user_data.out_data_map, submod.cyc_outdata_len))
{
return false;
}
}
else if ((submod.cyc_data_dir == PROFINET_SUBMOD_DIR_IO) || (submod.cyc_data_dir == PROFINET_SUBMOD_DIR_OUTPUT))
{
/// Данные должны быть
return false;
}
/// Выделяем память под циклические данные
if (submod.cyc_indata_len)
{
@ -317,44 +480,8 @@ static bool programconf_getProfinetModulesSettings(json& j, ProfinetData_Map * p
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);
}
/// Читаем конфигурацию параметров
programconf_getParamsSettings(submod_sett, submod, shared_data, submod_user_data.params_map);
}
}
@ -364,7 +491,8 @@ static bool programconf_getProfinetModulesSettings(json& j, ProfinetData_Map * p
bool programconf_getProfinetDeviceSettings(std::string file_path, ProfinetDeviceSettings& pn_sett,
ProfinetData_Map * p_data_map, ProfinetSharedData& shared_data)
ProfinetData_Map * p_data_map, ProfinetSharedData& shared_data,
UserDataMap& user_data_map)
{
json j;
@ -378,7 +506,7 @@ bool programconf_getProfinetDeviceSettings(std::string file_path, ProfinetDevice
return false;
}
if (!programconf_getProfinetModulesSettings(j, p_data_map, shared_data))
if (!programconf_getProfinetModulesSettings(j, p_data_map, shared_data, user_data_map))
{
return false;
}

View File

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

2
src/endian/endian.cmake Normal file
View File

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

22
src/endian/endian.cpp Normal file
View File

@ -0,0 +1,22 @@
#include "endian.hpp"
bool endian_swapbytes(void * p_data, uint32_t size)
{
uint8_t * p_data_ = static_cast<uint8_t*>(p_data);
if ((size % 2) || (size < 2))
return false; /// Только четные длины можно свапить
uint32_t size_half = size >> 1;
for (int i = 0; i < size_half; ++i)
{
uint32_t j = size - i - 1;
uint8_t tmp = p_data_[i];
p_data_[i] = p_data_[j];
p_data_[j] = tmp;
}
return true;
}

5
src/endian/endian.hpp Normal file
View File

@ -0,0 +1,5 @@
#pragma once
#include <cstdint>
bool endian_swapbytes(void * p_data, uint32_t size);

View File

@ -2,6 +2,7 @@
#include <vector>
#include <cstdint>
#include <iostream>
#include "../../nlohmann_json/json.hpp"
using json = nlohmann::json;
using string = std::string;
@ -163,7 +164,164 @@ static bool json_readSubmoduleParameter(json& j_in, json& j_out, ProfinetIface *
return true;
}
bool json_rpc_handler(std::string& input, std::string& output, ProfinetIface * p_profinet)
static bool json_writeCyclicIoDataNamed(json& j_in, json& j_out, ProfinetIface * p_profinet,
UserDataMap& user_data_map)
{
if (!j_in.contains("params"))
return false;
/// Запись
j_out["result"] = json::array();
for (auto& param : j_in["params"])
{
if (!param.contains("module_id") || !param.contains("submodule_id") ||
!param.contains("data"))
return false;
json j_arr;
uint32_t module_id = param.at("module_id").get<int>();
uint32_t submodule_id = param.at("submodule_id").get<int>();
int data_len = p_profinet->getSubmoduleCyclicInpDataLen(module_id, submodule_id);
if (data_len < 0)
{
return false;
}
std::vector<uint8_t> pn_data(data_len);
auto& j_data = param.at("data");
auto& submod_data_map = UserData_getSubmod(module_id, submodule_id, user_data_map);
/// Берем данные из rpc json и преобразовываем в бинарный вид в user_data
for (auto& m : submod_data_map.inp_data_map)
{
auto& user_data = m.second;
user_data.convertJsonToBinary(j_data, pn_data);
}
/// Копируем данные в профинет
auto result = p_profinet->putInputCyclicData(module_id, submodule_id, pn_data);
///
j_arr["module_id"] = module_id;
j_arr["submodule_id"] = submodule_id;
string status;
json_rpc_set_status(static_cast<ProfinetIface::ErrorCode>(result), status);
j_arr["status"] = status;
j_out["result"].push_back(j_arr);
}
return true;
}
static bool json_readCyclicIoDataNamed(const json& j_in, json& j_out, ProfinetIface * p_profinet,
UserDataMap& user_data_map)
{
if (!j_in.contains("params"))
return false;
/// Запись
j_out["result"] = json::array();
for (auto& param : j_in.at("params"))
{
json j_arr;
uint32_t module_id = param.at("module_id").get<int>();
uint32_t submodule_id = param.at("submodule_id").get<int>();
std::vector<uint8_t> pn_data;
/// Извлекаем данные из профинета
auto result = p_profinet->getOutputCyclicData(module_id, submodule_id, pn_data);
///
j_arr["module_id"] = module_id;
j_arr["submodule_id"] = submodule_id;
string status;
json_rpc_set_status(static_cast<ProfinetIface::ErrorCode>(result), status);
j_arr["status"] = status;
if (result == ProfinetIface::ERR_SUCCESS)
{
auto& submod_data_map = UserData_getSubmod(module_id, submodule_id, user_data_map);
/// Преобразовываем бинарные данные в rpc json
for (auto& m : submod_data_map.out_data_map)
{
auto& user_data = m.second;
user_data.convertBinaryToJson(pn_data, j_arr["data"]);
}
}
else
{
j_arr["data"];
}
j_out["result"].push_back(j_arr);
}
return true;
}
static bool json_readSubmoduleParameterNamed(json& j_in, json& j_out, ProfinetIface * p_profinet,
UserDataMap& user_data_map)
{
if (!j_in.contains("params"))
return false;
/// Запись
j_out["result"] = json::array();
for (auto& param : j_in["params"])
{
json j_arr;
uint32_t module_id = param["module_id"].get<int>();
uint32_t submodule_id = param["submodule_id"].get<int>();
uint32_t param_idx = param["param_index"].get<int>();
std::vector<uint8_t> data;
auto result = p_profinet->getSubmoduleParam(module_id, submodule_id, param_idx, data);
///
j_arr["module_id"] = module_id;
j_arr["submodule_id"] = submodule_id;
j_arr["param_index"] = param_idx;
string status;
json_rpc_set_status(static_cast<ProfinetIface::ErrorCode>(result), status);
j_arr["status"] = status;
if (result == ProfinetIface::ERR_SUCCESS)
{
auto& submod_data_map = UserData_getSubmod(module_id, submodule_id, user_data_map);
auto& param_user_data = submod_data_map.params_map.at(param_idx);
param_user_data.convertBinaryToJson(data, j_arr["data"]);
}
else
{
j_arr["data"];
}
j_out["result"].push_back(j_arr);
}
return true;
}
bool json_rpc_handler(std::string& input, std::string& output,
ProfinetIface * p_profinet,
UserDataMap * p_UserDataMap)
{
json j_in;
@ -215,6 +373,18 @@ bool json_rpc_handler(std::string& input, std::string& output, ProfinetIface * p
{
json_readSubmoduleParameter(j_in, j_out, p_profinet);
}
else if (method == "writeCyclicIoDataNamed")
{
json_writeCyclicIoDataNamed(j_in, j_out, p_profinet, *p_UserDataMap);
}
else if (method == "readCyclicIoDataNamed")
{
json_readCyclicIoDataNamed(j_in, j_out, p_profinet, *p_UserDataMap);
}
else if (method == "readSubmoduleParameterNamed")
{
json_readSubmoduleParameterNamed(j_in, j_out, p_profinet, *p_UserDataMap);
}
j_out["method"] = method;
j_out["id"] = rpc_id;

View File

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

View File

@ -217,3 +217,233 @@
0-й бит -
"id": 0
}
5. Метод записи именованых входных циклических данных(данные к ПЛК):
-->
{
"jsonrpc": "2.0",
"method": "writeCyclicIoDataNamed",
"params":
[
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"data":
{
"Имя поля данных 1": значение 1,
"Имя поля данных 1": значение 2,
:
"Имя поля данных N": значение N,
Биты представляются следующим образом:
"Битовое поле данных ":
{
"Имя бита 0": значение 0 или 1,
"Имя бита 1": значение 0 или 1,
:
"Имя бита N": значение 0 или 1
}
}
},
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"data":
{
данные (см. выше)
}
},ProfinetIface::
:
:
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"data":
{
данные (см. выше)
}
},
]
"id": 0
}
<--
{
"jsonrpc": "2.0",
"method": "writeCyclicIoDataNamed"
"result":
[
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"status": "OK", "WRONG_MODULE_ID", "WRONG_SUBMODULE_ID", "WRONG_DATA_LENGTH"
},
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"status": "OK", "WRONG_MODULE_ID", "WRONG_SUBMODULE_ID", "WRONG_DATA_LENGTH"
},
:
:
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"status": "OK", "WRONG_MODULE_ID", "WRONG_SUBMODULE_ID", "WRONG_DATA_LENGTH"
},
]
"id": 0
}
6. Метод чтения именованых выходных циклических данных(данные от ПЛК):
-->
{
"jsonrpc": "2.0",
"method": "readCyclicIoDataNamed",
"params":
[
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
},
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
},
:
:
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
},
]
"id": 0
}
<--
{
"jsonrpc": "2.0",
"method": "readCyclicIoDataNamed"
"result":
[
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"status": "OK", "WRONG_MODULE_ID", "WRONG_SUBMODULE_ID"
"data":
{
"Имя поля данных 1": значение 1,
"Имя поля данных 1": значение 2,
:
"Имя поля данных N": значение N,
Биты представляются следующим образом:
"Битовое поле данных ":
{
"Имя бита 0": значение 0 или 1,
"Имя бита 1": значение 0 или 1,
:
"Имя бита N": значение 0 или 1
}
} или null в, если status!=ОК
},
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"status": "OK", "WRONG_MODULE_ID", "WRONG_SUBMODULE_ID"
"data":
{
данные (см. выше)
} или null в, если status!=ОК
},
:
:
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"status": "OK", "WRONG_MODULE_ID", "WRONG_SUBMODULE_ID"
"data":
{
данные (см. выше)
} или null в, если status!=ОК
},
]
"id": 0
}
3. Метод чтения именованых параметров подмодуля:
Устройство может только читать параметры. Записывает параметры только Profinet контроллер(ПЛК)
после установления связи и перед началом циклического обмена.
-->
{
"jsonrpc": "2.0",
"method": "readSubmoduleParameterNamed",
"params":
[
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"param_index": Индекс парамтера
},
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"param_index": Индекс парамтера
},
:
:
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"param_index": Индекс парамтера
},
]
"id": 0
}
<--
{
"jsonrpc": "2.0",
"method": "readSubmoduleParameterNamed",
"result":
[
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"param_index": Индекс парамтера
"status": "OK", "WRONG_MODULE_ID", "WRONG_SUBMODULE_ID", "WRONG_PARAM_INDEX"
"data":
{
"Имя поля данных 1": значение 1,
"Имя поля данных 1": значение 2,
:
"Имя поля данных N": значение N,
Биты представляются следующим образом:
"Битовое поле данных ":
{
"Имя бита 0": значение 0 или 1,
"Имя бита 1": значение 0 или 1,
:
"Имя бита N": значение 0 или 1
}
} или null в, если status!=ОК
},
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"param_index": Индекс парамтера
"status": "OK", "WRONG_MODULE_ID", "WRONG_SUBMODULE_ID", "WRONG_PARAM_INDEX"
"data":
{
данные (см. выше)
} или null в, если status!=ОК
},
:
:
{
"module_id": Идентификатор модуля
"submodule_id": Идентификатор подмодуля
"param_index": Индекс парамтера
"status": "OK", "WRONG_MODULE_ID", "WRONG_SUBMODULE_ID", "WRONG_PARAM_INDEX"
"data":
{
данные (см. выше)
} или null в, если status!=ОК
},
]
"id": 0
}

View File

@ -36,7 +36,7 @@ void ProfinetPipes::Init( ProfinetPipesSettings& sett )
p_output_stream_ = std::move( std::get<1>( streams ) );
}
void ProfinetPipes::listen_pipe(ProfinetIface * p_profinet)
void ProfinetPipes::listen_pipe(ProfinetIface * p_profinet, UserDataMap * p_UserDataMap)
{
std::string request_str;
@ -46,7 +46,7 @@ void ProfinetPipes::listen_pipe(ProfinetIface * p_profinet)
std::string answer_str;
std::getline( *p_input_stream_.get(), request_str);
//std::cout << "Request: " << request_str << std::endl;
if (json_rpc_handler(request_str, answer_str, p_profinet))
if (json_rpc_handler(request_str, answer_str, p_profinet, p_UserDataMap))
{
//std::cout << "Answer: " << answer_str << std::endl;
*p_output_stream_ << answer_str << std::endl;
@ -79,10 +79,10 @@ void ProfinetPipes::listen_pipe(ProfinetIface * p_profinet)
}
}
void ProfinetPipes::Start(ProfinetIface * p_profinet)
void ProfinetPipes::Start(ProfinetIface * p_profinet, UserDataMap * p_UserDataMap)
{
if (!enable_)
return;
listen_thread_ = std::move(std::thread(&ProfinetPipes::listen_pipe, this, p_profinet));
listen_thread_ = std::move(std::thread(&ProfinetPipes::listen_pipe, this, p_profinet, p_UserDataMap));
}

View File

@ -5,8 +5,10 @@
#include <thread>
#include <atomic>
#include <string>
#include <map>
#include "../../profinet/profinet_iface.hpp"
#include "../../user_data/user_data.hpp"
struct ProfinetPipesSettings {
bool enable;
@ -22,9 +24,9 @@ public:
void Init( ProfinetPipesSettings& sett );
void listen_pipe(ProfinetIface * p_profinet);
void listen_pipe(ProfinetIface * p_profinet, UserDataMap * p_UserDataMap);
void Start(ProfinetIface * p_profinet);
void Start(ProfinetIface * p_profinet, UserDataMap * p_UserDataMap);
private:
/** Поток обработки входящих данных от именнованого канала. */

View File

@ -689,7 +689,9 @@ int Profinet::putInputCyclicData(uint32_t module_id,
return ErrorCode::ERR_SUBMODULE_DOES_NOT_EXIST;
if (data.size() != submodule->m_data_cfg.insize)
{
return ErrorCode::ERR_WRONG_DATA_LENGTH;
}
submodule->writeInputData(data);
@ -734,6 +736,31 @@ Profinet::~Profinet()
}
}
int Profinet::getSubmoduleCyclicInpDataLen(uint32_t module_id, uint32_t submodule_id) const
{
if (!m_modules.contains(module_id))
return ErrorCode::ERR_MODULE_DOES_NOT_EXIST;
auto submodule = m_modules.at(module_id)->getSubmodulePtr(submodule_id);
if (submodule == nullptr)
return ErrorCode::ERR_SUBMODULE_DOES_NOT_EXIST;
return submodule->m_data_cfg.insize;
}
int Profinet::getSubmoduleCyclicOutDataLen(uint32_t module_id, uint32_t submodule_id) const
{
if (!m_modules.contains(module_id))
return ErrorCode::ERR_MODULE_DOES_NOT_EXIST;
auto submodule = m_modules.at(module_id)->getSubmodulePtr(submodule_id);
if (submodule == nullptr)
return ErrorCode::ERR_SUBMODULE_DOES_NOT_EXIST;
return submodule->m_data_cfg.outsize;
}
/**\
* =========================================================================================

View File

@ -168,6 +168,10 @@ public:
uint32_t submodule_id,
uint32_t param_id,
std::vector<uint8_t>& data) override;
virtual int getSubmoduleCyclicInpDataLen(uint32_t module_id, uint32_t submodule_id) const override;
virtual int getSubmoduleCyclicOutDataLen(uint32_t module_id, uint32_t submodule_id) const override;
private:
ProfinetServiceData m_pnet_data;

View File

@ -70,4 +70,8 @@ public:
* @return uint32_t
*/
virtual void clearEvents(uint32_t mask) = 0;
virtual int getSubmoduleCyclicInpDataLen(uint32_t module_id, uint32_t submodule_id) const = 0;
virtual int getSubmoduleCyclicOutDataLen(uint32_t module_id, uint32_t submodule_id) const = 0;
};

View File

@ -54,14 +54,142 @@
{
"id": "0x00000140",
"name": "Echo submodule",
"cyc_data_dir": "PNET_DIR_IO",
"cyc_inp_data_size": 8,
"cyc_out_data_size": 8,
"cyclic_data":
{
"dir": "PNET_DIR_IO",
"Inputs": [
{
"name": "EchoModuleInpFloat",
"DataType": "Float32"
},
{
"name": "EchoModuleInpInt",
"DataType": "Unsigned32"
}
],
"Outputs": [
{
"name": "EchoModuleOutFloat",
"DataType": "Float32"
},
{
"name": "EchoModuleOutInt",
"DataType": "Unsigned32"
}
]
},
"parameters": [
{
"index": 125,
"name": "Echo gain setting",
"data_size": 4
"name": "EchoGain",
"DataType": "Unsigned32"
}
]
}
]
},
{
"id": "0x00000032",
"name": "I8O8 module",
"submodules": [
{
"id": "0x00000132",
"name": "I8O8 submodule",
"cyclic_data":
{
"dir": "PNET_DIR_IO",
"Inputs": [
{
"name": "I808InpBits",
"DataType": "Unsigned8",
"bits": [
{
"name": "in_bit_0",
"BitOffset": 0
},
{
"name": "in_bit_1",
"BitOffset": 1
},
{
"name": "in_bit_2",
"BitOffset": 2
},
{
"name": "in_bit_3",
"BitOffset": 3
},
{
"name": "in_bit_4",
"BitOffset": 4
},
{
"name": "in_bit_5",
"BitOffset": 5
},
{
"name": "in_bit_6",
"BitOffset": 6
},
{
"name": "in_bit_7",
"BitOffset": 7
}
]
}
],
"Outputs": [
{
"name": "I808OutBits",
"DataType": "Unsigned8",
"bits": [
{
"name": "out_bit_0",
"BitOffset": 0
},
{
"name": "out_bit_1",
"BitOffset": 1
},
{
"name": "out_bit_2",
"BitOffset": 2
},
{
"name": "out_bit_3",
"BitOffset": 3
},
{
"name": "out_bit_4",
"BitOffset": 4
},
{
"name": "out_bit_5",
"BitOffset": 5
},
{
"name": "out_bit_6",
"BitOffset": 6
},
{
"name": "out_bit_7",
"BitOffset": 7
}
]
}
]
},
"parameters": [
{
"index": 123,
"name": "I8O8Param1",
"DataType": "Unsigned32"
},
{
"index": 124,
"name": "I8O8Param2",
"DataType": "Unsigned32"
}
]
}

View File

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

292
src/user_data/user_data.cpp Normal file
View File

@ -0,0 +1,292 @@
#include "user_data.hpp"
#include <bit>
#include "../endian/endian.hpp"
using json = nlohmann::json;
const std::map<UserData::UserDataTypes, uint32_t> UserData::data_size{
{UserDataTypes::BOOL, 1},
{UserDataTypes::UINT8, 1},
{UserDataTypes::UINT16, 2},
{UserDataTypes::UINT32, 4},
{UserDataTypes::UINT64, 8},
{UserDataTypes::INT64, 8},
{UserDataTypes::INT32, 4},
{UserDataTypes::INT16, 2},
{UserDataTypes::INT8, 1},
{UserDataTypes::FLOAT32, 4},
{UserDataTypes::FLOAT64, 8}
};
const std::map<std::string, UserData::UserDataTypes> UserData::data_corr {
{"Boolean", UserDataTypes::BOOL},
{"Unsigned8", UserDataTypes::UINT8},
{"Unsigned16", UserDataTypes::UINT16},
{"Unsigned32", UserDataTypes::UINT32},
{"Unsigned64", UserDataTypes::UINT64},
{"Integer64", UserDataTypes::INT64},
{"Integer32", UserDataTypes::INT32},
{"Integer16", UserDataTypes::INT16},
{"Integer8", UserDataTypes::INT8},
{"Float32", UserDataTypes::FLOAT32},
{"Float64", UserDataTypes::FLOAT64}
};
bool UserData::putBit(std::string& bit_name, uint32_t bit_pos)
{
if (bits_.size() >= size)
return false; /// Все биты уже заняты
bits_.push_back(UserDataBit{bit_name, bit_pos});
return true;
}
bool UserData::toRpcJson(nlohmann::json& j)
{
if (bits_.empty())
{
switch(type)
{
case UserDataTypes::UINT8:
{
j[name] = data_.uint8[0];
}
break;
case UserDataTypes::UINT16:
{
j[name] = data_.uint16[0];
}
break;
case UserDataTypes::UINT32:
{
j[name] = data_.uint32[0];
}
break;
case UserDataTypes::UINT64:
{
j[name] = data_.uint64;
}
break;
case UserDataTypes::INT8:
{
j[name] = data_.int8[0];
}
break;
case UserDataTypes::INT16:
{
j[name] = data_.int16[0];
}
break;
case UserDataTypes::INT32:
{
j[name] = data_.int32[0];
}
break;
case UserDataTypes::INT64:
{
j[name] = data_.int64;
}
break;
case UserDataTypes::FLOAT32:
{
j[name] = data_.float32;
}
break;
case UserDataTypes::FLOAT64:
{
j[name] = data_.float64;
}
break;
case UserDataTypes::BOOL:
{
j[name] = data_.boolean;
}
break;
}
}
else
{
/// Только беззнаковые типы могут быть битовыми
if ((type < UserDataTypes::UINT8) || (type > UserDataTypes::UINT64))
return false;
json& j_bit = j[name];
for (auto bit : bits_)
{
j_bit[bit.Name] = static_cast<bool>(data_.uint64 & (1 << bit.Pos));
}
}
return true;
}
bool UserData::fromRpcJson(const nlohmann::json& j)
{
if (!j.contains(name))
return false;
if (!bits_.size()) /// Данные
{
switch(type)
{
case UserDataTypes::UINT8:
{
data_.uint8[0] = j[name].get<uint8_t>();
}
break;
case UserDataTypes::UINT16:
{
data_.uint16[0] = j[name].get<uint16_t>();
}
break;
case UserDataTypes::UINT32:
{
data_.uint32[0] = j[name].get<uint32_t>();
}
break;
case UserDataTypes::UINT64:
{
data_.uint64 = j[name].get<uint64_t>();
}
break;
case UserDataTypes::INT8:
{
data_.int8[0] = j[name].get<int8_t>();
}
break;
case UserDataTypes::INT16:
{
data_.int16[0] = j[name].get<int16_t>();
}
break;
case UserDataTypes::INT32:
{
data_.int32[0] = j[name].get<int32_t>();
}
break;
case UserDataTypes::INT64:
{
data_.int64 = j[name].get<int64_t>();
}
break;
case UserDataTypes::FLOAT32:
{
data_.float32 = j[name].get<float>();
}
break;
case UserDataTypes::FLOAT64:
{
data_.float64 = j[name].get<double>();
}
break;
case UserDataTypes::BOOL:
{
data_.boolean = j[name].get<bool>();
}
break;
}
}
else /// Биты
{
/// Только беззнаковые типы могут быть битовыми
if ((type < UserDataTypes::UINT8) || (type > UserDataTypes::UINT64))
return false;
const json& j_bit = j[name];
data_.uint64 = 0;
for (auto bit : bits_)
{
if (!j_bit.contains(bit.Name))
return false;
uint8_t value = j_bit[bit.Name].get<uint8_t>();
data_.uint64 |= static_cast<uint64_t>(value) << bit.Pos;
}
}
return true;
}
bool UserData::convertBinaryToJson(const std::vector<uint8_t>& vect_inp, nlohmann::json& j_out)
{
/// Вектор должен иметь подходящий размер
if (vect_inp.size() < (size + data_offset_))
return false;
/// Копируем бинарные данные из вектора с учетом офсета и эндиана
insertData(vect_inp);
/// Преобразовываем бинарные данные в json
toRpcJson(j_out);
return true;
}
bool UserData::convertJsonToBinary(const nlohmann::json& j_inp, std::vector<uint8_t>& vect_out)
{
/// Вектор должен иметь подходящий размер
if (vect_out.size() < (size + data_offset_))
return false;
/// Берем данные из json и преобразуем их в бинарный вид
fromRpcJson(j_inp);
/// Вставляем бинарные данные с учетом офсета в вектор
extractData(vect_out);
return true;
}
void UserData::extractData(std::vector<uint8_t>& vect)
{
/// Копируем данные в vect
uint8_t buf[size];
std::copy(&data_.uint8[0], &data_.uint8[size], buf);
/// Данные в профинет передаются в формате BigEndian
/// преобразуем из Little в Big
endian_swapbytes(&buf[0], size);
/// Вставляем данные в вектор по офсету
std::copy(buf, &buf[size], vect.begin() + data_offset_);
}
void UserData::insertData(const std::vector<uint8_t>& vect)
{
/// Копируем данные из вектора
std::copy(&vect[data_offset_], &vect[data_offset_ + size], data_.uint8);
/// Конвертим
if ((type >= UserDataTypes::UINT16) &&
(type <= UserDataTypes::FLOAT64))
{
/// Данные в профинет передаются в формате BigEndian
/// преобразуем из Big в Little
endian_swapbytes(data_.uint8, size);
}
}
/*void UserData::init(std::string& Name, std::string& DataType, uint16_t offset)
{
name = Name;
type = data_corr.at(DataType);
size = data_size.at(type);
data_offset_ = offset;
};*/
SubmoduleUserData& UserData_addNewSubmod(uint32_t mod_id, uint32_t submod_id, UserDataMap& user_data_map)
{
uint64_t key = (static_cast<uint64_t>(mod_id) << 32) + submod_id;
return user_data_map[key];
}
SubmoduleUserData& UserData_getSubmod(uint32_t mod_id, uint32_t submod_id, UserDataMap& user_data_map)
{
uint64_t key = (static_cast<uint64_t>(mod_id) << 32) + submod_id;
return user_data_map.at(key);
}
int UserData::getDataSize(const std::string& type_name)
{
if (!data_corr.contains(type_name))
return 0;
return data_size.at(data_corr.at(type_name));
}

137
src/user_data/user_data.hpp Normal file
View File

@ -0,0 +1,137 @@
#pragma once
#include <string>
#include <cstdint>
#include <vector>
#include <map>
#include <memory>
#include "../nlohmann_json/json.hpp"
struct UserDataBit {
const std::string Name;
const uint32_t Pos;
};
/**
* @brief Класс представления данных внутри датаграммы циклического обмена профинет в виде
* пользовательского набора стандартных типов. Нужен для выполнения RpcJson запросов оканчивающихся на
* Named.
*
*/
class UserData {
public:
enum class UserDataTypes
{
BOOL,
UINT8,
UINT16,
UINT32,
UINT64,
INT8,
INT16,
INT32,
INT64,
FLOAT32,
FLOAT64,
};
UserData(std::string& Name, std::string& DataType, uint16_t offset) :
name{Name},
type{data_corr.at(DataType)},
size{data_size.at(type)},
data_offset_{offset}
{
};
union AnyData {
bool boolean;
uint8_t uint8[8];
uint16_t uint16[4];
uint32_t uint32[2];
uint64_t uint64;
uint8_t int8[8];
uint16_t int16[4];
uint32_t int32[2];
uint64_t int64;
float float32;
double float64;
};
//void init(std::string& Name, std::string& DataType, uint16_t offset);
bool convertBinaryToJson(const std::vector<uint8_t>& vect_inp, nlohmann::json& j_out);
bool convertJsonToBinary(const nlohmann::json& j_inp, std::vector<uint8_t>& vect_out);
bool putBit(std::string& bit_name, uint32_t bit_pos);
static int getDataSize(const std::string& type_name);
private:
/**
* @brief Извлекает данные в вектор с учетом оффсета и эндиана
*
* @param vect
*/
void extractData(std::vector<uint8_t>& vect);
/**
* @brief Берет данные из вектора с учетом оффсета и эндиана
*
* @param vect
*/
void insertData(const std::vector<uint8_t>& vect);
/**
* @brief Кладет внутренние бинарные данные в rpc json
*
* @param j
* @return true
* @return false
*/
bool toRpcJson(nlohmann::json& j);
/**
* @brief Преобразовывает данные их rpc json в бинарные данные
*
* @param j
* @return true
* @return false
*/
bool fromRpcJson(const nlohmann::json& j);
private:
static const std::map<UserDataTypes, uint32_t> data_size;
static const std::map<std::string, UserDataTypes> data_corr;
public:
const std::string name; /// Имя данных
const UserDataTypes type; /// Тип данных
const uint32_t size; /// Размер данных
private:
AnyData data_; /// Данные
std::vector<UserDataBit> bits_;
const uint16_t data_offset_; /// Офсет от начала датаграммы откуда начинаются данные
};
struct SubmoduleUserData
{
std::map<std::string, UserData> inp_data_map; /// Ключ - имя данных внутри даташраммы
std::map<std::string, UserData> out_data_map; /// Ключ - имя данных внутри даташраммы
std::map<uint32_t, UserData> params_map;
};
enum
{
USER_DATA_ID_SUBMOD,
USER_DATA_ID_MOD,
USER_DATA_ID_TOTAL
};
using UserDataMap = std::map<uint64_t, SubmoduleUserData>;
SubmoduleUserData& UserData_addNewSubmod(uint32_t mod_id, uint32_t submod_id, UserDataMap& user_data_map);
SubmoduleUserData& UserData_getSubmod(uint32_t mod_id, uint32_t submod_id, UserDataMap& user_data_map);