From fe46a8b03f3698a4a31068266c3cfdbd61a30771 Mon Sep 17 00:00:00 2001 From: Vadim Sychev Date: Fri, 26 Aug 2022 14:43:30 +0300 Subject: [PATCH] =?UTF-8?q?dev(UML-981):=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=B5=20rpc=20json=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=BF=D1=80=D0=BE=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Эти запросы позволяют получать именованные данные датаграммы profinet --- profinet_test/sample_app_echo/main.cpp | 326 ++++++++++++++----- src/CMakeLists.txt | 4 +- src/app.cpp | 8 +- src/app.hpp | 3 + src/configuration/program_config.cpp | 252 ++++++++++---- src/configuration/program_config.hpp | 4 +- src/endian/endian.cmake | 2 + src/endian/endian.cpp | 22 ++ src/endian/endian.hpp | 5 + src/interprocess/pipes/json_rpc_handler.cpp | 172 +++++++++- src/interprocess/pipes/json_rpc_handler.hpp | 7 +- src/interprocess/pipes/json_rpc_protocol.txt | 230 +++++++++++++ src/interprocess/pipes/profinet_pipes.cpp | 8 +- src/interprocess/pipes/profinet_pipes.hpp | 6 +- src/profinet/profinet.cpp | 27 ++ src/profinet/profinet.hpp | 4 + src/profinet/profinet_iface.hpp | 4 + src/profinet_device_configure.json | 138 +++++++- src/user_data/user_data.cmake | 2 + src/user_data/user_data.cpp | 292 +++++++++++++++++ src/user_data/user_data.hpp | 137 ++++++++ 21 files changed, 1489 insertions(+), 164 deletions(-) create mode 100644 src/endian/endian.cmake create mode 100644 src/endian/endian.cpp create mode 100644 src/endian/endian.hpp create mode 100644 src/user_data/user_data.cmake create mode 100644 src/user_data/user_data.cpp create mode 100644 src/user_data/user_data.hpp diff --git a/profinet_test/sample_app_echo/main.cpp b/profinet_test/sample_app_echo/main.cpp index f7d02df..f5bffa2 100644 --- a/profinet_test/sample_app_echo/main.cpp +++ b/profinet_test/sample_app_echo/main.cpp @@ -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& in_vect, uint8_t * pParam, uint16_t param_size) +{ + in_vect.clear(); + in_vect = jx["data"]["bytes"].get>(); + + 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 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() == 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 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(); submodule_id = jx["submodule_id"].get(); param_idx = jx["param_index"].get(); - - 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>(); + Echo_Gain = jx.at("data").at("EchoGain").get(); - 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(); + 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(); - 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(); submodule_id = jx["submodule_id"].get(); - 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(); + Echo_outCycData.data.data_i = jx.at("data").at("EchoModuleOutInt").get(); + + 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>(); + 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>(); - - 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 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; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6ad6881..e07fbc0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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}) diff --git a/src/app.cpp b/src/app.cpp index 5a0c560..1adde01 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -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; } diff --git a/src/app.hpp b/src/app.hpp index f8b1295..89d63e6 100644 --- a/src/app.hpp +++ b/src/app.hpp @@ -4,6 +4,7 @@ #include "./interprocess/shared_memory/profinet_shared_data.hpp" #include "./interprocess/pipes/profinet_pipes.hpp" #include +#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 и обратно }; \ No newline at end of file diff --git a/src/configuration/program_config.cpp b/src/configuration/program_config.cpp index 736c203..e25a08e 100644 --- a/src/configuration/program_config.cpp +++ b/src/configuration/program_config.cpp @@ -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()); + } + + 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(submod_sett["cyc_inp_data_size"].get()); - uint16_t out_data_len = static_cast(submod_sett["cyc_out_data_size"].get()); - + 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(parm_sett["data_size"].get()); - 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()); + + return true; +} + +static bool programconf_getCyclicDataSettings(const json& j_cyc_data, std::map& 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(); + auto Type = j.at("DataType").get(); + + 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(); + auto Type = j_bit.at("BitOffset").get(); + + 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& 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(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(); + param.index = static_cast(parm_sett.at("index").get()); + param.name = parm_sett.at("name").get(); + + user_data.emplace(std::make_pair(param.index, UserData{param.name, data_type, 0})); + + param.length = static_cast(user_data.at(param.index).size); + + if (param.length == 0) + { + return false; + } + /// Выделяем память для параметров + uint8_t * p_data = shared_data.allocateData(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(); - submod.cyc_data_dir = programconf_dataDirConvert(submod_sett["cyc_data_dir"].get()); - submod.cyc_indata_len = static_cast(submod_sett["cyc_inp_data_size"].get()); - submod.cyc_outdata_len = static_cast(submod_sett["cyc_out_data_size"].get()); - + 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(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(parm_sett["index"].get()); - param.name = parm_sett["name"].get(); - param.length = static_cast(parm_sett["data_size"].get()); - - if (param.length == 0) - { - return false; - } - /// Выделяем память для параметров - uint8_t * p_data = shared_data.allocateData(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; } diff --git a/src/configuration/program_config.hpp b/src/configuration/program_config.hpp index fe46c2c..6623c3a 100644 --- a/src/configuration/program_config.hpp +++ b/src/configuration/program_config.hpp @@ -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); diff --git a/src/endian/endian.cmake b/src/endian/endian.cmake new file mode 100644 index 0000000..a3052f2 --- /dev/null +++ b/src/endian/endian.cmake @@ -0,0 +1,2 @@ +set(SRC_FILES ${SRC_FILES} ./endian/endian.cpp) +set(INC_DIRS ${INC_DIRS} ./endian) \ No newline at end of file diff --git a/src/endian/endian.cpp b/src/endian/endian.cpp new file mode 100644 index 0000000..85abd14 --- /dev/null +++ b/src/endian/endian.cpp @@ -0,0 +1,22 @@ +#include "endian.hpp" + +bool endian_swapbytes(void * p_data, uint32_t size) +{ + uint8_t * p_data_ = static_cast(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; +} \ No newline at end of file diff --git a/src/endian/endian.hpp b/src/endian/endian.hpp new file mode 100644 index 0000000..ae14e2a --- /dev/null +++ b/src/endian/endian.hpp @@ -0,0 +1,5 @@ +#pragma once + +#include + +bool endian_swapbytes(void * p_data, uint32_t size); \ No newline at end of file diff --git a/src/interprocess/pipes/json_rpc_handler.cpp b/src/interprocess/pipes/json_rpc_handler.cpp index a9cbaea..0f0ce87 100644 --- a/src/interprocess/pipes/json_rpc_handler.cpp +++ b/src/interprocess/pipes/json_rpc_handler.cpp @@ -2,6 +2,7 @@ #include #include #include +#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(); + uint32_t submodule_id = param.at("submodule_id").get(); + + int data_len = p_profinet->getSubmoduleCyclicInpDataLen(module_id, submodule_id); + + if (data_len < 0) + { + return false; + } + + std::vector 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(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(); + uint32_t submodule_id = param.at("submodule_id").get(); + std::vector 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(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(); + uint32_t submodule_id = param["submodule_id"].get(); + uint32_t param_idx = param["param_index"].get(); + std::vector 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(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; diff --git a/src/interprocess/pipes/json_rpc_handler.hpp b/src/interprocess/pipes/json_rpc_handler.hpp index 8e90b2c..b61d2ee 100644 --- a/src/interprocess/pipes/json_rpc_handler.hpp +++ b/src/interprocess/pipes/json_rpc_handler.hpp @@ -1,11 +1,14 @@ #pragma once -#include "../../nlohmann_json/json.hpp" #include "../../profinet/profinet_iface.hpp" +#include "../../user_data/user_data.hpp" #include -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 ); diff --git a/src/interprocess/pipes/json_rpc_protocol.txt b/src/interprocess/pipes/json_rpc_protocol.txt index bcef838..06c98b0 100644 --- a/src/interprocess/pipes/json_rpc_protocol.txt +++ b/src/interprocess/pipes/json_rpc_protocol.txt @@ -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 +} \ No newline at end of file diff --git a/src/interprocess/pipes/profinet_pipes.cpp b/src/interprocess/pipes/profinet_pipes.cpp index 0a7cbf6..770455b 100644 --- a/src/interprocess/pipes/profinet_pipes.cpp +++ b/src/interprocess/pipes/profinet_pipes.cpp @@ -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)); } \ No newline at end of file diff --git a/src/interprocess/pipes/profinet_pipes.hpp b/src/interprocess/pipes/profinet_pipes.hpp index 5f9cdbf..2a19bd9 100644 --- a/src/interprocess/pipes/profinet_pipes.hpp +++ b/src/interprocess/pipes/profinet_pipes.hpp @@ -5,8 +5,10 @@ #include #include #include +#include #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: /** Поток обработки входящих данных от именнованого канала. */ diff --git a/src/profinet/profinet.cpp b/src/profinet/profinet.cpp index a4dce78..6395992 100644 --- a/src/profinet/profinet.cpp +++ b/src/profinet/profinet.cpp @@ -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; +} /**\ * ========================================================================================= diff --git a/src/profinet/profinet.hpp b/src/profinet/profinet.hpp index 7875698..546f63d 100644 --- a/src/profinet/profinet.hpp +++ b/src/profinet/profinet.hpp @@ -168,6 +168,10 @@ public: uint32_t submodule_id, uint32_t param_id, std::vector& 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; diff --git a/src/profinet/profinet_iface.hpp b/src/profinet/profinet_iface.hpp index 31e012f..cf12f32 100644 --- a/src/profinet/profinet_iface.hpp +++ b/src/profinet/profinet_iface.hpp @@ -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; }; \ No newline at end of file diff --git a/src/profinet_device_configure.json b/src/profinet_device_configure.json index bbc08eb..19d1fa6 100644 --- a/src/profinet_device_configure.json +++ b/src/profinet_device_configure.json @@ -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" } ] } diff --git a/src/user_data/user_data.cmake b/src/user_data/user_data.cmake new file mode 100644 index 0000000..4bd7f70 --- /dev/null +++ b/src/user_data/user_data.cmake @@ -0,0 +1,2 @@ +set(SRC_FILES ${SRC_FILES} ./user_data/user_data.cpp) +set(INC_DIRS ${INC_DIRS} ./user_data) \ No newline at end of file diff --git a/src/user_data/user_data.cpp b/src/user_data/user_data.cpp new file mode 100644 index 0000000..c6eb244 --- /dev/null +++ b/src/user_data/user_data.cpp @@ -0,0 +1,292 @@ +#include "user_data.hpp" +#include +#include "../endian/endian.hpp" + +using json = nlohmann::json; + +const std::map 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 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(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(); + } + break; + case UserDataTypes::UINT16: + { + data_.uint16[0] = j[name].get(); + } + break; + case UserDataTypes::UINT32: + { + data_.uint32[0] = j[name].get(); + } + break; + case UserDataTypes::UINT64: + { + data_.uint64 = j[name].get(); + } + break; + case UserDataTypes::INT8: + { + data_.int8[0] = j[name].get(); + } + break; + case UserDataTypes::INT16: + { + data_.int16[0] = j[name].get(); + } + break; + case UserDataTypes::INT32: + { + data_.int32[0] = j[name].get(); + } + break; + case UserDataTypes::INT64: + { + data_.int64 = j[name].get(); + } + break; + case UserDataTypes::FLOAT32: + { + data_.float32 = j[name].get(); + } + break; + case UserDataTypes::FLOAT64: + { + data_.float64 = j[name].get(); + } + break; + case UserDataTypes::BOOL: + { + data_.boolean = j[name].get(); + } + 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(); + data_.uint64 |= static_cast(value) << bit.Pos; + } + } + + return true; +} + +bool UserData::convertBinaryToJson(const std::vector& 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& 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& 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& 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(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(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)); +} diff --git a/src/user_data/user_data.hpp b/src/user_data/user_data.hpp new file mode 100644 index 0000000..3422112 --- /dev/null +++ b/src/user_data/user_data.hpp @@ -0,0 +1,137 @@ +#pragma once + +#include +#include +#include +#include +#include + +#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& vect_inp, nlohmann::json& j_out); + + bool convertJsonToBinary(const nlohmann::json& j_inp, std::vector& 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& vect); + + /** + * @brief Берет данные из вектора с учетом оффсета и эндиана + * + * @param vect + */ + void insertData(const std::vector& 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 data_size; + static const std::map data_corr; + +public: + const std::string name; /// Имя данных + const UserDataTypes type; /// Тип данных + const uint32_t size; /// Размер данных + +private: + AnyData data_; /// Данные + std::vector bits_; + const uint16_t data_offset_; /// Офсет от начала датаграммы откуда начинаются данные +}; + +struct SubmoduleUserData +{ + std::map inp_data_map; /// Ключ - имя данных внутри даташраммы + std::map out_data_map; /// Ключ - имя данных внутри даташраммы + std::map params_map; +}; + +enum +{ + USER_DATA_ID_SUBMOD, + USER_DATA_ID_MOD, + USER_DATA_ID_TOTAL +}; + +using UserDataMap = std::map; + +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); \ No newline at end of file