From a3b329cd7d014e474bc14b9c79657dade8e5aade Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Wed, 15 Nov 2023 17:10:09 +0200 Subject: [PATCH 01/31] add server mode to be controller outside --- .../libtatum/tatum/TimingReporter.cpp | 6 +- .../libtatum/tatum/TimingReporter.hpp | 4 +- libs/libarchfpga/src/read_xml_arch_file.cpp | 2 +- libs/libpugiutil/src/pugixml_util.cpp | 9 +- vpr/src/base/SetupVPR.cpp | 3 + vpr/src/base/SetupVPR.h | 1 + vpr/src/base/read_options.cpp | 8 + vpr/src/base/read_options.h | 2 + vpr/src/base/vpr_api.cpp | 57 ++++- vpr/src/base/vpr_api.h | 3 + vpr/src/base/vpr_context.h | 24 ++ vpr/src/base/vpr_types.h | 1 + vpr/src/draw/draw.cpp | 44 +++- vpr/src/draw/draw.h | 3 +- vpr/src/draw/draw_basic.cpp | 19 +- vpr/src/draw/ui_setup.cpp | 9 + vpr/src/main.cpp | 6 + vpr/src/route/segment_stats.cpp | 5 + vpr/src/server/keys.h | 16 ++ vpr/src/server/pathhelper.cpp | 217 ++++++++++++++++++ vpr/src/server/pathhelper.h | 12 + vpr/src/server/server.cpp | 208 +++++++++++++++++ vpr/src/server/server.h | 55 +++++ vpr/src/server/task.h | 69 ++++++ vpr/src/server/taskresolver.cpp | 140 +++++++++++ vpr/src/server/taskresolver.h | 36 +++ vpr/src/server/telegramparser.cpp | 55 +++++ vpr/src/server/telegramparser.h | 15 ++ 28 files changed, 1010 insertions(+), 19 deletions(-) create mode 100644 vpr/src/server/keys.h create mode 100644 vpr/src/server/pathhelper.cpp create mode 100644 vpr/src/server/pathhelper.h create mode 100644 vpr/src/server/server.cpp create mode 100644 vpr/src/server/server.h create mode 100644 vpr/src/server/task.h create mode 100644 vpr/src/server/taskresolver.cpp create mode 100644 vpr/src/server/taskresolver.h create mode 100644 vpr/src/server/telegramparser.cpp create mode 100644 vpr/src/server/telegramparser.h diff --git a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp index 609b0c0b03e..6ff29649824 100644 --- a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp +++ b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp @@ -631,7 +631,11 @@ Time TimingReporter::report_timing_data_arrival_subpath(std::ostream& os, path += delay_component.delay; path_helper.update_print_path(os, point, path); } - TATUM_ASSERT_MSG(nearly_equal(path, path_elem.tag().time()), "Delay breakdown must match calculated delay"); + // wOlek: here we have a crash on hold report + if (!nearly_equal(path, path_elem.tag().time())) { + std::cout << "~~~ path=" << path << ", path_elem.tag().time()=" << path_elem.tag().time() << std::endl; + } + // TATUM_ASSERT_MSG(nearly_equal(path, path_elem.tag().time()), "Delay breakdown must match calculated delay"); } std::string point = name_resolver_.node_name(path_elem.node()) + " (" + name_resolver_.node_type_name(path_elem.node()) + ")"; diff --git a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp index 1569aa6d704..96862c4d3ea 100644 --- a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp +++ b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp @@ -80,6 +80,8 @@ class TimingReporter { void report_unconstrained_hold(std::string filename, const tatum::HoldTimingAnalyzer& hold_analyzer) const; void report_unconstrained_hold(std::ostream& os, const tatum::HoldTimingAnalyzer& hold_analyzer) const; + void report_timing(std::ostream& os, const std::vector& paths) const; + private: struct PathSkew { NodeId launch_node; @@ -94,8 +96,6 @@ class TimingReporter { }; private: - void report_timing(std::ostream& os, const std::vector& paths) const; - void report_timing_path(std::ostream& os, const TimingPath& path) const; void report_unconstrained(std::ostream& os, const NodeType type, const detail::TagRetriever& tag_retriever) const; diff --git a/libs/libarchfpga/src/read_xml_arch_file.cpp b/libs/libarchfpga/src/read_xml_arch_file.cpp index 978865ec6de..368105d68ff 100644 --- a/libs/libarchfpga/src/read_xml_arch_file.cpp +++ b/libs/libarchfpga/src/read_xml_arch_file.cpp @@ -317,7 +317,7 @@ void XmlReadArch(const char* ArchFile, pugi::xml_node Next; ReqOpt POWER_REQD, SWITCHBLOCKLIST_REQD; - if ((vtr::check_file_name_extension(ArchFile, ".xml") == false) && (vtr::check_file_name_extension(ArchFile, ".xml") == false)) { + if ((vtr::check_file_name_extension(ArchFile, ".xml") == false) && (vtr::check_file_name_extension(ArchFile, ".xmle") == false)) { VTR_LOG_WARN( "Architecture file '%s' may be in incorrect format. " "Expecting .xml or .xmle format for architecture files.\n", diff --git a/libs/libpugiutil/src/pugixml_util.cpp b/libs/libpugiutil/src/pugixml_util.cpp index 8002ce620ff..68badf0e3f3 100644 --- a/libs/libpugiutil/src/pugixml_util.cpp +++ b/libs/libpugiutil/src/pugixml_util.cpp @@ -9,9 +9,12 @@ namespace pugiutil { loc_data load_xml(pugi::xml_document& doc, //Document object to be loaded with file contents const std::string filename) { //Filename to load from //store the position of last '.' in the file name - int position = filename.find_last_of("."); - //store the characters after the '.' from the file_name string - std::string result = filename.substr(position); + size_t position = filename.find_last_of("."); + std::string result = ""; + if(position != std::string::npos) { + //store the characters after the '.' from the file_name string + result = filename.substr(position); + } if (result == ".xmle") { Decryption E1(filename); std::string fn_file = E1.getDecryptedContent(); diff --git a/vpr/src/base/SetupVPR.cpp b/vpr/src/base/SetupVPR.cpp index efef48ed4c1..310d498897d 100644 --- a/vpr/src/base/SetupVPR.cpp +++ b/vpr/src/base/SetupVPR.cpp @@ -107,6 +107,7 @@ void SetupVPR(const t_options* Options, int* GraphPause, bool* SaveGraphics, std::string* GraphicsCommands, + bool* server, t_power_opts* PowerOpts, t_vpr_setup* vpr_setup) { using argparse::Provenance; @@ -314,6 +315,8 @@ void SetupVPR(const t_options* Options, *SaveGraphics = Options->save_graphics; *GraphicsCommands = Options->graphics_commands; + *server = Options->server; + if (getEchoEnabled() && isEchoFileEnabled(E_ECHO_ARCH)) { EchoArch(getEchoFileName(E_ECHO_ARCH), device_ctx.physical_tile_types, device_ctx.logical_block_types, Arch); } diff --git a/vpr/src/base/SetupVPR.h b/vpr/src/base/SetupVPR.h index 7f7bb7105ea..aa1813ac7b3 100644 --- a/vpr/src/base/SetupVPR.h +++ b/vpr/src/base/SetupVPR.h @@ -28,6 +28,7 @@ void SetupVPR(const t_options* Options, int* GraphPause, bool* SaveGraphics, std::string* GraphicsCommands, + bool* server, t_power_opts* PowerOpts, t_vpr_setup* vpr_setup); #endif diff --git a/vpr/src/base/read_options.cpp b/vpr/src/base/read_options.cpp index ad935c44faa..bf8bf29acd4 100644 --- a/vpr/src/base/read_options.cpp +++ b/vpr/src/base/read_options.cpp @@ -1280,6 +1280,11 @@ argparse::ArgumentParser create_arg_parser(std::string prog_name, t_options& arg .action(argparse::Action::STORE_TRUE) .default_value("off"); + stage_grp.add_argument(args.server, "--server") + .help("Run server mode") + .action(argparse::Action::STORE_TRUE) + .default_value("off"); + stage_grp.epilog( "If none of the stage options are specified, all stages are run.\n" "Analysis is always run after routing, unless the implementation\n" @@ -1377,6 +1382,9 @@ argparse::ArgumentParser create_arg_parser(std::string prog_name, t_options& arg .help("Show version information then exit") .action(argparse::Action::VERSION); + gen_grp.add_argument(args.show_resource_usage_only_mode, "--show_resource_usage") + .help("Show resource usage then exit"); + gen_grp.add_argument(args.device_layout, "--device") .help( "Controls which device layout/floorplan is used from the architecture file." diff --git a/vpr/src/base/read_options.h b/vpr/src/base/read_options.h index 97645367680..463479aa1dc 100644 --- a/vpr/src/base/read_options.h +++ b/vpr/src/base/read_options.h @@ -57,6 +57,7 @@ struct t_options { /* General options */ argparse::ArgValue show_help; argparse::ArgValue show_version; + argparse::ArgValue show_resource_usage_only_mode; argparse::ArgValue num_workers; argparse::ArgValue timing_analysis; argparse::ArgValue timing_update_type; @@ -73,6 +74,7 @@ struct t_options { argparse::ArgValue suppress_warnings; argparse::ArgValue allow_dangling_combinational_nodes; argparse::ArgValue terminate_if_timing_fails; + argparse::ArgValue server; /* Atom netlist options */ argparse::ArgValue absorb_buffer_luts; diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index 8cfd23b18be..6de050d750b 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -298,6 +298,7 @@ void vpr_init_with_options(const t_options* options, t_vpr_setup* vpr_setup, t_a &vpr_setup->GraphPause, &vpr_setup->SaveGraphics, &vpr_setup->GraphicsCommands, + &vpr_setup->server, &vpr_setup->PowerOpts, vpr_setup); @@ -365,6 +366,53 @@ void vpr_init_with_options(const t_options* options, t_vpr_setup* vpr_setup, t_a device_ctx.pad_loc_type = vpr_setup->PlacerOpts.pad_loc_type; } +void vpr_show_resource_usage(const t_vpr_setup& vpr_setup, const t_arch& Arch) +{ + vtr::ScopedStartFinishTimer timer("Build Device Grid"); + /* Read in netlist file for placement and routing */ + auto& cluster_ctx = g_vpr_ctx.clustering(); + auto& device_ctx = g_vpr_ctx.mutable_device(); + + device_ctx.arch = &Arch; + + /* + *Load the device grid + */ + + //Record the resource requirement + std::map num_type_instances; + + //Build the device + for (const auto& l: Arch.grid_layouts) { + std::string device_layout_variant = l.name; + + float target_device_utilization = vpr_setup.PackerOpts.target_device_utilization; + device_ctx.grid = create_device_grid(device_layout_variant, Arch.grid_layouts, num_type_instances, target_device_utilization); + + /* + *Report on the device + */ + size_t num_grid_tiles = count_grid_tiles(device_ctx.grid); + VTR_LOG("FPGA sized to %zu x %zu: %zu grid tiles (%s)\n", device_ctx.grid.width(), device_ctx.grid.height(), num_grid_tiles, device_ctx.grid.name().c_str()); + + std::string title("\nResource usage for device layout " + device_layout_variant + "...\n"); + VTR_LOG(title.c_str()); + for (const auto& type : device_ctx.logical_block_types) { + if (is_empty_type(&type)) continue; + + VTR_LOG("\tArchitecture\n"); + for (const auto equivalent_tile : type.equivalent_tiles) { + auto num_instances = 0; + //get the number of equivalent tile across all layers + num_instances = (int)device_ctx.grid.num_instances(equivalent_tile, -1); + + VTR_LOG("\t\t%d\tblocks of type: %s\n", + num_instances, equivalent_tile->name); + } + } + } +} + bool vpr_flow(t_vpr_setup& vpr_setup, t_arch& arch) { if (vpr_setup.exit_before_pack) { VTR_LOG_WARN("Exiting before packing as requested.\n"); @@ -805,7 +853,7 @@ RouteStatus vpr_route_flow(const Netlist<>& net_list, net_delay = make_net_pins_matrix(net_list); //Initialize the delay calculator - std::shared_ptr timing_info = nullptr; + std::shared_ptr& timing_info = g_vpr_ctx.hold_timing_info; // shortcut std::shared_ptr routing_delay_calc = nullptr; if (vpr_setup.Timing.timing_analysis_enabled) { auto& atom_ctx = g_vpr_ctx.atom(); @@ -1051,7 +1099,7 @@ void vpr_init_graphics(const t_vpr_setup& vpr_setup, const t_arch& arch, bool is /* Startup X graphics */ init_graphics_state(vpr_setup.ShowGraphics, vpr_setup.GraphPause, vpr_setup.RouterOpts.route_type, vpr_setup.SaveGraphics, - vpr_setup.GraphicsCommands, is_flat); + vpr_setup.GraphicsCommands, is_flat, vpr_setup.server); if (vpr_setup.ShowGraphics || vpr_setup.SaveGraphics || !vpr_setup.GraphicsCommands.empty()) alloc_draw_structs(&arch); } @@ -1274,6 +1322,7 @@ void vpr_setup_vpr(t_options* Options, int* GraphPause, bool* SaveGraphics, std::string* GraphicsCommands, + bool* server, t_power_opts* PowerOpts, t_vpr_setup* vpr_setup) { SetupVPR(Options, @@ -1298,6 +1347,7 @@ void vpr_setup_vpr(t_options* Options, GraphPause, SaveGraphics, GraphicsCommands, + server, PowerOpts, vpr_setup); } @@ -1420,7 +1470,8 @@ void vpr_analysis(const Netlist<>& net_list, //Do final timing analysis auto analysis_delay_calc = std::make_shared(atom_ctx.nlist, atom_ctx.lookup, net_delay, vpr_setup.RouterOpts.flat_routing); - auto timing_info = make_setup_hold_timing_info(analysis_delay_calc, vpr_setup.AnalysisOpts.timing_update_type); + g_vpr_ctx.hold_timing_info = make_setup_hold_timing_info(analysis_delay_calc, vpr_setup.AnalysisOpts.timing_update_type); + auto& timing_info = g_vpr_ctx.hold_timing_info; // shortcut timing_info->update(); if (isEchoFileEnabled(E_ECHO_ANALYSIS_TIMING_GRAPH)) { diff --git a/vpr/src/base/vpr_api.h b/vpr/src/base/vpr_api.h index 15509be1115..46ed92e5a68 100644 --- a/vpr/src/base/vpr_api.h +++ b/vpr/src/base/vpr_api.h @@ -132,6 +132,8 @@ void vpr_analysis(const Netlist<>& net_list, ///@brief Create the device (grid + rr graph) void vpr_create_device(t_vpr_setup& vpr_setup, const t_arch& Arch, bool is_flat); +void vpr_show_resource_usage(const t_vpr_setup& vpr_setup, const t_arch& Arch); + ///@brief Create the device grid void vpr_create_device_grid(const t_vpr_setup& vpr_setup, const t_arch& Arch); @@ -185,6 +187,7 @@ void vpr_setup_vpr(t_options* Options, int* GraphPause, bool* SaveGraphics, std::string* GraphicsCommands, + bool* server, t_power_opts* PowerOpts, t_vpr_setup* vpr_setup); diff --git a/vpr/src/base/vpr_context.h b/vpr/src/base/vpr_context.h index 260c7f7addc..1bcf38dbd24 100644 --- a/vpr/src/base/vpr_context.h +++ b/vpr/src/base/vpr_context.h @@ -33,6 +33,14 @@ #include "noc_traffic_flows.h" #include "noc_routing.h" +#include "server.h" +#include "taskresolver.h" +#include "tatum/report/TimingPath.hpp" +#include + +class SetupHoldTimingInfo; + + /** * @brief A Context is collection of state relating to a particular part of VPR * @@ -639,6 +647,18 @@ class VprContext : public Context { const PackingMultithreadingContext& packing_multithreading() const { return packing_multithreading_; } PackingMultithreadingContext& mutable_packing_multithreading() { return packing_multithreading_; } + const Server& server() const { return server_; } + Server& server() { return server_; } + + const TaskResolver& task_resolver() const { return task_resolver_; } + TaskResolver& task_resolver() { return task_resolver_; } + + std::vector crit_paths; + int critical_path_num = 1; + std::string path_type = "setup"; + int crit_path_index = 0; + std::shared_ptr hold_timing_info; + private: DeviceContext device_; @@ -656,6 +676,10 @@ class VprContext : public Context { NocContext noc_; PackingMultithreadingContext packing_multithreading_; + + Server server_; + TaskResolver task_resolver_; + }; #endif diff --git a/vpr/src/base/vpr_types.h b/vpr/src/base/vpr_types.h index 7b98cc2c0e0..41fb94910f2 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -1804,6 +1804,7 @@ struct t_vpr_setup { e_clock_modeling clock_modeling; ///(data); + Server& server = g_vpr_ctx.server(); + TaskResolver& task_resolver = g_vpr_ctx.task_resolver(); + // + + bool isRunning = !server.isStopped(); + if (isRunning) { + if (!server.isStarted()) { + server.start(); + } + + std::vector tasksBuff; + + server.takeRecievedTasks(tasksBuff); + task_resolver.addTasks(tasksBuff); + + task_resolver.update(app); + + tasksBuff.clear(); + task_resolver.takeFinished(tasksBuff); + + server.addSendTasks(tasksBuff); + + // Call the redraw method of the application + app->refresh_drawing(); + } + + // Return TRUE to keep the timer running, or FALSE to stop it + return isRunning; +} + #endif // NO_GRAPHICS /********************** Subroutine definitions ******************************/ @@ -181,7 +218,8 @@ void init_graphics_state(bool show_graphics_val, enum e_route_type route_type, bool save_graphics, std::string graphics_commands, - bool is_flat) { + bool is_flat, + bool server) { #ifndef NO_GRAPHICS /* Call accessor functions to retrieve global variables. */ t_draw_state* draw_state = get_draw_state_vars(); @@ -197,6 +235,10 @@ void init_graphics_state(bool show_graphics_val, draw_state->graphics_commands = graphics_commands; draw_state->is_flat = is_flat; + if (server) { + guint timer_id = g_timeout_add(200, redraw_callback, &application); + } + #else //Suppress unused parameter warnings (void)show_graphics_val; diff --git a/vpr/src/draw/draw.h b/vpr/src/draw/draw.h index 145f05800aa..f31c60b5e7d 100644 --- a/vpr/src/draw/draw.h +++ b/vpr/src/draw/draw.h @@ -56,7 +56,8 @@ void init_graphics_state(bool show_graphics_val, enum e_route_type route_type, bool save_graphics, std::string graphics_commands, - bool is_flat); + bool is_flat, + bool server); /* Allocates the structures needed to draw the placement and routing.*/ void alloc_draw_structs(const t_arch* arch); diff --git a/vpr/src/draw/draw_basic.cpp b/vpr/src/draw/draw_basic.cpp index 0eb49a02034..16fcdf05b3c 100644 --- a/vpr/src/draw/draw_basic.cpp +++ b/vpr/src/draw/draw_basic.cpp @@ -37,6 +37,8 @@ #include "route_export.h" #include "tatum/report/TimingPathCollector.hpp" +#include "pathhelper.h" + #ifdef VTR_ENABLE_DEBUG_LOGGING # include "move_utils.h" #endif @@ -939,8 +941,6 @@ void draw_routing_util(ezgl::renderer* g) { * b) during routing, critical path is shown by both flylines and routed net connections. */ void draw_crit_path(ezgl::renderer* g) { - tatum::TimingPathCollector path_collector; - t_draw_state* draw_state = get_draw_state_vars(); auto& timing_ctx = g_vpr_ctx.timing(); @@ -952,11 +952,16 @@ void draw_crit_path(ezgl::renderer* g) { return; //No timing to draw } - //Get the worst timing path - auto paths = path_collector.collect_worst_setup_timing_paths( - *timing_ctx.graph, - *(draw_state->setup_timing_info->setup_analyzer()), 1); - tatum::TimingPath path = paths[0]; + calcCritPath(g_vpr_ctx.critical_path_num, g_vpr_ctx.path_type); + + const auto& paths = g_vpr_ctx.crit_paths; // shortcut + + // check index inside the range + if (g_vpr_ctx.crit_path_index >= paths.size()) { + g_vpr_ctx.crit_path_index = -1; + return; + } + tatum::TimingPath path = paths[g_vpr_ctx.crit_path_index]; //Walk through the timing path drawing each edge tatum::NodeId prev_node; diff --git a/vpr/src/draw/ui_setup.cpp b/vpr/src/draw/ui_setup.cpp index 5aacd684aaa..15e18cae793 100644 --- a/vpr/src/draw/ui_setup.cpp +++ b/vpr/src/draw/ui_setup.cpp @@ -23,6 +23,10 @@ # include "ezgl/application.hpp" # include "ezgl/graphics.hpp" +void on_window_destroy() { + g_vpr_ctx.server().stop(); +} + void basic_button_setup(ezgl::application* app) { //button to enter window_mode, created in main.ui GtkButton* window = (GtkButton*)app->get_object("Window"); @@ -42,6 +46,8 @@ void basic_button_setup(ezgl::application* app) { //combo box for search type, created in main.ui GObject* search_type = (GObject*)app->get_object("SearchType"); g_signal_connect(search_type, "changed", G_CALLBACK(search_type_changed), app); + + g_signal_connect(window, "destroy", G_CALLBACK(on_window_destroy), NULL); } /* @@ -176,6 +182,9 @@ void hide_crit_path_routing(ezgl::application* app, bool hide) { gtk_combo_box_text_remove(toggle_crit_path, 4); gtk_combo_box_text_remove(toggle_crit_path, 3); } else { + gtk_combo_box_text_remove(toggle_crit_path, 4); // to make sure we don't double items + gtk_combo_box_text_remove(toggle_crit_path, 3); // to make sure we don't double items + gtk_combo_box_text_insert_text(toggle_crit_path, 3, "Crit Path Routing"); gtk_combo_box_text_insert_text(toggle_crit_path, 4, "Crit Path Routing Delays"); } diff --git a/vpr/src/main.cpp b/vpr/src/main.cpp index 6643de94840..2a4e74a4641 100644 --- a/vpr/src/main.cpp +++ b/vpr/src/main.cpp @@ -58,6 +58,12 @@ int main(int argc, const char** argv) { return SUCCESS_EXIT_CODE; } + if (Options.show_resource_usage_only_mode) { + vpr_show_resource_usage(vpr_setup, Arch); + vpr_free_all(Arch, vpr_setup); + return SUCCESS_EXIT_CODE; + } + bool flow_succeeded = vpr_flow(vpr_setup, Arch); if (!flow_succeeded) { VTR_LOG("VPR failed to implement circuit\n"); diff --git a/vpr/src/route/segment_stats.cpp b/vpr/src/route/segment_stats.cpp index d19271477be..d504dacf2a3 100644 --- a/vpr/src/route/segment_stats.cpp +++ b/vpr/src/route/segment_stats.cpp @@ -111,12 +111,17 @@ void get_segment_usage_stats(std::vector& segment_inf) { int seg_name_size = static_cast(seg_name.size()); int occ = 0; int cap = 0; + std::string seg_occ = seg_name + std::string("_O"); + std::string seg_cap = seg_name + std::string("_C"); for (auto ax : {X_AXIS, Y_AXIS}) { occ += directed_occ_by_length[ax][seg_length]; cap += directed_cap_by_length[ax][seg_length]; } utilization = (float)occ / (float)cap; VTR_LOG(" %s%s %4d %11.3g\n", std::string(std::max(4 - seg_name_size, (max_segment_name_length - seg_name_size)), ' ').c_str(), seg_name.c_str(), seg_type, utilization); + + VTR_LOG(" %s %4d\n", seg_occ.c_str(), occ); + VTR_LOG(" %s %4d\n", seg_cap.c_str(), cap); } } } diff --git a/vpr/src/server/keys.h b/vpr/src/server/keys.h new file mode 100644 index 00000000000..bcffe3a25cc --- /dev/null +++ b/vpr/src/server/keys.h @@ -0,0 +1,16 @@ +#ifndef KEYS_H +#define KEYS_H + +static const char* KEY_JOB_ID = "JOB_ID"; +static const char* KEY_CMD = "CMD"; +static const char* KEY_OPTIONS = "OPTIONS"; +static const char* KEY_DATA = "DATA"; +static const char* KEY_STATUS = "STATUS"; +static const char* END_TELEGRAM_SEQUENCE = "###___END_FRAME___###"; + +enum CMD { + CMD_GET_PATH_LIST_ID=0, + CMD_DRAW_PATH_ID +}; + +#endif diff --git a/vpr/src/server/pathhelper.cpp b/vpr/src/server/pathhelper.cpp new file mode 100644 index 00000000000..da6018b1109 --- /dev/null +++ b/vpr/src/server/pathhelper.cpp @@ -0,0 +1,217 @@ +#include "pathhelper.h" +#include "globals.h" +#include "vpr_net_pins_matrix.h" +#include "VprTimingGraphResolver.h" +#include "tatum/TimingReporter.hpp" + +#include "draw_types.h" +#include "draw_global.h" + +#include +#include + +std::string getPathsStr(const std::vector& paths, int detailesLevel, bool is_flat_routing) +{ + // shortcuts + auto& atom_ctx = g_vpr_ctx.atom(); + auto& timing_ctx = g_vpr_ctx.timing(); + // shortcuts + + // build deps + NetPinsMatrix net_delay = make_net_pins_matrix(atom_ctx.nlist); + auto analysis_delay_calc = std::make_shared(atom_ctx.nlist, atom_ctx.lookup, net_delay, is_flat_routing); + + VprTimingGraphResolver resolver(atom_ctx.nlist, atom_ctx.lookup, *timing_ctx.graph, *analysis_delay_calc.get(), is_flat_routing); + resolver.set_detail_level(static_cast(detailesLevel)); + // + + std::stringstream ss; + tatum::TimingReporter timing_reporter(resolver, *timing_ctx.graph, *timing_ctx.constraints); + timing_reporter.report_timing(ss, paths); + + return ss.str(); +} + +void calcCritPath(int numMax, const std::string& type) +{ + g_vpr_ctx.critical_path_num = numMax; + g_vpr_ctx.path_type = type; + + tatum::TimingPathCollector path_collector; + + t_draw_state* draw_state = get_draw_state_vars(); + auto& timing_ctx = g_vpr_ctx.timing(); + + //Get the worst timing path + if (type == "setup") { + g_vpr_ctx.crit_paths = path_collector.collect_worst_setup_timing_paths( + *timing_ctx.graph, + *(draw_state->setup_timing_info->setup_analyzer()), numMax); + } else if (type == "hold") { + /* ########################### + // TODO: recalculate g_vpr_ctx.hold_timing_info, because it may cause the assert path check for index 0 + // ########################### + + // std::vector TimingPathCollector::collect_worst_hold_timing_paths(const TimingGraph& timing_graph, const tatum::HoldTimingAnalyzer& hold_analyzer, size_t npaths) const { + // auto& atom_ctx = g_vpr_ctx.atom(); + + // NetPinsMatrix net_delay = make_net_pins_matrix(net_list); + // load_net_delay_from_routing(net_list, + // net_delay); + + // auto analysis_delay_calc = std::make_shared(atom_ctx.nlist, atom_ctx.lookup, net_delay, vpr_setup.RouterOpts.flat_routing); + // auto timing_info = make_setup_hold_timing_info(analysis_delay_calc, vpr_setup.AnalysisOpts.timing_update_type); + // timing_info->update(); + /* */ + + if (g_vpr_ctx.hold_timing_info) { + g_vpr_ctx.crit_paths = path_collector.collect_worst_hold_timing_paths( + *timing_ctx.graph, + *(g_vpr_ctx.hold_timing_info->hold_analyzer()), numMax); + } else { + std::cerr << "g_vpr_ctx.hold_timing_info is nullptr" << std::endl; + } + } +} + + + +// void draw_crit_path(ezgl::renderer* g) { +// std::cout << "~~~ draw_crit_path start" << std::endl; +// tatum::TimingPathCollector path_collector; + +// t_draw_state* draw_state = get_draw_state_vars(); +// auto& timing_ctx = g_vpr_ctx.timing(); + +// if (draw_state->show_crit_path == DRAW_NO_CRIT_PATH) { +// return; +// } + +// if (!draw_state->setup_timing_info) { +// return; //No timing to draw +// } + +// //Get the worst timing path +// auto paths = path_collector.collect_worst_setup_timing_paths( +// *timing_ctx.graph, +// *(draw_state->setup_timing_info->setup_analyzer()), 100); + +// std::string pathIdsStr = ""; //read_shared_string(); +// std::vector selectedPathIds; // = splitString(pathIdsStr, ';'); +// auto& server = g_vpr_ctx.server(); +// std::stringstream ss; +// dumpPathsToStr(ss, paths, false); +// //server.init(); +// //server.setPathsStr(ss.str()); +// //server.tick(); + +// //vpr_setup.AnalysisOpts, vpr_setup.RouterOpts.flat_routing + +// // name resolving +// //bool is_flat = vpr_setup.RouterOpts.flat_routing; +// // bool is_flat = true; +// // const Netlist<>& net_list = is_flat ? (const Netlist<>&)g_vpr_ctx.atom().nlist : (const Netlist<>&)g_vpr_ctx.clustering().clb_nlist; + +// auto& atom_ctx = g_vpr_ctx.atom(); +// NetPinsMatrix net_delay = make_net_pins_matrix(atom_ctx.nlist); +// // load_net_delay_from_routing(net_list, +// // net_delay); + +// auto analysis_delay_calc = std::make_shared(atom_ctx.nlist, atom_ctx.lookup, net_delay, true/*vpr_setup.RouterOpts.flat_routing*/); +// VprTimingGraphResolver name_resolver(atom_ctx.nlist, atom_ctx.lookup, *timing_ctx.graph, *analysis_delay_calc.get(), true/*is_flat*/); +// // + +// //std::unordered_set _set; +// int index = 0; +// for (const tatum::TimingPath& p: paths) { +// tatum::TimingPathInfo pi = p.path_info(); +// //std::cout << "~~~~~~~~~~" << std::endl; +// //std::cout << "pi.type=" << (int)pi.type() << std::endl; +// //std::cout << "pi.delay=" << pi.delay() << std::endl; +// //std::cout << "pi.slack=" << pi.slack() << std::endl; + +// //std::cout << "pi.startpoint=" << pi.startpoint() << std::endl; +// //std::cout << "pi.endpoint=" << pi.endpoint() << std::endl; +// //std::cout << "pi.launch_domain=" << pi.launch_domain() << std::endl; +// //std::cout << "pi.capture_domain=" << pi.capture_domain() << std::endl; + +// // size_t t1 = static_cast(pi.startpoint()); +// // size_t t2 = static_cast(pi.endpoint()); + +// // std::string st1 = std::to_string(t1); +// // std::string st2 = std::to_string(t2); + +// std::string st1 = name_resolver.node_name(pi.startpoint()); +// std::string st2 = name_resolver.node_name(pi.endpoint()); + +// std::string pathId = st1 + ":" + st2; +// for (const std::string& selectedPathId: selectedPathIds) { +// if (selectedPathId == pathId) { +// draw_state->crit_path_index = index; +// } +// } +// //_set.insert(pathId); +// index++; +// } +// // for (const std::string& key: _set) { +// // std::cout << "~~~ key = " << key << std::endl; +// // } +// //std::cout << "_set.size()=" << _set.size() << std::endl; + +// // if (!extStr.empty()) { +// // int indexCandidate = std::atoi(extStr.c_str()); +// // if (indexCandidate <= paths.size()) { +// // draw_state->crit_path_index = indexCandidate; +// // } +// // } + +// tatum::TimingPath path = paths[draw_state->crit_path_index]; +// tatum::TimingPathInfo pi = path.path_info(); +// // size_t t1 = static_cast(pi.startpoint()); +// // size_t t2 = static_cast(pi.endpoint()); +// // std::string st1 = std::to_string(t1); +// // std::string st2 = std::to_string(t2); +// std::string st1 = name_resolver.node_name(pi.startpoint()); +// std::string st2 = name_resolver.node_name(pi.endpoint()); + +// std::string pathId = st1 + ":" + st2; + +// std::cout << "~~~~~~~~~~~~~~~~ num of pathes=" << paths.size() << " index=" << draw_state->crit_path_index << " is drawn, id=" << pathId << std::endl; + +// //Walk through the timing path drawing each edge +// tatum::NodeId prev_node; +// float prev_arr_time = std::numeric_limits::quiet_NaN(); +// int i = 0; +// for (tatum::TimingPathElem elem : path.data_arrival_path().elements()) { +// tatum::NodeId node = elem.node(); +// float arr_time = elem.tag().time(); +// if (prev_node) { +// //We draw each 'edge' in a different color, this allows users to identify the stages and +// //any routing which corresponds to the edge +// // +// //We pick colors from the kelly max-contrast list, for long paths there may be repeats +// ezgl::color color = kelly_max_contrast_colors[i++ +// % kelly_max_contrast_colors.size()]; + +// float delay = arr_time - prev_arr_time; +// if (draw_state->show_crit_path == DRAW_CRIT_PATH_FLYLINES +// || draw_state->show_crit_path +// == DRAW_CRIT_PATH_FLYLINES_DELAYS) { +// g->set_color(color); +// g->set_line_dash(ezgl::line_dash::none); +// g->set_line_width(4); +// draw_flyline_timing_edge(tnode_draw_coord(prev_node), +// tnode_draw_coord(node), delay, g); +// } else { +// VTR_ASSERT(draw_state->show_crit_path != DRAW_NO_CRIT_PATH); + +// //Draw the routed version of the timing edge +// draw_routed_timing_edge(prev_node, node, delay, color, g); +// } +// } +// prev_node = node; +// prev_arr_time = arr_time; +// } + +// std::cout << "~~~ draw_crit_path end" << std::endl; +// } diff --git a/vpr/src/server/pathhelper.h b/vpr/src/server/pathhelper.h new file mode 100644 index 00000000000..d1b1bc589f2 --- /dev/null +++ b/vpr/src/server/pathhelper.h @@ -0,0 +1,12 @@ +#ifndef PATH_HELPER_H +#define PATH_HELPER_H + +#include +#include + +#include "tatum/report/TimingPath.hpp" + +std::string getPathsStr(const std::vector& paths, int detailesLevel, bool is_flat_routing); +void calcCritPath(int numMax, const std::string& type); + +#endif diff --git a/vpr/src/server/server.cpp b/vpr/src/server/server.cpp new file mode 100644 index 00000000000..1dfe6325e34 --- /dev/null +++ b/vpr/src/server/server.cpp @@ -0,0 +1,208 @@ +#include "server.h" +#include "telegramparser.h" + +#include +#include +#include +#include +#include +#include + +// debug +#include +#include +#include +#include +#include +// debug + +namespace { + +void writeFile(const std::filesystem::path& filename) +{ + std::ofstream file(filename); + file << "hello from thread" << std::this_thread::get_id(); + file.close(); + + // Optional: Check if the file was successfully created + if (std::filesystem::exists(filename)) { + std::cout << "File created: " << filename << "\n"; + } else { + std::cerr << "Failed to create file\n"; + } +} +} + +Server::Server() +{ + m_isStarted.store(false); + m_isStopped.store(false); + // m_isCleaned.store(false); +} + +Server::~Server() +{ + std::cout << "~~~ th=" << std::this_thread::get_id() << " ~Server()" << std::endl; + stop(); +} + +void Server::start() +{ + if (!m_isStarted.load()) { + std::cout << "~~~ th=" << std::this_thread::get_id() << " starting server" << std::endl; + writeFile("file_starting_server"); + m_isStarted.store(true); + m_thread = std::thread(&Server::startListening, this); + } +} + +void Server::stop() +{ + if (!m_isStopped.load()) { + m_isStopped.store(true); + std::cout << "~~~ th=" << std::this_thread::get_id() << " stopping server, is stopped=" << m_isStopped.load() << std::endl; + //writeFile("file_stopping_server"); + + // // Start a new thread to join the server thread (running this from GUI thread leads to deadlock) + // std::thread joiner([this]{ + // if (m_thread.joinable()) { + // //std::cout << "~~~ th=" << std::this_thread::get_id() << " join thread START" << std::endl; + // writeFile("file_join_START"); + // m_thread.join(); + // writeFile("file_join_END"); + // //std::cout << "~~~ th=" << std::this_thread::get_id() << " join thread FINISHED" << std::endl; + // } + // }); + + // // Detach the joiner thread since we don't need to join it + // joiner.detach(); + + //std::this_thread::sleep_for(std::chrono::milliseconds(5000)); + + // while(!m_isCleaned.load()) { + // std::cout << "~~~ th=" << std::this_thread::get_id() << " waiting thread cleaned" << std::endl; + // std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + // } + std::cout << "~~~ th=" << std::this_thread::get_id() << " Server.stop() finished" << std::endl; + } +} + +void Server::takeRecievedTasks(std::vector& tasks) +{ + tasks.clear(); + //std::cout<<"---m_receivedTasksMutex START"< lock(m_receivedTasksMutex); + if (m_receivedTasks.size() > 0) { + std::cout << "take " << m_receivedTasks.size() << " num of received tasks"<< std::endl; + } + std::swap(tasks, m_receivedTasks); + //std::cout<<"---m_receivedTasksMutex END"<& tasks) +{ + //std::cout<<"---m_sendTasksMutex START"< lock(m_sendTasksMutex); + for (const Task& task: tasks) { + std::cout << "addSendTasks id=" << task.jobId() << std::endl; + m_sendTasks.push_back(task); + } + //std::cout<<"---m_sendTasksMutex END"< lock(m_sendTasksMutex); + for (const Task& task: m_sendTasks) { + std::string response = task.toJsonStr(); + response += END_TELEGRAM_SEQUENCE; + std::cout << "sending" << response << "to client" << std::endl; + send(client_socket, response.c_str(), response.length(), 0); + } + m_sendTasks.clear(); + //std::cout<<"---m_sendTasksMutex222 END"< 0) { + // Process the received data (you can replace this with your own logic) + std::string message(buffer, bytes_received); + std::cout << "Received: " << message << std::endl; + + int jobId = telegramparser::extractJobId(message); + int cmd = telegramparser::extractCmd(message); + std::string options = telegramparser::extractOptions(message); + std::cout << "server: jobId=" << jobId << ", cmd=" << cmd << ", options=" << options << std::endl; + if ((jobId != -1) && (cmd != -1)) { + //std::cout<<"---m_receivedTasksMutex222 START"< lock(m_receivedTasksMutex); + m_receivedTasks.emplace_back(jobId, cmd, options); + //std::cout<<"---m_receivedTasksMutex222 END"< +#include + +#include +#include +#include +#include +#include + +#ifndef COMM_LOOP_INTERVAL_MS +#define COMM_LOOP_INTERVAL_MS 18 +#endif + +#ifndef PORT_NUM +#define PORT_NUM 61555 +#endif + +class Server +{ +public: + explicit Server(); + ~Server(); + + bool isStarted() const { return m_isStarted.load(); } + bool isStopped() const { return m_isStopped.load(); } + + void takeRecievedTasks(std::vector&); + void addSendTasks(const std::vector&); + + void start(); + void stop(); + +private: + std::string m_pathsStr; + std::atomic m_isStarted; + std::atomic m_isStopped; + // std::atomic m_isCleaned; + std::thread m_thread; + std::mutex m_pathsMutex; + + std::vector m_receivedTasks; + std::mutex m_receivedTasksMutex; + + std::vector m_sendTasks; + std::mutex m_sendTasksMutex; + + void startListening(); +}; + +#endif diff --git a/vpr/src/server/task.h b/vpr/src/server/task.h new file mode 100644 index 00000000000..e47452126cc --- /dev/null +++ b/vpr/src/server/task.h @@ -0,0 +1,69 @@ +#ifndef TASK_H +#define TASK_H + +#include +#include +#include + +#include "keys.h" + +class Task { +public: + Task(int jobId, int cmd, const std::string& options = ""): + m_jobId(jobId), m_cmd(cmd), m_options(options) {} + + int jobId() const { return m_jobId; } + int cmd() const { return m_cmd; } + const std::string& options() const { return m_options; } + const std::string& result() const { return m_result; } + + std::string info() const { + std::stringstream result; + result << "id=" << std::to_string(m_jobId) << ","; + result << "cmd=" << std::to_string(m_cmd) << ","; + result << "opt=" << m_options; + return result.str(); + } + + bool isFinished() const { return m_isFinished; } + bool hasError() const { return m_hasError; } + + void error(const std::string& error) { + std::cout << "task " << info() << " finished with error " << error << std::endl; + m_result = error; + m_isFinished = true; + m_hasError = true; + } + void success(const std::string& result = "") { + std::cout << "task " << info() << " finished with success " << result << std::endl; + m_result = result; + m_isFinished = true; + } + + std::string toJsonStr() const + { + std::stringstream ss; + ss << "{"; + + ss << "\"" << KEY_JOB_ID << "\":\"" << m_jobId << "\","; + ss << "\"" << KEY_CMD << "\":\"" << m_cmd << "\","; + ss << "\"" << KEY_OPTIONS << "\":\"" << m_options << "\","; + ss << "\"" << KEY_DATA << "\":\"" << m_result << "\","; + int status = m_hasError ? 0 : 1; + ss << "\"" << KEY_STATUS << "\":\"" << status << "\""; + + ss << "}"; + + return ss.str(); + } + +private: + int m_jobId = -1; + int m_cmd = -1; + std::string m_options; + std::string m_result; + bool m_isFinished = false; + bool m_hasError = false; +}; + +#endif diff --git a/vpr/src/server/taskresolver.cpp b/vpr/src/server/taskresolver.cpp new file mode 100644 index 00000000000..212aaa5bc93 --- /dev/null +++ b/vpr/src/server/taskresolver.cpp @@ -0,0 +1,140 @@ +#include "taskresolver.h" +#ifdef ENABLE_RANDOM_PATH_GENERATION +#include +#endif + +#ifndef STANDALONE_APP +#include "globals.h" +#include "pathhelper.h" +#include "telegramparser.h" +#include +#include +#endif + +#ifdef ENABLE_TASK_DELAY +#include +#include +#endif + +void TaskResolver::addTasks(const std::vector& tasks) +{ + for (const Task& task: tasks) { + addTask(task); + } +} + +void TaskResolver::addTask(Task task) +{ + for (auto& t: m_tasks) { + if (t.cmd() == task.cmd()) { + if (t.options() == task.options()) { + std::string msg = "similar task is already in execution, reject new task: " + t.info()+ " and waiting for old task: " + task.info() + " execution"; + task.error(msg); + } else { + if (task.jobId() > t.jobId()) { + std::string msg = "old task: " + t.info() + " is overriden by a new task: " + task.info(); + t.error(msg); + } + } + } + } + m_tasks.push_back(std::move(task)); +} + +void TaskResolver::takeFinished(std::vector& result) +{ + for (auto it=m_tasks.begin(); it != m_tasks.end();) { + Task task = *it; + if (task.isFinished()) { + result.push_back(std::move(task)); + it = m_tasks.erase(it); + } else { + ++it; + } + } +} + +std::vector splitString(const std::string& input, char delimiter) +{ + std::vector tokens; + std::istringstream tokenStream(input); + std::string token; + + while (std::getline(tokenStream, token, delimiter)) { + tokens.push_back(token); + } + + return tokens; +} + +#ifndef STANDALONE_APP +void TaskResolver::update(ezgl::application* app) +{ + for (auto& task: m_tasks) { + if (!task.isFinished()) { + // ugly, but let's have it for now + std::vector optionsList = splitString(task.options(), ';'); + if (task.cmd() == CMD_GET_PATH_LIST_ID) { + if (optionsList.size() == 4) { + int nCriticalPathNum = std::atoi(optionsList[0].c_str()); + std::string typePath = optionsList[1]; + int detailsLevel = std::atoi(optionsList[2].c_str()); + bool isFlat = std::atoi(optionsList[3].c_str()); + + calcCritPath(nCriticalPathNum, typePath); + std::string msg = getPathsStr(g_vpr_ctx.crit_paths, detailsLevel, isFlat); + if (typePath != g_vpr_ctx.path_type) { + g_vpr_ctx.crit_path_index = -1; + } + g_vpr_ctx.path_type = typePath; // duplicated + task.success(msg); + } else { + std::string msg = "bad options [" + task.options() + "] for get crit path list telegram"; + std::cerr << msg << std::endl; + task.error(msg); + } + } else if (task.cmd() == CMD_DRAW_PATH_ID) { + if (optionsList.size() == 2) { + int pathIndex = telegramparser::extractPathIndex(optionsList[0]) - 1; + int highLightMode = 1; + + try { + highLightMode = std::atoi(optionsList[1].c_str()); + } catch(...) { + + } + + if ((pathIndex >= 0) && (pathIndex < g_vpr_ctx.crit_paths.size())) { + g_vpr_ctx.crit_path_index = pathIndex; + + // update gtk UI + GtkComboBox* toggle_crit_path = GTK_COMBO_BOX(app->get_object("ToggleCritPath")); + gtk_combo_box_set_active(toggle_crit_path, highLightMode); + + task.success(); + } else { + std::string msg = "selectedIndex=" + std::to_string(pathIndex) + " is out of range [0-" + std::to_string(g_vpr_ctx.crit_paths.size()-1) + "]"; + task.error(msg); + } + } else { + std::string msg = "bad options [" + task.options() + "] for highlight crit path telegram"; + std::cerr << msg << std::endl; + task.error(msg); + } + } + } + } +} +#else +void TaskResolver::update() +{ + for (auto& task: m_tasks) { + if (!task.isFinished()) { +#ifdef ENABLE_TASK_DELAY + std::this_thread::sleep_for(std::chrono::milliseconds(TASK_RESOLVE_DELAY_MS)); +#endif + task.success(generateRandomPathsString()); + } + } +} +#endif diff --git a/vpr/src/server/taskresolver.h b/vpr/src/server/taskresolver.h new file mode 100644 index 00000000000..9efacf3de07 --- /dev/null +++ b/vpr/src/server/taskresolver.h @@ -0,0 +1,36 @@ +#ifndef TASKRESOLVER_H +#define TASKRESOLVER_H + +#include "task.h" + +#include + +#ifndef STANDALONE_APP +namespace ezgl { + class application; +}; +#endif + +class TaskResolver { +public: + TaskResolver()=default; + ~TaskResolver()=default; + + int tasksNum() const { return m_tasks.size(); } + + void addTasks(const std::vector&); + void addTask(Task); +#ifndef STANDALONE_APP + void update(ezgl::application*); +#else + void update(); +#endif + void takeFinished(std::vector&); + + const std::vector& tasks() const { return m_tasks; } + +private: + std::vector m_tasks; +}; + +#endif diff --git a/vpr/src/server/telegramparser.cpp b/vpr/src/server/telegramparser.cpp new file mode 100644 index 00000000000..c8355bf4fb7 --- /dev/null +++ b/vpr/src/server/telegramparser.cpp @@ -0,0 +1,55 @@ +#include "telegramparser.h" + +#include + +namespace telegramparser { + +int extractJobId(const std::string& message) +{ + std::regex pattern("\"JOB_ID\":(\\d+)"); + std::smatch match; + if (std::regex_search(message, match, pattern)) { + if (match.size() > 1) { + return std::atoi(match[1].str().c_str()); + } + } + return -1; +} + +int extractCmd(const std::string& message) +{ + std::regex pattern("\"CMD\":(\\d+)"); + std::smatch match; + if (std::regex_search(message, match, pattern)) { + if (match.size() > 1) { + return std::atoi(match[1].str().c_str()); + } + } + return -1; +} + +std::string extractOptions(const std::string& message) +{ + std::regex pattern("\"OPTIONS\":\"(.*?)\""); + std::smatch match; + if (std::regex_search(message, match, pattern)) { + if (match.size() > 1) { + return match[1].str(); + } + } + return ""; +} + +int extractPathIndex(const std::string& message) +{ + std::regex pattern("^#Path (\\d+)"); + std::smatch match; + if (std::regex_search(message, match, pattern)) { + if (match.size() > 1) { + return std::atoi(match[1].str().c_str()); + } + } + return -1; +} + +} // telegramparser diff --git a/vpr/src/server/telegramparser.h b/vpr/src/server/telegramparser.h new file mode 100644 index 00000000000..002cc2784a6 --- /dev/null +++ b/vpr/src/server/telegramparser.h @@ -0,0 +1,15 @@ +#ifndef TELEGRAMPARSER_H +#define TELEGRAMPARSER_H + +#include + +namespace telegramparser { + +int extractJobId(const std::string& message); +int extractCmd(const std::string& message); +std::string extractOptions(const std::string& message); +int extractPathIndex(const std::string& message); + +} // telegramparser + +#endif From 716b058caee9f8189b8484e26dec8a20be308dc2 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Wed, 15 Nov 2023 21:40:21 +0200 Subject: [PATCH 02/31] cleanup Server class, make recv() operation interuptable --- vpr/src/server/server.cpp | 138 +++++++++++++++++--------------------- vpr/src/server/server.h | 2 +- 2 files changed, 61 insertions(+), 79 deletions(-) diff --git a/vpr/src/server/server.cpp b/vpr/src/server/server.cpp index 1dfe6325e34..8e157b0b9b1 100644 --- a/vpr/src/server/server.cpp +++ b/vpr/src/server/server.cpp @@ -8,36 +8,10 @@ #include #include -// debug -#include -#include -#include -#include -#include -// debug - -namespace { - -void writeFile(const std::filesystem::path& filename) -{ - std::ofstream file(filename); - file << "hello from thread" << std::this_thread::get_id(); - file.close(); - - // Optional: Check if the file was successfully created - if (std::filesystem::exists(filename)) { - std::cout << "File created: " << filename << "\n"; - } else { - std::cerr << "Failed to create file\n"; - } -} -} - Server::Server() { m_isStarted.store(false); m_isStopped.store(false); - // m_isCleaned.store(false); } Server::~Server() @@ -50,7 +24,6 @@ void Server::start() { if (!m_isStarted.load()) { std::cout << "~~~ th=" << std::this_thread::get_id() << " starting server" << std::endl; - writeFile("file_starting_server"); m_isStarted.store(true); m_thread = std::thread(&Server::startListening, this); } @@ -61,53 +34,31 @@ void Server::stop() if (!m_isStopped.load()) { m_isStopped.store(true); std::cout << "~~~ th=" << std::this_thread::get_id() << " stopping server, is stopped=" << m_isStopped.load() << std::endl; - //writeFile("file_stopping_server"); - - // // Start a new thread to join the server thread (running this from GUI thread leads to deadlock) - // std::thread joiner([this]{ - // if (m_thread.joinable()) { - // //std::cout << "~~~ th=" << std::this_thread::get_id() << " join thread START" << std::endl; - // writeFile("file_join_START"); - // m_thread.join(); - // writeFile("file_join_END"); - // //std::cout << "~~~ th=" << std::this_thread::get_id() << " join thread FINISHED" << std::endl; - // } - // }); - - // // Detach the joiner thread since we don't need to join it - // joiner.detach(); - - //std::this_thread::sleep_for(std::chrono::milliseconds(5000)); - - // while(!m_isCleaned.load()) { - // std::cout << "~~~ th=" << std::this_thread::get_id() << " waiting thread cleaned" << std::endl; - // std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - // } - std::cout << "~~~ th=" << std::this_thread::get_id() << " Server.stop() finished" << std::endl; + if (m_thread.joinable()) { + std::cout << "~~~ th=" << std::this_thread::get_id() << " join thread START" << std::endl; + m_thread.join(); + std::cout << "~~~ th=" << std::this_thread::get_id() << " join thread FINISHED" << std::endl; + } } } void Server::takeRecievedTasks(std::vector& tasks) { tasks.clear(); - //std::cout<<"---m_receivedTasksMutex START"< lock(m_receivedTasksMutex); if (m_receivedTasks.size() > 0) { std::cout << "take " << m_receivedTasks.size() << " num of received tasks"<< std::endl; } std::swap(tasks, m_receivedTasks); - //std::cout<<"---m_receivedTasksMutex END"<& tasks) { - //std::cout<<"---m_sendTasksMutex START"< lock(m_sendTasksMutex); for (const Task& task: tasks) { std::cout << "addSendTasks id=" << task.jobId() << std::endl; m_sendTasks.push_back(task); } - //std::cout<<"---m_sendTasksMutex END"<= 0) { // Handle sending { - //std::cout<<"---m_sendTasksMutex222 START"< lock(m_sendTasksMutex); for (const Task& task: m_sendTasks) { std::string response = task.toJsonStr(); response += END_TELEGRAM_SEQUENCE; std::cout << "sending" << response << "to client" << std::endl; send(client_socket, response.c_str(), response.length(), 0); + std::cout << "sent" << std::endl; } m_sendTasks.clear(); - //std::cout<<"---m_sendTasksMutex222 END"< 0) { - // Process the received data (you can replace this with your own logic) - std::string message(buffer, bytes_received); - std::cout << "Received: " << message << std::endl; - - int jobId = telegramparser::extractJobId(message); - int cmd = telegramparser::extractCmd(message); - std::string options = telegramparser::extractOptions(message); - std::cout << "server: jobId=" << jobId << ", cmd=" << cmd << ", options=" << options << std::endl; - if ((jobId != -1) && (cmd != -1)) { - //std::cout<<"---m_receivedTasksMutex222 START"< lock(m_receivedTasksMutex); - m_receivedTasks.emplace_back(jobId, cmd, options); - //std::cout<<"---m_receivedTasksMutex222 END"< 0) { + // Process received data + std::string message(buffer, bytes_received); + std::cout << "Received: " << message << std::endl; + + int jobId = telegramparser::extractJobId(message); + int cmd = telegramparser::extractCmd(message); + std::string options = telegramparser::extractOptions(message); + std::cout << "server: jobId=" << jobId << ", cmd=" << cmd << ", options=" << options << std::endl; + if ((jobId != -1) && (cmd != -1)) { + std::unique_lock lock(m_receivedTasksMutex); + m_receivedTasks.emplace_back(jobId, cmd, options); + } + } else if (bytes_received == 0) { + std::cout << "Connection closed\n"; + } else { + std::cerr << "Error in recv()\n"; + } } } @@ -196,13 +181,10 @@ void Server::startListening() close(client_socket); } - std::cout << "~~~ th=" << std::this_thread::get_id() << "loop_______111" << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(COMM_LOOP_INTERVAL_MS)); - std::cout << "~~~ th=" << std::this_thread::get_id() << "loop_______222" << std::endl; + //std::cout << "~~~ th=" << std::this_thread::get_id() << " loop_222" << std::endl << std::endl; } - // m_isCleaned.store(true); - std::cout << "~~~ th=" << std::this_thread::get_id() << "exit thread call!!!" << std::endl; - close(server_socket); + //std::cout << "~~~ th=" << std::this_thread::get_id() << " exit thread call!!!" << std::endl; } diff --git a/vpr/src/server/server.h b/vpr/src/server/server.h index dbf4746a1b1..5fa3e31fce2 100644 --- a/vpr/src/server/server.h +++ b/vpr/src/server/server.h @@ -39,7 +39,7 @@ class Server std::string m_pathsStr; std::atomic m_isStarted; std::atomic m_isStopped; - // std::atomic m_isCleaned; + std::thread m_thread; std::mutex m_pathsMutex; From 2d64d08fd78071c662a0330e0cf50813e360c5d4 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Wed, 15 Nov 2023 21:59:44 +0200 Subject: [PATCH 03/31] Server maintain connection to specific client --- vpr/src/server/server.cpp | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/vpr/src/server/server.cpp b/vpr/src/server/server.cpp index 8e157b0b9b1..88a346ec3fd 100644 --- a/vpr/src/server/server.cpp +++ b/vpr/src/server/server.cpp @@ -63,7 +63,8 @@ void Server::addSendTasks(const std::vector& tasks) void Server::startListening() { - int server_socket, client_socket; + int server_socket; + int client_socket = -1; struct sockaddr_in address; int opt = 1; int addrlen = sizeof(address); @@ -104,15 +105,21 @@ void Server::startListening() exit(EXIT_FAILURE); } + bool connectionProblemDetected = false; + // Event loop while(!m_isStopped.load()) { //std::cout << "~~~ th=" << std::this_thread::get_id() << " loop_000" << " is stopped=" << m_isStopped.load() << std::endl; - int flags = fcntl(client_socket, F_GETFL, 0); - fcntl(client_socket, F_SETFL, flags | O_NONBLOCK); - client_socket = accept(server_socket, (struct sockaddr *)&address, (socklen_t*)&addrlen); + + if (connectionProblemDetected || client_socket < 0) { + int flags = fcntl(client_socket, F_GETFL, 0); + fcntl(client_socket, F_SETFL, flags | O_NONBLOCK); + client_socket = accept(server_socket, (struct sockaddr *)&address, (socklen_t*)&addrlen); + } //std::cout << "~~~ th=" << std::this_thread::get_id() << " loop_111" << std::endl; if (client_socket >= 0) { + connectionProblemDetected = false; // Handle sending { std::unique_lock lock(m_sendTasksMutex); @@ -142,20 +149,20 @@ void Server::startListening() FD_SET(client_socket, &readfds); // Wait until data is available or timeout - int result = select(client_socket + 1, &readfds, NULL, NULL, &tv); + int selectResult = select(client_socket + 1, &readfds, NULL, NULL, &tv); - if (result == -1) { + if (selectResult == -1) { std::cerr << "Error in select()\n"; - } else if (result == 0) { + } else if (selectResult == 0) { std::cout << "Timeout occurred! No data available.\n"; } else { if (FD_ISSET(client_socket, &readfds)) { // Data is available; proceed with recv char buffer[1024]; - std::cout << "before receive" << std::endl; + //std::cout << "before receive" << std::endl; ssize_t bytes_received = recv(client_socket, buffer, sizeof(buffer), 0); - std::cout << "after receive" << std::endl; + //std::cout << "after receive" << std::endl; if (bytes_received > 0) { // Process received data std::string message(buffer, bytes_received); @@ -171,20 +178,27 @@ void Server::startListening() } } else if (bytes_received == 0) { std::cout << "Connection closed\n"; + connectionProblemDetected = true; } else { std::cerr << "Error in recv()\n"; + connectionProblemDetected = true; } } } - // Close the client socket - close(client_socket); + if (connectionProblemDetected && (client_socket >= 0)) { + close(client_socket); + } } - std::this_thread::sleep_for(std::chrono::milliseconds(COMM_LOOP_INTERVAL_MS)); + //std::this_thread::sleep_for(std::chrono::milliseconds(COMM_LOOP_INTERVAL_MS)); //std::cout << "~~~ th=" << std::this_thread::get_id() << " loop_222" << std::endl << std::endl; } + if (client_socket >= 0) { + close(client_socket); + } + close(server_socket); //std::cout << "~~~ th=" << std::this_thread::get_id() << " exit thread call!!!" << std::endl; } From d64db1cba97c6a9a1214655116e14ab5d6055d0c Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Fri, 17 Nov 2023 18:21:16 +0200 Subject: [PATCH 04/31] implement TelegramBuffer which acts like a stream, with ability to extract well formed telegrams --- vpr/src/server/keys.h | 17 ++++----- vpr/src/server/server.cpp | 40 +++++++++++----------- vpr/src/server/server.h | 3 ++ vpr/src/server/telegrambuffer.cpp | 25 ++++++++++++++ vpr/src/server/telegrambuffer.h | 57 +++++++++++++++++++++++++++++++ 5 files changed, 113 insertions(+), 29 deletions(-) create mode 100644 vpr/src/server/telegrambuffer.cpp create mode 100644 vpr/src/server/telegrambuffer.h diff --git a/vpr/src/server/keys.h b/vpr/src/server/keys.h index bcffe3a25cc..c01634fa659 100644 --- a/vpr/src/server/keys.h +++ b/vpr/src/server/keys.h @@ -1,16 +1,13 @@ -#ifndef KEYS_H -#define KEYS_H +#pragma once -static const char* KEY_JOB_ID = "JOB_ID"; -static const char* KEY_CMD = "CMD"; -static const char* KEY_OPTIONS = "OPTIONS"; -static const char* KEY_DATA = "DATA"; -static const char* KEY_STATUS = "STATUS"; -static const char* END_TELEGRAM_SEQUENCE = "###___END_FRAME___###"; +constexpr const char* KEY_JOB_ID = "JOB_ID"; +constexpr const char* KEY_CMD = "CMD"; +constexpr const char* KEY_OPTIONS = "OPTIONS"; +constexpr const char* KEY_DATA = "DATA"; +constexpr const char* KEY_STATUS = "STATUS"; +constexpr const unsigned char TELEGRAM_FRAME_DELIMETER{0x17}; // 0x17 - End of Transmission Block enum CMD { CMD_GET_PATH_LIST_ID=0, CMD_DRAW_PATH_ID }; - -#endif diff --git a/vpr/src/server/server.cpp b/vpr/src/server/server.cpp index 88a346ec3fd..059511b93ab 100644 --- a/vpr/src/server/server.cpp +++ b/vpr/src/server/server.cpp @@ -125,10 +125,9 @@ void Server::startListening() std::unique_lock lock(m_sendTasksMutex); for (const Task& task: m_sendTasks) { std::string response = task.toJsonStr(); - response += END_TELEGRAM_SEQUENCE; + response += static_cast(TELEGRAM_FRAME_DELIMETER); std::cout << "sending" << response << "to client" << std::endl; send(client_socket, response.c_str(), response.length(), 0); - std::cout << "sent" << std::endl; } m_sendTasks.clear(); } @@ -158,24 +157,10 @@ void Server::startListening() } else { if (FD_ISSET(client_socket, &readfds)) { // Data is available; proceed with recv - char buffer[1024]; - - //std::cout << "before receive" << std::endl; - ssize_t bytes_received = recv(client_socket, buffer, sizeof(buffer), 0); - //std::cout << "after receive" << std::endl; + char data[1024]; + ssize_t bytes_received = recv(client_socket, data, sizeof(data), 0); if (bytes_received > 0) { - // Process received data - std::string message(buffer, bytes_received); - std::cout << "Received: " << message << std::endl; - - int jobId = telegramparser::extractJobId(message); - int cmd = telegramparser::extractCmd(message); - std::string options = telegramparser::extractOptions(message); - std::cout << "server: jobId=" << jobId << ", cmd=" << cmd << ", options=" << options << std::endl; - if ((jobId != -1) && (cmd != -1)) { - std::unique_lock lock(m_receivedTasksMutex); - m_receivedTasks.emplace_back(jobId, cmd, options); - } + m_telegramBuff.append(ByteArray{data}); } else if (bytes_received == 0) { std::cout << "Connection closed\n"; connectionProblemDetected = true; @@ -186,8 +171,25 @@ void Server::startListening() } } + auto frames = m_telegramBuff.takeFrames(); + for (const ByteArray& frame: frames) { + // Process received data + std::string message{frame.to_string()}; + std::cout << "Received: " << message << std::endl; + + int jobId = telegramparser::extractJobId(message); + int cmd = telegramparser::extractCmd(message); + std::string options = telegramparser::extractOptions(message); + std::cout << "server: jobId=" << jobId << ", cmd=" << cmd << ", options=" << options << std::endl; + if ((jobId != -1) && (cmd != -1)) { + std::unique_lock lock(m_receivedTasksMutex); + m_receivedTasks.emplace_back(jobId, cmd, options); + } + } + if (connectionProblemDetected && (client_socket >= 0)) { close(client_socket); + m_telegramBuff.clear(); } } diff --git a/vpr/src/server/server.h b/vpr/src/server/server.h index 5fa3e31fce2..6a63e327adc 100644 --- a/vpr/src/server/server.h +++ b/vpr/src/server/server.h @@ -2,6 +2,7 @@ #define SERVER_H #include "task.h" +#include "telegrambuffer.h" #include #include @@ -49,6 +50,8 @@ class Server std::vector m_sendTasks; std::mutex m_sendTasksMutex; + TelegramBuffer m_telegramBuff; + void startListening(); }; diff --git a/vpr/src/server/telegrambuffer.cpp b/vpr/src/server/telegrambuffer.cpp new file mode 100644 index 00000000000..5cf22012c8b --- /dev/null +++ b/vpr/src/server/telegrambuffer.cpp @@ -0,0 +1,25 @@ +#include "telegrambuffer.h" +#include "keys.h" + +void TelegramBuffer::append(const ByteArray& bytes) +{ + m_rawBuffer.append(bytes); +} + +std::vector TelegramBuffer::takeFrames() +{ + std::vector result; + ByteArray candidate; + for (unsigned char b: m_rawBuffer.data()) { + if (b == TELEGRAM_FRAME_DELIMETER) { + if (!candidate.empty()) { + result.push_back(candidate); + } + candidate = ByteArray(); + } else { + candidate.append(b); + } + } + std::swap(m_rawBuffer, candidate); + return result; +} diff --git a/vpr/src/server/telegrambuffer.h b/vpr/src/server/telegrambuffer.h new file mode 100644 index 00000000000..5c33bdbd0da --- /dev/null +++ b/vpr/src/server/telegrambuffer.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include + +class ByteArray { +public: + static const std::size_t DEFAULT_SIZE_HINT = 1024; + + ByteArray(const char* data) + : m_data(reinterpret_cast(data), + reinterpret_cast(data + strlen(data))) + {} + ByteArray(std::size_t sizeHint = DEFAULT_SIZE_HINT) { + m_data.reserve(sizeHint); + } + + void append(const ByteArray& appendix) { + for (unsigned char b: appendix.data()) { + m_data.push_back(b); + } + } + + void append(unsigned char b) { + m_data.push_back(b); + } + + std::string to_string() const { + return std::string(reinterpret_cast(m_data.data()), m_data.size()); + } + + bool empty() const { return m_data.empty(); } + const std::vector& data() const { return m_data; } + void clear() { m_data.clear(); } + +private: + std::vector m_data; +}; + +class TelegramBuffer +{ + static const std::size_t DEFAULT_SIZE_HINT = 1024; +public: + TelegramBuffer(std::size_t sizeHint = DEFAULT_SIZE_HINT): m_rawBuffer(sizeHint) {} + ~TelegramBuffer()=default; + + void clear() { m_rawBuffer.clear(); } + + void append(const ByteArray&); + std::vector takeFrames(); + + const ByteArray& data() const { return m_rawBuffer; } + +private: + ByteArray m_rawBuffer; +}; From 22bbf9c3d059119d91602fd220bf1adbb79f891f Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Wed, 22 Nov 2023 17:36:34 +0200 Subject: [PATCH 05/31] add telegramoptions helper class and it's usage to telegramsolver --- vpr/src/server/keys.h | 7 ++ vpr/src/server/taskresolver.cpp | 34 ++++----- vpr/src/server/telegramoptions.h | 115 ++++++++++++++++++++++++++++++ vpr/src/server/telegramparser.cpp | 18 +---- vpr/src/server/telegramparser.h | 1 - 5 files changed, 139 insertions(+), 36 deletions(-) create mode 100644 vpr/src/server/telegramoptions.h diff --git a/vpr/src/server/keys.h b/vpr/src/server/keys.h index c01634fa659..238dbb38370 100644 --- a/vpr/src/server/keys.h +++ b/vpr/src/server/keys.h @@ -7,6 +7,13 @@ constexpr const char* KEY_DATA = "DATA"; constexpr const char* KEY_STATUS = "STATUS"; constexpr const unsigned char TELEGRAM_FRAME_DELIMETER{0x17}; // 0x17 - End of Transmission Block +constexpr const char* OPTION_PATH_NUM = "path_num"; +constexpr const char* OPTION_PATH_TYPE = "path_type"; +constexpr const char* OPTION_DETAILS_LEVEL = "details_level"; +constexpr const char* OPTION_IS_FLOAT_ROUTING = "is_flat_routing"; +constexpr const char* OPTION_PATH_INDEX = "path_index"; +constexpr const char* OPTION_HIGHTLIGHT_MODE = "hight_light_mode"; + enum CMD { CMD_GET_PATH_LIST_ID=0, CMD_DRAW_PATH_ID diff --git a/vpr/src/server/taskresolver.cpp b/vpr/src/server/taskresolver.cpp index 212aaa5bc93..d538118b64f 100644 --- a/vpr/src/server/taskresolver.cpp +++ b/vpr/src/server/taskresolver.cpp @@ -6,6 +6,7 @@ #ifndef STANDALONE_APP #include "globals.h" #include "pathhelper.h" +#include "telegramoptions.h" #include "telegramparser.h" #include #include @@ -72,15 +73,14 @@ void TaskResolver::update(ezgl::application* app) { for (auto& task: m_tasks) { if (!task.isFinished()) { - // ugly, but let's have it for now - std::vector optionsList = splitString(task.options(), ';'); + TelegramOptions options{task.options()}; if (task.cmd() == CMD_GET_PATH_LIST_ID) { - if (optionsList.size() == 4) { - int nCriticalPathNum = std::atoi(optionsList[0].c_str()); - std::string typePath = optionsList[1]; - int detailsLevel = std::atoi(optionsList[2].c_str()); - bool isFlat = std::atoi(optionsList[3].c_str()); - + options.validateNamePresence({OPTION_PATH_NUM, OPTION_PATH_TYPE, OPTION_DETAILS_LEVEL, OPTION_IS_FLOAT_ROUTING}); + int nCriticalPathNum = options.getInt(OPTION_PATH_NUM, 1); + std::string typePath = options.getString(OPTION_PATH_TYPE); + int detailsLevel = options.getInt(OPTION_DETAILS_LEVEL, 0); + bool isFlat = options.getBool(OPTION_IS_FLOAT_ROUTING, false); + if (!options.hasErrors()) { calcCritPath(nCriticalPathNum, typePath); std::string msg = getPathsStr(g_vpr_ctx.crit_paths, detailsLevel, isFlat); if (typePath != g_vpr_ctx.path_type) { @@ -89,21 +89,15 @@ void TaskResolver::update(ezgl::application* app) g_vpr_ctx.path_type = typePath; // duplicated task.success(msg); } else { - std::string msg = "bad options [" + task.options() + "] for get crit path list telegram"; + std::string msg = "options errors in get crit path list telegram: " + options.errorsStr(); std::cerr << msg << std::endl; task.error(msg); } } else if (task.cmd() == CMD_DRAW_PATH_ID) { - if (optionsList.size() == 2) { - int pathIndex = telegramparser::extractPathIndex(optionsList[0]) - 1; - int highLightMode = 1; - - try { - highLightMode = std::atoi(optionsList[1].c_str()); - } catch(...) { - - } - + options.validateNamePresence({OPTION_PATH_INDEX, OPTION_HIGHTLIGHT_MODE}); + int pathIndex = options.getInt(OPTION_PATH_INDEX, -1); + int highLightMode = options.getInt(OPTION_HIGHTLIGHT_MODE, 1); + if (!options.hasErrors()) { if ((pathIndex >= 0) && (pathIndex < g_vpr_ctx.crit_paths.size())) { g_vpr_ctx.crit_path_index = pathIndex; @@ -117,7 +111,7 @@ void TaskResolver::update(ezgl::application* app) task.error(msg); } } else { - std::string msg = "bad options [" + task.options() + "] for highlight crit path telegram"; + std::string msg = "options errors in highlight crit path telegram: " + options.errorsStr(); std::cerr << msg << std::endl; task.error(msg); } diff --git a/vpr/src/server/telegramoptions.h b/vpr/src/server/telegramoptions.h new file mode 100644 index 00000000000..3e2bfa5d99a --- /dev/null +++ b/vpr/src/server/telegramoptions.h @@ -0,0 +1,115 @@ +#pragma once + +#include +#include +#include +#include +#include + +class TelegramOptions { +private: + enum { + INDEX_TYPE=0, + INDEX_NAME, + INDEX_VALUE, + TOTAL_INDEXES_NUM + }; + + struct Option { + std::string type; + std::string value; + }; + +public: + TelegramOptions(const std::string& data) { + std::vector options = splitString(data, ';'); + for (const std::string& optionStr: options) { + std::vector fragments = splitString(optionStr, ':'); + if (fragments.size() == TOTAL_INDEXES_NUM) { + std::string name = fragments[INDEX_NAME]; + Option option{fragments[INDEX_TYPE], fragments[INDEX_VALUE]}; + if (validateType(option.type)) { + m_options[name] = option; + } else { + m_errors.emplace_back("bad type for option [" + optionStr + "]"); + } + } else { + m_errors.emplace_back("bad option [" + optionStr + "]"); + } + } + } + + ~TelegramOptions() {} + + bool hasErrors() const { return !m_errors.empty(); } + + bool validateNamePresence(const std::vector& names) { + bool result = true; + for (const std::string& name: names) { + if (m_options.find(name) == m_options.end()) { + m_errors.emplace_back("cannot find required option " + name); + result = false; + } + } + return result; + } + + std::string getString(const std::string& name) { + std::string result; + if (auto it = m_options.find(name); it != m_options.end()) { + result = it->second.value; + } + return result; + } + + int getInt(const std::string& name, int defaultValue) { + int result = defaultValue; + try { + result = std::atoi(m_options[name].value.c_str()); + } catch(...) { + m_errors.emplace_back("cannot get int value for option " + name); + } + return result; + } + + bool getBool(const std::string& name, bool defaultValue) { + bool result = defaultValue; + try { + result = std::atoi(m_options[name].value.c_str()); + } catch(...) { + m_errors.emplace_back("cannot get bool value for option " + name); + } + return result; + } + + std::string errorsStr() const { + std::string result; + for (const std::string& error: m_errors) { + result += error + ";"; + } + return result; + } + +private: + std::unordered_map m_options; + std::vector m_errors; + + + std::vector splitString(const std::string& input, char delimiter) + { + std::vector tokens; + std::istringstream tokenStream(input); + std::string token; + + while (std::getline(tokenStream, token, delimiter)) { + tokens.push_back(token); + } + + return tokens; + } + + bool validateType(const std::string& type) { + static std::set supportedTypes{"int", "string", "bool"}; + return supportedTypes.count(type) != 0; + } +}; diff --git a/vpr/src/server/telegramparser.cpp b/vpr/src/server/telegramparser.cpp index c8355bf4fb7..3a7f0300f3e 100644 --- a/vpr/src/server/telegramparser.cpp +++ b/vpr/src/server/telegramparser.cpp @@ -6,7 +6,7 @@ namespace telegramparser { int extractJobId(const std::string& message) { - std::regex pattern("\"JOB_ID\":(\\d+)"); + static std::regex pattern("\"JOB_ID\":(\\d+)"); std::smatch match; if (std::regex_search(message, match, pattern)) { if (match.size() > 1) { @@ -18,7 +18,7 @@ int extractJobId(const std::string& message) int extractCmd(const std::string& message) { - std::regex pattern("\"CMD\":(\\d+)"); + static std::regex pattern("\"CMD\":(\\d+)"); std::smatch match; if (std::regex_search(message, match, pattern)) { if (match.size() > 1) { @@ -30,7 +30,7 @@ int extractCmd(const std::string& message) std::string extractOptions(const std::string& message) { - std::regex pattern("\"OPTIONS\":\"(.*?)\""); + static std::regex pattern("\"OPTIONS\":\"(.*?)\""); std::smatch match; if (std::regex_search(message, match, pattern)) { if (match.size() > 1) { @@ -40,16 +40,4 @@ std::string extractOptions(const std::string& message) return ""; } -int extractPathIndex(const std::string& message) -{ - std::regex pattern("^#Path (\\d+)"); - std::smatch match; - if (std::regex_search(message, match, pattern)) { - if (match.size() > 1) { - return std::atoi(match[1].str().c_str()); - } - } - return -1; -} - } // telegramparser diff --git a/vpr/src/server/telegramparser.h b/vpr/src/server/telegramparser.h index 002cc2784a6..23fccd922f2 100644 --- a/vpr/src/server/telegramparser.h +++ b/vpr/src/server/telegramparser.h @@ -8,7 +8,6 @@ namespace telegramparser { int extractJobId(const std::string& message); int extractCmd(const std::string& message); std::string extractOptions(const std::string& message); -int extractPathIndex(const std::string& message); } // telegramparser From c0fa676ec90edfd9b0c22cb236cadf90f12a0794 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Wed, 29 Nov 2023 14:44:11 +0200 Subject: [PATCH 06/31] add gtkcomboboxhelper functions, some opetions were changed from int-index to string-value type, to have more robust way to transmit request between client and server --- vpr/src/server/gtkcomboboxhelper.cpp | 39 ++++++++++++++++++++++++++++ vpr/src/server/gtkcomboboxhelper.h | 10 +++++++ vpr/src/server/pathhelper.cpp | 16 ++++++++++-- vpr/src/server/pathhelper.h | 2 +- vpr/src/server/task.h | 2 +- vpr/src/server/taskresolver.cpp | 31 +++++++++++++--------- vpr/src/server/taskresolver.h | 2 +- 7 files changed, 85 insertions(+), 17 deletions(-) create mode 100644 vpr/src/server/gtkcomboboxhelper.cpp create mode 100644 vpr/src/server/gtkcomboboxhelper.h diff --git a/vpr/src/server/gtkcomboboxhelper.cpp b/vpr/src/server/gtkcomboboxhelper.cpp new file mode 100644 index 00000000000..f2eb4cd042c --- /dev/null +++ b/vpr/src/server/gtkcomboboxhelper.cpp @@ -0,0 +1,39 @@ +#include "gtkcomboboxhelper.h" +#include + +gint get_item_count(gpointer combo_box) { + GtkComboBoxText* combo = GTK_COMBO_BOX_TEXT(combo_box); + + // Get the model of the combo box + GtkTreeModel* model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); + + // Get the number of items (indexes) in the combo box + gint count = gtk_tree_model_iter_n_children(model, NULL); + return count; +} + +gint get_item_index_by_text(gpointer combo_box, gchar* target_item) { + gint result_index = -1; + GtkComboBoxText* combo = GTK_COMBO_BOX_TEXT(combo_box); + + // Get the model of the combo box + GtkTreeModel* model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); + + gchar* current_item_text = nullptr; + + for (gint index=0; index + +gint get_item_count(gpointer combo_box); + +gint get_item_index_by_text(gpointer combo_box, gchar* target_item); + +#endif // GTKCOMBOBOXHELPER_H diff --git a/vpr/src/server/pathhelper.cpp b/vpr/src/server/pathhelper.cpp index da6018b1109..6a3431d18e0 100644 --- a/vpr/src/server/pathhelper.cpp +++ b/vpr/src/server/pathhelper.cpp @@ -10,7 +10,7 @@ #include #include -std::string getPathsStr(const std::vector& paths, int detailesLevel, bool is_flat_routing) +std::string getPathsStr(const std::vector& paths, const std::string& detailsLevel, bool is_flat_routing) { // shortcuts auto& atom_ctx = g_vpr_ctx.atom(); @@ -22,7 +22,19 @@ std::string getPathsStr(const std::vector& paths, int detaile auto analysis_delay_calc = std::make_shared(atom_ctx.nlist, atom_ctx.lookup, net_delay, is_flat_routing); VprTimingGraphResolver resolver(atom_ctx.nlist, atom_ctx.lookup, *timing_ctx.graph, *analysis_delay_calc.get(), is_flat_routing); - resolver.set_detail_level(static_cast(detailesLevel)); + e_timing_report_detail detailesLevelEnum = e_timing_report_detail::NETLIST; + if (detailsLevel == "netlist") { + detailesLevelEnum = e_timing_report_detail::NETLIST; + } else if (detailsLevel == "aggregated") { + detailesLevelEnum = e_timing_report_detail::AGGREGATED; + } else if (detailsLevel == "detailed") { + detailesLevelEnum = e_timing_report_detail::DETAILED_ROUTING; + } else if (detailsLevel == "debug") { + detailesLevelEnum = e_timing_report_detail::DEBUG; + } else { + std::cerr << "unhandled option" << detailsLevel << std::endl; + } + resolver.set_detail_level(detailesLevelEnum); // std::stringstream ss; diff --git a/vpr/src/server/pathhelper.h b/vpr/src/server/pathhelper.h index d1b1bc589f2..47e9d432c29 100644 --- a/vpr/src/server/pathhelper.h +++ b/vpr/src/server/pathhelper.h @@ -6,7 +6,7 @@ #include "tatum/report/TimingPath.hpp" -std::string getPathsStr(const std::vector& paths, int detailesLevel, bool is_flat_routing); +std::string getPathsStr(const std::vector& paths, const std::string& detailesLevel, bool is_flat_routing); void calcCritPath(int numMax, const std::string& type); #endif diff --git a/vpr/src/server/task.h b/vpr/src/server/task.h index e47452126cc..24040028a28 100644 --- a/vpr/src/server/task.h +++ b/vpr/src/server/task.h @@ -28,7 +28,7 @@ class Task { bool isFinished() const { return m_isFinished; } bool hasError() const { return m_hasError; } - void error(const std::string& error) { + void fail(const std::string& error) { std::cout << "task " << info() << " finished with error " << error << std::endl; m_result = error; m_isFinished = true; diff --git a/vpr/src/server/taskresolver.cpp b/vpr/src/server/taskresolver.cpp index d538118b64f..5147a7f24e3 100644 --- a/vpr/src/server/taskresolver.cpp +++ b/vpr/src/server/taskresolver.cpp @@ -8,7 +8,7 @@ #include "pathhelper.h" #include "telegramoptions.h" #include "telegramparser.h" -#include +#include "gtkcomboboxhelper.h" #include #endif @@ -30,11 +30,11 @@ void TaskResolver::addTask(Task task) if (t.cmd() == task.cmd()) { if (t.options() == task.options()) { std::string msg = "similar task is already in execution, reject new task: " + t.info()+ " and waiting for old task: " + task.info() + " execution"; - task.error(msg); + task.fail(msg); } else { if (task.jobId() > t.jobId()) { std::string msg = "old task: " + t.info() + " is overriden by a new task: " + task.info(); - t.error(msg); + t.fail(msg); } } } @@ -78,7 +78,7 @@ void TaskResolver::update(ezgl::application* app) options.validateNamePresence({OPTION_PATH_NUM, OPTION_PATH_TYPE, OPTION_DETAILS_LEVEL, OPTION_IS_FLOAT_ROUTING}); int nCriticalPathNum = options.getInt(OPTION_PATH_NUM, 1); std::string typePath = options.getString(OPTION_PATH_TYPE); - int detailsLevel = options.getInt(OPTION_DETAILS_LEVEL, 0); + std::string detailsLevel = options.getString(OPTION_DETAILS_LEVEL); bool isFlat = options.getBool(OPTION_IS_FLOAT_ROUTING, false); if (!options.hasErrors()) { calcCritPath(nCriticalPathNum, typePath); @@ -91,29 +91,36 @@ void TaskResolver::update(ezgl::application* app) } else { std::string msg = "options errors in get crit path list telegram: " + options.errorsStr(); std::cerr << msg << std::endl; - task.error(msg); + task.fail(msg); } } else if (task.cmd() == CMD_DRAW_PATH_ID) { options.validateNamePresence({OPTION_PATH_INDEX, OPTION_HIGHTLIGHT_MODE}); int pathIndex = options.getInt(OPTION_PATH_INDEX, -1); - int highLightMode = options.getInt(OPTION_HIGHTLIGHT_MODE, 1); + std::string highLightMode = options.getString(OPTION_HIGHTLIGHT_MODE); if (!options.hasErrors()) { if ((pathIndex >= 0) && (pathIndex < g_vpr_ctx.crit_paths.size())) { g_vpr_ctx.crit_path_index = pathIndex; // update gtk UI GtkComboBox* toggle_crit_path = GTK_COMBO_BOX(app->get_object("ToggleCritPath")); - gtk_combo_box_set_active(toggle_crit_path, highLightMode); - - task.success(); + gint highLightModeIndex = get_item_index_by_text(toggle_crit_path, highLightMode.data()); + if (highLightModeIndex != -1) { + gtk_combo_box_set_active(toggle_crit_path, highLightModeIndex); + task.success(); + } else { + std::string msg{"cannot find ToggleCritPath qcombobox index for item " + highLightMode}; + std::cerr << msg << std::endl; + task.fail(msg); + } } else { std::string msg = "selectedIndex=" + std::to_string(pathIndex) + " is out of range [0-" + std::to_string(g_vpr_ctx.crit_paths.size()-1) + "]"; - task.error(msg); + std::cerr << msg << std::endl; + task.fail(msg); } } else { - std::string msg = "options errors in highlight crit path telegram: " + options.errorsStr(); + std::string msg{"options errors in highlight crit path telegram: " + options.errorsStr()}; std::cerr << msg << std::endl; - task.error(msg); + task.fail(msg); } } } diff --git a/vpr/src/server/taskresolver.h b/vpr/src/server/taskresolver.h index 9efacf3de07..f5f7e0c5c09 100644 --- a/vpr/src/server/taskresolver.h +++ b/vpr/src/server/taskresolver.h @@ -8,7 +8,7 @@ #ifndef STANDALONE_APP namespace ezgl { class application; -}; +} #endif class TaskResolver { From afa5bc1964beaba8ae5da991c94366841f58dda7 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Fri, 1 Dec 2023 15:18:21 +0200 Subject: [PATCH 07/31] fix regression of reseting crit path index selection on new path list request --- vpr/src/server/taskresolver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vpr/src/server/taskresolver.cpp b/vpr/src/server/taskresolver.cpp index 5147a7f24e3..0a2d106e3b9 100644 --- a/vpr/src/server/taskresolver.cpp +++ b/vpr/src/server/taskresolver.cpp @@ -81,11 +81,11 @@ void TaskResolver::update(ezgl::application* app) std::string detailsLevel = options.getString(OPTION_DETAILS_LEVEL); bool isFlat = options.getBool(OPTION_IS_FLOAT_ROUTING, false); if (!options.hasErrors()) { - calcCritPath(nCriticalPathNum, typePath); - std::string msg = getPathsStr(g_vpr_ctx.crit_paths, detailsLevel, isFlat); if (typePath != g_vpr_ctx.path_type) { g_vpr_ctx.crit_path_index = -1; } + calcCritPath(nCriticalPathNum, typePath); + std::string msg = getPathsStr(g_vpr_ctx.crit_paths, detailsLevel, isFlat); g_vpr_ctx.path_type = typePath; // duplicated task.success(msg); } else { From 4decf9b1bfdf01e5eef43281a3661a075333cd9c Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Fri, 1 Dec 2023 15:53:40 +0200 Subject: [PATCH 08/31] cleanup taskresolver code from unused sections --- vpr/src/server/taskresolver.cpp | 25 +------------------------ vpr/src/server/taskresolver.h | 7 +------ 2 files changed, 2 insertions(+), 30 deletions(-) diff --git a/vpr/src/server/taskresolver.cpp b/vpr/src/server/taskresolver.cpp index 0a2d106e3b9..c679cb0f7d4 100644 --- a/vpr/src/server/taskresolver.cpp +++ b/vpr/src/server/taskresolver.cpp @@ -1,21 +1,11 @@ #include "taskresolver.h" -#ifdef ENABLE_RANDOM_PATH_GENERATION -#include -#endif -#ifndef STANDALONE_APP #include "globals.h" #include "pathhelper.h" #include "telegramoptions.h" #include "telegramparser.h" #include "gtkcomboboxhelper.h" #include -#endif - -#ifdef ENABLE_TASK_DELAY -#include -#include -#endif void TaskResolver::addTasks(const std::vector& tasks) { @@ -68,7 +58,6 @@ std::vector splitString(const std::string& input, char delimiter) return tokens; } -#ifndef STANDALONE_APP void TaskResolver::update(ezgl::application* app) { for (auto& task: m_tasks) { @@ -126,16 +115,4 @@ void TaskResolver::update(ezgl::application* app) } } } -#else -void TaskResolver::update() -{ - for (auto& task: m_tasks) { - if (!task.isFinished()) { -#ifdef ENABLE_TASK_DELAY - std::this_thread::sleep_for(std::chrono::milliseconds(TASK_RESOLVE_DELAY_MS)); -#endif - task.success(generateRandomPathsString()); - } - } -} -#endif + diff --git a/vpr/src/server/taskresolver.h b/vpr/src/server/taskresolver.h index f5f7e0c5c09..d161f161d00 100644 --- a/vpr/src/server/taskresolver.h +++ b/vpr/src/server/taskresolver.h @@ -5,11 +5,9 @@ #include -#ifndef STANDALONE_APP namespace ezgl { class application; } -#endif class TaskResolver { public: @@ -20,11 +18,8 @@ class TaskResolver { void addTasks(const std::vector&); void addTask(Task); -#ifndef STANDALONE_APP + void update(ezgl::application*); -#else - void update(); -#endif void takeFinished(std::vector&); const std::vector& tasks() const { return m_tasks; } From 13c729d588f4b2ff54ece848429d9d63ee3eeba1 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Fri, 1 Dec 2023 17:07:38 +0200 Subject: [PATCH 09/31] update telegramparser in order to parse int wrapped as string for job_id and cmd keys --- vpr/src/server/telegramparser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vpr/src/server/telegramparser.cpp b/vpr/src/server/telegramparser.cpp index 3a7f0300f3e..5736577d093 100644 --- a/vpr/src/server/telegramparser.cpp +++ b/vpr/src/server/telegramparser.cpp @@ -6,7 +6,7 @@ namespace telegramparser { int extractJobId(const std::string& message) { - static std::regex pattern("\"JOB_ID\":(\\d+)"); + static std::regex pattern("\"JOB_ID\":\"(\\d+)\""); std::smatch match; if (std::regex_search(message, match, pattern)) { if (match.size() > 1) { @@ -18,7 +18,7 @@ int extractJobId(const std::string& message) int extractCmd(const std::string& message) { - static std::regex pattern("\"CMD\":(\\d+)"); + static std::regex pattern("\"CMD\":\"(\\d+)\""); std::smatch match; if (std::regex_search(message, match, pattern)) { if (match.size() > 1) { From 3dbbf1e6e0131d00a74e745ab938dd2b7f64b6f0 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Fri, 1 Dec 2023 22:23:09 +0200 Subject: [PATCH 10/31] get_item_index_by_text uses case-insensitive variant of string comparison --- vpr/src/server/gtkcomboboxhelper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vpr/src/server/gtkcomboboxhelper.cpp b/vpr/src/server/gtkcomboboxhelper.cpp index f2eb4cd042c..d53fb606f05 100644 --- a/vpr/src/server/gtkcomboboxhelper.cpp +++ b/vpr/src/server/gtkcomboboxhelper.cpp @@ -27,7 +27,7 @@ gint get_item_index_by_text(gpointer combo_box, gchar* target_item) { // Check if the index is within bounds if (gtk_tree_model_iter_nth_child(model, &iter, NULL, index)) { gtk_tree_model_get(model, &iter, 0, ¤t_item_text, -1); - if (g_strcmp0(target_item, current_item_text) == 0) { + if (g_strcasecmp(target_item, current_item_text) == 0) { result_index = index; break; } From 9923900164c32b76966b801866cf357f5048d305 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Sun, 3 Dec 2023 19:55:53 +0200 Subject: [PATCH 11/31] undo changes made tatum/TimingReporter.cpp related to assertion check --- libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp index 6ff29649824..609b0c0b03e 100644 --- a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp +++ b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp @@ -631,11 +631,7 @@ Time TimingReporter::report_timing_data_arrival_subpath(std::ostream& os, path += delay_component.delay; path_helper.update_print_path(os, point, path); } - // wOlek: here we have a crash on hold report - if (!nearly_equal(path, path_elem.tag().time())) { - std::cout << "~~~ path=" << path << ", path_elem.tag().time()=" << path_elem.tag().time() << std::endl; - } - // TATUM_ASSERT_MSG(nearly_equal(path, path_elem.tag().time()), "Delay breakdown must match calculated delay"); + TATUM_ASSERT_MSG(nearly_equal(path, path_elem.tag().time()), "Delay breakdown must match calculated delay"); } std::string point = name_resolver_.node_name(path_elem.node()) + " (" + name_resolver_.node_type_name(path_elem.node()) + ")"; From db7c79570795b468365349c1cc1eccea7ed84a30 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Mon, 4 Dec 2023 21:12:49 +0200 Subject: [PATCH 12/31] pass port number via cmdline arguments, allow to run servers which listen different ports --- vpr/src/base/SetupVPR.cpp | 12 +++++++++--- vpr/src/base/SetupVPR.h | 2 +- vpr/src/base/read_options.cpp | 7 ++++++- vpr/src/base/read_options.h | 3 ++- vpr/src/base/vpr_api.cpp | 8 ++++---- vpr/src/base/vpr_types.h | 7 ++++++- vpr/src/draw/draw.cpp | 4 +++- vpr/src/draw/draw.h | 3 ++- vpr/src/server/server.cpp | 22 +++++++++++++--------- vpr/src/server/server.h | 13 +++++-------- 10 files changed, 51 insertions(+), 30 deletions(-) diff --git a/vpr/src/base/SetupVPR.cpp b/vpr/src/base/SetupVPR.cpp index 310d498897d..a8b5bad7c23 100644 --- a/vpr/src/base/SetupVPR.cpp +++ b/vpr/src/base/SetupVPR.cpp @@ -38,6 +38,8 @@ static void SetupAnnealSched(const t_options& Options, static void SetupRouterOpts(const t_options& Options, t_router_opts* RouterOpts); static void SetupNocOpts(const t_options& Options, t_noc_opts* NocOpts); +static void SetupServerOpts(const t_options& Options, + t_server_opts* ServerOpts); static void SetupRoutingArch(const t_arch& Arch, t_det_routing_arch* RoutingArch); static void SetupTiming(const t_options& Options, const bool TimingEnabled, t_timing_inf* Timing); static void SetupSwitches(const t_arch& Arch, @@ -99,6 +101,7 @@ void SetupVPR(const t_options* Options, t_router_opts* RouterOpts, t_analysis_opts* AnalysisOpts, t_noc_opts* NocOpts, + t_server_opts* ServerOpts, t_det_routing_arch* RoutingArch, std::vector** PackerRRGraphs, std::vector& Segments, @@ -107,7 +110,6 @@ void SetupVPR(const t_options* Options, int* GraphPause, bool* SaveGraphics, std::string* GraphicsCommands, - bool* server, t_power_opts* PowerOpts, t_vpr_setup* vpr_setup) { using argparse::Provenance; @@ -145,6 +147,7 @@ void SetupVPR(const t_options* Options, SetupAnalysisOpts(*Options, *AnalysisOpts); SetupPowerOpts(*Options, PowerOpts, Arch); SetupNocOpts(*Options, NocOpts); + SetupServerOpts(*Options, ServerOpts); if (readArchFile == true) { vtr::ScopedStartFinishTimer t("Loading Architecture Description"); @@ -315,8 +318,6 @@ void SetupVPR(const t_options* Options, *SaveGraphics = Options->save_graphics; *GraphicsCommands = Options->graphics_commands; - *server = Options->server; - if (getEchoEnabled() && isEchoFileEnabled(E_ECHO_ARCH)) { EchoArch(getEchoFileName(E_ECHO_ARCH), device_ctx.physical_tile_types, device_ctx.logical_block_types, Arch); } @@ -753,6 +754,11 @@ static void SetupNocOpts(const t_options& Options, t_noc_opts* NocOpts) { return; } +static void SetupServerOpts(const t_options& Options, t_server_opts* ServerOpts) { + ServerOpts->is_enabled = Options.is_server_enabled; + ServerOpts->port_num = Options.server_port_num; +} + static void find_ipin_cblock_switch_index(const t_arch& Arch, int& wire_to_arch_ipin_switch, int& wire_to_arch_ipin_switch_between_dice) { for (auto cb_switch_name_index = 0; cb_switch_name_index < (int)Arch.ipin_cblock_switch_name.size(); cb_switch_name_index++) { int ipin_cblock_switch_index = UNDEFINED; diff --git a/vpr/src/base/SetupVPR.h b/vpr/src/base/SetupVPR.h index aa1813ac7b3..97499eb8614 100644 --- a/vpr/src/base/SetupVPR.h +++ b/vpr/src/base/SetupVPR.h @@ -20,6 +20,7 @@ void SetupVPR(const t_options* Options, t_router_opts* RouterOpts, t_analysis_opts* AnalysisOpts, t_noc_opts* NocOpts, + t_server_opts* ServerOpts, t_det_routing_arch* RoutingArch, std::vector** PackerRRGraphs, std::vector& Segments, @@ -28,7 +29,6 @@ void SetupVPR(const t_options* Options, int* GraphPause, bool* SaveGraphics, std::string* GraphicsCommands, - bool* server, t_power_opts* PowerOpts, t_vpr_setup* vpr_setup); #endif diff --git a/vpr/src/base/read_options.cpp b/vpr/src/base/read_options.cpp index bf8bf29acd4..e07277168ac 100644 --- a/vpr/src/base/read_options.cpp +++ b/vpr/src/base/read_options.cpp @@ -1280,11 +1280,16 @@ argparse::ArgumentParser create_arg_parser(std::string prog_name, t_options& arg .action(argparse::Action::STORE_TRUE) .default_value("off"); - stage_grp.add_argument(args.server, "--server") + stage_grp.add_argument(args.is_server_enabled, "--server") .help("Run server mode") .action(argparse::Action::STORE_TRUE) .default_value("off"); + stage_grp.add_argument(args.server_port_num, "--port") + .help("Server port number") + .default_value("60555") + .show_in(argparse::ShowIn::HELP_ONLY); + stage_grp.epilog( "If none of the stage options are specified, all stages are run.\n" "Analysis is always run after routing, unless the implementation\n" diff --git a/vpr/src/base/read_options.h b/vpr/src/base/read_options.h index 463479aa1dc..49c297512d4 100644 --- a/vpr/src/base/read_options.h +++ b/vpr/src/base/read_options.h @@ -74,7 +74,8 @@ struct t_options { argparse::ArgValue suppress_warnings; argparse::ArgValue allow_dangling_combinational_nodes; argparse::ArgValue terminate_if_timing_fails; - argparse::ArgValue server; + argparse::ArgValue is_server_enabled; + argparse::ArgValue server_port_num; /* Atom netlist options */ argparse::ArgValue absorb_buffer_luts; diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index 6de050d750b..6389c5047a3 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -290,6 +290,7 @@ void vpr_init_with_options(const t_options* options, t_vpr_setup* vpr_setup, t_a &vpr_setup->RouterOpts, &vpr_setup->AnalysisOpts, &vpr_setup->NocOpts, + &vpr_setup->ServerOpts, &vpr_setup->RoutingArch, &vpr_setup->PackerRRGraph, vpr_setup->Segments, @@ -298,7 +299,6 @@ void vpr_init_with_options(const t_options* options, t_vpr_setup* vpr_setup, t_a &vpr_setup->GraphPause, &vpr_setup->SaveGraphics, &vpr_setup->GraphicsCommands, - &vpr_setup->server, &vpr_setup->PowerOpts, vpr_setup); @@ -1099,7 +1099,7 @@ void vpr_init_graphics(const t_vpr_setup& vpr_setup, const t_arch& arch, bool is /* Startup X graphics */ init_graphics_state(vpr_setup.ShowGraphics, vpr_setup.GraphPause, vpr_setup.RouterOpts.route_type, vpr_setup.SaveGraphics, - vpr_setup.GraphicsCommands, is_flat, vpr_setup.server); + vpr_setup.GraphicsCommands, is_flat, vpr_setup.ServerOpts.is_enabled, vpr_setup.ServerOpts.port_num); if (vpr_setup.ShowGraphics || vpr_setup.SaveGraphics || !vpr_setup.GraphicsCommands.empty()) alloc_draw_structs(&arch); } @@ -1314,6 +1314,7 @@ void vpr_setup_vpr(t_options* Options, t_router_opts* RouterOpts, t_analysis_opts* AnalysisOpts, t_noc_opts* NocOpts, + t_server_opts* ServerOpts, t_det_routing_arch* RoutingArch, std::vector** PackerRRGraph, std::vector& Segments, @@ -1322,7 +1323,6 @@ void vpr_setup_vpr(t_options* Options, int* GraphPause, bool* SaveGraphics, std::string* GraphicsCommands, - bool* server, t_power_opts* PowerOpts, t_vpr_setup* vpr_setup) { SetupVPR(Options, @@ -1339,6 +1339,7 @@ void vpr_setup_vpr(t_options* Options, RouterOpts, AnalysisOpts, NocOpts, + ServerOpts, RoutingArch, PackerRRGraph, Segments, @@ -1347,7 +1348,6 @@ void vpr_setup_vpr(t_options* Options, GraphPause, SaveGraphics, GraphicsCommands, - server, PowerOpts, vpr_setup); } diff --git a/vpr/src/base/vpr_types.h b/vpr/src/base/vpr_types.h index 41fb94910f2..2cd56966a39 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -1776,6 +1776,11 @@ struct t_TokenPair { struct t_lb_type_rr_node; /* Defined in pack_types.h */ +struct t_server_opts { + bool is_enabled = false; + int port_num = -1; +}; + ///@brief Store settings for VPR struct t_vpr_setup { bool TimingEnabled; ///* PackerRRGraph; std::vector Segments; ///is_flat = is_flat; if (server) { + g_vpr_ctx.server().setPortNum(port_num); guint timer_id = g_timeout_add(200, redraw_callback, &application); } diff --git a/vpr/src/draw/draw.h b/vpr/src/draw/draw.h index f31c60b5e7d..9c68fd80ac5 100644 --- a/vpr/src/draw/draw.h +++ b/vpr/src/draw/draw.h @@ -57,7 +57,8 @@ void init_graphics_state(bool show_graphics_val, bool save_graphics, std::string graphics_commands, bool is_flat, - bool server); + bool server, + int port_num); /* Allocates the structures needed to draw the placement and routing.*/ void alloc_draw_structs(const t_arch* arch); diff --git a/vpr/src/server/server.cpp b/vpr/src/server/server.cpp index 059511b93ab..330bec57a92 100644 --- a/vpr/src/server/server.cpp +++ b/vpr/src/server/server.cpp @@ -20,6 +20,12 @@ Server::~Server() stop(); } +void Server::setPortNum(int portNum) +{ + std::unique_lock lock(m_portNumMutex); + m_portNum = portNum; +} + void Server::start() { if (!m_isStarted.load()) { @@ -83,14 +89,14 @@ void Server::startListening() address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; - address.sin_port = htons(PORT_NUM); + address.sin_port = htons(m_portNum); // Bind the socket to the network address and port if (bind(server_socket, (struct sockaddr *)&address, sizeof(address)) < 0) { perror("bind failed"); exit(EXIT_FAILURE); } else { - std::cout << "~~~ th=" << std::this_thread::get_id() << " start listening port: " << PORT_NUM << std::endl; + std::cout << "~~~ th=" << std::this_thread::get_id() << " start listening port: " << m_portNum << std::endl; } // Put the server socket in a passive mode, where it waits for the client to approach the server to make a connection @@ -109,15 +115,15 @@ void Server::startListening() // Event loop while(!m_isStopped.load()) { - //std::cout << "~~~ th=" << std::this_thread::get_id() << " loop_000" << " is stopped=" << m_isStopped.load() << std::endl; - if (connectionProblemDetected || client_socket < 0) { int flags = fcntl(client_socket, F_GETFL, 0); fcntl(client_socket, F_SETFL, flags | O_NONBLOCK); client_socket = accept(server_socket, (struct sockaddr *)&address, (socklen_t*)&addrlen); + if (client_socket > 0) { + std::cout << "accept client" << std::endl; + } } - //std::cout << "~~~ th=" << std::this_thread::get_id() << " loop_111" << std::endl; if (client_socket >= 0) { connectionProblemDetected = false; // Handle sending @@ -188,19 +194,17 @@ void Server::startListening() } if (connectionProblemDetected && (client_socket >= 0)) { + std::cout << "close client connection" << std::endl; close(client_socket); m_telegramBuff.clear(); } } - - //std::this_thread::sleep_for(std::chrono::milliseconds(COMM_LOOP_INTERVAL_MS)); - //std::cout << "~~~ th=" << std::this_thread::get_id() << " loop_222" << std::endl << std::endl; } if (client_socket >= 0) { + std::cout << "close client socket" << std::endl; close(client_socket); } close(server_socket); - //std::cout << "~~~ th=" << std::this_thread::get_id() << " exit thread call!!!" << std::endl; } diff --git a/vpr/src/server/server.h b/vpr/src/server/server.h index 6a63e327adc..ed7caa44ce4 100644 --- a/vpr/src/server/server.h +++ b/vpr/src/server/server.h @@ -13,20 +13,14 @@ #include #include -#ifndef COMM_LOOP_INTERVAL_MS -#define COMM_LOOP_INTERVAL_MS 18 -#endif - -#ifndef PORT_NUM -#define PORT_NUM 61555 -#endif - class Server { public: explicit Server(); ~Server(); + void setPortNum(int portNum); + bool isStarted() const { return m_isStarted.load(); } bool isStopped() const { return m_isStopped.load(); } @@ -37,6 +31,9 @@ class Server void stop(); private: + int m_portNum = -1; + std::mutex m_portNumMutex; + std::string m_pathsStr; std::atomic m_isStarted; std::atomic m_isStopped; From 0d487b37b345e23e8f2571a8c2dbaeec504aa838 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Tue, 5 Dec 2023 13:31:57 +0200 Subject: [PATCH 13/31] replace #pragma once with #ifdef, rename keys.h to serverconsts.h --- vpr/src/server/gtkcomboboxhelper.h | 2 +- vpr/src/server/pathhelper.h | 4 ++-- vpr/src/server/{keys.h => serverconsts.h} | 5 ++++- vpr/src/server/task.h | 2 +- vpr/src/server/telegrambuffer.cpp | 2 +- vpr/src/server/telegrambuffer.h | 5 ++++- vpr/src/server/telegramoptions.h | 5 ++++- 7 files changed, 17 insertions(+), 8 deletions(-) rename vpr/src/server/{keys.h => serverconsts.h} (93%) diff --git a/vpr/src/server/gtkcomboboxhelper.h b/vpr/src/server/gtkcomboboxhelper.h index 0fc933f1d00..52ac031f7a7 100644 --- a/vpr/src/server/gtkcomboboxhelper.h +++ b/vpr/src/server/gtkcomboboxhelper.h @@ -7,4 +7,4 @@ gint get_item_count(gpointer combo_box); gint get_item_index_by_text(gpointer combo_box, gchar* target_item); -#endif // GTKCOMBOBOXHELPER_H +#endif diff --git a/vpr/src/server/pathhelper.h b/vpr/src/server/pathhelper.h index 47e9d432c29..3623b16ee40 100644 --- a/vpr/src/server/pathhelper.h +++ b/vpr/src/server/pathhelper.h @@ -1,5 +1,5 @@ -#ifndef PATH_HELPER_H -#define PATH_HELPER_H +#ifndef PATHHELPER_H +#define PATHHELPER_H #include #include diff --git a/vpr/src/server/keys.h b/vpr/src/server/serverconsts.h similarity index 93% rename from vpr/src/server/keys.h rename to vpr/src/server/serverconsts.h index 238dbb38370..72cbdd41a6a 100644 --- a/vpr/src/server/keys.h +++ b/vpr/src/server/serverconsts.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef SERVERCONSTS_H +#define SERVERCONSTS_H constexpr const char* KEY_JOB_ID = "JOB_ID"; constexpr const char* KEY_CMD = "CMD"; @@ -18,3 +19,5 @@ enum CMD { CMD_GET_PATH_LIST_ID=0, CMD_DRAW_PATH_ID }; + +#endif diff --git a/vpr/src/server/task.h b/vpr/src/server/task.h index 24040028a28..d90980f90fc 100644 --- a/vpr/src/server/task.h +++ b/vpr/src/server/task.h @@ -5,7 +5,7 @@ #include #include -#include "keys.h" +#include "serverconsts.h" class Task { public: diff --git a/vpr/src/server/telegrambuffer.cpp b/vpr/src/server/telegrambuffer.cpp index 5cf22012c8b..d101b6ee77d 100644 --- a/vpr/src/server/telegrambuffer.cpp +++ b/vpr/src/server/telegrambuffer.cpp @@ -1,5 +1,5 @@ #include "telegrambuffer.h" -#include "keys.h" +#include "serverconsts.h" void TelegramBuffer::append(const ByteArray& bytes) { diff --git a/vpr/src/server/telegrambuffer.h b/vpr/src/server/telegrambuffer.h index 5c33bdbd0da..2bcb48e59f9 100644 --- a/vpr/src/server/telegrambuffer.h +++ b/vpr/src/server/telegrambuffer.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef TELEGRAMBUFFER_H +#define TELEGRAMBUFFER_H #include #include @@ -55,3 +56,5 @@ class TelegramBuffer private: ByteArray m_rawBuffer; }; + +#endif diff --git a/vpr/src/server/telegramoptions.h b/vpr/src/server/telegramoptions.h index 3e2bfa5d99a..c3dae3ffd3c 100644 --- a/vpr/src/server/telegramoptions.h +++ b/vpr/src/server/telegramoptions.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef TELEGRAMOPTIONS_H +#define TELEGRAMOPTIONS_H #include #include @@ -113,3 +114,5 @@ class TelegramOptions { return supportedTypes.count(type) != 0; } }; + +#endif From a1cc91bb5455300878df904d6d48e870dbefa27f Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Tue, 5 Dec 2023 14:41:44 +0200 Subject: [PATCH 14/31] cleanup --- vpr/src/base/vpr_api.h | 1 - 1 file changed, 1 deletion(-) diff --git a/vpr/src/base/vpr_api.h b/vpr/src/base/vpr_api.h index 46ed92e5a68..27c02c3d3b5 100644 --- a/vpr/src/base/vpr_api.h +++ b/vpr/src/base/vpr_api.h @@ -187,7 +187,6 @@ void vpr_setup_vpr(t_options* Options, int* GraphPause, bool* SaveGraphics, std::string* GraphicsCommands, - bool* server, t_power_opts* PowerOpts, t_vpr_setup* vpr_setup); From 2e6d7800864e9e49fea9e655c18525c6b936bbad Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Tue, 5 Dec 2023 16:26:19 +0200 Subject: [PATCH 15/31] add ServerContext as a member in vpr_context --- vpr/src/base/vpr_api.cpp | 9 +++--- vpr/src/base/vpr_context.h | 54 +++++++++++++++++++++++---------- vpr/src/draw/draw.cpp | 8 ++--- vpr/src/draw/draw_basic.cpp | 13 ++++---- vpr/src/draw/ui_setup.cpp | 2 +- vpr/src/server/pathhelper.cpp | 16 +++++----- vpr/src/server/taskresolver.cpp | 15 ++++----- 7 files changed, 72 insertions(+), 45 deletions(-) diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index 6389c5047a3..c8d98e96d6c 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -370,7 +370,6 @@ void vpr_show_resource_usage(const t_vpr_setup& vpr_setup, const t_arch& Arch) { vtr::ScopedStartFinishTimer timer("Build Device Grid"); /* Read in netlist file for placement and routing */ - auto& cluster_ctx = g_vpr_ctx.clustering(); auto& device_ctx = g_vpr_ctx.mutable_device(); device_ctx.arch = &Arch; @@ -853,7 +852,7 @@ RouteStatus vpr_route_flow(const Netlist<>& net_list, net_delay = make_net_pins_matrix(net_list); //Initialize the delay calculator - std::shared_ptr& timing_info = g_vpr_ctx.hold_timing_info; // shortcut + std::shared_ptr timing_info; std::shared_ptr routing_delay_calc = nullptr; if (vpr_setup.Timing.timing_analysis_enabled) { auto& atom_ctx = g_vpr_ctx.atom(); @@ -861,6 +860,7 @@ RouteStatus vpr_route_flow(const Netlist<>& net_list, routing_delay_calc = std::make_shared(atom_ctx.nlist, atom_ctx.lookup, net_delay, is_flat); timing_info = make_setup_hold_timing_info(routing_delay_calc, router_opts.timing_update_type); + g_vpr_ctx.server_ctx().set_hold_timing_info(timing_info); } if (router_opts.doRouting == STAGE_DO) { @@ -1470,8 +1470,7 @@ void vpr_analysis(const Netlist<>& net_list, //Do final timing analysis auto analysis_delay_calc = std::make_shared(atom_ctx.nlist, atom_ctx.lookup, net_delay, vpr_setup.RouterOpts.flat_routing); - g_vpr_ctx.hold_timing_info = make_setup_hold_timing_info(analysis_delay_calc, vpr_setup.AnalysisOpts.timing_update_type); - auto& timing_info = g_vpr_ctx.hold_timing_info; // shortcut + auto timing_info = make_setup_hold_timing_info(analysis_delay_calc, vpr_setup.AnalysisOpts.timing_update_type); timing_info->update(); if (isEchoFileEnabled(E_ECHO_ANALYSIS_TIMING_GRAPH)) { @@ -1503,6 +1502,8 @@ void vpr_analysis(const Netlist<>& net_list, if (vpr_setup.PowerOpts.do_power) { vpr_power_estimation(vpr_setup, Arch, *timing_info, route_status); } + + g_vpr_ctx.server_ctx().set_hold_timing_info(std::move(timing_info)); // grab timing_info for further usage } } diff --git a/vpr/src/base/vpr_context.h b/vpr/src/base/vpr_context.h index 1bcf38dbd24..e69843e0aaa 100644 --- a/vpr/src/base/vpr_context.h +++ b/vpr/src/base/vpr_context.h @@ -36,7 +36,6 @@ #include "server.h" #include "taskresolver.h" #include "tatum/report/TimingPath.hpp" -#include class SetupHoldTimingInfo; @@ -564,6 +563,40 @@ struct NocContext : public Context { NocRouting* noc_flows_router; }; +class ServerContext : public Context { + public: + const Server& server() const { return server_; } + Server& server() { return server_; } + + const TaskResolver& task_resolver() const { return task_resolver_; } + TaskResolver& task_resolver() { return task_resolver_; } + + void set_crit_paths(const std::vector& crit_paths) { crit_paths_ = crit_paths; } + const std::vector& crit_paths() const { return crit_paths_; } + + void set_critical_path_num(int critical_path_num) { critical_path_num_ = critical_path_num; } + int critical_path_num() const { return critical_path_num_; } + + void set_path_type(const std::string& path_type) { path_type_ = path_type; } + const std::string& path_type() const { return path_type_; } + + void set_crit_path_index(int crit_path_index) { crit_path_index_ = crit_path_index; } + int crit_path_index() const { return crit_path_index_; } + + void set_hold_timing_info(const std::shared_ptr& hold_timing_info) { hold_timing_info_ = hold_timing_info; } + const std::shared_ptr& hold_timing_info() const { return hold_timing_info_; } + + private: + Server server_; + TaskResolver task_resolver_; + + std::vector crit_paths_; + int critical_path_num_ = 1; + std::string path_type_ = "setup"; + int crit_path_index_ = -1; + std::shared_ptr hold_timing_info_; +}; + /** * @brief This object encapsulates VPR's state. * @@ -647,17 +680,8 @@ class VprContext : public Context { const PackingMultithreadingContext& packing_multithreading() const { return packing_multithreading_; } PackingMultithreadingContext& mutable_packing_multithreading() { return packing_multithreading_; } - const Server& server() const { return server_; } - Server& server() { return server_; } - - const TaskResolver& task_resolver() const { return task_resolver_; } - TaskResolver& task_resolver() { return task_resolver_; } - - std::vector crit_paths; - int critical_path_num = 1; - std::string path_type = "setup"; - int crit_path_index = 0; - std::shared_ptr hold_timing_info; + const ServerContext& server_ctx() const { return server_ctx_; } + ServerContext& server_ctx() { return server_ctx_; } private: DeviceContext device_; @@ -675,11 +699,9 @@ class VprContext : public Context { FloorplanningContext constraints_; NocContext noc_; - PackingMultithreadingContext packing_multithreading_; - - Server server_; - TaskResolver task_resolver_; + ServerContext server_ctx_; + PackingMultithreadingContext packing_multithreading_; }; #endif diff --git a/vpr/src/draw/draw.cpp b/vpr/src/draw/draw.cpp index 733256e922e..efbea43ea82 100644 --- a/vpr/src/draw/draw.cpp +++ b/vpr/src/draw/draw.cpp @@ -179,8 +179,8 @@ std::string rr_highlight_message; gboolean redraw_callback(gpointer data) { // shortcuts ezgl::application* app = static_cast(data); - Server& server = g_vpr_ctx.server(); - TaskResolver& task_resolver = g_vpr_ctx.task_resolver(); + Server& server = g_vpr_ctx.server_ctx().server(); + TaskResolver& task_resolver = g_vpr_ctx.server_ctx().task_resolver(); // bool isRunning = !server.isStopped(); @@ -237,8 +237,8 @@ void init_graphics_state(bool show_graphics_val, draw_state->is_flat = is_flat; if (server) { - g_vpr_ctx.server().setPortNum(port_num); - guint timer_id = g_timeout_add(200, redraw_callback, &application); + g_vpr_ctx.server_ctx().server().setPortNum(port_num); + g_timeout_add(200, redraw_callback, &application); } #else diff --git a/vpr/src/draw/draw_basic.cpp b/vpr/src/draw/draw_basic.cpp index 16fcdf05b3c..2868ec7ac43 100644 --- a/vpr/src/draw/draw_basic.cpp +++ b/vpr/src/draw/draw_basic.cpp @@ -952,16 +952,17 @@ void draw_crit_path(ezgl::renderer* g) { return; //No timing to draw } - calcCritPath(g_vpr_ctx.critical_path_num, g_vpr_ctx.path_type); + ServerContext& server_ctx = g_vpr_ctx.server_ctx(); // shortcut + calcCritPath(server_ctx.critical_path_num(), server_ctx.path_type()); - const auto& paths = g_vpr_ctx.crit_paths; // shortcut + const auto& paths = server_ctx.crit_paths(); // shortcut - // check index inside the range - if (g_vpr_ctx.crit_path_index >= paths.size()) { - g_vpr_ctx.crit_path_index = -1; + // check path index + if (server_ctx.crit_path_index() >= static_cast(paths.size())) { + server_ctx.set_crit_path_index(-1); return; } - tatum::TimingPath path = paths[g_vpr_ctx.crit_path_index]; + tatum::TimingPath path = paths[server_ctx.crit_path_index()]; //Walk through the timing path drawing each edge tatum::NodeId prev_node; diff --git a/vpr/src/draw/ui_setup.cpp b/vpr/src/draw/ui_setup.cpp index 15e18cae793..bdf50a6b10c 100644 --- a/vpr/src/draw/ui_setup.cpp +++ b/vpr/src/draw/ui_setup.cpp @@ -24,7 +24,7 @@ # include "ezgl/graphics.hpp" void on_window_destroy() { - g_vpr_ctx.server().stop(); + g_vpr_ctx.server_ctx().server().stop(); } void basic_button_setup(ezgl::application* app) { diff --git a/vpr/src/server/pathhelper.cpp b/vpr/src/server/pathhelper.cpp index 6a3431d18e0..c3fd0157bf6 100644 --- a/vpr/src/server/pathhelper.cpp +++ b/vpr/src/server/pathhelper.cpp @@ -46,8 +46,10 @@ std::string getPathsStr(const std::vector& paths, const std:: void calcCritPath(int numMax, const std::string& type) { - g_vpr_ctx.critical_path_num = numMax; - g_vpr_ctx.path_type = type; + ServerContext& server_ctx = g_vpr_ctx.server_ctx(); // shortcut + + server_ctx.set_critical_path_num(numMax); + server_ctx.set_path_type(type); tatum::TimingPathCollector path_collector; @@ -56,9 +58,9 @@ void calcCritPath(int numMax, const std::string& type) //Get the worst timing path if (type == "setup") { - g_vpr_ctx.crit_paths = path_collector.collect_worst_setup_timing_paths( + server_ctx.set_crit_paths(path_collector.collect_worst_setup_timing_paths( *timing_ctx.graph, - *(draw_state->setup_timing_info->setup_analyzer()), numMax); + *(draw_state->setup_timing_info->setup_analyzer()), numMax)); } else if (type == "hold") { /* ########################### // TODO: recalculate g_vpr_ctx.hold_timing_info, because it may cause the assert path check for index 0 @@ -76,10 +78,10 @@ void calcCritPath(int numMax, const std::string& type) // timing_info->update(); /* */ - if (g_vpr_ctx.hold_timing_info) { - g_vpr_ctx.crit_paths = path_collector.collect_worst_hold_timing_paths( + if (server_ctx.hold_timing_info()) { + server_ctx.set_crit_paths(path_collector.collect_worst_hold_timing_paths( *timing_ctx.graph, - *(g_vpr_ctx.hold_timing_info->hold_analyzer()), numMax); + *(server_ctx.hold_timing_info()->hold_analyzer()), numMax)); } else { std::cerr << "g_vpr_ctx.hold_timing_info is nullptr" << std::endl; } diff --git a/vpr/src/server/taskresolver.cpp b/vpr/src/server/taskresolver.cpp index c679cb0f7d4..75f5f9d33a5 100644 --- a/vpr/src/server/taskresolver.cpp +++ b/vpr/src/server/taskresolver.cpp @@ -60,6 +60,7 @@ std::vector splitString(const std::string& input, char delimiter) void TaskResolver::update(ezgl::application* app) { + ServerContext& server_ctx = g_vpr_ctx.server_ctx(); for (auto& task: m_tasks) { if (!task.isFinished()) { TelegramOptions options{task.options()}; @@ -70,12 +71,12 @@ void TaskResolver::update(ezgl::application* app) std::string detailsLevel = options.getString(OPTION_DETAILS_LEVEL); bool isFlat = options.getBool(OPTION_IS_FLOAT_ROUTING, false); if (!options.hasErrors()) { - if (typePath != g_vpr_ctx.path_type) { - g_vpr_ctx.crit_path_index = -1; + if (typePath != server_ctx.path_type()) { + server_ctx.set_crit_path_index(-1); } calcCritPath(nCriticalPathNum, typePath); - std::string msg = getPathsStr(g_vpr_ctx.crit_paths, detailsLevel, isFlat); - g_vpr_ctx.path_type = typePath; // duplicated + std::string msg = getPathsStr(server_ctx.crit_paths(), detailsLevel, isFlat); + server_ctx.set_path_type(typePath); // duplicated task.success(msg); } else { std::string msg = "options errors in get crit path list telegram: " + options.errorsStr(); @@ -87,8 +88,8 @@ void TaskResolver::update(ezgl::application* app) int pathIndex = options.getInt(OPTION_PATH_INDEX, -1); std::string highLightMode = options.getString(OPTION_HIGHTLIGHT_MODE); if (!options.hasErrors()) { - if ((pathIndex >= 0) && (pathIndex < g_vpr_ctx.crit_paths.size())) { - g_vpr_ctx.crit_path_index = pathIndex; + if ((pathIndex >= 0) && (pathIndex < static_cast(server_ctx.crit_paths().size()))) { + server_ctx.set_crit_path_index(pathIndex); // update gtk UI GtkComboBox* toggle_crit_path = GTK_COMBO_BOX(app->get_object("ToggleCritPath")); @@ -102,7 +103,7 @@ void TaskResolver::update(ezgl::application* app) task.fail(msg); } } else { - std::string msg = "selectedIndex=" + std::to_string(pathIndex) + " is out of range [0-" + std::to_string(g_vpr_ctx.crit_paths.size()-1) + "]"; + std::string msg = "selectedIndex=" + std::to_string(pathIndex) + " is out of range [0-" + std::to_string(static_cast(server_ctx.crit_paths().size())-1) + "]"; std::cerr << msg << std::endl; task.fail(msg); } From 630a1df726c6fb2fe672b8702e335cb0bbab7dd7 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Tue, 5 Dec 2023 17:54:30 +0200 Subject: [PATCH 16/31] fix compilation warnings, remove commented obsolete code --- vpr/src/base/vpr_api.h | 1 + vpr/src/draw/draw.cpp | 9 +- vpr/src/draw/draw.h | 3 + vpr/src/draw/draw_basic.cpp | 1 - vpr/src/draw/ui_setup.h | 2 + vpr/src/server/pathhelper.cpp | 161 +------------------------------- vpr/src/server/taskresolver.cpp | 19 +--- vpr/src/server/taskresolver.h | 2 +- 8 files changed, 18 insertions(+), 180 deletions(-) diff --git a/vpr/src/base/vpr_api.h b/vpr/src/base/vpr_api.h index 27c02c3d3b5..fda2573374c 100644 --- a/vpr/src/base/vpr_api.h +++ b/vpr/src/base/vpr_api.h @@ -179,6 +179,7 @@ void vpr_setup_vpr(t_options* Options, t_router_opts* RouterOpts, t_analysis_opts* AnalysisOpts, t_noc_opts* NocOpts, + t_server_opts* ServerOpts, t_det_routing_arch* RoutingArch, std::vector** PackerRRGraph, std::vector& Segments, diff --git a/vpr/src/draw/draw.cpp b/vpr/src/draw/draw.cpp index efbea43ea82..3bb135eedfb 100644 --- a/vpr/src/draw/draw.cpp +++ b/vpr/src/draw/draw.cpp @@ -175,7 +175,6 @@ ezgl::point2d point_1(0, 0); ezgl::rectangle initial_world; std::string rr_highlight_message; -// Define a callback function that will be called by the timer gboolean redraw_callback(gpointer data) { // shortcuts ezgl::application* app = static_cast(data); @@ -194,15 +193,17 @@ gboolean redraw_callback(gpointer data) { server.takeRecievedTasks(tasksBuff); task_resolver.addTasks(tasksBuff); - task_resolver.update(app); + bool process_task = task_resolver.update(app); tasksBuff.clear(); task_resolver.takeFinished(tasksBuff); server.addSendTasks(tasksBuff); - // Call the redraw method of the application - app->refresh_drawing(); + // Call the redraw method of the application if any of task was processed + if (process_task) { + app->refresh_drawing(); + } } // Return TRUE to keep the timer running, or FALSE to stop it diff --git a/vpr/src/draw/draw.h b/vpr/src/draw/draw.h index 9c68fd80ac5..ce43488e3f4 100644 --- a/vpr/src/draw/draw.h +++ b/vpr/src/draw/draw.h @@ -42,6 +42,9 @@ extern ezgl::application application; #endif /* NO_GRAPHICS */ +// Define a callback function that will be called by the timer +gboolean redraw_callback(gpointer data); + void update_screen(ScreenUpdatePriority priority, const char* msg, enum pic_type pic_on_screen_val, std::shared_ptr timing_info); //Initializes the drawing locations. diff --git a/vpr/src/draw/draw_basic.cpp b/vpr/src/draw/draw_basic.cpp index 2868ec7ac43..0a96a703ac4 100644 --- a/vpr/src/draw/draw_basic.cpp +++ b/vpr/src/draw/draw_basic.cpp @@ -942,7 +942,6 @@ void draw_routing_util(ezgl::renderer* g) { */ void draw_crit_path(ezgl::renderer* g) { t_draw_state* draw_state = get_draw_state_vars(); - auto& timing_ctx = g_vpr_ctx.timing(); if (draw_state->show_crit_path == DRAW_NO_CRIT_PATH) { return; diff --git a/vpr/src/draw/ui_setup.h b/vpr/src/draw/ui_setup.h index 567b3ec8659..1a111c73f9a 100644 --- a/vpr/src/draw/ui_setup.h +++ b/vpr/src/draw/ui_setup.h @@ -20,6 +20,8 @@ # include "ezgl/application.hpp" # include "ezgl/graphics.hpp" +void on_window_destroy(); + /** * @brief configures basic buttons * diff --git a/vpr/src/server/pathhelper.cpp b/vpr/src/server/pathhelper.cpp index c3fd0157bf6..e71fc3c9f82 100644 --- a/vpr/src/server/pathhelper.cpp +++ b/vpr/src/server/pathhelper.cpp @@ -56,28 +56,11 @@ void calcCritPath(int numMax, const std::string& type) t_draw_state* draw_state = get_draw_state_vars(); auto& timing_ctx = g_vpr_ctx.timing(); - //Get the worst timing path if (type == "setup") { server_ctx.set_crit_paths(path_collector.collect_worst_setup_timing_paths( *timing_ctx.graph, *(draw_state->setup_timing_info->setup_analyzer()), numMax)); } else if (type == "hold") { - /* ########################### - // TODO: recalculate g_vpr_ctx.hold_timing_info, because it may cause the assert path check for index 0 - // ########################### - - // std::vector TimingPathCollector::collect_worst_hold_timing_paths(const TimingGraph& timing_graph, const tatum::HoldTimingAnalyzer& hold_analyzer, size_t npaths) const { - // auto& atom_ctx = g_vpr_ctx.atom(); - - // NetPinsMatrix net_delay = make_net_pins_matrix(net_list); - // load_net_delay_from_routing(net_list, - // net_delay); - - // auto analysis_delay_calc = std::make_shared(atom_ctx.nlist, atom_ctx.lookup, net_delay, vpr_setup.RouterOpts.flat_routing); - // auto timing_info = make_setup_hold_timing_info(analysis_delay_calc, vpr_setup.AnalysisOpts.timing_update_type); - // timing_info->update(); - /* */ - if (server_ctx.hold_timing_info()) { server_ctx.set_crit_paths(path_collector.collect_worst_hold_timing_paths( *timing_ctx.graph, @@ -86,146 +69,4 @@ void calcCritPath(int numMax, const std::string& type) std::cerr << "g_vpr_ctx.hold_timing_info is nullptr" << std::endl; } } -} - - - -// void draw_crit_path(ezgl::renderer* g) { -// std::cout << "~~~ draw_crit_path start" << std::endl; -// tatum::TimingPathCollector path_collector; - -// t_draw_state* draw_state = get_draw_state_vars(); -// auto& timing_ctx = g_vpr_ctx.timing(); - -// if (draw_state->show_crit_path == DRAW_NO_CRIT_PATH) { -// return; -// } - -// if (!draw_state->setup_timing_info) { -// return; //No timing to draw -// } - -// //Get the worst timing path -// auto paths = path_collector.collect_worst_setup_timing_paths( -// *timing_ctx.graph, -// *(draw_state->setup_timing_info->setup_analyzer()), 100); - -// std::string pathIdsStr = ""; //read_shared_string(); -// std::vector selectedPathIds; // = splitString(pathIdsStr, ';'); -// auto& server = g_vpr_ctx.server(); -// std::stringstream ss; -// dumpPathsToStr(ss, paths, false); -// //server.init(); -// //server.setPathsStr(ss.str()); -// //server.tick(); - -// //vpr_setup.AnalysisOpts, vpr_setup.RouterOpts.flat_routing - -// // name resolving -// //bool is_flat = vpr_setup.RouterOpts.flat_routing; -// // bool is_flat = true; -// // const Netlist<>& net_list = is_flat ? (const Netlist<>&)g_vpr_ctx.atom().nlist : (const Netlist<>&)g_vpr_ctx.clustering().clb_nlist; - -// auto& atom_ctx = g_vpr_ctx.atom(); -// NetPinsMatrix net_delay = make_net_pins_matrix(atom_ctx.nlist); -// // load_net_delay_from_routing(net_list, -// // net_delay); - -// auto analysis_delay_calc = std::make_shared(atom_ctx.nlist, atom_ctx.lookup, net_delay, true/*vpr_setup.RouterOpts.flat_routing*/); -// VprTimingGraphResolver name_resolver(atom_ctx.nlist, atom_ctx.lookup, *timing_ctx.graph, *analysis_delay_calc.get(), true/*is_flat*/); -// // - -// //std::unordered_set _set; -// int index = 0; -// for (const tatum::TimingPath& p: paths) { -// tatum::TimingPathInfo pi = p.path_info(); -// //std::cout << "~~~~~~~~~~" << std::endl; -// //std::cout << "pi.type=" << (int)pi.type() << std::endl; -// //std::cout << "pi.delay=" << pi.delay() << std::endl; -// //std::cout << "pi.slack=" << pi.slack() << std::endl; - -// //std::cout << "pi.startpoint=" << pi.startpoint() << std::endl; -// //std::cout << "pi.endpoint=" << pi.endpoint() << std::endl; -// //std::cout << "pi.launch_domain=" << pi.launch_domain() << std::endl; -// //std::cout << "pi.capture_domain=" << pi.capture_domain() << std::endl; - -// // size_t t1 = static_cast(pi.startpoint()); -// // size_t t2 = static_cast(pi.endpoint()); - -// // std::string st1 = std::to_string(t1); -// // std::string st2 = std::to_string(t2); - -// std::string st1 = name_resolver.node_name(pi.startpoint()); -// std::string st2 = name_resolver.node_name(pi.endpoint()); - -// std::string pathId = st1 + ":" + st2; -// for (const std::string& selectedPathId: selectedPathIds) { -// if (selectedPathId == pathId) { -// draw_state->crit_path_index = index; -// } -// } -// //_set.insert(pathId); -// index++; -// } -// // for (const std::string& key: _set) { -// // std::cout << "~~~ key = " << key << std::endl; -// // } -// //std::cout << "_set.size()=" << _set.size() << std::endl; - -// // if (!extStr.empty()) { -// // int indexCandidate = std::atoi(extStr.c_str()); -// // if (indexCandidate <= paths.size()) { -// // draw_state->crit_path_index = indexCandidate; -// // } -// // } - -// tatum::TimingPath path = paths[draw_state->crit_path_index]; -// tatum::TimingPathInfo pi = path.path_info(); -// // size_t t1 = static_cast(pi.startpoint()); -// // size_t t2 = static_cast(pi.endpoint()); -// // std::string st1 = std::to_string(t1); -// // std::string st2 = std::to_string(t2); -// std::string st1 = name_resolver.node_name(pi.startpoint()); -// std::string st2 = name_resolver.node_name(pi.endpoint()); - -// std::string pathId = st1 + ":" + st2; - -// std::cout << "~~~~~~~~~~~~~~~~ num of pathes=" << paths.size() << " index=" << draw_state->crit_path_index << " is drawn, id=" << pathId << std::endl; - -// //Walk through the timing path drawing each edge -// tatum::NodeId prev_node; -// float prev_arr_time = std::numeric_limits::quiet_NaN(); -// int i = 0; -// for (tatum::TimingPathElem elem : path.data_arrival_path().elements()) { -// tatum::NodeId node = elem.node(); -// float arr_time = elem.tag().time(); -// if (prev_node) { -// //We draw each 'edge' in a different color, this allows users to identify the stages and -// //any routing which corresponds to the edge -// // -// //We pick colors from the kelly max-contrast list, for long paths there may be repeats -// ezgl::color color = kelly_max_contrast_colors[i++ -// % kelly_max_contrast_colors.size()]; - -// float delay = arr_time - prev_arr_time; -// if (draw_state->show_crit_path == DRAW_CRIT_PATH_FLYLINES -// || draw_state->show_crit_path -// == DRAW_CRIT_PATH_FLYLINES_DELAYS) { -// g->set_color(color); -// g->set_line_dash(ezgl::line_dash::none); -// g->set_line_width(4); -// draw_flyline_timing_edge(tnode_draw_coord(prev_node), -// tnode_draw_coord(node), delay, g); -// } else { -// VTR_ASSERT(draw_state->show_crit_path != DRAW_NO_CRIT_PATH); - -// //Draw the routed version of the timing edge -// draw_routed_timing_edge(prev_node, node, delay, color, g); -// } -// } -// prev_node = node; -// prev_arr_time = arr_time; -// } - -// std::cout << "~~~ draw_crit_path end" << std::endl; -// } +} \ No newline at end of file diff --git a/vpr/src/server/taskresolver.cpp b/vpr/src/server/taskresolver.cpp index 75f5f9d33a5..110403d645a 100644 --- a/vpr/src/server/taskresolver.cpp +++ b/vpr/src/server/taskresolver.cpp @@ -45,24 +45,13 @@ void TaskResolver::takeFinished(std::vector& result) } } -std::vector splitString(const std::string& input, char delimiter) -{ - std::vector tokens; - std::istringstream tokenStream(input); - std::string token; - - while (std::getline(tokenStream, token, delimiter)) { - tokens.push_back(token); - } - - return tokens; -} - -void TaskResolver::update(ezgl::application* app) +bool TaskResolver::update(ezgl::application* app) { + bool process_task = false; ServerContext& server_ctx = g_vpr_ctx.server_ctx(); for (auto& task: m_tasks) { if (!task.isFinished()) { + process_task = true; TelegramOptions options{task.options()}; if (task.cmd() == CMD_GET_PATH_LIST_ID) { options.validateNamePresence({OPTION_PATH_NUM, OPTION_PATH_TYPE, OPTION_DETAILS_LEVEL, OPTION_IS_FLOAT_ROUTING}); @@ -115,5 +104,7 @@ void TaskResolver::update(ezgl::application* app) } } } + + return process_task; } diff --git a/vpr/src/server/taskresolver.h b/vpr/src/server/taskresolver.h index d161f161d00..236fe97d05d 100644 --- a/vpr/src/server/taskresolver.h +++ b/vpr/src/server/taskresolver.h @@ -19,7 +19,7 @@ class TaskResolver { void addTasks(const std::vector&); void addTask(Task); - void update(ezgl::application*); + bool update(ezgl::application*); void takeFinished(std::vector&); const std::vector& tasks() const { return m_tasks; } From 8c1e4dc0ba2b398d7d39381a773539e3be4f9fe5 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Tue, 5 Dec 2023 21:49:56 +0200 Subject: [PATCH 17/31] improve crit_index set change --- vpr/src/base/vpr_api.cpp | 4 +++- vpr/src/base/vpr_context.h | 2 +- vpr/src/draw/draw_basic.cpp | 3 +++ vpr/src/server/pathhelper.cpp | 10 ++++++---- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index c8d98e96d6c..e5a368517fb 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -852,7 +852,8 @@ RouteStatus vpr_route_flow(const Netlist<>& net_list, net_delay = make_net_pins_matrix(net_list); //Initialize the delay calculator - std::shared_ptr timing_info; + std::shared_ptr timing_info = nullptr; + std::shared_ptr routing_delay_calc = nullptr; if (vpr_setup.Timing.timing_analysis_enabled) { auto& atom_ctx = g_vpr_ctx.atom(); @@ -1471,6 +1472,7 @@ void vpr_analysis(const Netlist<>& net_list, //Do final timing analysis auto analysis_delay_calc = std::make_shared(atom_ctx.nlist, atom_ctx.lookup, net_delay, vpr_setup.RouterOpts.flat_routing); auto timing_info = make_setup_hold_timing_info(analysis_delay_calc, vpr_setup.AnalysisOpts.timing_update_type); + timing_info->update(); if (isEchoFileEnabled(E_ECHO_ANALYSIS_TIMING_GRAPH)) { diff --git a/vpr/src/base/vpr_context.h b/vpr/src/base/vpr_context.h index e69843e0aaa..137a1527246 100644 --- a/vpr/src/base/vpr_context.h +++ b/vpr/src/base/vpr_context.h @@ -593,7 +593,7 @@ class ServerContext : public Context { std::vector crit_paths_; int critical_path_num_ = 1; std::string path_type_ = "setup"; - int crit_path_index_ = -1; + int crit_path_index_ = 0; std::shared_ptr hold_timing_info_; }; diff --git a/vpr/src/draw/draw_basic.cpp b/vpr/src/draw/draw_basic.cpp index 0a96a703ac4..831093201cd 100644 --- a/vpr/src/draw/draw_basic.cpp +++ b/vpr/src/draw/draw_basic.cpp @@ -959,8 +959,11 @@ void draw_crit_path(ezgl::renderer* g) { // check path index if (server_ctx.crit_path_index() >= static_cast(paths.size())) { server_ctx.set_crit_path_index(-1); + } + if (server_ctx.crit_path_index() == -1) { return; } + tatum::TimingPath path = paths[server_ctx.crit_path_index()]; //Walk through the timing path drawing each edge diff --git a/vpr/src/server/pathhelper.cpp b/vpr/src/server/pathhelper.cpp index e71fc3c9f82..3deacb06f9e 100644 --- a/vpr/src/server/pathhelper.cpp +++ b/vpr/src/server/pathhelper.cpp @@ -57,14 +57,16 @@ void calcCritPath(int numMax, const std::string& type) auto& timing_ctx = g_vpr_ctx.timing(); if (type == "setup") { - server_ctx.set_crit_paths(path_collector.collect_worst_setup_timing_paths( + auto paths = path_collector.collect_worst_setup_timing_paths( *timing_ctx.graph, - *(draw_state->setup_timing_info->setup_analyzer()), numMax)); + *(draw_state->setup_timing_info->setup_analyzer()), numMax); + server_ctx.set_crit_paths(paths); } else if (type == "hold") { if (server_ctx.hold_timing_info()) { - server_ctx.set_crit_paths(path_collector.collect_worst_hold_timing_paths( + auto paths = path_collector.collect_worst_hold_timing_paths( *timing_ctx.graph, - *(server_ctx.hold_timing_info()->hold_analyzer()), numMax)); + *(server_ctx.hold_timing_info()->hold_analyzer()), numMax); + server_ctx.set_crit_paths(paths); } else { std::cerr << "g_vpr_ctx.hold_timing_info is nullptr" << std::endl; } From ae0dc2d2706c3d2385c7599862664cbb5f3829a0 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Tue, 5 Dec 2023 22:58:05 +0200 Subject: [PATCH 18/31] refactor top level logic of crit path calculation --- vpr/src/base/vpr_context.h | 4 ++++ vpr/src/draw/draw.cpp | 1 + vpr/src/draw/draw_basic.cpp | 31 ++++++++++++++++++--------- vpr/src/draw/draw_basic.h | 2 ++ vpr/src/server/pathhelper.cpp | 34 ++++++++++++----------------- vpr/src/server/pathhelper.h | 7 +++++- vpr/src/server/taskresolver.cpp | 38 ++++++++++++++++++++++++++------- 7 files changed, 78 insertions(+), 39 deletions(-) diff --git a/vpr/src/base/vpr_context.h b/vpr/src/base/vpr_context.h index 137a1527246..ca644ae7edd 100644 --- a/vpr/src/base/vpr_context.h +++ b/vpr/src/base/vpr_context.h @@ -565,6 +565,9 @@ struct NocContext : public Context { class ServerContext : public Context { public: + bool is_enabled() const { return is_enabled_; } + void set_enabled(bool is_enabled) { is_enabled_ = is_enabled; } + const Server& server() const { return server_; } Server& server() { return server_; } @@ -587,6 +590,7 @@ class ServerContext : public Context { const std::shared_ptr& hold_timing_info() const { return hold_timing_info_; } private: + bool is_enabled_ = false; Server server_; TaskResolver task_resolver_; diff --git a/vpr/src/draw/draw.cpp b/vpr/src/draw/draw.cpp index 3bb135eedfb..83d4e814842 100644 --- a/vpr/src/draw/draw.cpp +++ b/vpr/src/draw/draw.cpp @@ -238,6 +238,7 @@ void init_graphics_state(bool show_graphics_val, draw_state->is_flat = is_flat; if (server) { + g_vpr_ctx.server_ctx().set_enabled(true); g_vpr_ctx.server_ctx().server().setPortNum(port_num); g_timeout_add(200, redraw_callback, &application); } diff --git a/vpr/src/draw/draw_basic.cpp b/vpr/src/draw/draw_basic.cpp index 831093201cd..f5e60b14ae2 100644 --- a/vpr/src/draw/draw_basic.cpp +++ b/vpr/src/draw/draw_basic.cpp @@ -951,20 +951,31 @@ void draw_crit_path(ezgl::renderer* g) { return; //No timing to draw } - ServerContext& server_ctx = g_vpr_ctx.server_ctx(); // shortcut - calcCritPath(server_ctx.critical_path_num(), server_ctx.path_type()); + if (g_vpr_ctx.server_ctx().is_enabled()) { + ServerContext& server_ctx = g_vpr_ctx.server_ctx(); // shortcut - const auto& paths = server_ctx.crit_paths(); // shortcut + auto paths = server_ctx.crit_paths(); - // check path index - if (server_ctx.crit_path_index() >= static_cast(paths.size())) { - server_ctx.set_crit_path_index(-1); - } - if (server_ctx.crit_path_index() == -1) { - return; + // check path index + if (server_ctx.crit_path_index() >= static_cast(paths.size())) { + server_ctx.set_crit_path_index(-1); + } + if (server_ctx.crit_path_index() != -1) { + tatum::TimingPath path = paths[server_ctx.crit_path_index()]; + draw_crit_path(path, g); + } + } else { + const int crit_path_index = 0; + auto paths = calcSetupCritPaths(1); + if (paths.size()>0) { + auto path = paths[crit_path_index]; + draw_crit_path(path, g); + } } +} - tatum::TimingPath path = paths[server_ctx.crit_path_index()]; +void draw_crit_path(const tatum::TimingPath& path, ezgl::renderer* g) { + t_draw_state* draw_state = get_draw_state_vars(); //Walk through the timing path drawing each edge tatum::NodeId prev_node; diff --git a/vpr/src/draw/draw_basic.h b/vpr/src/draw/draw_basic.h index 4b2fad749c2..0c391a49bbe 100644 --- a/vpr/src/draw/draw_basic.h +++ b/vpr/src/draw/draw_basic.h @@ -86,6 +86,8 @@ void draw_routing_util(ezgl::renderer* g); */ void draw_crit_path(ezgl::renderer* g); +void draw_crit_path(const tatum::TimingPath& path, ezgl::renderer* g); + /* Draws critical path shown as flylines. Takes in start and end coordinates, time delay, & renderer.*/ void draw_flyline_timing_edge(ezgl::point2d start, ezgl::point2d end, float incr_delay, ezgl::renderer* g); diff --git a/vpr/src/server/pathhelper.cpp b/vpr/src/server/pathhelper.cpp index 3deacb06f9e..92f5251a577 100644 --- a/vpr/src/server/pathhelper.cpp +++ b/vpr/src/server/pathhelper.cpp @@ -44,31 +44,25 @@ std::string getPathsStr(const std::vector& paths, const std:: return ss.str(); } -void calcCritPath(int numMax, const std::string& type) +std::vector calcSetupCritPaths(int numMax) { - ServerContext& server_ctx = g_vpr_ctx.server_ctx(); // shortcut + tatum::TimingPathCollector path_collector; - server_ctx.set_critical_path_num(numMax); - server_ctx.set_path_type(type); + t_draw_state* draw_state = get_draw_state_vars(); + auto& timing_ctx = g_vpr_ctx.timing(); + return path_collector.collect_worst_setup_timing_paths( + *timing_ctx.graph, + *(draw_state->setup_timing_info->setup_analyzer()), numMax); +} + +std::vector calcHoldCritPaths(int numMax, const std::shared_ptr& hold_timing_info) +{ tatum::TimingPathCollector path_collector; - t_draw_state* draw_state = get_draw_state_vars(); auto& timing_ctx = g_vpr_ctx.timing(); - if (type == "setup") { - auto paths = path_collector.collect_worst_setup_timing_paths( - *timing_ctx.graph, - *(draw_state->setup_timing_info->setup_analyzer()), numMax); - server_ctx.set_crit_paths(paths); - } else if (type == "hold") { - if (server_ctx.hold_timing_info()) { - auto paths = path_collector.collect_worst_hold_timing_paths( - *timing_ctx.graph, - *(server_ctx.hold_timing_info()->hold_analyzer()), numMax); - server_ctx.set_crit_paths(paths); - } else { - std::cerr << "g_vpr_ctx.hold_timing_info is nullptr" << std::endl; - } - } + return path_collector.collect_worst_hold_timing_paths( + *timing_ctx.graph, + *(hold_timing_info->hold_analyzer()), numMax); } \ No newline at end of file diff --git a/vpr/src/server/pathhelper.h b/vpr/src/server/pathhelper.h index 3623b16ee40..b7e43ab2cff 100644 --- a/vpr/src/server/pathhelper.h +++ b/vpr/src/server/pathhelper.h @@ -3,10 +3,15 @@ #include #include +#include #include "tatum/report/TimingPath.hpp" +class SetupHoldTimingInfo; + std::string getPathsStr(const std::vector& paths, const std::string& detailesLevel, bool is_flat_routing); -void calcCritPath(int numMax, const std::string& type); + +std::vector calcSetupCritPaths(int numMax); +std::vector calcHoldCritPaths(int numMax, const std::shared_ptr& hold_timing_info); #endif diff --git a/vpr/src/server/taskresolver.cpp b/vpr/src/server/taskresolver.cpp index 110403d645a..fa1f6dcf74b 100644 --- a/vpr/src/server/taskresolver.cpp +++ b/vpr/src/server/taskresolver.cpp @@ -56,19 +56,41 @@ bool TaskResolver::update(ezgl::application* app) if (task.cmd() == CMD_GET_PATH_LIST_ID) { options.validateNamePresence({OPTION_PATH_NUM, OPTION_PATH_TYPE, OPTION_DETAILS_LEVEL, OPTION_IS_FLOAT_ROUTING}); int nCriticalPathNum = options.getInt(OPTION_PATH_NUM, 1); - std::string typePath = options.getString(OPTION_PATH_TYPE); + std::string pathType = options.getString(OPTION_PATH_TYPE); std::string detailsLevel = options.getString(OPTION_DETAILS_LEVEL); bool isFlat = options.getBool(OPTION_IS_FLOAT_ROUTING, false); if (!options.hasErrors()) { - if (typePath != server_ctx.path_type()) { + if (pathType != server_ctx.path_type()) { server_ctx.set_crit_path_index(-1); } - calcCritPath(nCriticalPathNum, typePath); - std::string msg = getPathsStr(server_ctx.crit_paths(), detailsLevel, isFlat); - server_ctx.set_path_type(typePath); // duplicated - task.success(msg); + + server_ctx.set_path_type(pathType); + server_ctx.set_critical_path_num(nCriticalPathNum); + if (pathType == "setup") { + auto paths = calcSetupCritPaths(nCriticalPathNum); + server_ctx.set_crit_paths(paths); + } else if (pathType == "hold") { + auto hold_timing_info = server_ctx.hold_timing_info(); + if (hold_timing_info) { + auto paths = calcHoldCritPaths(nCriticalPathNum, hold_timing_info); + server_ctx.set_crit_paths(paths); + } else { + std::string msg{"cannot calculate hold critical path due to hold_timing_info nullptr"}; + std::cerr << msg << std::endl; + task.fail(msg); + } + } else { + std::string msg{"unknown path type " + pathType}; + std::cerr << msg << std::endl; + task.fail(msg); + } + + if (!task.hasError()) { + std::string msg{getPathsStr(server_ctx.crit_paths(), detailsLevel, isFlat)}; + task.success(msg); + } } else { - std::string msg = "options errors in get crit path list telegram: " + options.errorsStr(); + std::string msg{"options errors in get crit path list telegram: " + options.errorsStr()}; std::cerr << msg << std::endl; task.fail(msg); } @@ -92,7 +114,7 @@ bool TaskResolver::update(ezgl::application* app) task.fail(msg); } } else { - std::string msg = "selectedIndex=" + std::to_string(pathIndex) + " is out of range [0-" + std::to_string(static_cast(server_ctx.crit_paths().size())-1) + "]"; + std::string msg{"selectedIndex=" + std::to_string(pathIndex) + " is out of range [0-" + std::to_string(static_cast(server_ctx.crit_paths().size())-1) + "]"}; std::cerr << msg << std::endl; task.fail(msg); } From 2e6d0e72efff33231d351df0b35defca3546c9e2 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Wed, 6 Dec 2023 13:20:29 +0200 Subject: [PATCH 19/31] avoid printing timeout event in Server::startListening --- vpr/src/server/server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vpr/src/server/server.cpp b/vpr/src/server/server.cpp index 330bec57a92..c06101d8c7a 100644 --- a/vpr/src/server/server.cpp +++ b/vpr/src/server/server.cpp @@ -159,7 +159,7 @@ void Server::startListening() if (selectResult == -1) { std::cerr << "Error in select()\n"; } else if (selectResult == 0) { - std::cout << "Timeout occurred! No data available.\n"; + //std::cout << "Timeout occurred! No data available.\n"; } else { if (FD_ISSET(client_socket, &readfds)) { // Data is available; proceed with recv From 3a01f54896520e129c23b6514d1af9d62e160b19 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Wed, 6 Dec 2023 16:27:12 +0200 Subject: [PATCH 20/31] redraw_callback timer period is descreased from 200ms to 100ms to be more responsive --- vpr/src/draw/draw.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vpr/src/draw/draw.cpp b/vpr/src/draw/draw.cpp index 83d4e814842..b67272ced84 100644 --- a/vpr/src/draw/draw.cpp +++ b/vpr/src/draw/draw.cpp @@ -240,7 +240,7 @@ void init_graphics_state(bool show_graphics_val, if (server) { g_vpr_ctx.server_ctx().set_enabled(true); g_vpr_ctx.server_ctx().server().setPortNum(port_num); - g_timeout_add(200, redraw_callback, &application); + g_timeout_add(100, redraw_callback, &application); } #else From b45b7aee8fabf676f16049ddd73e8fde8d79c437 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Fri, 22 Dec 2023 20:37:34 +0200 Subject: [PATCH 21/31] changes: fix path calculation + report generation for Ineractive Path Analysis tool. ServerContext now is a pointer and instantiated only when --server mode is activated. cleanup --- .../libtatum/tatum/TimingReporter.cpp | 18 +++ .../libtatum/tatum/TimingReporter.hpp | 2 + vpr/src/base/vpr_api.cpp | 3 - vpr/src/base/vpr_context.h | 20 ++-- vpr/src/draw/draw.cpp | 50 +++++---- vpr/src/draw/draw_basic.cpp | 26 +++-- vpr/src/draw/ui_setup.cpp | 4 +- vpr/src/server/pathhelper.cpp | 103 ++++++++++-------- vpr/src/server/pathhelper.h | 11 +- vpr/src/server/server.cpp | 22 +++- vpr/src/server/server.h | 1 + vpr/src/server/taskresolver.cpp | 58 +++++----- vpr/src/server/taskresolver.h | 3 + 13 files changed, 193 insertions(+), 128 deletions(-) diff --git a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp index 609b0c0b03e..3ae2d7192e1 100644 --- a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp +++ b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp @@ -99,6 +99,15 @@ void TimingReporter::report_timing_setup(std::ostream& os, report_timing(os, paths); } +void TimingReporter::report_timing_setup(std::vector& paths, + std::ostream& os, + const SetupTimingAnalyzer& setup_analyzer, + size_t npaths) const { + paths = path_collector_.collect_worst_setup_timing_paths(timing_graph_, setup_analyzer, npaths); + + report_timing(os, paths); +} + void TimingReporter::report_timing_hold(std::string filename, const HoldTimingAnalyzer& hold_analyzer, size_t npaths) const { @@ -114,6 +123,15 @@ void TimingReporter::report_timing_hold(std::ostream& os, report_timing(os, paths); } +void TimingReporter::report_timing_hold(std::vector& paths, + std::ostream& os, + const HoldTimingAnalyzer& hold_analyzer, + size_t npaths) const { + paths = path_collector_.collect_worst_hold_timing_paths(timing_graph_, hold_analyzer, npaths); + + report_timing(os, paths); +} + void TimingReporter::report_skew_setup(std::string filename, const SetupTimingAnalyzer& setup_analyzer, size_t nworst) const { diff --git a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp index 96862c4d3ea..22e428aa6ed 100644 --- a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp +++ b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp @@ -64,9 +64,11 @@ class TimingReporter { public: void report_timing_setup(std::string filename, const tatum::SetupTimingAnalyzer& setup_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; void report_timing_setup(std::ostream& os, const tatum::SetupTimingAnalyzer& setup_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; + void report_timing_setup(std::vector& paths, std::ostream& os, const tatum::SetupTimingAnalyzer& setup_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; void report_timing_hold(std::string filename, const tatum::HoldTimingAnalyzer& hold_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; void report_timing_hold(std::ostream& os, const tatum::HoldTimingAnalyzer& hold_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; + void report_timing_hold(std::vector& paths, std::ostream& os, const tatum::HoldTimingAnalyzer& hold_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; void report_skew_setup(std::string filename, const tatum::SetupTimingAnalyzer& setup_analyzer, size_t nworst=REPORT_TIMING_DEFAULT_NPATHS) const; void report_skew_setup(std::ostream& os, const tatum::SetupTimingAnalyzer& setup_analyzer, size_t nworst=REPORT_TIMING_DEFAULT_NPATHS) const; diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index e5a368517fb..f5f000b03ca 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -861,7 +861,6 @@ RouteStatus vpr_route_flow(const Netlist<>& net_list, routing_delay_calc = std::make_shared(atom_ctx.nlist, atom_ctx.lookup, net_delay, is_flat); timing_info = make_setup_hold_timing_info(routing_delay_calc, router_opts.timing_update_type); - g_vpr_ctx.server_ctx().set_hold_timing_info(timing_info); } if (router_opts.doRouting == STAGE_DO) { @@ -1504,8 +1503,6 @@ void vpr_analysis(const Netlist<>& net_list, if (vpr_setup.PowerOpts.do_power) { vpr_power_estimation(vpr_setup, Arch, *timing_info, route_status); } - - g_vpr_ctx.server_ctx().set_hold_timing_info(std::move(timing_info)); // grab timing_info for further usage } } diff --git a/vpr/src/base/vpr_context.h b/vpr/src/base/vpr_context.h index ca644ae7edd..6c298898f9a 100644 --- a/vpr/src/base/vpr_context.h +++ b/vpr/src/base/vpr_context.h @@ -565,9 +565,6 @@ struct NocContext : public Context { class ServerContext : public Context { public: - bool is_enabled() const { return is_enabled_; } - void set_enabled(bool is_enabled) { is_enabled_ = is_enabled; } - const Server& server() const { return server_; } Server& server() { return server_; } @@ -586,9 +583,6 @@ class ServerContext : public Context { void set_crit_path_index(int crit_path_index) { crit_path_index_ = crit_path_index; } int crit_path_index() const { return crit_path_index_; } - void set_hold_timing_info(const std::shared_ptr& hold_timing_info) { hold_timing_info_ = hold_timing_info; } - const std::shared_ptr& hold_timing_info() const { return hold_timing_info_; } - private: bool is_enabled_ = false; Server server_; @@ -598,8 +592,8 @@ class ServerContext : public Context { int critical_path_num_ = 1; std::string path_type_ = "setup"; int crit_path_index_ = 0; - std::shared_ptr hold_timing_info_; }; +using ServerContextPtr = std::unique_ptr; /** * @brief This object encapsulates VPR's state. @@ -684,8 +678,14 @@ class VprContext : public Context { const PackingMultithreadingContext& packing_multithreading() const { return packing_multithreading_; } PackingMultithreadingContext& mutable_packing_multithreading() { return packing_multithreading_; } - const ServerContext& server_ctx() const { return server_ctx_; } - ServerContext& server_ctx() { return server_ctx_; } + void create_server_ctx(int port_num) { + server_ctx_ = std::make_unique(); + if (server_ctx_) { + server_ctx_->server().setPortNum(port_num); + } + } + + const ServerContextPtr& server_ctx() const { return server_ctx_; } private: DeviceContext device_; @@ -703,7 +703,7 @@ class VprContext : public Context { FloorplanningContext constraints_; NocContext noc_; - ServerContext server_ctx_; + ServerContextPtr server_ctx_; PackingMultithreadingContext packing_multithreading_; }; diff --git a/vpr/src/draw/draw.cpp b/vpr/src/draw/draw.cpp index b67272ced84..73c8250e475 100644 --- a/vpr/src/draw/draw.cpp +++ b/vpr/src/draw/draw.cpp @@ -176,36 +176,39 @@ ezgl::rectangle initial_world; std::string rr_highlight_message; gboolean redraw_callback(gpointer data) { - // shortcuts - ezgl::application* app = static_cast(data); - Server& server = g_vpr_ctx.server_ctx().server(); - TaskResolver& task_resolver = g_vpr_ctx.server_ctx().task_resolver(); - // - - bool isRunning = !server.isStopped(); - if (isRunning) { - if (!server.isStarted()) { - server.start(); - } + bool isRunning = false; + if (g_vpr_ctx.server_ctx()) { + // shortcuts + ezgl::application* app = static_cast(data); + Server& server = g_vpr_ctx.server_ctx()->server(); + TaskResolver& task_resolver = g_vpr_ctx.server_ctx()->task_resolver(); + // + + isRunning = !server.isStopped(); + if (isRunning) { + if (!server.isStarted()) { + server.start(); + } - std::vector tasksBuff; + std::vector tasksBuff; - server.takeRecievedTasks(tasksBuff); - task_resolver.addTasks(tasksBuff); + server.takeRecievedTasks(tasksBuff); + task_resolver.addTasks(tasksBuff); - bool process_task = task_resolver.update(app); + bool process_task = task_resolver.update(app); - tasksBuff.clear(); - task_resolver.takeFinished(tasksBuff); + tasksBuff.clear(); + task_resolver.takeFinished(tasksBuff); - server.addSendTasks(tasksBuff); + server.addSendTasks(tasksBuff); - // Call the redraw method of the application if any of task was processed - if (process_task) { - app->refresh_drawing(); + // Call the redraw method of the application if any of task was processed + if (process_task) { + app->refresh_drawing(); + } } } - + // Return TRUE to keep the timer running, or FALSE to stop it return isRunning; } @@ -238,8 +241,7 @@ void init_graphics_state(bool show_graphics_val, draw_state->is_flat = is_flat; if (server) { - g_vpr_ctx.server_ctx().set_enabled(true); - g_vpr_ctx.server_ctx().server().setPortNum(port_num); + g_vpr_ctx.create_server_ctx(port_num); g_timeout_add(100, redraw_callback, &application); } diff --git a/vpr/src/draw/draw_basic.cpp b/vpr/src/draw/draw_basic.cpp index f5e60b14ae2..b2936661195 100644 --- a/vpr/src/draw/draw_basic.cpp +++ b/vpr/src/draw/draw_basic.cpp @@ -951,24 +951,30 @@ void draw_crit_path(ezgl::renderer* g) { return; //No timing to draw } - if (g_vpr_ctx.server_ctx().is_enabled()) { - ServerContext& server_ctx = g_vpr_ctx.server_ctx(); // shortcut + if (g_vpr_ctx.server_ctx()) { + const ServerContextPtr& server_ctx = g_vpr_ctx.server_ctx(); // shortcut - auto paths = server_ctx.crit_paths(); + auto paths = server_ctx->crit_paths(); // check path index - if (server_ctx.crit_path_index() >= static_cast(paths.size())) { - server_ctx.set_crit_path_index(-1); + if (server_ctx->crit_path_index() >= static_cast(paths.size())) { + server_ctx->set_crit_path_index(-1); } - if (server_ctx.crit_path_index() != -1) { - tatum::TimingPath path = paths[server_ctx.crit_path_index()]; + if (server_ctx->crit_path_index() != -1) { + tatum::TimingPath path = paths[server_ctx->crit_path_index()]; draw_crit_path(path, g); } } else { - const int crit_path_index = 0; - auto paths = calcSetupCritPaths(1); + tatum::TimingPathCollector path_collector; + + auto& timing_ctx = g_vpr_ctx.timing(); + + auto paths = path_collector.collect_worst_setup_timing_paths( + *timing_ctx.graph, + *(draw_state->setup_timing_info->setup_analyzer()), 1); + if (paths.size()>0) { - auto path = paths[crit_path_index]; + auto path = paths[0]; draw_crit_path(path, g); } } diff --git a/vpr/src/draw/ui_setup.cpp b/vpr/src/draw/ui_setup.cpp index bdf50a6b10c..902cecfaf60 100644 --- a/vpr/src/draw/ui_setup.cpp +++ b/vpr/src/draw/ui_setup.cpp @@ -24,7 +24,9 @@ # include "ezgl/graphics.hpp" void on_window_destroy() { - g_vpr_ctx.server_ctx().server().stop(); + if (g_vpr_ctx.server_ctx()) { + g_vpr_ctx.server_ctx()->server().stop(); + } } void basic_button_setup(ezgl::application* app) { diff --git a/vpr/src/server/pathhelper.cpp b/vpr/src/server/pathhelper.cpp index 92f5251a577..369569f18bd 100644 --- a/vpr/src/server/pathhelper.cpp +++ b/vpr/src/server/pathhelper.cpp @@ -6,63 +6,80 @@ #include "draw_types.h" #include "draw_global.h" +#include "net_delay.h" +#include "concrete_timing_info.h" + +#include "timing_info_fwd.h" +#include "AnalysisDelayCalculator.h" +#include "vpr_types.h" #include #include -std::string getPathsStr(const std::vector& paths, const std::string& detailsLevel, bool is_flat_routing) -{ - // shortcuts - auto& atom_ctx = g_vpr_ctx.atom(); +namespace { + +CritPathsResult generate_setup_timing_report(const SetupTimingInfo& timing_info, const AnalysisDelayCalculator& delay_calc, const t_analysis_opts& analysis_opts, bool is_flat) { auto& timing_ctx = g_vpr_ctx.timing(); - // shortcuts - - // build deps - NetPinsMatrix net_delay = make_net_pins_matrix(atom_ctx.nlist); - auto analysis_delay_calc = std::make_shared(atom_ctx.nlist, atom_ctx.lookup, net_delay, is_flat_routing); - - VprTimingGraphResolver resolver(atom_ctx.nlist, atom_ctx.lookup, *timing_ctx.graph, *analysis_delay_calc.get(), is_flat_routing); - e_timing_report_detail detailesLevelEnum = e_timing_report_detail::NETLIST; - if (detailsLevel == "netlist") { - detailesLevelEnum = e_timing_report_detail::NETLIST; - } else if (detailsLevel == "aggregated") { - detailesLevelEnum = e_timing_report_detail::AGGREGATED; - } else if (detailsLevel == "detailed") { - detailesLevelEnum = e_timing_report_detail::DETAILED_ROUTING; - } else if (detailsLevel == "debug") { - detailesLevelEnum = e_timing_report_detail::DEBUG; - } else { - std::cerr << "unhandled option" << detailsLevel << std::endl; - } - resolver.set_detail_level(detailesLevelEnum); - // + auto& atom_ctx = g_vpr_ctx.atom(); + + VprTimingGraphResolver resolver(atom_ctx.nlist, atom_ctx.lookup, *timing_ctx.graph, delay_calc, is_flat); + resolver.set_detail_level(analysis_opts.timing_report_detail); - std::stringstream ss; tatum::TimingReporter timing_reporter(resolver, *timing_ctx.graph, *timing_ctx.constraints); - timing_reporter.report_timing(ss, paths); - return ss.str(); + std::vector paths; + std::stringstream ss; + timing_reporter.report_timing_setup(paths, ss, *timing_info.setup_analyzer(), analysis_opts.timing_report_npaths); + return CritPathsResult{paths, ss.str()}; } -std::vector calcSetupCritPaths(int numMax) -{ - tatum::TimingPathCollector path_collector; - - t_draw_state* draw_state = get_draw_state_vars(); +CritPathsResult generate_hold_timing_report(const HoldTimingInfo& timing_info, const AnalysisDelayCalculator& delay_calc, const t_analysis_opts& analysis_opts, bool is_flat) { auto& timing_ctx = g_vpr_ctx.timing(); + auto& atom_ctx = g_vpr_ctx.atom(); - return path_collector.collect_worst_setup_timing_paths( - *timing_ctx.graph, - *(draw_state->setup_timing_info->setup_analyzer()), numMax); + VprTimingGraphResolver resolver(atom_ctx.nlist, atom_ctx.lookup, *timing_ctx.graph, delay_calc, is_flat); + resolver.set_detail_level(analysis_opts.timing_report_detail); + + tatum::TimingReporter timing_reporter(resolver, *timing_ctx.graph, *timing_ctx.constraints); + + std::vector paths; + std::stringstream ss; + timing_reporter.report_timing_hold(paths, ss, *timing_info.hold_analyzer(), analysis_opts.timing_report_npaths); + return CritPathsResult{paths, ss.str()}; } -std::vector calcHoldCritPaths(int numMax, const std::shared_ptr& hold_timing_info) -{ - tatum::TimingPathCollector path_collector; +} // namespace +CritPathsResult calcCriticalPath(const std::string& type, int critPathNum, e_timing_report_detail detailsLevel, bool is_flat_routing) +{ + // shortcuts + auto& atom_ctx = g_vpr_ctx.atom(); auto& timing_ctx = g_vpr_ctx.timing(); - return path_collector.collect_worst_hold_timing_paths( - *timing_ctx.graph, - *(hold_timing_info->hold_analyzer()), numMax); -} \ No newline at end of file + //Load the net delays + const Netlist<>& router_net_list = is_flat_routing ? (const Netlist<>&)g_vpr_ctx.atom().nlist : (const Netlist<>&)g_vpr_ctx.clustering().clb_nlist; + const Netlist<>& net_list = router_net_list; + + NetPinsMatrix net_delay = make_net_pins_matrix(net_list); + load_net_delay_from_routing(net_list, + net_delay); + + //Do final timing analysis + auto analysis_delay_calc = std::make_shared(atom_ctx.nlist, atom_ctx.lookup, net_delay, is_flat_routing); + + e_timing_update_type timing_update_type = e_timing_update_type::AUTO; // FULL, INCREMENTAL, AUTO + auto timing_info = make_setup_hold_timing_info(analysis_delay_calc, timing_update_type); + timing_info->update(); + + t_analysis_opts analysis_opt; + analysis_opt.timing_report_detail = detailsLevel; + analysis_opt.timing_report_npaths = critPathNum; + + if (type == "setup") { + return generate_setup_timing_report(*timing_info, *analysis_delay_calc, analysis_opt, is_flat_routing); + } else if (type == "hold") { + return generate_hold_timing_report(*timing_info, *analysis_delay_calc, analysis_opt, is_flat_routing); + } + return CritPathsResult{std::vector(), ""}; +} + diff --git a/vpr/src/server/pathhelper.h b/vpr/src/server/pathhelper.h index b7e43ab2cff..7fb48a49552 100644 --- a/vpr/src/server/pathhelper.h +++ b/vpr/src/server/pathhelper.h @@ -6,12 +6,13 @@ #include #include "tatum/report/TimingPath.hpp" +#include "vpr_types.h" -class SetupHoldTimingInfo; +struct CritPathsResult { + std::vector paths; + std::string report; +}; -std::string getPathsStr(const std::vector& paths, const std::string& detailesLevel, bool is_flat_routing); - -std::vector calcSetupCritPaths(int numMax); -std::vector calcHoldCritPaths(int numMax, const std::shared_ptr& hold_timing_info); +CritPathsResult calcCriticalPath(const std::string& type, int critPathNum, e_timing_report_detail detailsLevel, bool is_flat_routing); #endif diff --git a/vpr/src/server/server.cpp b/vpr/src/server/server.cpp index c06101d8c7a..cf779455b26 100644 --- a/vpr/src/server/server.cpp +++ b/vpr/src/server/server.cpp @@ -16,7 +16,9 @@ Server::Server() Server::~Server() { - std::cout << "~~~ th=" << std::this_thread::get_id() << " ~Server()" << std::endl; + if (m_debugLog) { + std::cout << "~~~ th=" << std::this_thread::get_id() << " ~Server()" << std::endl; + } stop(); } @@ -29,7 +31,9 @@ void Server::setPortNum(int portNum) void Server::start() { if (!m_isStarted.load()) { - std::cout << "~~~ th=" << std::this_thread::get_id() << " starting server" << std::endl; + if (m_debugLog) { + std::cout << "~~~ th=" << std::this_thread::get_id() << " starting server" << std::endl; + } m_isStarted.store(true); m_thread = std::thread(&Server::startListening, this); } @@ -39,11 +43,17 @@ void Server::stop() { if (!m_isStopped.load()) { m_isStopped.store(true); - std::cout << "~~~ th=" << std::this_thread::get_id() << " stopping server, is stopped=" << m_isStopped.load() << std::endl; + if (m_debugLog) { + std::cout << "~~~ th=" << std::this_thread::get_id() << " stopping server, is stopped=" << m_isStopped.load() << std::endl; + } if (m_thread.joinable()) { - std::cout << "~~~ th=" << std::this_thread::get_id() << " join thread START" << std::endl; + if (m_debugLog) { + std::cout << "~~~ th=" << std::this_thread::get_id() << " join thread START" << std::endl; + } m_thread.join(); - std::cout << "~~~ th=" << std::this_thread::get_id() << " join thread FINISHED" << std::endl; + if (m_debugLog) { + std::cout << "~~~ th=" << std::this_thread::get_id() << " join thread FINISHED" << std::endl; + } } } } @@ -96,7 +106,7 @@ void Server::startListening() perror("bind failed"); exit(EXIT_FAILURE); } else { - std::cout << "~~~ th=" << std::this_thread::get_id() << " start listening port: " << m_portNum << std::endl; + std::cout << "th=" << std::this_thread::get_id() << " start listening port: " << m_portNum << std::endl; } // Put the server socket in a passive mode, where it waits for the client to approach the server to make a connection diff --git a/vpr/src/server/server.h b/vpr/src/server/server.h index ed7caa44ce4..64fce70494e 100644 --- a/vpr/src/server/server.h +++ b/vpr/src/server/server.h @@ -31,6 +31,7 @@ class Server void stop(); private: + bool m_debugLog = false; int m_portNum = -1; std::mutex m_portNumMutex; diff --git a/vpr/src/server/taskresolver.cpp b/vpr/src/server/taskresolver.cpp index fa1f6dcf74b..2cec2a88f53 100644 --- a/vpr/src/server/taskresolver.cpp +++ b/vpr/src/server/taskresolver.cpp @@ -45,10 +45,29 @@ void TaskResolver::takeFinished(std::vector& result) } } +e_timing_report_detail TaskResolver::getDetailsLevelEnum(const std::string& pathDetailsLevelStr) const { + e_timing_report_detail detailesLevel = e_timing_report_detail::NETLIST; + if (pathDetailsLevelStr == "netlist") { + detailesLevel = e_timing_report_detail::NETLIST; + } else if (pathDetailsLevelStr == "aggregated") { + detailesLevel = e_timing_report_detail::AGGREGATED; + } else if (pathDetailsLevelStr == "detailed") { + detailesLevel = e_timing_report_detail::DETAILED_ROUTING; + } else if (pathDetailsLevelStr == "debug") { + detailesLevel = e_timing_report_detail::DEBUG; + } else { + std::cerr << "unhandled option" << pathDetailsLevelStr << std::endl; + } + return detailesLevel; +} + bool TaskResolver::update(ezgl::application* app) { bool process_task = false; - ServerContext& server_ctx = g_vpr_ctx.server_ctx(); + const ServerContextPtr& server_ctx = g_vpr_ctx.server_ctx(); + if (!server_ctx) { + return false; + } for (auto& task: m_tasks) { if (!task.isFinished()) { process_task = true; @@ -60,33 +79,20 @@ bool TaskResolver::update(ezgl::application* app) std::string detailsLevel = options.getString(OPTION_DETAILS_LEVEL); bool isFlat = options.getBool(OPTION_IS_FLOAT_ROUTING, false); if (!options.hasErrors()) { - if (pathType != server_ctx.path_type()) { - server_ctx.set_crit_path_index(-1); - } + server_ctx->set_crit_path_index(-1); // reset selection if path list options has changed - server_ctx.set_path_type(pathType); - server_ctx.set_critical_path_num(nCriticalPathNum); - if (pathType == "setup") { - auto paths = calcSetupCritPaths(nCriticalPathNum); - server_ctx.set_crit_paths(paths); - } else if (pathType == "hold") { - auto hold_timing_info = server_ctx.hold_timing_info(); - if (hold_timing_info) { - auto paths = calcHoldCritPaths(nCriticalPathNum, hold_timing_info); - server_ctx.set_crit_paths(paths); - } else { - std::string msg{"cannot calculate hold critical path due to hold_timing_info nullptr"}; - std::cerr << msg << std::endl; - task.fail(msg); - } - } else { - std::string msg{"unknown path type " + pathType}; + server_ctx->set_path_type(pathType); + server_ctx->set_critical_path_num(nCriticalPathNum); + CritPathsResult crit_paths_result = calcCriticalPath(pathType, nCriticalPathNum, getDetailsLevelEnum(detailsLevel), isFlat); + server_ctx->set_crit_paths(crit_paths_result.paths); + if (crit_paths_result.report.empty()) { + std::string msg{"Critical paths report is empty"}; std::cerr << msg << std::endl; task.fail(msg); } - + if (!task.hasError()) { - std::string msg{getPathsStr(server_ctx.crit_paths(), detailsLevel, isFlat)}; + std::string msg{crit_paths_result.report}; task.success(msg); } } else { @@ -99,8 +105,8 @@ bool TaskResolver::update(ezgl::application* app) int pathIndex = options.getInt(OPTION_PATH_INDEX, -1); std::string highLightMode = options.getString(OPTION_HIGHTLIGHT_MODE); if (!options.hasErrors()) { - if ((pathIndex >= 0) && (pathIndex < static_cast(server_ctx.crit_paths().size()))) { - server_ctx.set_crit_path_index(pathIndex); + if ((pathIndex >= 0) && (pathIndex < static_cast(server_ctx->crit_paths().size()))) { + server_ctx->set_crit_path_index(pathIndex); // update gtk UI GtkComboBox* toggle_crit_path = GTK_COMBO_BOX(app->get_object("ToggleCritPath")); @@ -114,7 +120,7 @@ bool TaskResolver::update(ezgl::application* app) task.fail(msg); } } else { - std::string msg{"selectedIndex=" + std::to_string(pathIndex) + " is out of range [0-" + std::to_string(static_cast(server_ctx.crit_paths().size())-1) + "]"}; + std::string msg{"selectedIndex=" + std::to_string(pathIndex) + " is out of range [0-" + std::to_string(static_cast(server_ctx->crit_paths().size())-1) + "]"}; std::cerr << msg << std::endl; task.fail(msg); } diff --git a/vpr/src/server/taskresolver.h b/vpr/src/server/taskresolver.h index 236fe97d05d..ddfd99bfb61 100644 --- a/vpr/src/server/taskresolver.h +++ b/vpr/src/server/taskresolver.h @@ -2,6 +2,7 @@ #define TASKRESOLVER_H #include "task.h" +#include "vpr_types.h" #include @@ -26,6 +27,8 @@ class TaskResolver { private: std::vector m_tasks; + + e_timing_report_detail getDetailsLevelEnum(const std::string& pathDetailsLevelStr) const ; }; #endif From b4a2c939fcf2a38496af73c6add974f0239394e2 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Thu, 4 Jan 2024 13:49:09 +0200 Subject: [PATCH 22/31] add comments for Interactie Path Analysis tool, code structure refactoring --- .../libtatum/tatum/TimingReporter.cpp | 4 +- .../libtatum/tatum/TimingReporter.hpp | 4 +- vpr/src/base/SetupVPR.cpp | 2 +- vpr/src/base/read_options.cpp | 6 +- vpr/src/base/read_options.h | 4 +- vpr/src/base/vpr_api.cpp | 3 +- vpr/src/base/vpr_api.h | 1 + vpr/src/base/vpr_context.h | 64 ++++--- vpr/src/base/vpr_types.h | 3 +- vpr/src/draw/draw.cpp | 55 ++---- vpr/src/draw/draw.h | 3 - vpr/src/draw/draw_basic.cpp | 42 +++-- vpr/src/draw/draw_basic.h | 12 +- vpr/src/draw/ui_setup.cpp | 8 - vpr/src/draw/ui_setup.h | 4 +- vpr/src/server/{server.cpp => gateio.cpp} | 64 +++---- vpr/src/server/gateio.h | 66 +++++++ vpr/src/server/gtkcomboboxhelper.cpp | 17 +- vpr/src/server/gtkcomboboxhelper.h | 10 +- vpr/src/server/pathhelper.cpp | 13 +- vpr/src/server/pathhelper.h | 15 +- vpr/src/server/server.h | 56 ------ vpr/src/server/serverconsts.h | 4 + vpr/src/server/serverupdate.cpp | 43 +++++ vpr/src/server/serverupdate.h | 23 +++ vpr/src/server/task.h | 12 +- vpr/src/server/taskresolver.cpp | 170 ++++++++++-------- vpr/src/server/taskresolver.h | 23 ++- vpr/src/server/telegrambuffer.cpp | 4 + vpr/src/server/telegrambuffer.h | 14 +- vpr/src/server/telegramoptions.h | 46 +++-- vpr/src/server/telegramparser.cpp | 10 +- vpr/src/server/telegramparser.h | 23 ++- 33 files changed, 505 insertions(+), 323 deletions(-) rename vpr/src/server/{server.cpp => gateio.cpp} (78%) create mode 100644 vpr/src/server/gateio.h delete mode 100644 vpr/src/server/server.h create mode 100644 vpr/src/server/serverupdate.cpp create mode 100644 vpr/src/server/serverupdate.h diff --git a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp index 3ae2d7192e1..69c9ba07a83 100644 --- a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp +++ b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp @@ -100,7 +100,7 @@ void TimingReporter::report_timing_setup(std::ostream& os, } void TimingReporter::report_timing_setup(std::vector& paths, - std::ostream& os, + std::ostream& os, const SetupTimingAnalyzer& setup_analyzer, size_t npaths) const { paths = path_collector_.collect_worst_setup_timing_paths(timing_graph_, setup_analyzer, npaths); @@ -124,7 +124,7 @@ void TimingReporter::report_timing_hold(std::ostream& os, } void TimingReporter::report_timing_hold(std::vector& paths, - std::ostream& os, + std::ostream& os, const HoldTimingAnalyzer& hold_analyzer, size_t npaths) const { paths = path_collector_.collect_worst_hold_timing_paths(timing_graph_, hold_analyzer, npaths); diff --git a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp index 22e428aa6ed..590e3bca690 100644 --- a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp +++ b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp @@ -82,8 +82,6 @@ class TimingReporter { void report_unconstrained_hold(std::string filename, const tatum::HoldTimingAnalyzer& hold_analyzer) const; void report_unconstrained_hold(std::ostream& os, const tatum::HoldTimingAnalyzer& hold_analyzer) const; - void report_timing(std::ostream& os, const std::vector& paths) const; - private: struct PathSkew { NodeId launch_node; @@ -98,6 +96,8 @@ class TimingReporter { }; private: + void report_timing(std::ostream& os, const std::vector& paths) const; + void report_timing_path(std::ostream& os, const TimingPath& path) const; void report_unconstrained(std::ostream& os, const NodeType type, const detail::TagRetriever& tag_retriever) const; diff --git a/vpr/src/base/SetupVPR.cpp b/vpr/src/base/SetupVPR.cpp index a8b5bad7c23..ffecd4aa9f6 100644 --- a/vpr/src/base/SetupVPR.cpp +++ b/vpr/src/base/SetupVPR.cpp @@ -755,7 +755,7 @@ static void SetupNocOpts(const t_options& Options, t_noc_opts* NocOpts) { } static void SetupServerOpts(const t_options& Options, t_server_opts* ServerOpts) { - ServerOpts->is_enabled = Options.is_server_enabled; + ServerOpts->is_server_mode_enabled = Options.is_server_mode_enabled; ServerOpts->port_num = Options.server_port_num; } diff --git a/vpr/src/base/read_options.cpp b/vpr/src/base/read_options.cpp index e07277168ac..e8c2a3c74e2 100644 --- a/vpr/src/base/read_options.cpp +++ b/vpr/src/base/read_options.cpp @@ -1280,11 +1280,11 @@ argparse::ArgumentParser create_arg_parser(std::string prog_name, t_options& arg .action(argparse::Action::STORE_TRUE) .default_value("off"); - stage_grp.add_argument(args.is_server_enabled, "--server") - .help("Run server mode") + stage_grp.add_argument(args.is_server_mode_enabled, "--server") + .help("Run in server mode") .action(argparse::Action::STORE_TRUE) .default_value("off"); - + stage_grp.add_argument(args.server_port_num, "--port") .help("Server port number") .default_value("60555") diff --git a/vpr/src/base/read_options.h b/vpr/src/base/read_options.h index 49c297512d4..d1e0b200893 100644 --- a/vpr/src/base/read_options.h +++ b/vpr/src/base/read_options.h @@ -74,7 +74,9 @@ struct t_options { argparse::ArgValue suppress_warnings; argparse::ArgValue allow_dangling_combinational_nodes; argparse::ArgValue terminate_if_timing_fails; - argparse::ArgValue is_server_enabled; + + /* Server options */ + argparse::ArgValue is_server_mode_enabled; argparse::ArgValue server_port_num; /* Atom netlist options */ diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index f5f000b03ca..3e545f15af8 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -1099,7 +1099,7 @@ void vpr_init_graphics(const t_vpr_setup& vpr_setup, const t_arch& arch, bool is /* Startup X graphics */ init_graphics_state(vpr_setup.ShowGraphics, vpr_setup.GraphPause, vpr_setup.RouterOpts.route_type, vpr_setup.SaveGraphics, - vpr_setup.GraphicsCommands, is_flat, vpr_setup.ServerOpts.is_enabled, vpr_setup.ServerOpts.port_num); + vpr_setup.GraphicsCommands, is_flat, vpr_setup.ServerOpts.is_server_mode_enabled, vpr_setup.ServerOpts.port_num); if (vpr_setup.ShowGraphics || vpr_setup.SaveGraphics || !vpr_setup.GraphicsCommands.empty()) alloc_draw_structs(&arch); } @@ -1471,7 +1471,6 @@ void vpr_analysis(const Netlist<>& net_list, //Do final timing analysis auto analysis_delay_calc = std::make_shared(atom_ctx.nlist, atom_ctx.lookup, net_delay, vpr_setup.RouterOpts.flat_routing); auto timing_info = make_setup_hold_timing_info(analysis_delay_calc, vpr_setup.AnalysisOpts.timing_update_type); - timing_info->update(); if (isEchoFileEnabled(E_ECHO_ANALYSIS_TIMING_GRAPH)) { diff --git a/vpr/src/base/vpr_api.h b/vpr/src/base/vpr_api.h index fda2573374c..c1ca38c7cf4 100644 --- a/vpr/src/base/vpr_api.h +++ b/vpr/src/base/vpr_api.h @@ -132,6 +132,7 @@ void vpr_analysis(const Netlist<>& net_list, ///@brief Create the device (grid + rr graph) void vpr_create_device(t_vpr_setup& vpr_setup, const t_arch& Arch, bool is_flat); +/// @brief Show resource usage void vpr_show_resource_usage(const t_vpr_setup& vpr_setup, const t_arch& Arch); ///@brief Create the device grid diff --git a/vpr/src/base/vpr_context.h b/vpr/src/base/vpr_context.h index 6c298898f9a..252d915fea3 100644 --- a/vpr/src/base/vpr_context.h +++ b/vpr/src/base/vpr_context.h @@ -32,13 +32,10 @@ #include "noc_storage.h" #include "noc_traffic_flows.h" #include "noc_routing.h" - -#include "server.h" +#include "gateio.h" #include "taskresolver.h" #include "tatum/report/TimingPath.hpp" -class SetupHoldTimingInfo; - /** * @brief A Context is collection of state relating to a particular part of VPR @@ -563,13 +560,19 @@ struct NocContext : public Context { NocRouting* noc_flows_router; }; +/** + * @brief State relating to server mode + * + * This should contain only data structures that + * related to server state. + */ class ServerContext : public Context { public: - const Server& server() const { return server_; } - Server& server() { return server_; } + const server::GateIO& gateIO() const { return gate_io_; } + server::GateIO& mutable_gateIO() { return gate_io_; } - const TaskResolver& task_resolver() const { return task_resolver_; } - TaskResolver& task_resolver() { return task_resolver_; } + const server::TaskResolver& task_resolver() const { return task_resolver_; } + server::TaskResolver& mutable_task_resolver() { return task_resolver_; } void set_crit_paths(const std::vector& crit_paths) { crit_paths_ = crit_paths; } const std::vector& crit_paths() const { return crit_paths_; } @@ -584,16 +587,41 @@ class ServerContext : public Context { int crit_path_index() const { return crit_path_index_; } private: - bool is_enabled_ = false; - Server server_; - TaskResolver task_resolver_; + server::GateIO gate_io_; + server::TaskResolver task_resolver_; + /** + * @brief Stores the critical path items. + * + * This value is used when rendering the critical path by the selected index. + * Once calculated upon request, it provides the value for a specific critical path + * to be rendered upon user request. + */ std::vector crit_paths_; + + /** + * @brief Stores the number of critical paths items. + * + * This value is used to generate a critical path report with a certain number of items, + * which will be sent back to the client upon request. + */ int critical_path_num_ = 1; + + /** + * @brief Stores the critical path type. + * + * This value is used to generate a specific type of critical path report and send + * it back to the client upon request. + */ std::string path_type_ = "setup"; + + /** + * @brief Stores the last selected critical path index. + * + * This value is used to render the selected critical path upon client request. + */ int crit_path_index_ = 0; }; -using ServerContextPtr = std::unique_ptr; /** * @brief This object encapsulates VPR's state. @@ -678,14 +706,8 @@ class VprContext : public Context { const PackingMultithreadingContext& packing_multithreading() const { return packing_multithreading_; } PackingMultithreadingContext& mutable_packing_multithreading() { return packing_multithreading_; } - void create_server_ctx(int port_num) { - server_ctx_ = std::make_unique(); - if (server_ctx_) { - server_ctx_->server().setPortNum(port_num); - } - } - - const ServerContextPtr& server_ctx() const { return server_ctx_; } + const ServerContext& server() const { return server_; } + ServerContext& mutable_server() { return server_; } private: DeviceContext device_; @@ -703,7 +725,7 @@ class VprContext : public Context { FloorplanningContext constraints_; NocContext noc_; - ServerContextPtr server_ctx_; + ServerContext server_; PackingMultithreadingContext packing_multithreading_; }; diff --git a/vpr/src/base/vpr_types.h b/vpr/src/base/vpr_types.h index 2cd56966a39..158ae4b7fce 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -1776,8 +1776,9 @@ struct t_TokenPair { struct t_lb_type_rr_node; /* Defined in pack_types.h */ +/// @brief Stores settings for VPR server mode struct t_server_opts { - bool is_enabled = false; + bool is_server_mode_enabled = false; int port_num = -1; }; diff --git a/vpr/src/draw/draw.cpp b/vpr/src/draw/draw.cpp index 73c8250e475..5e5a45c8333 100644 --- a/vpr/src/draw/draw.cpp +++ b/vpr/src/draw/draw.cpp @@ -63,8 +63,8 @@ #include "ui_setup.h" #include "buttons.h" -#include "server.h" -#include "taskresolver.h" +#include "gateio.h" +#include "serverupdate.h" #ifdef VTR_ENABLE_DEBUG_LOGGING # include "move_utils.h" @@ -175,44 +175,6 @@ ezgl::point2d point_1(0, 0); ezgl::rectangle initial_world; std::string rr_highlight_message; -gboolean redraw_callback(gpointer data) { - bool isRunning = false; - if (g_vpr_ctx.server_ctx()) { - // shortcuts - ezgl::application* app = static_cast(data); - Server& server = g_vpr_ctx.server_ctx()->server(); - TaskResolver& task_resolver = g_vpr_ctx.server_ctx()->task_resolver(); - // - - isRunning = !server.isStopped(); - if (isRunning) { - if (!server.isStarted()) { - server.start(); - } - - std::vector tasksBuff; - - server.takeRecievedTasks(tasksBuff); - task_resolver.addTasks(tasksBuff); - - bool process_task = task_resolver.update(app); - - tasksBuff.clear(); - task_resolver.takeFinished(tasksBuff); - - server.addSendTasks(tasksBuff); - - // Call the redraw method of the application if any of task was processed - if (process_task) { - app->refresh_drawing(); - } - } - } - - // Return TRUE to keep the timer running, or FALSE to stop it - return isRunning; -} - #endif // NO_GRAPHICS /********************** Subroutine definitions ******************************/ @@ -223,7 +185,7 @@ void init_graphics_state(bool show_graphics_val, bool save_graphics, std::string graphics_commands, bool is_flat, - bool server, + bool enable_server, int port_num) { #ifndef NO_GRAPHICS /* Call accessor functions to retrieve global variables. */ @@ -240,11 +202,14 @@ void init_graphics_state(bool show_graphics_val, draw_state->graphics_commands = graphics_commands; draw_state->is_flat = is_flat; - if (server) { - g_vpr_ctx.create_server_ctx(port_num); - g_timeout_add(100, redraw_callback, &application); + if (enable_server) { + /* Set up a server and its callback to be triggered at 100ms intervals by the timer's timeout event. */ + server::GateIO& gate_io = g_vpr_ctx.mutable_server().mutable_gateIO(); + if (!gate_io.isRunning()) { + gate_io.start(port_num); + g_timeout_add(/*interval_ms*/ 100, server::update, &application); + } } - #else //Suppress unused parameter warnings (void)show_graphics_val; diff --git a/vpr/src/draw/draw.h b/vpr/src/draw/draw.h index ce43488e3f4..9c68fd80ac5 100644 --- a/vpr/src/draw/draw.h +++ b/vpr/src/draw/draw.h @@ -42,9 +42,6 @@ extern ezgl::application application; #endif /* NO_GRAPHICS */ -// Define a callback function that will be called by the timer -gboolean redraw_callback(gpointer data); - void update_screen(ScreenUpdatePriority priority, const char* msg, enum pic_type pic_on_screen_val, std::shared_ptr timing_info); //Initializes the drawing locations. diff --git a/vpr/src/draw/draw_basic.cpp b/vpr/src/draw/draw_basic.cpp index b2936661195..54e5b156502 100644 --- a/vpr/src/draw/draw_basic.cpp +++ b/vpr/src/draw/draw_basic.cpp @@ -37,8 +37,6 @@ #include "route_export.h" #include "tatum/report/TimingPathCollector.hpp" -#include "pathhelper.h" - #ifdef VTR_ENABLE_DEBUG_LOGGING # include "move_utils.h" #endif @@ -934,11 +932,7 @@ void draw_routing_util(ezgl::renderer* g) { draw_state->color_map = std::move(cmap); } -/* Draws the critical path if Crit. Path (in the GUI) is selected. Each stage between primitive - * pins is shown in a different colour. - * User can toggle between two different visualizations: - * a) during placement, critical path only shown as flylines - * b) during routing, critical path is shown by both flylines and routed net connections. +/* Draws the critical path when selected in the GUI or requested by the client in server mode. */ void draw_crit_path(ezgl::renderer* g) { t_draw_state* draw_state = get_draw_state_vars(); @@ -951,18 +945,22 @@ void draw_crit_path(ezgl::renderer* g) { return; //No timing to draw } - if (g_vpr_ctx.server_ctx()) { - const ServerContextPtr& server_ctx = g_vpr_ctx.server_ctx(); // shortcut + if (g_vpr_ctx.server().gateIO().isRunning()) { + const ServerContext& server_ctx = g_vpr_ctx.server(); // shortcut + const auto& paths = server_ctx.crit_paths(); - auto paths = server_ctx->crit_paths(); + const int index = server_ctx.crit_path_index(); // shortcut - // check path index - if (server_ctx->crit_path_index() >= static_cast(paths.size())) { - server_ctx->set_crit_path_index(-1); - } - if (server_ctx->crit_path_index() != -1) { - tatum::TimingPath path = paths[server_ctx->crit_path_index()]; - draw_crit_path(path, g); + /* check critical path index bounds and render critical path, + * otherwise, resets selected critical path index with value -1. + */ + if ((index >= 0) && (index < static_cast(paths.size()))) { + tatum::TimingPath path = paths[index]; + draw_concrete_crit_path(path, g); + } else { + if (index != -1) { + g_vpr_ctx.mutable_server().set_crit_path_index(-1); + } } } else { tatum::TimingPathCollector path_collector; @@ -975,12 +973,18 @@ void draw_crit_path(ezgl::renderer* g) { if (paths.size()>0) { auto path = paths[0]; - draw_crit_path(path, g); + draw_concrete_crit_path(path, g); } } } -void draw_crit_path(const tatum::TimingPath& path, ezgl::renderer* g) { +/* Draws the concrete critical path helper function. + * Each stage between primitive pins is shown in a different colour. + * User can toggle between two different visualizations: + * a) during placement, critical path only shown as flylines + * b) during routing, critical path is shown by both flylines and routed net connections. + */ +void draw_concrete_crit_path(const tatum::TimingPath& path, ezgl::renderer* g) { t_draw_state* draw_state = get_draw_state_vars(); //Walk through the timing path drawing each edge diff --git a/vpr/src/draw/draw_basic.h b/vpr/src/draw/draw_basic.h index 0c391a49bbe..31f9342d9ae 100644 --- a/vpr/src/draw/draw_basic.h +++ b/vpr/src/draw/draw_basic.h @@ -78,15 +78,17 @@ void draw_partial_route(const std::vector& rr_nodes_to_draw, * channels, while darker colours (e.g. blue) correspond to lower utilization.*/ void draw_routing_util(ezgl::renderer* g); -/* Draws the critical path if Crit. Path (in the GUI) is selected. Each stage between primitive - * pins is shown in a different colour. +/* Draws the critical path when selected in the GUI or requested by the client in server mode. + */ +void draw_crit_path(ezgl::renderer* g); + +/* Draws the concrete critical path helper function. + * Each stage between primitive pins is shown in a different colour. * User can toggle between two different visualizations: * a) during placement, critical path only shown as flylines * b) during routing, critical path is shown by both flylines and routed net connections. */ -void draw_crit_path(ezgl::renderer* g); - -void draw_crit_path(const tatum::TimingPath& path, ezgl::renderer* g); +void draw_concrete_crit_path(const tatum::TimingPath& path, ezgl::renderer* g); /* Draws critical path shown as flylines. Takes in start and end coordinates, time delay, & renderer.*/ void draw_flyline_timing_edge(ezgl::point2d start, ezgl::point2d end, float incr_delay, ezgl::renderer* g); diff --git a/vpr/src/draw/ui_setup.cpp b/vpr/src/draw/ui_setup.cpp index 902cecfaf60..9e57e881b50 100644 --- a/vpr/src/draw/ui_setup.cpp +++ b/vpr/src/draw/ui_setup.cpp @@ -23,12 +23,6 @@ # include "ezgl/application.hpp" # include "ezgl/graphics.hpp" -void on_window_destroy() { - if (g_vpr_ctx.server_ctx()) { - g_vpr_ctx.server_ctx()->server().stop(); - } -} - void basic_button_setup(ezgl::application* app) { //button to enter window_mode, created in main.ui GtkButton* window = (GtkButton*)app->get_object("Window"); @@ -48,8 +42,6 @@ void basic_button_setup(ezgl::application* app) { //combo box for search type, created in main.ui GObject* search_type = (GObject*)app->get_object("SearchType"); g_signal_connect(search_type, "changed", G_CALLBACK(search_type_changed), app); - - g_signal_connect(window, "destroy", G_CALLBACK(on_window_destroy), NULL); } /* diff --git a/vpr/src/draw/ui_setup.h b/vpr/src/draw/ui_setup.h index 1a111c73f9a..6a65d768ace 100644 --- a/vpr/src/draw/ui_setup.h +++ b/vpr/src/draw/ui_setup.h @@ -20,8 +20,6 @@ # include "ezgl/application.hpp" # include "ezgl/graphics.hpp" -void on_window_destroy(); - /** * @brief configures basic buttons * @@ -96,4 +94,4 @@ void show_widget(std::string widgetName, ezgl::application* app); #endif /* NO_GRAPHICS */ -#endif /* UISETUP_H */ \ No newline at end of file +#endif /* UISETUP_H */ diff --git a/vpr/src/server/server.cpp b/vpr/src/server/gateio.cpp similarity index 78% rename from vpr/src/server/server.cpp rename to vpr/src/server/gateio.cpp index cf779455b26..f80cbaa9494 100644 --- a/vpr/src/server/server.cpp +++ b/vpr/src/server/gateio.cpp @@ -1,4 +1,4 @@ -#include "server.h" +#include "gateio.h" #include "telegramparser.h" #include @@ -8,57 +8,39 @@ #include #include -Server::Server() -{ - m_isStarted.store(false); - m_isStopped.store(false); -} +namespace server { -Server::~Server() +GateIO::GateIO() { - if (m_debugLog) { - std::cout << "~~~ th=" << std::this_thread::get_id() << " ~Server()" << std::endl; - } - stop(); + m_isRunning.store(false); } -void Server::setPortNum(int portNum) +GateIO::~GateIO() { - std::unique_lock lock(m_portNumMutex); - m_portNum = portNum; + stop(); } -void Server::start() +void GateIO::start(int portNum) { - if (!m_isStarted.load()) { - if (m_debugLog) { - std::cout << "~~~ th=" << std::this_thread::get_id() << " starting server" << std::endl; - } - m_isStarted.store(true); - m_thread = std::thread(&Server::startListening, this); + if (!m_isRunning.load()) { + m_portNum = portNum; + std::cout << "th=" << std::this_thread::get_id() << " starting server" << std::endl; + m_isRunning.store(true); + m_thread = std::thread(&GateIO::startListening, this); } } -void Server::stop() +void GateIO::stop() { - if (!m_isStopped.load()) { - m_isStopped.store(true); - if (m_debugLog) { - std::cout << "~~~ th=" << std::this_thread::get_id() << " stopping server, is stopped=" << m_isStopped.load() << std::endl; - } + if (m_isRunning.load()) { + m_isRunning.store(false); if (m_thread.joinable()) { - if (m_debugLog) { - std::cout << "~~~ th=" << std::this_thread::get_id() << " join thread START" << std::endl; - } m_thread.join(); - if (m_debugLog) { - std::cout << "~~~ th=" << std::this_thread::get_id() << " join thread FINISHED" << std::endl; - } } } } -void Server::takeRecievedTasks(std::vector& tasks) +void GateIO::takeRecievedTasks(std::vector& tasks) { tasks.clear(); std::unique_lock lock(m_receivedTasksMutex); @@ -68,7 +50,7 @@ void Server::takeRecievedTasks(std::vector& tasks) std::swap(tasks, m_receivedTasks); } -void Server::addSendTasks(const std::vector& tasks) +void GateIO::addSendTasks(const std::vector& tasks) { std::unique_lock lock(m_sendTasksMutex); for (const Task& task: tasks) { @@ -77,7 +59,7 @@ void Server::addSendTasks(const std::vector& tasks) } } -void Server::startListening() +void GateIO::startListening() { int server_socket; int client_socket = -1; @@ -124,7 +106,7 @@ void Server::startListening() bool connectionProblemDetected = false; // Event loop - while(!m_isStopped.load()) { + while(m_isRunning.load()) { if (connectionProblemDetected || client_socket < 0) { int flags = fcntl(client_socket, F_GETFL, 0); fcntl(client_socket, F_SETFL, flags | O_NONBLOCK); @@ -193,9 +175,9 @@ void Server::startListening() std::string message{frame.to_string()}; std::cout << "Received: " << message << std::endl; - int jobId = telegramparser::extractJobId(message); - int cmd = telegramparser::extractCmd(message); - std::string options = telegramparser::extractOptions(message); + int jobId = TelegramParser::extractJobId(message); + int cmd = TelegramParser::extractCmd(message); + std::string options = TelegramParser::extractOptions(message); std::cout << "server: jobId=" << jobId << ", cmd=" << cmd << ", options=" << options << std::endl; if ((jobId != -1) && (cmd != -1)) { std::unique_lock lock(m_receivedTasksMutex); @@ -218,3 +200,5 @@ void Server::startListening() close(server_socket); } + +} // namespace server diff --git a/vpr/src/server/gateio.h b/vpr/src/server/gateio.h new file mode 100644 index 00000000000..a1d6c0a83c9 --- /dev/null +++ b/vpr/src/server/gateio.h @@ -0,0 +1,66 @@ +#ifndef GATEIO_H +#define GATEIO_H + +#include "task.h" +#include "telegrambuffer.h" + +#include +#include + +#include +#include +#include +#include +#include + +namespace server { + +/** + * @brief Implements the socket communication layer with the outside world. + * It begins listening on the specified port number for incoming client requests, + * collects and encapsulates them into tasks. + * The incoming tasks are extracted and handled by the top-level logic (TaskResolver). + * Once the tasks are resolved by the TaskResolver, they are returned + * to be sent back to the client as a response. + * + * Note: + * - gateio is not started automatically upon creation; you have to use the 'start' method with the port number. + * - The gateio runs in a separate thread to ensure smooth IO behavior. + * - The socket is initialized in a non-blocking mode to function properly in a multithreaded environment. +*/ +class GateIO +{ +public: + explicit GateIO(); + ~GateIO(); + + bool isRunning() const { return m_isRunning.load(); } + + void takeRecievedTasks(std::vector&); + void addSendTasks(const std::vector&); + + void start(int portNum); + void stop(); + +private: + int m_portNum = -1; + + std::atomic m_isRunning; // is true when started + + std::thread m_thread; // thread to execute socket IO work + + std::vector m_receivedTasks; // tasks from clinet (requests) + std::mutex m_receivedTasksMutex; + + std::vector m_sendTasks; // task to client (reponses) + std::mutex m_sendTasksMutex; + + TelegramBuffer m_telegramBuff; + + void startListening(); // thread worker function +}; + +} // namespace server + +#endif // GATEIO_H + diff --git a/vpr/src/server/gtkcomboboxhelper.cpp b/vpr/src/server/gtkcomboboxhelper.cpp index d53fb606f05..ac6d5de23ae 100644 --- a/vpr/src/server/gtkcomboboxhelper.cpp +++ b/vpr/src/server/gtkcomboboxhelper.cpp @@ -1,7 +1,12 @@ #include "gtkcomboboxhelper.h" #include -gint get_item_count(gpointer combo_box) { +namespace { + +/** + * @brief Helper function to retrieve the count of items in a GTK combobox. + */ +gint get_items_count(gpointer combo_box) { GtkComboBoxText* combo = GTK_COMBO_BOX_TEXT(combo_box); // Get the model of the combo box @@ -12,7 +17,13 @@ gint get_item_count(gpointer combo_box) { return count; } -gint get_item_index_by_text(gpointer combo_box, gchar* target_item) { +} // namespace + +/** + * @brief Helper function to retrieve the index of an item by its text. + * Returns -1 if the item with the specified text is absent. + */ +gint get_item_index_by_text(gpointer combo_box, const gchar* target_item) { gint result_index = -1; GtkComboBoxText* combo = GTK_COMBO_BOX_TEXT(combo_box); @@ -21,7 +32,7 @@ gint get_item_index_by_text(gpointer combo_box, gchar* target_item) { gchar* current_item_text = nullptr; - for (gint index=0; index -gint get_item_count(gpointer combo_box); +/** + * @brief Helper function to retrieve the index of an item by its text. + * Returns -1 if the item with the specified text is absent. + */ +gint get_item_index_by_text(gpointer combo_box, const gchar* target_item); -gint get_item_index_by_text(gpointer combo_box, gchar* target_item); - -#endif +#endif // GTKCOMBOBOXHELPER_H diff --git a/vpr/src/server/pathhelper.cpp b/vpr/src/server/pathhelper.cpp index 369569f18bd..5837cc57bf4 100644 --- a/vpr/src/server/pathhelper.cpp +++ b/vpr/src/server/pathhelper.cpp @@ -16,8 +16,13 @@ #include #include +namespace server { + namespace { +/** + * @brief helper function to calculate the setup critical path with specified parameters. + */ CritPathsResult generate_setup_timing_report(const SetupTimingInfo& timing_info, const AnalysisDelayCalculator& delay_calc, const t_analysis_opts& analysis_opts, bool is_flat) { auto& timing_ctx = g_vpr_ctx.timing(); auto& atom_ctx = g_vpr_ctx.atom(); @@ -33,6 +38,9 @@ CritPathsResult generate_setup_timing_report(const SetupTimingInfo& timing_info, return CritPathsResult{paths, ss.str()}; } +/** + * @brief helper function to calculate the hold critical path with specified parameters. + */ CritPathsResult generate_hold_timing_report(const HoldTimingInfo& timing_info, const AnalysisDelayCalculator& delay_calc, const t_analysis_opts& analysis_opts, bool is_flat) { auto& timing_ctx = g_vpr_ctx.timing(); auto& atom_ctx = g_vpr_ctx.atom(); @@ -50,11 +58,13 @@ CritPathsResult generate_hold_timing_report(const HoldTimingInfo& timing_info, c } // namespace +/** + * @brief Unified helper function to calculate the critical path with specified parameters. + */ CritPathsResult calcCriticalPath(const std::string& type, int critPathNum, e_timing_report_detail detailsLevel, bool is_flat_routing) { // shortcuts auto& atom_ctx = g_vpr_ctx.atom(); - auto& timing_ctx = g_vpr_ctx.timing(); //Load the net delays const Netlist<>& router_net_list = is_flat_routing ? (const Netlist<>&)g_vpr_ctx.atom().nlist : (const Netlist<>&)g_vpr_ctx.clustering().clb_nlist; @@ -83,3 +93,4 @@ CritPathsResult calcCriticalPath(const std::string& type, int critPathNum, e_tim return CritPathsResult{std::vector(), ""}; } +} // namespace server diff --git a/vpr/src/server/pathhelper.h b/vpr/src/server/pathhelper.h index 7fb48a49552..61c86295634 100644 --- a/vpr/src/server/pathhelper.h +++ b/vpr/src/server/pathhelper.h @@ -8,11 +8,24 @@ #include "tatum/report/TimingPath.hpp" #include "vpr_types.h" +namespace server { + +/** + * @brief Structure to retain the calculation result of the critical path. + * + * It contains the critical path list and the generated report as a string. +*/ struct CritPathsResult { + bool isValid() const { return !report.empty(); } std::vector paths; std::string report; }; +/** + * @brief Unified helper function to calculate the critical path with specified parameters. + */ CritPathsResult calcCriticalPath(const std::string& type, int critPathNum, e_timing_report_detail detailsLevel, bool is_flat_routing); -#endif +} // namespace server + +#endif // PATHHELPER_H diff --git a/vpr/src/server/server.h b/vpr/src/server/server.h deleted file mode 100644 index 64fce70494e..00000000000 --- a/vpr/src/server/server.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef SERVER_H -#define SERVER_H - -#include "task.h" -#include "telegrambuffer.h" - -#include -#include - -#include -#include -#include -#include -#include - -class Server -{ -public: - explicit Server(); - ~Server(); - - void setPortNum(int portNum); - - bool isStarted() const { return m_isStarted.load(); } - bool isStopped() const { return m_isStopped.load(); } - - void takeRecievedTasks(std::vector&); - void addSendTasks(const std::vector&); - - void start(); - void stop(); - -private: - bool m_debugLog = false; - int m_portNum = -1; - std::mutex m_portNumMutex; - - std::string m_pathsStr; - std::atomic m_isStarted; - std::atomic m_isStopped; - - std::thread m_thread; - std::mutex m_pathsMutex; - - std::vector m_receivedTasks; - std::mutex m_receivedTasksMutex; - - std::vector m_sendTasks; - std::mutex m_sendTasksMutex; - - TelegramBuffer m_telegramBuff; - - void startListening(); -}; - -#endif diff --git a/vpr/src/server/serverconsts.h b/vpr/src/server/serverconsts.h index 72cbdd41a6a..cc9b381f676 100644 --- a/vpr/src/server/serverconsts.h +++ b/vpr/src/server/serverconsts.h @@ -1,6 +1,8 @@ #ifndef SERVERCONSTS_H #define SERVERCONSTS_H +namespace server { + constexpr const char* KEY_JOB_ID = "JOB_ID"; constexpr const char* KEY_CMD = "CMD"; constexpr const char* KEY_OPTIONS = "OPTIONS"; @@ -20,4 +22,6 @@ enum CMD { CMD_DRAW_PATH_ID }; +} // namespace server + #endif diff --git a/vpr/src/server/serverupdate.cpp b/vpr/src/server/serverupdate.cpp new file mode 100644 index 00000000000..e084cdf9cfb --- /dev/null +++ b/vpr/src/server/serverupdate.cpp @@ -0,0 +1,43 @@ +#include "serverupdate.h" +#include "gateio.h" +#include "taskresolver.h" +#include "globals.h" +#include "ezgl/application.hpp" + +#ifndef NO_GRAPHICS + +namespace server { + +gboolean update(gpointer data) { + bool isRunning = g_vpr_ctx.server().gateIO().isRunning(); + if (isRunning) { + // shortcuts + ezgl::application* app = static_cast(data); + GateIO& gate_io = g_vpr_ctx.mutable_server().mutable_gateIO(); + TaskResolver& task_resolver = g_vpr_ctx.mutable_server().mutable_task_resolver(); + + std::vector tasksBuff; + + gate_io.takeRecievedTasks(tasksBuff); + task_resolver.addTasks(tasksBuff); + + bool process_task = task_resolver.update(app); + + tasksBuff.clear(); + task_resolver.takeFinished(tasksBuff); + + gate_io.addSendTasks(tasksBuff); + + // Call the redraw method of the application if any of task was processed + if (process_task) { + app->refresh_drawing(); + } + } + + // Return TRUE to keep the timer running, or FALSE to stop it + return isRunning; +} + +} // namespace server + +#endif // NO_GRAPHICS \ No newline at end of file diff --git a/vpr/src/server/serverupdate.h b/vpr/src/server/serverupdate.h new file mode 100644 index 00000000000..17301988b45 --- /dev/null +++ b/vpr/src/server/serverupdate.h @@ -0,0 +1,23 @@ +#ifndef SERVERUPDATE_H +#define SERVERUPDATE_H + +#include + +#ifndef NO_GRAPHICS + +namespace server { + +/** + * @brief Main server update callback. + * + * This function is a periodic callback invoked at a fixed interval to manage and handle incoming client requests. + * It acts as the central control point for processing client interactions and orchestrating server-side operations + * within the specified time intervals. + */ +gboolean update(gpointer); + +} // namespace server + +#endif // NO_GRAPHICS + +#endif // SERVERUPDATE_H diff --git a/vpr/src/server/task.h b/vpr/src/server/task.h index d90980f90fc..d4e1464c1ce 100644 --- a/vpr/src/server/task.h +++ b/vpr/src/server/task.h @@ -7,6 +7,14 @@ #include "serverconsts.h" +namespace server { + +/** + * @brief Implements the server task. + * + * This structure aids in encapsulating the client request, request result, and result status. + * It generates a JSON data structure to be sent back to the client as a response. + */ class Task { public: Task(int jobId, int cmd, const std::string& options = ""): @@ -66,4 +74,6 @@ class Task { bool m_hasError = false; }; -#endif +} // namespace server + +#endif // TASK_H diff --git a/vpr/src/server/taskresolver.cpp b/vpr/src/server/taskresolver.cpp index 2cec2a88f53..1c986b062ac 100644 --- a/vpr/src/server/taskresolver.cpp +++ b/vpr/src/server/taskresolver.cpp @@ -7,21 +7,18 @@ #include "gtkcomboboxhelper.h" #include -void TaskResolver::addTasks(const std::vector& tasks) -{ - for (const Task& task: tasks) { - addTask(task); - } -} +namespace server { void TaskResolver::addTask(Task task) { + // pre-process task before adding, where we could quickly detect failure scenario for (auto& t: m_tasks) { if (t.cmd() == task.cmd()) { if (t.options() == task.options()) { std::string msg = "similar task is already in execution, reject new task: " + t.info()+ " and waiting for old task: " + task.info() + " execution"; task.fail(msg); } else { + // case when task has same jobId but different options if (task.jobId() > t.jobId()) { std::string msg = "old task: " + t.info() + " is overriden by a new task: " + task.info(); t.fail(msg); @@ -29,9 +26,18 @@ void TaskResolver::addTask(Task task) } } } + + // add task m_tasks.push_back(std::move(task)); } +void TaskResolver::addTasks(const std::vector& tasks) +{ + for (const Task& task: tasks) { + addTask(task); + } +} + void TaskResolver::takeFinished(std::vector& result) { for (auto it=m_tasks.begin(); it != m_tasks.end();) { @@ -63,76 +69,98 @@ e_timing_report_detail TaskResolver::getDetailsLevelEnum(const std::string& path bool TaskResolver::update(ezgl::application* app) { - bool process_task = false; - const ServerContextPtr& server_ctx = g_vpr_ctx.server_ctx(); - if (!server_ctx) { - return false; - } + bool has_processed_task = false; for (auto& task: m_tasks) { if (!task.isFinished()) { - process_task = true; - TelegramOptions options{task.options()}; - if (task.cmd() == CMD_GET_PATH_LIST_ID) { - options.validateNamePresence({OPTION_PATH_NUM, OPTION_PATH_TYPE, OPTION_DETAILS_LEVEL, OPTION_IS_FLOAT_ROUTING}); - int nCriticalPathNum = options.getInt(OPTION_PATH_NUM, 1); - std::string pathType = options.getString(OPTION_PATH_TYPE); - std::string detailsLevel = options.getString(OPTION_DETAILS_LEVEL); - bool isFlat = options.getBool(OPTION_IS_FLOAT_ROUTING, false); - if (!options.hasErrors()) { - server_ctx->set_crit_path_index(-1); // reset selection if path list options has changed - - server_ctx->set_path_type(pathType); - server_ctx->set_critical_path_num(nCriticalPathNum); - CritPathsResult crit_paths_result = calcCriticalPath(pathType, nCriticalPathNum, getDetailsLevelEnum(detailsLevel), isFlat); - server_ctx->set_crit_paths(crit_paths_result.paths); - if (crit_paths_result.report.empty()) { - std::string msg{"Critical paths report is empty"}; - std::cerr << msg << std::endl; - task.fail(msg); - } - - if (!task.hasError()) { - std::string msg{crit_paths_result.report}; - task.success(msg); - } - } else { - std::string msg{"options errors in get crit path list telegram: " + options.errorsStr()}; - std::cerr << msg << std::endl; - task.fail(msg); - } - } else if (task.cmd() == CMD_DRAW_PATH_ID) { - options.validateNamePresence({OPTION_PATH_INDEX, OPTION_HIGHTLIGHT_MODE}); - int pathIndex = options.getInt(OPTION_PATH_INDEX, -1); - std::string highLightMode = options.getString(OPTION_HIGHTLIGHT_MODE); - if (!options.hasErrors()) { - if ((pathIndex >= 0) && (pathIndex < static_cast(server_ctx->crit_paths().size()))) { - server_ctx->set_crit_path_index(pathIndex); - - // update gtk UI - GtkComboBox* toggle_crit_path = GTK_COMBO_BOX(app->get_object("ToggleCritPath")); - gint highLightModeIndex = get_item_index_by_text(toggle_crit_path, highLightMode.data()); - if (highLightModeIndex != -1) { - gtk_combo_box_set_active(toggle_crit_path, highLightModeIndex); - task.success(); - } else { - std::string msg{"cannot find ToggleCritPath qcombobox index for item " + highLightMode}; - std::cerr << msg << std::endl; - task.fail(msg); - } - } else { - std::string msg{"selectedIndex=" + std::to_string(pathIndex) + " is out of range [0-" + std::to_string(static_cast(server_ctx->crit_paths().size())-1) + "]"}; - std::cerr << msg << std::endl; - task.fail(msg); - } - } else { - std::string msg{"options errors in highlight crit path telegram: " + options.errorsStr()}; - std::cerr << msg << std::endl; - task.fail(msg); + switch(task.cmd()) { + case CMD_GET_PATH_LIST_ID: { + processGetPathListTask(app, task); + has_processed_task = true; + break; + } + case CMD_DRAW_PATH_ID: { + processDrawCriticalPathTask(app, task); + has_processed_task = true; + break; } - } + } } } - return process_task; + return has_processed_task; +} + +void TaskResolver::processGetPathListTask(ezgl::application* app, Task& task) +{ + TelegramOptions options{task.options(), {OPTION_PATH_NUM, OPTION_PATH_TYPE, OPTION_DETAILS_LEVEL, OPTION_IS_FLOAT_ROUTING}}; + if (!options.hasErrors()) { + ServerContext& server_ctx = g_vpr_ctx.mutable_server(); // shortcut + + server_ctx.set_crit_path_index(-1); // reset selection if path list options has changed + + // read options + const int nCriticalPathNum = options.getInt(OPTION_PATH_NUM, 1); + const std::string pathType = options.getString(OPTION_PATH_TYPE); + const std::string detailsLevel = options.getString(OPTION_DETAILS_LEVEL); + const bool isFlat = options.getBool(OPTION_IS_FLOAT_ROUTING, false); + + // calculate critical path depending on options and store result in server context + CritPathsResult crit_paths_result = calcCriticalPath(pathType, nCriticalPathNum, getDetailsLevelEnum(detailsLevel), isFlat); + + // setup context + server_ctx.set_path_type(pathType); + server_ctx.set_critical_path_num(nCriticalPathNum); + server_ctx.set_crit_paths(crit_paths_result.paths); + + if (crit_paths_result.isValid()) { + std::string msg{crit_paths_result.report}; + task.success(msg); + } else { + std::string msg{"Critical paths report is empty"}; + std::cerr << msg << std::endl; + task.fail(msg); + } + } else { + std::string msg{"options errors in get crit path list telegram: " + options.errorsStr()}; + std::cerr << msg << std::endl; + task.fail(msg); + } +} + +void TaskResolver::processDrawCriticalPathTask(ezgl::application* app, Task& task) +{ + TelegramOptions options{task.options(), {OPTION_PATH_INDEX, OPTION_HIGHTLIGHT_MODE}}; + if (!options.hasErrors()) { + ServerContext& server_ctx = g_vpr_ctx.mutable_server(); // shortcut + + const int pathIndex = options.getInt(OPTION_PATH_INDEX, -1); + const std::string highLightMode = options.getString(OPTION_HIGHTLIGHT_MODE); + + if ((pathIndex >= 0) && (pathIndex < static_cast(server_ctx.crit_paths().size()))) { + // set critical path index for rendering + server_ctx.set_crit_path_index(pathIndex); + + // update gtk UI + GtkComboBox* toggle_crit_path = GTK_COMBO_BOX(app->get_object("ToggleCritPath")); + gint highLightModeIndex = get_item_index_by_text(toggle_crit_path, highLightMode.c_str()); + if (highLightModeIndex != -1) { + gtk_combo_box_set_active(toggle_crit_path, highLightModeIndex); + task.success(); + } else { + std::string msg{"cannot find ToggleCritPath qcombobox index for item " + highLightMode}; + std::cerr << msg << std::endl; + task.fail(msg); + } + } else { + std::string msg{"selectedIndex=" + std::to_string(pathIndex) + " is out of range [0-" + std::to_string(static_cast(server_ctx.crit_paths().size())-1) + "]"}; + std::cerr << msg << std::endl; + task.fail(msg); + } + } else { + std::string msg{"options errors in highlight crit path telegram: " + options.errorsStr()}; + std::cerr << msg << std::endl; + task.fail(msg); + } } +} // namespace server diff --git a/vpr/src/server/taskresolver.h b/vpr/src/server/taskresolver.h index ddfd99bfb61..d4a6a7f7ef1 100644 --- a/vpr/src/server/taskresolver.h +++ b/vpr/src/server/taskresolver.h @@ -10,6 +10,14 @@ namespace ezgl { class application; } +namespace server { + +/** + * @brief Resolve server task. + * + * Process and resolve server task, store result and status for processed task. +*/ + class TaskResolver { public: TaskResolver()=default; @@ -17,10 +25,14 @@ class TaskResolver { int tasksNum() const { return m_tasks.size(); } - void addTasks(const std::vector&); + /* add tasks to process */ void addTask(Task); + void addTasks(const std::vector&); + /* process tasks */ bool update(ezgl::application*); + + /* extract finished tasks */ void takeFinished(std::vector&); const std::vector& tasks() const { return m_tasks; } @@ -28,7 +40,12 @@ class TaskResolver { private: std::vector m_tasks; - e_timing_report_detail getDetailsLevelEnum(const std::string& pathDetailsLevelStr) const ; + void processGetPathListTask(ezgl::application*, Task&); + void processDrawCriticalPathTask(ezgl::application*, Task&); + + e_timing_report_detail getDetailsLevelEnum(const std::string& pathDetailsLevelStr) const; }; -#endif +} // namespace server + +#endif // TASKRESOLVER_H diff --git a/vpr/src/server/telegrambuffer.cpp b/vpr/src/server/telegrambuffer.cpp index d101b6ee77d..72fcc270b89 100644 --- a/vpr/src/server/telegrambuffer.cpp +++ b/vpr/src/server/telegrambuffer.cpp @@ -1,6 +1,8 @@ #include "telegrambuffer.h" #include "serverconsts.h" +namespace server { + void TelegramBuffer::append(const ByteArray& bytes) { m_rawBuffer.append(bytes); @@ -23,3 +25,5 @@ std::vector TelegramBuffer::takeFrames() std::swap(m_rawBuffer, candidate); return result; } + +} // namespace server diff --git a/vpr/src/server/telegrambuffer.h b/vpr/src/server/telegrambuffer.h index 2bcb48e59f9..d5af4a1b059 100644 --- a/vpr/src/server/telegrambuffer.h +++ b/vpr/src/server/telegrambuffer.h @@ -5,6 +5,11 @@ #include #include +namespace server { + +/** + * @brief Implements dynamic bytes array with simple interface. +*/ class ByteArray { public: static const std::size_t DEFAULT_SIZE_HINT = 1024; @@ -39,6 +44,11 @@ class ByteArray { std::vector m_data; }; +/** + * @brief Implements Telegram Buffer as a wrapper over BytesArray + * + * It aggregates received bytes and return only well filled frames, separated by telegram delimerer byte. +*/ class TelegramBuffer { static const std::size_t DEFAULT_SIZE_HINT = 1024; @@ -57,4 +67,6 @@ class TelegramBuffer ByteArray m_rawBuffer; }; -#endif +} // namespace server + +#endif // TELEGRAMBUFFER_H diff --git a/vpr/src/server/telegramoptions.h b/vpr/src/server/telegramoptions.h index c3dae3ffd3c..b643edabc22 100644 --- a/vpr/src/server/telegramoptions.h +++ b/vpr/src/server/telegramoptions.h @@ -7,6 +7,16 @@ #include #include +namespace server { + +/** + * @brief Option class Parser + * + * Parse the string of options in the format "TYPE:KEY1:VALUE1;TYPE:KEY2:VALUE2", + * for example "int:path_num:11;string:path_type:debug;int:details_level:3;bool:is_flat_routing:0". + * It provides a simple interface to check value presence and access them. +*/ + class TelegramOptions { private: enum { @@ -22,14 +32,15 @@ class TelegramOptions { }; public: - TelegramOptions(const std::string& data) { + TelegramOptions(const std::string& data, const std::vector& expectedKeys) { + // parse data string std::vector options = splitString(data, ';'); for (const std::string& optionStr: options) { std::vector fragments = splitString(optionStr, ':'); if (fragments.size() == TOTAL_INDEXES_NUM) { std::string name = fragments[INDEX_NAME]; Option option{fragments[INDEX_TYPE], fragments[INDEX_VALUE]}; - if (validateType(option.type)) { + if (isDataTypeSupported(option.type)) { m_options[name] = option; } else { m_errors.emplace_back("bad type for option [" + optionStr + "]"); @@ -38,23 +49,15 @@ class TelegramOptions { m_errors.emplace_back("bad option [" + optionStr + "]"); } } + + // check keys presense + checkKeysPresence(expectedKeys); } ~TelegramOptions() {} bool hasErrors() const { return !m_errors.empty(); } - bool validateNamePresence(const std::vector& names) { - bool result = true; - for (const std::string& name: names) { - if (m_options.find(name) == m_options.end()) { - m_errors.emplace_back("cannot find required option " + name); - result = false; - } - } - return result; - } - std::string getString(const std::string& name) { std::string result; if (auto it = m_options.find(name); it != m_options.end()) { @@ -109,10 +112,23 @@ class TelegramOptions { return tokens; } - bool validateType(const std::string& type) { + bool isDataTypeSupported(const std::string& type) { static std::set supportedTypes{"int", "string", "bool"}; return supportedTypes.count(type) != 0; } + + bool checkKeysPresence(const std::vector& keys) { + bool result = true; + for (const std::string& key: keys) { + if (m_options.find(key) == m_options.end()) { + m_errors.emplace_back("cannot find required option " + key); + result = false; + } + } + return result; + } }; -#endif +} // namespace server + +#endif // TELEGRAMOPTIONS_H diff --git a/vpr/src/server/telegramparser.cpp b/vpr/src/server/telegramparser.cpp index 5736577d093..fc6e32b1495 100644 --- a/vpr/src/server/telegramparser.cpp +++ b/vpr/src/server/telegramparser.cpp @@ -2,9 +2,9 @@ #include -namespace telegramparser { +namespace server { -int extractJobId(const std::string& message) +int TelegramParser::extractJobId(const std::string& message) { static std::regex pattern("\"JOB_ID\":\"(\\d+)\""); std::smatch match; @@ -16,7 +16,7 @@ int extractJobId(const std::string& message) return -1; } -int extractCmd(const std::string& message) +int TelegramParser::extractCmd(const std::string& message) { static std::regex pattern("\"CMD\":\"(\\d+)\""); std::smatch match; @@ -28,7 +28,7 @@ int extractCmd(const std::string& message) return -1; } -std::string extractOptions(const std::string& message) +std::string TelegramParser::extractOptions(const std::string& message) { static std::regex pattern("\"OPTIONS\":\"(.*?)\""); std::smatch match; @@ -40,4 +40,4 @@ std::string extractOptions(const std::string& message) return ""; } -} // telegramparser +} // namespace server diff --git a/vpr/src/server/telegramparser.h b/vpr/src/server/telegramparser.h index 23fccd922f2..0e6d589f265 100644 --- a/vpr/src/server/telegramparser.h +++ b/vpr/src/server/telegramparser.h @@ -3,12 +3,23 @@ #include -namespace telegramparser { +namespace server { -int extractJobId(const std::string& message); -int extractCmd(const std::string& message); -std::string extractOptions(const std::string& message); +/** + * @brief Dummy JSON parser using regular expressions. + * + * This module provides helper methods to extract values such as "id", "cmd", or "options" + * from a JSON schema structured as follows: {id: num, cmd: enum, options: string}. + * The regular expressions implemented in this parser aim to retrieve specific fields' values + * from a given JSON structure, facilitating data extraction and manipulation. + */ +class TelegramParser { +public: + static int extractJobId(const std::string& message); + static int extractCmd(const std::string& message); + static std::string extractOptions(const std::string& message); +}; -} // telegramparser +} // namespace server -#endif +#endif // TELEGRAMPARSER_H From ce2edb542c0634bf878b24c3b1d0c570c6960256 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Thu, 11 Jan 2024 15:43:30 +0200 Subject: [PATCH 23/31] reset selected crit path to render if requested to render critical path with index -1 --- vpr/src/server/taskresolver.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/vpr/src/server/taskresolver.cpp b/vpr/src/server/taskresolver.cpp index 1c986b062ac..6d5fdc5cb8a 100644 --- a/vpr/src/server/taskresolver.cpp +++ b/vpr/src/server/taskresolver.cpp @@ -136,7 +136,10 @@ void TaskResolver::processDrawCriticalPathTask(ezgl::application* app, Task& tas const int pathIndex = options.getInt(OPTION_PATH_INDEX, -1); const std::string highLightMode = options.getString(OPTION_HIGHTLIGHT_MODE); - if ((pathIndex >= 0) && (pathIndex < static_cast(server_ctx.crit_paths().size()))) { + if (pathIndex == -1) { + server_ctx.set_crit_path_index(-1); // clear selection + task.success(); + } else if ((pathIndex >= 0) && (pathIndex < static_cast(server_ctx.crit_paths().size()))) { // set critical path index for rendering server_ctx.set_crit_path_index(pathIndex); From 6c2ac9dd9cb0b7cdbf6adecf4081e520f2f04c5b Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Wed, 31 Jan 2024 19:42:03 +0200 Subject: [PATCH 24/31] sync changes from 474-ipa-enhancement-selectionrendering-multiple-paths-and-segments branch --- vpr/src/base/vpr_context.h | 18 ++- vpr/src/draw/draw_basic.cpp | 120 +++++++++++++++--- vpr/src/draw/draw_basic.h | 8 +- .../{serverconsts.h => commconstants.h} | 18 ++- vpr/src/server/convertutils.cpp | 24 ++++ vpr/src/server/convertutils.h | 9 ++ vpr/src/server/gateio.cpp | 22 ++-- vpr/src/server/gateio.h | 2 +- vpr/src/server/pathhelper.cpp | 14 +- vpr/src/server/pathhelper.h | 2 +- vpr/src/server/task.h | 12 +- vpr/src/server/taskresolver.cpp | 53 ++++---- vpr/src/server/telegrambuffer.cpp | 8 +- vpr/src/server/telegrambuffer.h | 34 +++-- vpr/src/server/telegramoptions.h | 52 ++++++-- vpr/src/server/telegramparser.cpp | 90 +++++++++---- vpr/src/server/telegramparser.h | 24 ++-- 17 files changed, 353 insertions(+), 157 deletions(-) rename vpr/src/server/{serverconsts.h => commconstants.h} (60%) create mode 100644 vpr/src/server/convertutils.cpp create mode 100644 vpr/src/server/convertutils.h diff --git a/vpr/src/base/vpr_context.h b/vpr/src/base/vpr_context.h index 252d915fea3..4a712a823cd 100644 --- a/vpr/src/base/vpr_context.h +++ b/vpr/src/base/vpr_context.h @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include "vpr_types.h" @@ -583,8 +585,11 @@ class ServerContext : public Context { void set_path_type(const std::string& path_type) { path_type_ = path_type; } const std::string& path_type() const { return path_type_; } - void set_crit_path_index(int crit_path_index) { crit_path_index_ = crit_path_index; } - int crit_path_index() const { return crit_path_index_; } + void set_crit_path_elements(std::map> crit_path_element_indexes) { crit_path_element_indexes_ = crit_path_element_indexes; } + std::map> crit_path_element_indexes() const { return crit_path_element_indexes_; } + + void set_draw_crit_path_contour(bool draw_crit_path_contour) { draw_crit_path_contour_ = draw_crit_path_contour; } + bool draw_crit_path_contour() const { return draw_crit_path_contour_; } private: server::GateIO gate_io_; @@ -616,11 +621,14 @@ class ServerContext : public Context { std::string path_type_ = "setup"; /** - * @brief Stores the last selected critical path index. + * @brief Stores the selected critical path elements. * - * This value is used to render the selected critical path upon client request. + * This value is used to render the selected critical path elements upon client request. + * The std::map key plays role of path index, where the element indexes are stored as std::set. */ - int crit_path_index_ = 0; + std::map> crit_path_element_indexes_; + + bool draw_crit_path_contour_ = false; }; /** diff --git a/vpr/src/draw/draw_basic.cpp b/vpr/src/draw/draw_basic.cpp index 54e5b156502..c52f5e9dffe 100644 --- a/vpr/src/draw/draw_basic.cpp +++ b/vpr/src/draw/draw_basic.cpp @@ -947,21 +947,8 @@ void draw_crit_path(ezgl::renderer* g) { if (g_vpr_ctx.server().gateIO().isRunning()) { const ServerContext& server_ctx = g_vpr_ctx.server(); // shortcut - const auto& paths = server_ctx.crit_paths(); - const int index = server_ctx.crit_path_index(); // shortcut - - /* check critical path index bounds and render critical path, - * otherwise, resets selected critical path index with value -1. - */ - if ((index >= 0) && (index < static_cast(paths.size()))) { - tatum::TimingPath path = paths[index]; - draw_concrete_crit_path(path, g); - } else { - if (index != -1) { - g_vpr_ctx.mutable_server().set_crit_path_index(-1); - } - } + draw_crit_path_elements(server_ctx.crit_paths(), server_ctx.crit_path_element_indexes(), g); } else { tatum::TimingPathCollector path_collector; @@ -1023,8 +1010,103 @@ void draw_concrete_crit_path(const tatum::TimingPath& path, ezgl::renderer* g) { } } +void draw_concrete_crit_path_DEBUG(const tatum::TimingPath& path, ezgl::renderer* g) { + t_draw_state* draw_state = get_draw_state_vars(); + + //Walk through the timing path drawing each edge + tatum::NodeId prev_node; + float prev_arr_time = std::numeric_limits::quiet_NaN(); + int i = 0; + for (tatum::TimingPathElem elem : path.data_arrival_path().elements()) { + tatum::NodeId node = elem.node(); + float arr_time = elem.tag().time(); + if (prev_node) { + //We draw each 'edge' in a different color, this allows users to identify the stages and + //any routing which corresponds to the edge + // + ezgl::color color{0,0,0,40}; + + float delay = arr_time - prev_arr_time; + if (draw_state->show_crit_path == DRAW_CRIT_PATH_FLYLINES + || draw_state->show_crit_path + == DRAW_CRIT_PATH_FLYLINES_DELAYS) { + g->set_color(color); + g->set_line_dash(ezgl::line_dash::none); + g->set_line_width(1); + draw_flyline_timing_edge(tnode_draw_coord(prev_node), + tnode_draw_coord(node), delay, g, /*skipDrawDelays*/true); + } else { + VTR_ASSERT(draw_state->show_crit_path != DRAW_NO_CRIT_PATH); + + //Draw the routed version of the timing edge + draw_routed_timing_edge(prev_node, node, delay, color, g, /*skipDrawDelays*/true); + } + } + prev_node = node; + prev_arr_time = arr_time; + } +} + +void draw_crit_path_elements(const std::vector& paths, const std::map>& crit_path_element_indexes, ezgl::renderer* g) +{ + t_draw_state* draw_state = get_draw_state_vars(); + + for (const auto& [pathIndex, elementIndexes]: crit_path_element_indexes) { + if (pathIndex < paths.size()) { + const tatum::TimingPath& path = paths[pathIndex]; + if (g_vpr_ctx.server().draw_crit_path_contour()) { + draw_concrete_crit_path_DEBUG(path, g); // debug + } + + //Walk through the timing path drawing each edge + tatum::NodeId prev_node; + float prev_arr_time = std::numeric_limits::quiet_NaN(); + int i = 0; + int elementCounter = 0; + for (tatum::TimingPathElem elem: path.data_arrival_path().elements()) { + bool drawCurrentElement = elementIndexes.find(elementCounter) != elementIndexes.end(); + + // draw element + tatum::NodeId node = elem.node(); + float arr_time = elem.tag().time(); + + //We draw each 'edge' in a different color, this allows users to identify the stages and + //any routing which corresponds to the edge + // + //We pick colors from the kelly max-contrast list, for long paths there may be repeats + ezgl::color color = kelly_max_contrast_colors[i++ + % kelly_max_contrast_colors.size()]; + + if (prev_node && drawCurrentElement) { + float delay = arr_time - prev_arr_time; + if (draw_state->show_crit_path == DRAW_CRIT_PATH_FLYLINES + || draw_state->show_crit_path + == DRAW_CRIT_PATH_FLYLINES_DELAYS) { + g->set_color(color); + g->set_line_dash(ezgl::line_dash::none); + g->set_line_width(4); + draw_flyline_timing_edge(tnode_draw_coord(prev_node), + tnode_draw_coord(node), delay, g); + } else { + VTR_ASSERT(draw_state->show_crit_path != DRAW_NO_CRIT_PATH); + + //Draw the routed version of the timing edge + draw_routed_timing_edge(prev_node, node, delay, color, g); + } + } + + prev_node = node; + prev_arr_time = arr_time; + // end draw element + + elementCounter++; + } + } + } +} + //Draws critical path shown as flylines. -void draw_flyline_timing_edge(ezgl::point2d start, ezgl::point2d end, float incr_delay, ezgl::renderer* g) { +void draw_flyline_timing_edge(ezgl::point2d start, ezgl::point2d end, float incr_delay, ezgl::renderer* g, bool skipDrawDelays) { g->draw_line(start, end); draw_triangle_along_line(g, start, end, 0.95, 40 * DEFAULT_ARROW_SIZE); draw_triangle_along_line(g, start, end, 0.05, 40 * DEFAULT_ARROW_SIZE); @@ -1032,7 +1114,8 @@ void draw_flyline_timing_edge(ezgl::point2d start, ezgl::point2d end, float incr bool draw_delays = (get_draw_state_vars()->show_crit_path == DRAW_CRIT_PATH_FLYLINES_DELAYS || get_draw_state_vars()->show_crit_path - == DRAW_CRIT_PATH_ROUTING_DELAYS); + == DRAW_CRIT_PATH_ROUTING_DELAYS) + && !skipDrawDelays; if (draw_delays) { //Determine the strict bounding box based on the lines start/end float min_x = std::min(start.x, end.x); @@ -1099,7 +1182,8 @@ void draw_routed_timing_edge(tatum::NodeId start_tnode, tatum::NodeId end_tnode, float incr_delay, ezgl::color color, - ezgl::renderer* g) { + ezgl::renderer* g, + bool skipDrawDelays) { draw_routed_timing_edge_connection(start_tnode, end_tnode, color, g); g->set_line_dash(ezgl::line_dash::asymmetric_5_3); @@ -1108,7 +1192,7 @@ void draw_routed_timing_edge(tatum::NodeId start_tnode, draw_flyline_timing_edge((ezgl::point2d)tnode_draw_coord(start_tnode), (ezgl::point2d)tnode_draw_coord(end_tnode), (float)incr_delay, - (ezgl::renderer*)g); + (ezgl::renderer*)g, skipDrawDelays); g->set_line_width(0); g->set_line_dash(ezgl::line_dash::none); diff --git a/vpr/src/draw/draw_basic.h b/vpr/src/draw/draw_basic.h index 31f9342d9ae..196755ecf3c 100644 --- a/vpr/src/draw/draw_basic.h +++ b/vpr/src/draw/draw_basic.h @@ -89,9 +89,12 @@ void draw_crit_path(ezgl::renderer* g); * b) during routing, critical path is shown by both flylines and routed net connections. */ void draw_concrete_crit_path(const tatum::TimingPath& path, ezgl::renderer* g); +void draw_concrete_crit_path_DEBUG(const tatum::TimingPath& path, ezgl::renderer* g); + +void draw_crit_path_elements(const std::vector& paths, const std::map>& indexes, ezgl::renderer* g); /* Draws critical path shown as flylines. Takes in start and end coordinates, time delay, & renderer.*/ -void draw_flyline_timing_edge(ezgl::point2d start, ezgl::point2d end, float incr_delay, ezgl::renderer* g); +void draw_flyline_timing_edge(ezgl::point2d start, ezgl::point2d end, float incr_delay, ezgl::renderer* g, bool skipDrawDelays=false); /* Draws critical path shown by both flylines and routed net connections. Takes in start and end nodes, * time delay, colour, & renderer. @@ -100,7 +103,8 @@ void draw_routed_timing_edge(tatum::NodeId start_tnode, tatum::NodeId end_tnode, float incr_delay, ezgl::color color, - ezgl::renderer* g); + ezgl::renderer* g, + bool skipDrawDelays=false); /* Collects all the drawing locations associated with the timing edge between start and end. * Only traces interconnect edges in detail, and treats all others as flylines. diff --git a/vpr/src/server/serverconsts.h b/vpr/src/server/commconstants.h similarity index 60% rename from vpr/src/server/serverconsts.h rename to vpr/src/server/commconstants.h index cc9b381f676..43c78704b44 100644 --- a/vpr/src/server/serverconsts.h +++ b/vpr/src/server/commconstants.h @@ -1,7 +1,7 @@ -#ifndef SERVERCONSTS_H -#define SERVERCONSTS_H +#ifndef COMMCONSTS_H +#define COMMCONSTS_H -namespace server { +namespace comm { constexpr const char* KEY_JOB_ID = "JOB_ID"; constexpr const char* KEY_CMD = "CMD"; @@ -14,14 +14,22 @@ constexpr const char* OPTION_PATH_NUM = "path_num"; constexpr const char* OPTION_PATH_TYPE = "path_type"; constexpr const char* OPTION_DETAILS_LEVEL = "details_level"; constexpr const char* OPTION_IS_FLOAT_ROUTING = "is_flat_routing"; -constexpr const char* OPTION_PATH_INDEX = "path_index"; +constexpr const char* OPTION_PATH_ELEMENTS = "path_elements"; constexpr const char* OPTION_HIGHTLIGHT_MODE = "hight_light_mode"; +constexpr const char* OPTION_DRAW_PATH_CONTOUR = "draw_path_contour"; + +constexpr const char* CRITICAL_PATH_ITEMS_SELECTION_NONE = "none"; + +// please don't change values as they are involved in socket communication +constexpr const char* KEY_SETUP_PATH_LIST = "setup"; +constexpr const char* KEY_HOLD_PATH_LIST = "hold"; +// enum CMD { CMD_GET_PATH_LIST_ID=0, CMD_DRAW_PATH_ID }; -} // namespace server +} // namespace comm #endif diff --git a/vpr/src/server/convertutils.cpp b/vpr/src/server/convertutils.cpp new file mode 100644 index 00000000000..51056eca5ec --- /dev/null +++ b/vpr/src/server/convertutils.cpp @@ -0,0 +1,24 @@ +#include "convertutils.h" + +std::optional tryConvertToInt(const std::string& str) +{ + std::optional result; + + char* endptr; // To store the end of the conversion + errno = 0; // Set errno to 0 before the call + int candidate = std::strtol(str.c_str(), &endptr, 10); + + // Check for conversion errors + if (errno != 0) { + return result; + } + + // Check if there were any non-numeric characters + if (endptr == str.c_str()) { + return result; + } + + result = candidate; + return result; +} + diff --git a/vpr/src/server/convertutils.h b/vpr/src/server/convertutils.h new file mode 100644 index 00000000000..0647449c6a8 --- /dev/null +++ b/vpr/src/server/convertutils.h @@ -0,0 +1,9 @@ +#ifndef CONVERTUTILS_H +#define CONVERTUTILS_H + +#include +#include + +std::optional tryConvertToInt(const std::string&); + +#endif // CONVERTUTILS_H diff --git a/vpr/src/server/gateio.cpp b/vpr/src/server/gateio.cpp index f80cbaa9494..dca29dfe89a 100644 --- a/vpr/src/server/gateio.cpp +++ b/vpr/src/server/gateio.cpp @@ -123,7 +123,7 @@ void GateIO::startListening() std::unique_lock lock(m_sendTasksMutex); for (const Task& task: m_sendTasks) { std::string response = task.toJsonStr(); - response += static_cast(TELEGRAM_FRAME_DELIMETER); + response += static_cast(comm::TELEGRAM_FRAME_DELIMETER); std::cout << "sending" << response << "to client" << std::endl; send(client_socket, response.c_str(), response.length(), 0); } @@ -155,10 +155,11 @@ void GateIO::startListening() } else { if (FD_ISSET(client_socket, &readfds)) { // Data is available; proceed with recv - char data[1024]; + char data[2048]; + std::memset(data, 0, sizeof(data)); ssize_t bytes_received = recv(client_socket, data, sizeof(data), 0); if (bytes_received > 0) { - m_telegramBuff.append(ByteArray{data}); + m_telegramBuff.append(comm::ByteArray{data, bytes_received}); } else if (bytes_received == 0) { std::cout << "Connection closed\n"; connectionProblemDetected = true; @@ -170,18 +171,17 @@ void GateIO::startListening() } auto frames = m_telegramBuff.takeFrames(); - for (const ByteArray& frame: frames) { + for (const comm::ByteArray& frame: frames) { // Process received data std::string message{frame.to_string()}; std::cout << "Received: " << message << std::endl; - - int jobId = TelegramParser::extractJobId(message); - int cmd = TelegramParser::extractCmd(message); - std::string options = TelegramParser::extractOptions(message); - std::cout << "server: jobId=" << jobId << ", cmd=" << cmd << ", options=" << options << std::endl; - if ((jobId != -1) && (cmd != -1)) { + std::optional jobIdOpt = comm::TelegramParser::tryExtractFieldJobId(message); + std::optional cmdOpt = comm::TelegramParser::tryExtractFieldCmd(message); + std::optional optionsOpt = comm::TelegramParser::tryExtractFieldOptions(message); + if (jobIdOpt && cmdOpt && optionsOpt) { + std::cout << "server: jobId=" << jobIdOpt.value() << ", cmd=" << cmdOpt.value() << ", options=" << optionsOpt.value() << std::endl; std::unique_lock lock(m_receivedTasksMutex); - m_receivedTasks.emplace_back(jobId, cmd, options); + m_receivedTasks.emplace_back(jobIdOpt.value(), cmdOpt.value(), optionsOpt.value()); } } diff --git a/vpr/src/server/gateio.h b/vpr/src/server/gateio.h index a1d6c0a83c9..a7ab782c9f7 100644 --- a/vpr/src/server/gateio.h +++ b/vpr/src/server/gateio.h @@ -55,7 +55,7 @@ class GateIO std::vector m_sendTasks; // task to client (reponses) std::mutex m_sendTasksMutex; - TelegramBuffer m_telegramBuff; + comm::TelegramBuffer m_telegramBuff; void startListening(); // thread worker function }; diff --git a/vpr/src/server/pathhelper.cpp b/vpr/src/server/pathhelper.cpp index 5837cc57bf4..3d8cafe9457 100644 --- a/vpr/src/server/pathhelper.cpp +++ b/vpr/src/server/pathhelper.cpp @@ -23,7 +23,7 @@ namespace { /** * @brief helper function to calculate the setup critical path with specified parameters. */ -CritPathsResult generate_setup_timing_report(const SetupTimingInfo& timing_info, const AnalysisDelayCalculator& delay_calc, const t_analysis_opts& analysis_opts, bool is_flat) { +CritPathsResult generate_setup_timing_report(const SetupTimingInfo& timing_info, const AnalysisDelayCalculator& delay_calc, const t_analysis_opts& analysis_opts, bool is_flat, bool usePathElementSeparator) { auto& timing_ctx = g_vpr_ctx.timing(); auto& atom_ctx = g_vpr_ctx.atom(); @@ -34,14 +34,14 @@ CritPathsResult generate_setup_timing_report(const SetupTimingInfo& timing_info, std::vector paths; std::stringstream ss; - timing_reporter.report_timing_setup(paths, ss, *timing_info.setup_analyzer(), analysis_opts.timing_report_npaths); + timing_reporter.report_timing_setup(paths, ss, *timing_info.setup_analyzer(), analysis_opts.timing_report_npaths, usePathElementSeparator); return CritPathsResult{paths, ss.str()}; } /** * @brief helper function to calculate the hold critical path with specified parameters. */ -CritPathsResult generate_hold_timing_report(const HoldTimingInfo& timing_info, const AnalysisDelayCalculator& delay_calc, const t_analysis_opts& analysis_opts, bool is_flat) { +CritPathsResult generate_hold_timing_report(const HoldTimingInfo& timing_info, const AnalysisDelayCalculator& delay_calc, const t_analysis_opts& analysis_opts, bool is_flat, bool usePathElementSeparator) { auto& timing_ctx = g_vpr_ctx.timing(); auto& atom_ctx = g_vpr_ctx.atom(); @@ -52,7 +52,7 @@ CritPathsResult generate_hold_timing_report(const HoldTimingInfo& timing_info, c std::vector paths; std::stringstream ss; - timing_reporter.report_timing_hold(paths, ss, *timing_info.hold_analyzer(), analysis_opts.timing_report_npaths); + timing_reporter.report_timing_hold(paths, ss, *timing_info.hold_analyzer(), analysis_opts.timing_report_npaths, usePathElementSeparator); return CritPathsResult{paths, ss.str()}; } @@ -61,7 +61,7 @@ CritPathsResult generate_hold_timing_report(const HoldTimingInfo& timing_info, c /** * @brief Unified helper function to calculate the critical path with specified parameters. */ -CritPathsResult calcCriticalPath(const std::string& type, int critPathNum, e_timing_report_detail detailsLevel, bool is_flat_routing) +CritPathsResult calcCriticalPath(const std::string& type, int critPathNum, e_timing_report_detail detailsLevel, bool is_flat_routing, bool usePathElementSeparator) { // shortcuts auto& atom_ctx = g_vpr_ctx.atom(); @@ -86,9 +86,9 @@ CritPathsResult calcCriticalPath(const std::string& type, int critPathNum, e_tim analysis_opt.timing_report_npaths = critPathNum; if (type == "setup") { - return generate_setup_timing_report(*timing_info, *analysis_delay_calc, analysis_opt, is_flat_routing); + return generate_setup_timing_report(*timing_info, *analysis_delay_calc, analysis_opt, is_flat_routing, usePathElementSeparator); } else if (type == "hold") { - return generate_hold_timing_report(*timing_info, *analysis_delay_calc, analysis_opt, is_flat_routing); + return generate_hold_timing_report(*timing_info, *analysis_delay_calc, analysis_opt, is_flat_routing, usePathElementSeparator); } return CritPathsResult{std::vector(), ""}; } diff --git a/vpr/src/server/pathhelper.h b/vpr/src/server/pathhelper.h index 61c86295634..211696f54d0 100644 --- a/vpr/src/server/pathhelper.h +++ b/vpr/src/server/pathhelper.h @@ -24,7 +24,7 @@ struct CritPathsResult { /** * @brief Unified helper function to calculate the critical path with specified parameters. */ -CritPathsResult calcCriticalPath(const std::string& type, int critPathNum, e_timing_report_detail detailsLevel, bool is_flat_routing); +CritPathsResult calcCriticalPath(const std::string& type, int critPathNum, e_timing_report_detail detailsLevel, bool is_flat_routing, bool usePathElementSeparator); } // namespace server diff --git a/vpr/src/server/task.h b/vpr/src/server/task.h index d4e1464c1ce..3d3e50ae268 100644 --- a/vpr/src/server/task.h +++ b/vpr/src/server/task.h @@ -5,7 +5,7 @@ #include #include -#include "serverconsts.h" +#include "commconstants.h" namespace server { @@ -53,12 +53,12 @@ class Task { std::stringstream ss; ss << "{"; - ss << "\"" << KEY_JOB_ID << "\":\"" << m_jobId << "\","; - ss << "\"" << KEY_CMD << "\":\"" << m_cmd << "\","; - ss << "\"" << KEY_OPTIONS << "\":\"" << m_options << "\","; - ss << "\"" << KEY_DATA << "\":\"" << m_result << "\","; + ss << "\"" << comm::KEY_JOB_ID << "\":\"" << m_jobId << "\","; + ss << "\"" << comm::KEY_CMD << "\":\"" << m_cmd << "\","; + ss << "\"" << comm::KEY_OPTIONS << "\":\"" << m_options << "\","; + ss << "\"" << comm::KEY_DATA << "\":\"" << m_result << "\","; int status = m_hasError ? 0 : 1; - ss << "\"" << KEY_STATUS << "\":\"" << status << "\""; + ss << "\"" << comm::KEY_STATUS << "\":\"" << status << "\""; ss << "}"; diff --git a/vpr/src/server/taskresolver.cpp b/vpr/src/server/taskresolver.cpp index 6d5fdc5cb8a..653fdd39099 100644 --- a/vpr/src/server/taskresolver.cpp +++ b/vpr/src/server/taskresolver.cpp @@ -73,12 +73,12 @@ bool TaskResolver::update(ezgl::application* app) for (auto& task: m_tasks) { if (!task.isFinished()) { switch(task.cmd()) { - case CMD_GET_PATH_LIST_ID: { + case comm::CMD_GET_PATH_LIST_ID: { processGetPathListTask(app, task); has_processed_task = true; break; } - case CMD_DRAW_PATH_ID: { + case comm::CMD_DRAW_PATH_ID: { processDrawCriticalPathTask(app, task); has_processed_task = true; break; @@ -92,20 +92,20 @@ bool TaskResolver::update(ezgl::application* app) void TaskResolver::processGetPathListTask(ezgl::application* app, Task& task) { - TelegramOptions options{task.options(), {OPTION_PATH_NUM, OPTION_PATH_TYPE, OPTION_DETAILS_LEVEL, OPTION_IS_FLOAT_ROUTING}}; + TelegramOptions options{task.options(), {comm::OPTION_PATH_NUM, comm::OPTION_PATH_TYPE, comm::OPTION_DETAILS_LEVEL, comm::OPTION_IS_FLOAT_ROUTING}}; if (!options.hasErrors()) { ServerContext& server_ctx = g_vpr_ctx.mutable_server(); // shortcut - server_ctx.set_crit_path_index(-1); // reset selection if path list options has changed + server_ctx.set_crit_path_elements(std::map>{}); // reset selection if path list options has changed // read options - const int nCriticalPathNum = options.getInt(OPTION_PATH_NUM, 1); - const std::string pathType = options.getString(OPTION_PATH_TYPE); - const std::string detailsLevel = options.getString(OPTION_DETAILS_LEVEL); - const bool isFlat = options.getBool(OPTION_IS_FLOAT_ROUTING, false); + const int nCriticalPathNum = options.getInt(comm::OPTION_PATH_NUM, 1); + const std::string pathType = options.getString(comm::OPTION_PATH_TYPE); + const std::string detailsLevel = options.getString(comm::OPTION_DETAILS_LEVEL); + const bool isFlat = options.getBool(comm::OPTION_IS_FLOAT_ROUTING, false); // calculate critical path depending on options and store result in server context - CritPathsResult crit_paths_result = calcCriticalPath(pathType, nCriticalPathNum, getDetailsLevelEnum(detailsLevel), isFlat); + CritPathsResult crit_paths_result = calcCriticalPath(pathType, nCriticalPathNum, getDetailsLevelEnum(detailsLevel), isFlat, /*usePathElementSeparator*/true); // setup context server_ctx.set_path_type(pathType); @@ -129,33 +129,26 @@ void TaskResolver::processGetPathListTask(ezgl::application* app, Task& task) void TaskResolver::processDrawCriticalPathTask(ezgl::application* app, Task& task) { - TelegramOptions options{task.options(), {OPTION_PATH_INDEX, OPTION_HIGHTLIGHT_MODE}}; + TelegramOptions options{task.options(), {comm::OPTION_PATH_ELEMENTS, comm::OPTION_HIGHTLIGHT_MODE, comm::OPTION_DRAW_PATH_CONTOUR}}; if (!options.hasErrors()) { ServerContext& server_ctx = g_vpr_ctx.mutable_server(); // shortcut - const int pathIndex = options.getInt(OPTION_PATH_INDEX, -1); - const std::string highLightMode = options.getString(OPTION_HIGHTLIGHT_MODE); + const std::map> path_elements = options.getMapOfSets(comm::OPTION_PATH_ELEMENTS); + const std::string highLightMode = options.getString(comm::OPTION_HIGHTLIGHT_MODE); + const bool drawPathContour = options.getBool(comm::OPTION_DRAW_PATH_CONTOUR, false); - if (pathIndex == -1) { - server_ctx.set_crit_path_index(-1); // clear selection + // set critical path elements to render + server_ctx.set_crit_path_elements(path_elements); + server_ctx.set_draw_crit_path_contour(drawPathContour); + + // update gtk UI + GtkComboBox* toggle_crit_path = GTK_COMBO_BOX(app->get_object("ToggleCritPath")); + gint highLightModeIndex = get_item_index_by_text(toggle_crit_path, highLightMode.c_str()); + if (highLightModeIndex != -1) { + gtk_combo_box_set_active(toggle_crit_path, highLightModeIndex); task.success(); - } else if ((pathIndex >= 0) && (pathIndex < static_cast(server_ctx.crit_paths().size()))) { - // set critical path index for rendering - server_ctx.set_crit_path_index(pathIndex); - - // update gtk UI - GtkComboBox* toggle_crit_path = GTK_COMBO_BOX(app->get_object("ToggleCritPath")); - gint highLightModeIndex = get_item_index_by_text(toggle_crit_path, highLightMode.c_str()); - if (highLightModeIndex != -1) { - gtk_combo_box_set_active(toggle_crit_path, highLightModeIndex); - task.success(); - } else { - std::string msg{"cannot find ToggleCritPath qcombobox index for item " + highLightMode}; - std::cerr << msg << std::endl; - task.fail(msg); - } } else { - std::string msg{"selectedIndex=" + std::to_string(pathIndex) + " is out of range [0-" + std::to_string(static_cast(server_ctx.crit_paths().size())-1) + "]"}; + std::string msg{"cannot find ToggleCritPath qcombobox index for item " + highLightMode}; std::cerr << msg << std::endl; task.fail(msg); } diff --git a/vpr/src/server/telegrambuffer.cpp b/vpr/src/server/telegrambuffer.cpp index 72fcc270b89..d80b70f7fed 100644 --- a/vpr/src/server/telegrambuffer.cpp +++ b/vpr/src/server/telegrambuffer.cpp @@ -1,7 +1,7 @@ #include "telegrambuffer.h" -#include "serverconsts.h" +#include "commconstants.h" -namespace server { +namespace comm { void TelegramBuffer::append(const ByteArray& bytes) { @@ -12,7 +12,7 @@ std::vector TelegramBuffer::takeFrames() { std::vector result; ByteArray candidate; - for (unsigned char b: m_rawBuffer.data()) { + for (unsigned char b: m_rawBuffer) { if (b == TELEGRAM_FRAME_DELIMETER) { if (!candidate.empty()) { result.push_back(candidate); @@ -26,4 +26,4 @@ std::vector TelegramBuffer::takeFrames() return result; } -} // namespace server +} // namespace comm diff --git a/vpr/src/server/telegrambuffer.h b/vpr/src/server/telegrambuffer.h index d5af4a1b059..1ad090aa2fa 100644 --- a/vpr/src/server/telegrambuffer.h +++ b/vpr/src/server/telegrambuffer.h @@ -5,43 +5,41 @@ #include #include -namespace server { +namespace comm { /** - * @brief Implements dynamic bytes array with simple interface. + * @brief ByteArray as a simple wrapper over std::vector */ -class ByteArray { +class ByteArray : public std::vector { public: static const std::size_t DEFAULT_SIZE_HINT = 1024; ByteArray(const char* data) - : m_data(reinterpret_cast(data), - reinterpret_cast(data + strlen(data))) + : std::vector(reinterpret_cast(data), + reinterpret_cast(data + std::strlen(data))) + {} + + ByteArray(const char* data, std::size_t size) + : std::vector(reinterpret_cast(data), + reinterpret_cast(data + size)) {} ByteArray(std::size_t sizeHint = DEFAULT_SIZE_HINT) { - m_data.reserve(sizeHint); + this->reserve(sizeHint); } void append(const ByteArray& appendix) { - for (unsigned char b: appendix.data()) { - m_data.push_back(b); + for (unsigned char b: appendix) { + this->push_back(b); } } void append(unsigned char b) { - m_data.push_back(b); + this->push_back(b); } std::string to_string() const { - return std::string(reinterpret_cast(m_data.data()), m_data.size()); + return std::string(reinterpret_cast(this->data()), this->size()); } - - bool empty() const { return m_data.empty(); } - const std::vector& data() const { return m_data; } - void clear() { m_data.clear(); } - -private: - std::vector m_data; }; /** @@ -67,6 +65,6 @@ class TelegramBuffer ByteArray m_rawBuffer; }; -} // namespace server +} // namespace comm #endif // TELEGRAMBUFFER_H diff --git a/vpr/src/server/telegramoptions.h b/vpr/src/server/telegramoptions.h index b643edabc22..8163b3eb38d 100644 --- a/vpr/src/server/telegramoptions.h +++ b/vpr/src/server/telegramoptions.h @@ -1,6 +1,8 @@ #ifndef TELEGRAMOPTIONS_H #define TELEGRAMOPTIONS_H +#include + #include #include #include @@ -58,6 +60,34 @@ class TelegramOptions { bool hasErrors() const { return !m_errors.empty(); } + std::map> getMapOfSets(const std::string& name) { + std::map> result; + std::string dataStr = getString(name); + if (!dataStr.empty()) { + std::vector pathes = splitString(dataStr, '|'); + for (const std::string& path: pathes) { + std::vector pathStruct = splitString(path, '#'); + if (pathStruct.size() == 2) { + std::string pathIndexStr = pathStruct[0]; + std::string pathElementIndexesStr = pathStruct[1]; + std::vector pathElementIndexes = splitString(pathElementIndexesStr, ','); + std::set elements; + for (const std::string& pathElementIndex: pathElementIndexes) { + if (std::optional optValue = tryConvertToInt(pathElementIndex.c_str())) { + elements.insert(optValue.value()); + } + } + if (std::optional optPathIndex = tryConvertToInt(pathIndexStr.c_str())) { + result[optPathIndex.value()] = elements; + } + } else { + m_errors.emplace_back("wrong path data structure = " + path); + } + } + } + return result; + } + std::string getString(const std::string& name) { std::string result; if (auto it = m_options.find(name); it != m_options.end()) { @@ -66,24 +96,22 @@ class TelegramOptions { return result; } - int getInt(const std::string& name, int defaultValue) { - int result = defaultValue; - try { - result = std::atoi(m_options[name].value.c_str()); - } catch(...) { + int getInt(const std::string& name, int failValue) { + if (std::optional opt = tryConvertToInt(m_options[name].value)) { + return opt.value(); + } else { m_errors.emplace_back("cannot get int value for option " + name); + return failValue; } - return result; } - bool getBool(const std::string& name, bool defaultValue) { - bool result = defaultValue; - try { - result = std::atoi(m_options[name].value.c_str()); - } catch(...) { + bool getBool(const std::string& name, bool failValue) { + if (std::optional opt = tryConvertToInt(m_options[name].value)) { + return opt.value(); + } else { m_errors.emplace_back("cannot get bool value for option " + name); + return failValue; } - return result; } std::string errorsStr() const { diff --git a/vpr/src/server/telegramparser.cpp b/vpr/src/server/telegramparser.cpp index fc6e32b1495..d2862828627 100644 --- a/vpr/src/server/telegramparser.cpp +++ b/vpr/src/server/telegramparser.cpp @@ -1,43 +1,79 @@ #include "telegramparser.h" +#include "convertutils.h" +#include "commconstants.h" -#include +#include -namespace server { +namespace comm { -int TelegramParser::extractJobId(const std::string& message) +std::optional TelegramParser::tryExtractJsonValueStr(const std::string& jsonString, const std::string& key) { - static std::regex pattern("\"JOB_ID\":\"(\\d+)\""); - std::smatch match; - if (std::regex_search(message, match, pattern)) { - if (match.size() > 1) { - return std::atoi(match[1].str().c_str()); - } + std::optional result; + + // Find the position of the key + size_t keyPos = jsonString.find("\"" + key + "\":"); + + if (keyPos == std::string::npos) { + // Key not found + return result; + } + + // Find the position of the value after the key + size_t valuePosStart = jsonString.find("\"", keyPos + key.length() + std::string("\":\"").size()); + + if (valuePosStart == std::string::npos) { + // Value not found + return result; } - return -1; + + // Find the position of the closing quote for the value + size_t valueEnd = jsonString.find("\"", valuePosStart + std::string("\"").size()); + + if (valueEnd == std::string::npos) { + // Closing quote not found + return result; + } + + // Extract the value substring + result = jsonString.substr(valuePosStart + 1, (valueEnd - valuePosStart) - 1); + return result; } -int TelegramParser::extractCmd(const std::string& message) +std::optional TelegramParser::tryExtractFieldJobId(const std::string& message) { - static std::regex pattern("\"CMD\":\"(\\d+)\""); - std::smatch match; - if (std::regex_search(message, match, pattern)) { - if (match.size() > 1) { - return std::atoi(match[1].str().c_str()); - } + std::optional result; + if (std::optional strOpt = tryExtractJsonValueStr(message, comm::KEY_JOB_ID)) { + result = tryConvertToInt(strOpt.value()); } - return -1; + return result; +} + +std::optional TelegramParser::tryExtractFieldCmd(const std::string& message) +{ + std::optional result; + if (std::optional strOpt = tryExtractJsonValueStr(message, comm::KEY_CMD)) { + result = tryConvertToInt(strOpt.value()); + } + return result; +} + +std::optional TelegramParser::tryExtractFieldOptions(const std::string& message) +{ + return tryExtractJsonValueStr(message, comm::KEY_OPTIONS); +} + +std::optional TelegramParser::tryExtractFieldData(const std::string& message) +{ + return tryExtractJsonValueStr(message, comm::KEY_DATA); } -std::string TelegramParser::extractOptions(const std::string& message) +std::optional TelegramParser::tryExtractFieldStatus(const std::string& message) { - static std::regex pattern("\"OPTIONS\":\"(.*?)\""); - std::smatch match; - if (std::regex_search(message, match, pattern)) { - if (match.size() > 1) { - return match[1].str(); - } + std::optional result; + if (std::optional strOpt = tryExtractJsonValueStr(message, comm::KEY_STATUS)) { + result = tryConvertToInt(strOpt.value()); } - return ""; + return result; } -} // namespace server +} // namespace comm diff --git a/vpr/src/server/telegramparser.h b/vpr/src/server/telegramparser.h index 0e6d589f265..a27c737eec1 100644 --- a/vpr/src/server/telegramparser.h +++ b/vpr/src/server/telegramparser.h @@ -2,24 +2,28 @@ #define TELEGRAMPARSER_H #include +#include -namespace server { +namespace comm { /** * @brief Dummy JSON parser using regular expressions. - * - * This module provides helper methods to extract values such as "id", "cmd", or "options" - * from a JSON schema structured as follows: {id: num, cmd: enum, options: string}. - * The regular expressions implemented in this parser aim to retrieve specific fields' values - * from a given JSON structure, facilitating data extraction and manipulation. + * + * This module provides helper methods to extract values for a keys as "JOB_ID", "CMD", or "OPTIONS" + * from a JSON schema structured as follows: {JOB_ID:num, CMD:enum, OPTIONS:string}. */ class TelegramParser { public: - static int extractJobId(const std::string& message); - static int extractCmd(const std::string& message); - static std::string extractOptions(const std::string& message); + static std::optional tryExtractFieldJobId(const std::string& message); + static std::optional tryExtractFieldCmd(const std::string& message); + static std::optional tryExtractFieldOptions(const std::string& message); + static std::optional tryExtractFieldData(const std::string& message); + static std::optional tryExtractFieldStatus(const std::string& message); + +private: + static std::optional tryExtractJsonValueStr(const std::string& jsonString, const std::string& key); }; -} // namespace server +} // namespace comm #endif // TELEGRAMPARSER_H From 156060be7a4fb5a26437bb66950f94d1ef03b5a1 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Wed, 31 Jan 2024 20:18:30 +0200 Subject: [PATCH 25/31] update changes for libtatum/tatum/TimingReporter --- .../libtatum/tatum/TimingReporter.cpp | 23 +++++++++++-------- .../libtatum/tatum/TimingReporter.hpp | 11 +++++---- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp index 69c9ba07a83..bda7eb4e8b1 100644 --- a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp +++ b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp @@ -102,10 +102,10 @@ void TimingReporter::report_timing_setup(std::ostream& os, void TimingReporter::report_timing_setup(std::vector& paths, std::ostream& os, const SetupTimingAnalyzer& setup_analyzer, - size_t npaths) const { + size_t npaths, bool usePathElementSeparator) const { paths = path_collector_.collect_worst_setup_timing_paths(timing_graph_, setup_analyzer, npaths); - report_timing(os, paths); + report_timing(os, paths, usePathElementSeparator); } void TimingReporter::report_timing_hold(std::string filename, @@ -126,10 +126,11 @@ void TimingReporter::report_timing_hold(std::ostream& os, void TimingReporter::report_timing_hold(std::vector& paths, std::ostream& os, const HoldTimingAnalyzer& hold_analyzer, - size_t npaths) const { + size_t npaths, + bool usePathElementSeparator) const { paths = path_collector_.collect_worst_hold_timing_paths(timing_graph_, hold_analyzer, npaths); - report_timing(os, paths); + report_timing(os, paths, usePathElementSeparator); } void TimingReporter::report_skew_setup(std::string filename, @@ -213,7 +214,7 @@ void TimingReporter::report_unconstrained_hold(std::ostream& os, */ void TimingReporter::report_timing(std::ostream& os, - const std::vector& paths) const { + const std::vector& paths, bool usePathElementSeparator) const { tatum::OsFormatGuard flag_guard(os); os << "#Timing report of worst " << paths.size() << " path(s)\n"; @@ -224,14 +225,14 @@ void TimingReporter::report_timing(std::ostream& os, size_t i = 0; for(const auto& path : paths) { os << "#Path " << ++i << "\n"; - report_timing_path(os, path); + report_timing_path(os, path, usePathElementSeparator); os << "\n"; } os << "#End of timing report\n"; } -void TimingReporter::report_timing_path(std::ostream& os, const TimingPath& timing_path) const { +void TimingReporter::report_timing_path(std::ostream& os, const TimingPath& timing_path, bool usePathElementSeparator) const { std::string divider = "--------------------------------------------------------------------------------"; TimingPathInfo path_info = timing_path.path_info(); @@ -270,7 +271,7 @@ void TimingReporter::report_timing_path(std::ostream& os, const TimingPath& timi arr_path = report_timing_clock_launch_subpath(os, path_helper, timing_path.clock_launch_path(), path_info.launch_domain(), path_info.type()); - arr_path = report_timing_data_arrival_subpath(os, path_helper, timing_path.data_arrival_path(), path_info.launch_domain(), path_info.type(), arr_path); + arr_path = report_timing_data_arrival_subpath(os, path_helper, timing_path.data_arrival_path(), path_info.launch_domain(), path_info.type(), arr_path, usePathElementSeparator); { //Final arrival time @@ -602,7 +603,8 @@ Time TimingReporter::report_timing_data_arrival_subpath(std::ostream& os, const TimingSubPath& subpath, DomainId domain, TimingType timing_type, - Time path) const { + Time path, + bool usePathElementSeparator) const { { //Input constraint @@ -633,7 +635,7 @@ Time TimingReporter::report_timing_data_arrival_subpath(std::ostream& os, //Launch data for(const TimingPathElem& path_elem : subpath.elements()) { - + if (usePathElementSeparator) os << "el{\n"; //Ask the application for a detailed breakdown of the edge delays auto delay_breakdown = name_resolver_.edge_delay_breakdown(path_elem.incomming_edge(), delay_type); if (!delay_breakdown.components.empty()) { @@ -662,6 +664,7 @@ Time TimingReporter::report_timing_data_arrival_subpath(std::ostream& os, path = path_elem.tag().time(); path_helper.update_print_path(os, point, path); + if (usePathElementSeparator) os << "el}\n"; } return path; } diff --git a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp index 590e3bca690..a3a381fe6a7 100644 --- a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp +++ b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp @@ -64,11 +64,11 @@ class TimingReporter { public: void report_timing_setup(std::string filename, const tatum::SetupTimingAnalyzer& setup_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; void report_timing_setup(std::ostream& os, const tatum::SetupTimingAnalyzer& setup_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; - void report_timing_setup(std::vector& paths, std::ostream& os, const tatum::SetupTimingAnalyzer& setup_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; + void report_timing_setup(std::vector& paths, std::ostream& os, const tatum::SetupTimingAnalyzer& setup_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS, bool usePathElementSeparator=false) const; void report_timing_hold(std::string filename, const tatum::HoldTimingAnalyzer& hold_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; void report_timing_hold(std::ostream& os, const tatum::HoldTimingAnalyzer& hold_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; - void report_timing_hold(std::vector& paths, std::ostream& os, const tatum::HoldTimingAnalyzer& hold_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; + void report_timing_hold(std::vector& paths, std::ostream& os, const tatum::HoldTimingAnalyzer& hold_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS, bool usePathElementSeparator=false) const; void report_skew_setup(std::string filename, const tatum::SetupTimingAnalyzer& setup_analyzer, size_t nworst=REPORT_TIMING_DEFAULT_NPATHS) const; void report_skew_setup(std::ostream& os, const tatum::SetupTimingAnalyzer& setup_analyzer, size_t nworst=REPORT_TIMING_DEFAULT_NPATHS) const; @@ -96,9 +96,9 @@ class TimingReporter { }; private: - void report_timing(std::ostream& os, const std::vector& paths) const; + void report_timing(std::ostream& os, const std::vector& paths, bool usePathElementSeparator = false) const; - void report_timing_path(std::ostream& os, const TimingPath& path) const; + void report_timing_path(std::ostream& os, const TimingPath& path, bool usePathElementSeparator = false) const; void report_unconstrained(std::ostream& os, const NodeType type, const detail::TagRetriever& tag_retriever) const; @@ -125,7 +125,8 @@ class TimingReporter { const TimingSubPath& subpath, DomainId domain, TimingType timing_type, - Time path) const; + Time path, + bool usePathElementSeparator = false) const; Time report_timing_data_required_element(std::ostream& os, detail::ReportTimingPathHelper& path_helper, From 3ecbac2ef2c9bf2bb47fc88d9ac0ba6a882e1aea Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Thu, 15 Feb 2024 05:22:00 +0200 Subject: [PATCH 26/31] incorporate sockpp integration with fixed header telegram protocol, different perfomance optimization for a big data chunks --- vpr/CMakeLists.txt | 8 +- vpr/src/server/bytearray.h | 78 ++++++ vpr/src/server/commconstants.h | 5 +- vpr/src/server/convertutils.cpp | 75 +++++- vpr/src/server/convertutils.h | 5 + vpr/src/server/gateio.cpp | 300 +++++++++++++---------- vpr/src/server/gateio.h | 115 +++++++-- vpr/src/server/gtkcomboboxhelper.cpp | 2 +- vpr/src/server/serverupdate.cpp | 7 +- vpr/src/server/task.cpp | 111 +++++++++ vpr/src/server/task.h | 84 +++---- vpr/src/server/taskresolver.cpp | 61 ++--- vpr/src/server/taskresolver.h | 14 +- vpr/src/server/telegrambuffer.cpp | 65 ++++- vpr/src/server/telegrambuffer.h | 52 ++-- vpr/src/server/telegramframe.h | 19 ++ vpr/src/server/telegramheader.cpp | 76 ++++++ vpr/src/server/telegramheader.h | 61 +++++ vpr/src/server/telegramoptions.h | 4 +- vpr/src/server/telegramparser.cpp | 32 +-- vpr/src/server/telegramparser.h | 6 +- vpr/src/server/zlibutils.cpp | 79 ++++++ vpr/src/server/zlibutils.h | 10 + vpr/test/test_server_convertutils.cpp | 18 ++ vpr/test/test_server_taskresolver.cpp | 82 +++++++ vpr/test/test_server_telegrambuffer.cpp | 89 +++++++ vpr/test/test_server_telegramoptions.cpp | 19 ++ vpr/test/test_server_telegramparser.cpp | 21 ++ vpr/thirdparty/sockpp | 1 + 29 files changed, 1188 insertions(+), 311 deletions(-) create mode 100644 vpr/src/server/bytearray.h create mode 100644 vpr/src/server/task.cpp create mode 100644 vpr/src/server/telegramframe.h create mode 100644 vpr/src/server/telegramheader.cpp create mode 100644 vpr/src/server/telegramheader.h create mode 100644 vpr/src/server/zlibutils.cpp create mode 100644 vpr/src/server/zlibutils.h create mode 100644 vpr/test/test_server_convertutils.cpp create mode 100644 vpr/test/test_server_taskresolver.cpp create mode 100644 vpr/test/test_server_telegrambuffer.cpp create mode 100644 vpr/test/test_server_telegramoptions.cpp create mode 100644 vpr/test/test_server_telegramparser.cpp create mode 160000 vpr/thirdparty/sockpp diff --git a/vpr/CMakeLists.txt b/vpr/CMakeLists.txt index 1568ff0547f..d2903730b02 100644 --- a/vpr/CMakeLists.txt +++ b/vpr/CMakeLists.txt @@ -15,6 +15,10 @@ set(VPR_PGO_DATA_DIR "." CACHE PATH "Where to store and retrieve PGO data") #Handle graphics setup set(GRAPHICS_DEFINES "") +#sockpp +add_subdirectory(thirdparty/sockpp) +set(THIRDPARTY_INCLUDE_DIRS thirdparty/sockpp/include) + if (VPR_USE_EZGL STREQUAL "on") message(STATUS "EZGL: graphics enabled") set( @@ -54,7 +58,7 @@ add_library(libvpr STATIC ) -target_include_directories(libvpr PUBLIC ${LIB_INCLUDE_DIRS}) +target_include_directories(libvpr PUBLIC ${LIB_INCLUDE_DIRS} ${THIRDPARTY_INCLUDE_DIRS}) #VPR_ANALYTIC_PLACE is inisitalized in the root CMakeLists #Check Eigen dependency @@ -84,6 +88,8 @@ target_link_libraries(libvpr libargparse libpugixml librrgraph + sockpp + -lz ) #link graphics library only when graphics set to on diff --git a/vpr/src/server/bytearray.h b/vpr/src/server/bytearray.h new file mode 100644 index 00000000000..164c6ed8c5b --- /dev/null +++ b/vpr/src/server/bytearray.h @@ -0,0 +1,78 @@ +#ifndef BYTEARRAY_H +#define BYTEARRAY_H + +#include +#include +#include + +namespace comm { + +/** + * @brief ByteArray as a simple wrapper over std::vector +*/ +class ByteArray : public std::vector { +public: + static const std::size_t DEFAULT_SIZE_HINT = 1024; + + ByteArray(const char* data) + : std::vector(reinterpret_cast(data), + reinterpret_cast(data + std::strlen(data))) + {} + + ByteArray(const char* data, std::size_t size) + : std::vector(reinterpret_cast(data), + reinterpret_cast(data + size)) + {} + + ByteArray(std::size_t sizeHint = DEFAULT_SIZE_HINT) { + reserve(sizeHint); + } + + template + ByteArray(Iterator first, Iterator last): std::vector(first, last) {} + + void append(const ByteArray& appendix) { + insert(end(), appendix.begin(), appendix.end()); + } + + void append(uint8_t b) { + push_back(b); + } + + std::size_t findSequence(const char* sequence, std::size_t n) { + for (std::size_t i = 0; i <= size() - n; ++i) { + bool found = true; + for (std::size_t j = 0; j < n; ++j) { + if (at(i + j) != sequence[j]) { + found = false; + break; + } + } + if (found) { + return i; + } + } + return std::size_t(-1); + } + + std::string to_string() const { + return std::string(reinterpret_cast(this->data()), this->size()); + } + + uint32_t calcCheckSum() { + return calcCheckSum(*this); + } + + template + static uint32_t calcCheckSum(const T& iterable) { + uint32_t sum = 0; + for (uint8_t c : iterable) { + sum += static_cast(c); + } + return sum; + } +}; + +} // namespace comm + +#endif // BYTEARRAY_H diff --git a/vpr/src/server/commconstants.h b/vpr/src/server/commconstants.h index 43c78704b44..6845b80e32f 100644 --- a/vpr/src/server/commconstants.h +++ b/vpr/src/server/commconstants.h @@ -8,7 +8,10 @@ constexpr const char* KEY_CMD = "CMD"; constexpr const char* KEY_OPTIONS = "OPTIONS"; constexpr const char* KEY_DATA = "DATA"; constexpr const char* KEY_STATUS = "STATUS"; -constexpr const unsigned char TELEGRAM_FRAME_DELIMETER{0x17}; // 0x17 - End of Transmission Block +constexpr const char* ECHO_DATA = "ECHO"; + +const unsigned char ZLIB_COMPRESSOR_ID = 'z'; +const unsigned char NONE_COMPRESSOR_ID = '\x0'; constexpr const char* OPTION_PATH_NUM = "path_num"; constexpr const char* OPTION_PATH_TYPE = "path_type"; diff --git a/vpr/src/server/convertutils.cpp b/vpr/src/server/convertutils.cpp index 51056eca5ec..be73c4590e3 100644 --- a/vpr/src/server/convertutils.cpp +++ b/vpr/src/server/convertutils.cpp @@ -1,24 +1,77 @@ #include "convertutils.h" +#include +#include std::optional tryConvertToInt(const std::string& str) { std::optional result; - char* endptr; // To store the end of the conversion - errno = 0; // Set errno to 0 before the call - int candidate = std::strtol(str.c_str(), &endptr, 10); - - // Check for conversion errors - if (errno != 0) { - return result; + std::istringstream iss(str); + int intValue; + if (iss >> intValue) { + // Check if there are no any characters left in the stream + char remaining; + if (!(iss >> remaining)) { + result = intValue; + } } + return result; +} - // Check if there were any non-numeric characters - if (endptr == str.c_str()) { - return result; +namespace { +std::string getPrettyStrFromFloat(float value) +{ + std::ostringstream ss; + ss << std::fixed << std::setprecision(2) << value; // Set precision to 2 digit after the decimal point + return ss.str(); +} +} // namespace + +std::string getPrettyDurationStrFromMs(int64_t durationMs) +{ + std::string result; + if (durationMs >= 1000) { + result = getPrettyStrFromFloat(durationMs/1000.0f) + " sec"; + } else { + result = std::to_string(durationMs); + result += " ms"; } + return result; +} - result = candidate; +std::string getPrettySizeStrFromBytesNum(int64_t bytesNum) +{ + std::string result; + if (bytesNum >= 1024*1024*1024) { + result = getPrettyStrFromFloat(bytesNum/float(1024*1024*1024)) + "Gb"; + } else if (bytesNum >= 1024*1024) { + result = getPrettyStrFromFloat(bytesNum/float(1024*1024)) + "Mb"; + } else if (bytesNum >= 1024) { + result = getPrettyStrFromFloat(bytesNum/float(1024)) + "Kb"; + } else { + result = std::to_string(bytesNum) + "bytes"; + } return result; } + +std::string getTruncatedMiddleStr(const std::string& src, std::size_t num) { + std::string result; + static std::size_t minimalStringSizeToTruncate = 20; + if (num < minimalStringSizeToTruncate) { + num = minimalStringSizeToTruncate; + } + static std::string middlePlaceHolder("..."); + const std::size_t srcSize = src.size(); + if (srcSize > num) { + int prefixNum = num/2; + int suffixNum = num/2 - middlePlaceHolder.size(); + result.append(std::move(src.substr(0, prefixNum))); + result.append(middlePlaceHolder); + result.append(std::move(src.substr(srcSize - suffixNum))); + } else { + result = src; + } + + return result; +} diff --git a/vpr/src/server/convertutils.h b/vpr/src/server/convertutils.h index 0647449c6a8..93d4574bc09 100644 --- a/vpr/src/server/convertutils.h +++ b/vpr/src/server/convertutils.h @@ -4,6 +4,11 @@ #include #include +const std::size_t DEFAULT_PRINT_STRING_MAX_NUM = 50; + std::optional tryConvertToInt(const std::string&); +std::string getPrettyDurationStrFromMs(int64_t durationMs); +std::string getPrettySizeStrFromBytesNum(int64_t bytesNum); +std::string getTruncatedMiddleStr(const std::string& src, std::size_t num = DEFAULT_PRINT_STRING_MAX_NUM); #endif // CONVERTUTILS_H diff --git a/vpr/src/server/gateio.cpp b/vpr/src/server/gateio.cpp index dca29dfe89a..ff134e68c45 100644 --- a/vpr/src/server/gateio.cpp +++ b/vpr/src/server/gateio.cpp @@ -1,12 +1,12 @@ #include "gateio.h" #include "telegramparser.h" +#include "telegrambuffer.h" +#include "commconstants.h" +#include "convertutils.h" + +#include "sockpp/tcp6_acceptor.h" -#include -#include #include -#include -#include -#include namespace server { @@ -24,7 +24,7 @@ void GateIO::start(int portNum) { if (!m_isRunning.load()) { m_portNum = portNum; - std::cout << "th=" << std::this_thread::get_id() << " starting server" << std::endl; + std::cout << "starting server from thread=" << std::this_thread::get_id() << std::endl; m_isRunning.store(true); m_thread = std::thread(&GateIO::startListening, this); } @@ -37,168 +37,216 @@ void GateIO::stop() if (m_thread.joinable()) { m_thread.join(); } - } + } } -void GateIO::takeRecievedTasks(std::vector& tasks) +void GateIO::takeRecievedTasks(std::vector& tasks) { - tasks.clear(); - std::unique_lock lock(m_receivedTasksMutex); + std::unique_lock lock(m_tasksMutex); if (m_receivedTasks.size() > 0) { - std::cout << "take " << m_receivedTasks.size() << " num of received tasks"<< std::endl; + m_logger.queue(LogLevel::Debug, "take", m_receivedTasks.size(), "num of received tasks"); } std::swap(tasks, m_receivedTasks); } -void GateIO::addSendTasks(const std::vector& tasks) +void GateIO::moveTasksToSendQueue(std::vector& tasks) { - std::unique_lock lock(m_sendTasksMutex); - for (const Task& task: tasks) { - std::cout << "addSendTasks id=" << task.jobId() << std::endl; - m_sendTasks.push_back(task); + std::unique_lock lock(m_tasksMutex); + for (TaskPtr& task: tasks) { + if (task->hasError()) { + m_logger.queue(LogLevel::Debug, "task id=", task->jobId(), "finished with error", task->error(), "moving it to send queue"); + } else { + m_logger.queue(LogLevel::Debug, "task id=", task->jobId(), "finished with success, moving it to send queue"); + } + + m_sendTasks.push_back(std::move(task)); } } void GateIO::startListening() { - int server_socket; - int client_socket = -1; - struct sockaddr_in address; - int opt = 1; - int addrlen = sizeof(address); - - // Creating socket file descriptor - if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == 0) { - perror("socket failed"); - exit(EXIT_FAILURE); - } + std::unique_ptr clientAliveTrackerPtr = + std::make_unique(std::chrono::milliseconds{5000}, std::chrono::milliseconds{20000}); + static const std::string echoData{comm::ECHO_DATA}; - // Forcefully attaching socket to the port - if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) { - perror("setsockopt"); - exit(EXIT_FAILURE); - } + comm::TelegramBuffer telegramBuff; + std::vector telegramFrames; - address.sin_family = AF_INET; - address.sin_addr.s_addr = INADDR_ANY; - address.sin_port = htons(m_portNum); + sockpp::initialize(); + sockpp::tcp6_acceptor tcpServer(m_portNum); + tcpServer.set_non_blocking(true); - // Bind the socket to the network address and port - if (bind(server_socket, (struct sockaddr *)&address, sizeof(address)) < 0) { - perror("bind failed"); - exit(EXIT_FAILURE); - } else { - std::cout << "th=" << std::this_thread::get_id() << " start listening port: " << m_portNum << std::endl; - } + const std::size_t chunkMaxBytesNum = 0.5*1024*1024; // 0.5Mb - // Put the server socket in a passive mode, where it waits for the client to approach the server to make a connection - if (listen(server_socket, 3) < 0) { - perror("listen"); - exit(EXIT_FAILURE); - } - - // Set the server socket to non-blocking mode - if (fcntl(server_socket, F_SETFL, fcntl(server_socket, F_GETFL, 0) | O_NONBLOCK) < 0) { - perror("Error setting socket to non-blocking"); - exit(EXIT_FAILURE); + if (tcpServer) { + m_logger.queue(LogLevel::Info, "open server, port=", m_portNum); + } else { + m_logger.queue(LogLevel::Info, "fail to open server, port=", m_portNum); } - bool connectionProblemDetected = false; + std::optional clientOpt; - // Event loop + /// comm event loop while(m_isRunning.load()) { - if (connectionProblemDetected || client_socket < 0) { - int flags = fcntl(client_socket, F_GETFL, 0); - fcntl(client_socket, F_SETFL, flags | O_NONBLOCK); - client_socket = accept(server_socket, (struct sockaddr *)&address, (socklen_t*)&addrlen); - if (client_socket > 0) { - std::cout << "accept client" << std::endl; + bool isCommunicationProblemDetected = false; + + /// check for the client connection + if (!clientOpt) { + sockpp::inet6_address peer; + sockpp::tcp6_socket client = tcpServer.accept(&peer); + if (client) { + m_logger.queue(LogLevel::Info, "client", client.address().to_string() , "connection accepted"); + client.set_non_blocking(true); + clientOpt = std::move(client); + + if (clientAliveTrackerPtr) { + clientAliveTrackerPtr->reset(); + } } } - if (client_socket >= 0) { - connectionProblemDetected = false; - // Handle sending + if (clientOpt) { + sockpp::tcp6_socket& client = clientOpt.value(); + + /// handle sending response { - std::unique_lock lock(m_sendTasksMutex); - for (const Task& task: m_sendTasks) { - std::string response = task.toJsonStr(); - response += static_cast(comm::TELEGRAM_FRAME_DELIMETER); - std::cout << "sending" << response << "to client" << std::endl; - send(client_socket, response.c_str(), response.length(), 0); + std::unique_lock lock(m_tasksMutex); + for (const TaskPtr& task: m_sendTasks) { + try { + std::size_t bytesToSend = std::min(chunkMaxBytesNum, task->responseBuffer().size()); + std::size_t bytesActuallyWritten = client.write_n(task->responseBuffer().data(), bytesToSend); + if (bytesActuallyWritten <= task->origReponseBytesNum()) { + task->chopNumSentBytesFromResponseBuffer(bytesActuallyWritten); + m_logger.queue(LogLevel::Detail, + "sent chunk:", getPrettySizeStrFromBytesNum(bytesActuallyWritten), + "from", getPrettySizeStrFromBytesNum(task->origReponseBytesNum()), + "left:", getPrettySizeStrFromBytesNum(task->responseBuffer().size())); + if (clientAliveTrackerPtr) { + clientAliveTrackerPtr->onClientActivity(); + } + } + } catch(...) { + m_logger.queue(LogLevel::Detail, "error while writing chunk"); + isCommunicationProblemDetected = true; + } + + if (task->isResponseFullySent()) { + m_logger.queue(LogLevel::Info, + "sent:", task->telegramHeader().info(), task->info()); + } } - m_sendTasks.clear(); + + // remove reported tasks + auto partitionIter = std::partition(m_sendTasks.begin(), m_sendTasks.end(), + [](const TaskPtr& task) { return !task->isResponseFullySent(); }); + m_sendTasks.erase(partitionIter, m_sendTasks.end()); + + } // release lock + + /// handle receiving + std::string receivedMessage; + receivedMessage.resize(chunkMaxBytesNum); + std::size_t bytesActuallyReceived{0}; + try { + bytesActuallyReceived = client.read_n(&receivedMessage[0], chunkMaxBytesNum); + } catch(...) { + m_logger.queue(LogLevel::Error, "fail to recieving"); + isCommunicationProblemDetected = true; } - //////////////////// - // Handle receiving - fd_set readfds; - struct timeval tv; - - // Set a timeout - tv.tv_sec = 1; - tv.tv_usec = 0; - - // Clear the set ahead of time - FD_ZERO(&readfds); - - // Add our descriptor to the set - FD_SET(client_socket, &readfds); - - // Wait until data is available or timeout - int selectResult = select(client_socket + 1, &readfds, NULL, NULL, &tv); - - if (selectResult == -1) { - std::cerr << "Error in select()\n"; - } else if (selectResult == 0) { - //std::cout << "Timeout occurred! No data available.\n"; - } else { - if (FD_ISSET(client_socket, &readfds)) { - // Data is available; proceed with recv - char data[2048]; - std::memset(data, 0, sizeof(data)); - ssize_t bytes_received = recv(client_socket, data, sizeof(data), 0); - if (bytes_received > 0) { - m_telegramBuff.append(comm::ByteArray{data, bytes_received}); - } else if (bytes_received == 0) { - std::cout << "Connection closed\n"; - connectionProblemDetected = true; + if ((bytesActuallyReceived > 0) && (bytesActuallyReceived <= chunkMaxBytesNum)) { + m_logger.queue(LogLevel::Detail, "received chunk:", getPrettySizeStrFromBytesNum(bytesActuallyReceived)); + telegramBuff.append(comm::ByteArray{receivedMessage.c_str(), bytesActuallyReceived}); + if (clientAliveTrackerPtr) { + clientAliveTrackerPtr->onClientActivity(); + } + } + + /// handle telegrams + telegramFrames.clear(); + telegramBuff.takeTelegramFrames(telegramFrames); + for (const comm::TelegramFramePtr& telegramFrame: telegramFrames) { + // process received data + std::string message{telegramFrame->data.to_string()}; + bool isEchoTelegram = false; + if (clientAliveTrackerPtr) { + if ((message.size() == echoData.size()) && (message == echoData)) { + m_logger.queue(LogLevel::Detail, "received", echoData); + clientAliveTrackerPtr->onClientActivity(); + isEchoTelegram = true; + } + } + + if (!isEchoTelegram) { + m_logger.queue(LogLevel::Detail, "received composed", getPrettySizeStrFromBytesNum(message.size()), ":", getTruncatedMiddleStr(message)); + std::optional jobIdOpt = comm::TelegramParser::tryExtractFieldJobId(message); + std::optional cmdOpt = comm::TelegramParser::tryExtractFieldCmd(message); + std::optional optionsOpt; + comm::TelegramParser::tryExtractFieldOptions(message, optionsOpt); + if (jobIdOpt && cmdOpt && optionsOpt) { + TaskPtr task = std::make_unique(jobIdOpt.value(), cmdOpt.value(), optionsOpt.value()); + const comm::TelegramHeader& header = telegramFrame->header; + m_logger.queue(LogLevel::Info, + "received:", header.info(), task->info(/*skipDuration*/true)); + std::unique_lock lock(m_tasksMutex); + m_receivedTasks.push_back(std::move(task)); } else { - std::cerr << "Error in recv()\n"; - connectionProblemDetected = true; + m_logger.queue(LogLevel::Error, "broken telegram detected, fail extract options from", message); + } + } + } + + // forward telegramBuffer errors + std::vector telegramBufferErrors; + telegramBuff.takeErrors(telegramBufferErrors); + for (const std::string& error: telegramBufferErrors) { + m_logger.queue(LogLevel::Info, error); + } + + /// handle client alive tracker + if (clientAliveTrackerPtr) { + if (clientAliveTrackerPtr->isTimeToSentEcho()) { + comm::TelegramHeader echoHeader = comm::TelegramHeader::constructFromData(echoData); + std::string message = echoHeader.buffer().to_string(); + message.append(echoData); + try { + std::size_t bytesActuallySent = client.write(message); + if (bytesActuallySent == message.size()) { + m_logger.queue(LogLevel::Detail, "sent", echoData); + clientAliveTrackerPtr->onEchoSent(); + } + } catch(...) { + m_logger.queue(LogLevel::Debug, "fail to sent", echoData); + isCommunicationProblemDetected = true; } } } - auto frames = m_telegramBuff.takeFrames(); - for (const comm::ByteArray& frame: frames) { - // Process received data - std::string message{frame.to_string()}; - std::cout << "Received: " << message << std::endl; - std::optional jobIdOpt = comm::TelegramParser::tryExtractFieldJobId(message); - std::optional cmdOpt = comm::TelegramParser::tryExtractFieldCmd(message); - std::optional optionsOpt = comm::TelegramParser::tryExtractFieldOptions(message); - if (jobIdOpt && cmdOpt && optionsOpt) { - std::cout << "server: jobId=" << jobIdOpt.value() << ", cmd=" << cmdOpt.value() << ", options=" << optionsOpt.value() << std::endl; - std::unique_lock lock(m_receivedTasksMutex); - m_receivedTasks.emplace_back(jobIdOpt.value(), cmdOpt.value(), optionsOpt.value()); + /// handle client alive + if (clientAliveTrackerPtr) { + if (clientAliveTrackerPtr->isClientTimeout()) { + m_logger.queue(LogLevel::Error, "client didn't repond too long"); + isCommunicationProblemDetected = true; } } - if (connectionProblemDetected && (client_socket >= 0)) { - std::cout << "close client connection" << std::endl; - close(client_socket); - m_telegramBuff.clear(); + /// handle communication problem + if (isCommunicationProblemDetected) { + clientOpt = std::nullopt; + if (!telegramBuff.empty()) { + m_logger.queue(LogLevel::Debug, "clear telegramBuff"); + telegramBuff.clear(); + } } } - } - if (client_socket >= 0) { - std::cout << "close client socket" << std::endl; - close(client_socket); + std::this_thread::sleep_for(std::chrono::milliseconds{LOOP_INTERVAL_MS}); } +} - close(server_socket); +void GateIO::printLogs() +{ + m_logger.flush(); } } // namespace server diff --git a/vpr/src/server/gateio.h b/vpr/src/server/gateio.h index a7ab782c9f7..f23948fc657 100644 --- a/vpr/src/server/gateio.h +++ b/vpr/src/server/gateio.h @@ -2,22 +2,22 @@ #define GATEIO_H #include "task.h" -#include "telegrambuffer.h" -#include -#include - -#include +#include +#include +#include #include #include #include #include +#include namespace server { -/** - * @brief Implements the socket communication layer with the outside world. - * It begins listening on the specified port number for incoming client requests, +/** + * @brief Implements the socket communication layer with the outside world. + * Operable only with a single client. As soon as client connection is detected + * it begins listening on the specified port number for incoming client requests, * collects and encapsulates them into tasks. * The incoming tasks are extracted and handled by the top-level logic (TaskResolver). * Once the tasks are resolved by the TaskResolver, they are returned @@ -30,14 +30,99 @@ namespace server { */ class GateIO { + class ClientAliveTracker { + public: + ClientAliveTracker(const std::chrono::milliseconds& echoIntervalMs, const std::chrono::milliseconds& clientTimeoutMs) + : m_echoIntervalMs(echoIntervalMs), m_clientTimeoutMs(clientTimeoutMs) { + reset(); + } + ClientAliveTracker()=default; + + void onClientActivity() { + m_lastClientActivityTime = std::chrono::high_resolution_clock::now(); + } + + void onEchoSent() { + m_lastEchoSentTime = std::chrono::high_resolution_clock::now(); + } + + bool isTimeToSentEcho() const { + return (durationSinceLastClientActivityMs() > m_echoIntervalMs) && (durationSinceLastEchoSentMs() > m_echoIntervalMs); + } + bool isClientTimeout() const { return durationSinceLastClientActivityMs() > m_clientTimeoutMs; } + + void reset() { + onClientActivity(); + } + + private: + std::chrono::high_resolution_clock::time_point m_lastClientActivityTime; + std::chrono::high_resolution_clock::time_point m_lastEchoSentTime; + std::chrono::milliseconds m_echoIntervalMs; + std::chrono::milliseconds m_clientTimeoutMs; + + std::chrono::milliseconds durationSinceLastClientActivityMs() const { + auto now = std::chrono::high_resolution_clock::now(); + return std::chrono::duration_cast(now - m_lastClientActivityTime); + } + std::chrono::milliseconds durationSinceLastEchoSentMs() const { + auto now = std::chrono::high_resolution_clock::now(); + return std::chrono::duration_cast(now - m_lastEchoSentTime); + } + }; + + enum class LogLevel: int { + Error, + Info, + Detail, + Debug + }; + + class TLogger { + public: + TLogger() { + m_logLevel = static_cast(LogLevel::Debug); + } + ~TLogger() {} + + template + void queue(LogLevel logLevel, Args&&... args) { + if (static_cast(logLevel) <= m_logLevel) { + std::unique_lock lock(m_logStreamMutex); + if (logLevel == LogLevel::Error) { + m_logStream << "ERROR:"; + } + ((m_logStream << ' ' << std::forward(args)), ...); + m_logStream << "\n"; + } + } + + void flush() { + std::unique_lock lock(m_logStreamMutex); + if (!m_logStream.str().empty()) { + std::cout << m_logStream.str(); + m_logStream.str(""); + } + } + + private: + std::stringstream m_logStream; + std::mutex m_logStreamMutex; + std::atomic m_logLevel; + }; + public: explicit GateIO(); ~GateIO(); + const int LOOP_INTERVAL_MS = 1; + bool isRunning() const { return m_isRunning.load(); } - void takeRecievedTasks(std::vector&); - void addSendTasks(const std::vector&); + void takeRecievedTasks(std::vector&); + void moveTasksToSendQueue(std::vector&); + + void printLogs(); // called from main thread void start(int portNum); void stop(); @@ -49,13 +134,11 @@ class GateIO std::thread m_thread; // thread to execute socket IO work - std::vector m_receivedTasks; // tasks from clinet (requests) - std::mutex m_receivedTasksMutex; - - std::vector m_sendTasks; // task to client (reponses) - std::mutex m_sendTasksMutex; + std::mutex m_tasksMutex; + std::vector m_receivedTasks; // tasks from client (requests) + std::vector m_sendTasks; // task to client (reponses) - comm::TelegramBuffer m_telegramBuff; + TLogger m_logger; void startListening(); // thread worker function }; diff --git a/vpr/src/server/gtkcomboboxhelper.cpp b/vpr/src/server/gtkcomboboxhelper.cpp index ac6d5de23ae..a96d4b93347 100644 --- a/vpr/src/server/gtkcomboboxhelper.cpp +++ b/vpr/src/server/gtkcomboboxhelper.cpp @@ -38,7 +38,7 @@ gint get_item_index_by_text(gpointer combo_box, const gchar* target_item) { // Check if the index is within bounds if (gtk_tree_model_iter_nth_child(model, &iter, NULL, index)) { gtk_tree_model_get(model, &iter, 0, ¤t_item_text, -1); - if (g_strcasecmp(target_item, current_item_text) == 0) { + if (g_ascii_strcasecmp(target_item, current_item_text) == 0) { result_index = index; break; } diff --git a/vpr/src/server/serverupdate.cpp b/vpr/src/server/serverupdate.cpp index e084cdf9cfb..09b94443414 100644 --- a/vpr/src/server/serverupdate.cpp +++ b/vpr/src/server/serverupdate.cpp @@ -16,7 +16,7 @@ gboolean update(gpointer data) { GateIO& gate_io = g_vpr_ctx.mutable_server().mutable_gateIO(); TaskResolver& task_resolver = g_vpr_ctx.mutable_server().mutable_task_resolver(); - std::vector tasksBuff; + std::vector tasksBuff; gate_io.takeRecievedTasks(tasksBuff); task_resolver.addTasks(tasksBuff); @@ -26,7 +26,8 @@ gboolean update(gpointer data) { tasksBuff.clear(); task_resolver.takeFinished(tasksBuff); - gate_io.addSendTasks(tasksBuff); + gate_io.moveTasksToSendQueue(tasksBuff); + gate_io.printLogs(); // Call the redraw method of the application if any of task was processed if (process_task) { @@ -40,4 +41,4 @@ gboolean update(gpointer data) { } // namespace server -#endif // NO_GRAPHICS \ No newline at end of file +#endif // NO_GRAPHICS diff --git a/vpr/src/server/task.cpp b/vpr/src/server/task.cpp new file mode 100644 index 00000000000..52a70479eeb --- /dev/null +++ b/vpr/src/server/task.cpp @@ -0,0 +1,111 @@ +#include "task.h" + +#include + +#include "telegrambuffer.h" + +#include "convertutils.h" +#include "commconstants.h" +#include "zlibutils.h" + +namespace server { + +Task::Task(int jobId, int cmd, const std::string& options) +: m_jobId(jobId), m_cmd(cmd), m_options(options) +{ + m_creationTime = std::chrono::high_resolution_clock::now(); +} + +void Task::chopNumSentBytesFromResponseBuffer(std::size_t bytesSentNum) +{ + if (m_responseBuffer.size() >= bytesSentNum) { + m_responseBuffer.erase(0, bytesSentNum); + } else { + m_responseBuffer.clear(); + } + if (m_responseBuffer.empty()) { + m_isResponseFullySent = true; + } +} + +bool Task::optionsMatch(const class std::unique_ptr& other) +{ + if (other->options().size() != m_options.size()) { + return false; + } + return other->options() == m_options; +} + +void Task::fail(const std::string& error) { + m_isFinished = true; + m_error = error; + bakeResponse(); + } +void Task::success(const std::string& result) { + m_result = result; + m_isFinished = true; + bakeResponse(); + } + +std::string Task::info(bool skipDuration) const { + std::stringstream ss; + ss << "task[" + << "id=" << std::to_string(m_jobId) + << ",cmd=" << std::to_string(m_cmd); + if (!skipDuration) { + ss << ",exists=" << getPrettyDurationStrFromMs(timeMsElapsed()); + } + ss << "]"; + return ss.str(); + } + +int64_t Task::timeMsElapsed() const + { + auto now = std::chrono::high_resolution_clock::now(); + return std::chrono::duration_cast(now - m_creationTime).count(); +} + +void Task::bakeResponse() +{ + std::stringstream ss; + ss << "{"; + + ss << "\"" << comm::KEY_JOB_ID << "\":\"" << m_jobId << "\","; + ss << "\"" << comm::KEY_CMD << "\":\"" << m_cmd << "\","; + ss << "\"" << comm::KEY_OPTIONS << "\":\"" << m_options << "\","; + if (hasError()) { + ss << "\"" << comm::KEY_DATA << "\":\"" << m_error << "\","; + } else { + ss << "\"" << comm::KEY_DATA << "\":\"" << m_result << "\","; + } + int status = hasError() ? 0 : 1; + ss << "\"" << comm::KEY_STATUS << "\":\"" << status << "\""; + + ss << "}"; + + std::optional bodyOpt; + uint8_t compressorId = comm::NONE_COMPRESSOR_ID; +#ifndef DISABLE_ZLIB_TELEGRAM_DATA_FIELD_COMPRESSION + bodyOpt = tryCompress(ss.str()); + if (bodyOpt) { + compressorId = comm::ZLIB_COMPRESSOR_ID; + } +#endif + if (!bodyOpt) { + // fail to compress, use raw + compressorId = comm::NONE_COMPRESSOR_ID; + bodyOpt = std::move(ss.str()); + } + + std::string body = bodyOpt.value(); + m_telegramHeader = comm::TelegramHeader::constructFromData(body, compressorId); + + m_responseBuffer.append(m_telegramHeader.buffer().begin(), m_telegramHeader.buffer().end()); + m_responseBuffer.append(std::move(body)); + body.clear(); + m_origReponseBytesNum = m_responseBuffer.size(); +} + + +} // namespace server + diff --git a/vpr/src/server/task.h b/vpr/src/server/task.h index 3d3e50ae268..a14fd4228f6 100644 --- a/vpr/src/server/task.h +++ b/vpr/src/server/task.h @@ -2,10 +2,10 @@ #define TASK_H #include -#include -#include +#include +#include -#include "commconstants.h" +#include "telegramheader.h" namespace server { @@ -17,62 +17,56 @@ namespace server { */ class Task { public: - Task(int jobId, int cmd, const std::string& options = ""): - m_jobId(jobId), m_cmd(cmd), m_options(options) {} + Task(int jobId, int cmd, const std::string& options = ""); + + Task(const Task&) = delete; + Task& operator=(const Task&) = delete; int jobId() const { return m_jobId; } int cmd() const { return m_cmd; } - const std::string& options() const { return m_options; } - const std::string& result() const { return m_result; } - std::string info() const { - std::stringstream result; - result << "id=" << std::to_string(m_jobId) << ","; - result << "cmd=" << std::to_string(m_cmd) << ","; - result << "opt=" << m_options; - return result.str(); - } + void chopNumSentBytesFromResponseBuffer(std::size_t bytesSentNum); + + bool optionsMatch(const class std::unique_ptr& other); + + const std::string& responseBuffer() const { return m_responseBuffer; } bool isFinished() const { return m_isFinished; } - bool hasError() const { return m_hasError; } - - void fail(const std::string& error) { - std::cout << "task " << info() << " finished with error " << error << std::endl; - m_result = error; - m_isFinished = true; - m_hasError = true; - } - void success(const std::string& result = "") { - std::cout << "task " << info() << " finished with success " << result << std::endl; - m_result = result; - m_isFinished = true; - } - - std::string toJsonStr() const - { - std::stringstream ss; - ss << "{"; - - ss << "\"" << comm::KEY_JOB_ID << "\":\"" << m_jobId << "\","; - ss << "\"" << comm::KEY_CMD << "\":\"" << m_cmd << "\","; - ss << "\"" << comm::KEY_OPTIONS << "\":\"" << m_options << "\","; - ss << "\"" << comm::KEY_DATA << "\":\"" << m_result << "\","; - int status = m_hasError ? 0 : 1; - ss << "\"" << comm::KEY_STATUS << "\":\"" << status << "\""; - - ss << "}"; - - return ss.str(); - } + bool hasError() const { return !m_error.empty(); } + const std::string& error() const { return m_error; } + + std::size_t origReponseBytesNum() const { return m_origReponseBytesNum; } + + bool isResponseFullySent() const { return m_isResponseFullySent; } + + void fail(const std::string& error); + void success(const std::string& result = ""); + + std::string info(bool skipDuration = false) const; + + const comm::TelegramHeader& telegramHeader() const { return m_telegramHeader; } + + const std::string& options() const { return m_options; } private: int m_jobId = -1; int m_cmd = -1; std::string m_options; std::string m_result; + std::string m_error; bool m_isFinished = false; - bool m_hasError = false; + comm::TelegramHeader m_telegramHeader; + std::string m_responseBuffer; + std::size_t m_origReponseBytesNum = 0; + bool m_isResponseFullySent = false; + + std::chrono::high_resolution_clock::time_point m_creationTime; + + int64_t timeMsElapsed() const; + + void bakeResponse(); }; +using TaskPtr = std::unique_ptr; } // namespace server diff --git a/vpr/src/server/taskresolver.cpp b/vpr/src/server/taskresolver.cpp index 653fdd39099..ca602162e8a 100644 --- a/vpr/src/server/taskresolver.cpp +++ b/vpr/src/server/taskresolver.cpp @@ -1,48 +1,50 @@ #include "taskresolver.h" +#include "commconstants.h" #include "globals.h" #include "pathhelper.h" #include "telegramoptions.h" #include "telegramparser.h" #include "gtkcomboboxhelper.h" + #include namespace server { -void TaskResolver::addTask(Task task) +void TaskResolver::addTask(TaskPtr& newTask) { - // pre-process task before adding, where we could quickly detect failure scenario - for (auto& t: m_tasks) { - if (t.cmd() == task.cmd()) { - if (t.options() == task.options()) { - std::string msg = "similar task is already in execution, reject new task: " + t.info()+ " and waiting for old task: " + task.info() + " execution"; - task.fail(msg); + // pre-process task before adding, where we could quickly detect failure scenarios + for (const auto& task: m_tasks) { + if (task->cmd() == newTask->cmd()) { + if (task->optionsMatch(newTask)) { + std::string msg = "similar task is already in execution, reject new " + task->info()+ " and waiting for old " + newTask->info() + " execution"; + newTask->fail(msg); } else { - // case when task has same jobId but different options - if (task.jobId() > t.jobId()) { - std::string msg = "old task: " + t.info() + " is overriden by a new task: " + task.info(); - t.fail(msg); + // handle case when task has same jobId but different options + if (newTask->jobId() > task->jobId()) { + std::string msg = "old " + task->info() + " is overriden by a new " + newTask->info(); + task->fail(msg); } } } } // add task - m_tasks.push_back(std::move(task)); + m_tasks.push_back(std::move(newTask)); } -void TaskResolver::addTasks(const std::vector& tasks) +void TaskResolver::addTasks(std::vector& tasks) { - for (const Task& task: tasks) { + for (TaskPtr& task: tasks) { addTask(task); } } -void TaskResolver::takeFinished(std::vector& result) +void TaskResolver::takeFinished(std::vector& result) { for (auto it=m_tasks.begin(); it != m_tasks.end();) { - Task task = *it; - if (task.isFinished()) { + TaskPtr& task = *it; + if (task->isFinished()) { result.push_back(std::move(task)); it = m_tasks.erase(it); } else { @@ -71,8 +73,8 @@ bool TaskResolver::update(ezgl::application* app) { bool has_processed_task = false; for (auto& task: m_tasks) { - if (!task.isFinished()) { - switch(task.cmd()) { + if (!task->isFinished()) { + switch(task->cmd()) { case comm::CMD_GET_PATH_LIST_ID: { processGetPathListTask(app, task); has_processed_task = true; @@ -83,6 +85,7 @@ bool TaskResolver::update(ezgl::application* app) has_processed_task = true; break; } + default: break; } } } @@ -90,9 +93,9 @@ bool TaskResolver::update(ezgl::application* app) return has_processed_task; } -void TaskResolver::processGetPathListTask(ezgl::application* app, Task& task) +void TaskResolver::processGetPathListTask(ezgl::application*, const TaskPtr& task) { - TelegramOptions options{task.options(), {comm::OPTION_PATH_NUM, comm::OPTION_PATH_TYPE, comm::OPTION_DETAILS_LEVEL, comm::OPTION_IS_FLOAT_ROUTING}}; + TelegramOptions options{task->options(), {comm::OPTION_PATH_NUM, comm::OPTION_PATH_TYPE, comm::OPTION_DETAILS_LEVEL, comm::OPTION_IS_FLOAT_ROUTING}}; if (!options.hasErrors()) { ServerContext& server_ctx = g_vpr_ctx.mutable_server(); // shortcut @@ -114,22 +117,22 @@ void TaskResolver::processGetPathListTask(ezgl::application* app, Task& task) if (crit_paths_result.isValid()) { std::string msg{crit_paths_result.report}; - task.success(msg); + task->success(msg); } else { std::string msg{"Critical paths report is empty"}; std::cerr << msg << std::endl; - task.fail(msg); + task->fail(msg); } } else { std::string msg{"options errors in get crit path list telegram: " + options.errorsStr()}; std::cerr << msg << std::endl; - task.fail(msg); + task->fail(msg); } } -void TaskResolver::processDrawCriticalPathTask(ezgl::application* app, Task& task) +void TaskResolver::processDrawCriticalPathTask(ezgl::application* app, const TaskPtr& task) { - TelegramOptions options{task.options(), {comm::OPTION_PATH_ELEMENTS, comm::OPTION_HIGHTLIGHT_MODE, comm::OPTION_DRAW_PATH_CONTOUR}}; + TelegramOptions options{task->options(), {comm::OPTION_PATH_ELEMENTS, comm::OPTION_HIGHTLIGHT_MODE, comm::OPTION_DRAW_PATH_CONTOUR}}; if (!options.hasErrors()) { ServerContext& server_ctx = g_vpr_ctx.mutable_server(); // shortcut @@ -146,16 +149,16 @@ void TaskResolver::processDrawCriticalPathTask(ezgl::application* app, Task& tas gint highLightModeIndex = get_item_index_by_text(toggle_crit_path, highLightMode.c_str()); if (highLightModeIndex != -1) { gtk_combo_box_set_active(toggle_crit_path, highLightModeIndex); - task.success(); + task->success(); } else { std::string msg{"cannot find ToggleCritPath qcombobox index for item " + highLightMode}; std::cerr << msg << std::endl; - task.fail(msg); + task->fail(msg); } } else { std::string msg{"options errors in highlight crit path telegram: " + options.errorsStr()}; std::cerr << msg << std::endl; - task.fail(msg); + task->fail(msg); } } diff --git a/vpr/src/server/taskresolver.h b/vpr/src/server/taskresolver.h index d4a6a7f7ef1..afb1ddcd266 100644 --- a/vpr/src/server/taskresolver.h +++ b/vpr/src/server/taskresolver.h @@ -26,22 +26,22 @@ class TaskResolver { int tasksNum() const { return m_tasks.size(); } /* add tasks to process */ - void addTask(Task); - void addTasks(const std::vector&); + void addTask(TaskPtr&); + void addTasks(std::vector&); /* process tasks */ bool update(ezgl::application*); /* extract finished tasks */ - void takeFinished(std::vector&); + void takeFinished(std::vector&); - const std::vector& tasks() const { return m_tasks; } + const std::vector& tasks() const { return m_tasks; } private: - std::vector m_tasks; + std::vector m_tasks; - void processGetPathListTask(ezgl::application*, Task&); - void processDrawCriticalPathTask(ezgl::application*, Task&); + void processGetPathListTask(ezgl::application*, const TaskPtr&); + void processDrawCriticalPathTask(ezgl::application*, const TaskPtr&); e_timing_report_detail getDetailsLevelEnum(const std::string& pathDetailsLevelStr) const; }; diff --git a/vpr/src/server/telegrambuffer.cpp b/vpr/src/server/telegrambuffer.cpp index d80b70f7fed..6eab588a339 100644 --- a/vpr/src/server/telegrambuffer.cpp +++ b/vpr/src/server/telegrambuffer.cpp @@ -1,29 +1,68 @@ #include "telegrambuffer.h" -#include "commconstants.h" namespace comm { - + void TelegramBuffer::append(const ByteArray& bytes) { m_rawBuffer.append(bytes); } -std::vector TelegramBuffer::takeFrames() +bool TelegramBuffer::checkRawBuffer() { - std::vector result; - ByteArray candidate; - for (unsigned char b: m_rawBuffer) { - if (b == TELEGRAM_FRAME_DELIMETER) { - if (!candidate.empty()) { - result.push_back(candidate); + std::size_t signatureStartIndex = m_rawBuffer.findSequence(TelegramHeader::SIGNATURE, TelegramHeader::SIGNATURE_SIZE); + if (signatureStartIndex != std::size_t(-1)) { + std::size_t index = signatureStartIndex; + if (index != 0) { + m_rawBuffer.erase(m_rawBuffer.begin(), m_rawBuffer.begin()+index); + } + return true; + } + return false; +} + +void TelegramBuffer::takeTelegramFrames(std::vector& result) +{ + if (m_rawBuffer.size() < TelegramHeader::size()) { + return; + } + if (!m_headerOpt) { + if (checkRawBuffer()) { + TelegramHeader header(m_rawBuffer); + if (header.isValid()) { + m_headerOpt = std::move(header); } - candidate = ByteArray(); - } else { - candidate.append(b); } } - std::swap(m_rawBuffer, candidate); + + if (m_headerOpt) { + const TelegramHeader& header = m_headerOpt.value(); + if (m_rawBuffer.size() >= TelegramHeader::size() + header.bodyBytesNum()) { + ByteArray data(m_rawBuffer.begin() + TelegramHeader::size(), m_rawBuffer.begin() + TelegramHeader::size() + header.bodyBytesNum()); + uint32_t actualCheckSum = data.calcCheckSum(); + if (actualCheckSum == header.bodyCheckSum()) { + TelegramFramePtr telegramFramePtr = std::make_shared(TelegramFrame{header, std::move(data)}); + data.clear(); + result.push_back(telegramFramePtr); + } else { + m_errors.push_back("wrong checkSums " + std::to_string(actualCheckSum) +" for " + header.info() + " , drop this chunk"); + } + m_rawBuffer.erase(m_rawBuffer.begin(), m_rawBuffer.begin()+TelegramHeader::size()+header.bodyBytesNum()); + m_headerOpt.reset(); + } + } +} + +std::vector TelegramBuffer::takeTelegramFrames() +{ + std::vector result; + takeTelegramFrames(result); return result; } +void TelegramBuffer::takeErrors(std::vector& errors) +{ + errors.clear(); + std::swap(errors, m_errors); +} + } // namespace comm diff --git a/vpr/src/server/telegrambuffer.h b/vpr/src/server/telegrambuffer.h index 1ad090aa2fa..fa8f25f0d6d 100644 --- a/vpr/src/server/telegrambuffer.h +++ b/vpr/src/server/telegrambuffer.h @@ -1,47 +1,16 @@ #ifndef TELEGRAMBUFFER_H #define TELEGRAMBUFFER_H +#include "bytearray.h" +#include "telegramframe.h" + #include #include #include +#include namespace comm { -/** - * @brief ByteArray as a simple wrapper over std::vector -*/ -class ByteArray : public std::vector { -public: - static const std::size_t DEFAULT_SIZE_HINT = 1024; - - ByteArray(const char* data) - : std::vector(reinterpret_cast(data), - reinterpret_cast(data + std::strlen(data))) - {} - - ByteArray(const char* data, std::size_t size) - : std::vector(reinterpret_cast(data), - reinterpret_cast(data + size)) - {} - ByteArray(std::size_t sizeHint = DEFAULT_SIZE_HINT) { - this->reserve(sizeHint); - } - - void append(const ByteArray& appendix) { - for (unsigned char b: appendix) { - this->push_back(b); - } - } - - void append(unsigned char b) { - this->push_back(b); - } - - std::string to_string() const { - return std::string(reinterpret_cast(this->data()), this->size()); - } -}; - /** * @brief Implements Telegram Buffer as a wrapper over BytesArray * @@ -50,21 +19,30 @@ class ByteArray : public std::vector { class TelegramBuffer { static const std::size_t DEFAULT_SIZE_HINT = 1024; + public: TelegramBuffer(std::size_t sizeHint = DEFAULT_SIZE_HINT): m_rawBuffer(sizeHint) {} ~TelegramBuffer()=default; + bool empty() { return m_rawBuffer.empty(); } + void clear() { m_rawBuffer.clear(); } void append(const ByteArray&); - std::vector takeFrames(); + void takeTelegramFrames(std::vector&); + std::vector takeTelegramFrames(); + void takeErrors(std::vector&); const ByteArray& data() const { return m_rawBuffer; } private: ByteArray m_rawBuffer; + std::vector m_errors; + std::optional m_headerOpt; + + bool checkRawBuffer(); }; -} // namespace comm +} // namespace comm #endif // TELEGRAMBUFFER_H diff --git a/vpr/src/server/telegramframe.h b/vpr/src/server/telegramframe.h new file mode 100644 index 00000000000..b942efa10ec --- /dev/null +++ b/vpr/src/server/telegramframe.h @@ -0,0 +1,19 @@ +#ifndef TELEGRAMFRAME_H +#define TELEGRAMFRAME_H + +#include "telegramheader.h" +#include "bytearray.h" + +#include + +namespace comm { + +struct TelegramFrame { + TelegramHeader header; + ByteArray data; +}; +using TelegramFramePtr = std::shared_ptr; + +} // namespace comm + +#endif // TELEGRAMFRAME_H diff --git a/vpr/src/server/telegramheader.cpp b/vpr/src/server/telegramheader.cpp new file mode 100644 index 00000000000..cb95e4ced5e --- /dev/null +++ b/vpr/src/server/telegramheader.cpp @@ -0,0 +1,76 @@ +#include "telegramheader.h" +#include "convertutils.h" + +#include + +namespace comm { + +TelegramHeader::TelegramHeader(uint32_t length, uint32_t checkSum, uint8_t compressorId) + : m_bodyBytesNum(length) + , m_bodyCheckSum(checkSum) + , m_compressorId(compressorId) +{ + m_buffer.resize(TelegramHeader::size()); + + // Write signature into a buffer + std::memcpy(m_buffer.data(), TelegramHeader::SIGNATURE, TelegramHeader::SIGNATURE_SIZE); + + // Write the length into the buffer in big-endian byte order + std::memcpy(m_buffer.data() + TelegramHeader::LENGTH_OFFSET, &length, TelegramHeader::LENGTH_SIZE); + + // Write the checksum into the buffer in big-endian byte order + std::memcpy(m_buffer.data() + TelegramHeader::CHECKSUM_OFFSET, &checkSum, TelegramHeader::CHECKSUM_SIZE); + + // Write compressor id + std::memcpy(m_buffer.data() + TelegramHeader::COMPRESSORID_OFFSET, &compressorId, TelegramHeader::COMPRESSORID_SIZE); + + m_isValid = true; +} + +TelegramHeader::TelegramHeader(const ByteArray& buffer) +{ + m_buffer.resize(TelegramHeader::size()); + + bool hasError = false; + + if (buffer.size() >= TelegramHeader::size()) { + // Check the signature to ensure that this is a valid header + if (std::memcmp(buffer.data(), TelegramHeader::SIGNATURE, TelegramHeader::SIGNATURE_SIZE)) { + hasError = true; + } + + // Read the length from the buffer in big-endian byte order + std::memcpy(&m_bodyBytesNum, buffer.data() + TelegramHeader::LENGTH_OFFSET, TelegramHeader::LENGTH_SIZE); + + // Read the checksum from the buffer in big-endian byte order + std::memcpy(&m_bodyCheckSum, buffer.data() + TelegramHeader::CHECKSUM_OFFSET, TelegramHeader::CHECKSUM_SIZE); + + // Read the checksum from the buffer in big-endian byte order + std::memcpy(&m_compressorId, buffer.data() + TelegramHeader::COMPRESSORID_OFFSET, TelegramHeader::COMPRESSORID_SIZE); + + if (m_bodyBytesNum == 0) { + hasError = false; + } + if (m_bodyCheckSum == 0) { + hasError = false; + } + } + + if (!hasError) { + m_isValid = true; + } +} + +std::string TelegramHeader::info() const { + std::stringstream ss; + ss << "header" << (m_isValid?"":"(INVALID)") << "[" + << "l=" << getPrettySizeStrFromBytesNum(m_bodyBytesNum) + << "/s=" << m_bodyCheckSum; + if (m_compressorId) { + ss << "/c=" << m_compressorId; + } + ss << "]"; + return ss.str(); +} + +} // namespace comm diff --git a/vpr/src/server/telegramheader.h b/vpr/src/server/telegramheader.h new file mode 100644 index 00000000000..eb9cac30e1c --- /dev/null +++ b/vpr/src/server/telegramheader.h @@ -0,0 +1,61 @@ +#ifndef TELEGRAMHEADER_H +#define TELEGRAMHEADER_H + +#include "bytearray.h" + +#include +#include + +namespace comm { + +class TelegramHeader { +public: + static constexpr const char SIGNATURE[] = "IPA"; + static constexpr size_t SIGNATURE_SIZE = sizeof(SIGNATURE); + static constexpr size_t LENGTH_SIZE = sizeof(uint32_t); + static constexpr size_t CHECKSUM_SIZE = LENGTH_SIZE; + static constexpr size_t COMPRESSORID_SIZE = 1; + + static constexpr size_t LENGTH_OFFSET = SIGNATURE_SIZE; + static constexpr size_t CHECKSUM_OFFSET = LENGTH_OFFSET + LENGTH_SIZE; + static constexpr size_t COMPRESSORID_OFFSET = CHECKSUM_OFFSET + CHECKSUM_SIZE; + + TelegramHeader()=default; + explicit TelegramHeader(uint32_t length, uint32_t checkSum, uint8_t compressorId = 0); + explicit TelegramHeader(const ByteArray& body); + ~TelegramHeader()=default; + + template + static comm::TelegramHeader constructFromData(const T& body, uint8_t compressorId = 0) { + uint32_t bodyCheckSum = ByteArray::calcCheckSum(body); + return comm::TelegramHeader{static_cast(body.size()), bodyCheckSum, compressorId}; + } + + static constexpr size_t size() { + return SIGNATURE_SIZE + LENGTH_SIZE + CHECKSUM_SIZE + COMPRESSORID_SIZE; + } + + bool isValid() const { return m_isValid; } + + const ByteArray& buffer() const { return m_buffer; } + + uint32_t bodyBytesNum() const { return m_bodyBytesNum; } + uint32_t bodyCheckSum() const { return m_bodyCheckSum; } + uint8_t compressorId() const { return m_compressorId; } + + bool isBodyCompressed() const { return m_compressorId != 0; } + + std::string info() const; + +private: + bool m_isValid = false; + ByteArray m_buffer; + + uint32_t m_bodyBytesNum = 0; + uint32_t m_bodyCheckSum = 0; + uint8_t m_compressorId = 0; +}; + +} // namespace comm + +#endif // TELEGRAMHEADER_H diff --git a/vpr/src/server/telegramoptions.h b/vpr/src/server/telegramoptions.h index 8163b3eb38d..1b37b15f8b2 100644 --- a/vpr/src/server/telegramoptions.h +++ b/vpr/src/server/telegramoptions.h @@ -1,13 +1,13 @@ #ifndef TELEGRAMOPTIONS_H #define TELEGRAMOPTIONS_H -#include +#include "convertutils.h" #include -#include #include #include #include +#include namespace server { diff --git a/vpr/src/server/telegramparser.cpp b/vpr/src/server/telegramparser.cpp index d2862828627..07bef029091 100644 --- a/vpr/src/server/telegramparser.cpp +++ b/vpr/src/server/telegramparser.cpp @@ -2,20 +2,17 @@ #include "convertutils.h" #include "commconstants.h" -#include namespace comm { -std::optional TelegramParser::tryExtractJsonValueStr(const std::string& jsonString, const std::string& key) +bool TelegramParser::tryExtractJsonValueStr(const std::string& jsonString, const std::string& key, std::optional& result) { - std::optional result; - // Find the position of the key size_t keyPos = jsonString.find("\"" + key + "\":"); if (keyPos == std::string::npos) { // Key not found - return result; + return false; } // Find the position of the value after the key @@ -23,7 +20,7 @@ std::optional TelegramParser::tryExtractJsonValueStr(const std::str if (valuePosStart == std::string::npos) { // Value not found - return result; + return false; } // Find the position of the closing quote for the value @@ -31,18 +28,19 @@ std::optional TelegramParser::tryExtractJsonValueStr(const std::str if (valueEnd == std::string::npos) { // Closing quote not found - return result; + return false; } // Extract the value substring - result = jsonString.substr(valuePosStart + 1, (valueEnd - valuePosStart) - 1); - return result; + result = std::move(jsonString.substr(valuePosStart + 1, (valueEnd - valuePosStart) - 1)); + return true; } std::optional TelegramParser::tryExtractFieldJobId(const std::string& message) { std::optional result; - if (std::optional strOpt = tryExtractJsonValueStr(message, comm::KEY_JOB_ID)) { + std::optional strOpt; + if (tryExtractJsonValueStr(message, comm::KEY_JOB_ID, strOpt)) { result = tryConvertToInt(strOpt.value()); } return result; @@ -51,26 +49,28 @@ std::optional TelegramParser::tryExtractFieldJobId(const std::string& messa std::optional TelegramParser::tryExtractFieldCmd(const std::string& message) { std::optional result; - if (std::optional strOpt = tryExtractJsonValueStr(message, comm::KEY_CMD)) { + std::optional strOpt; + if (tryExtractJsonValueStr(message, comm::KEY_CMD, strOpt)) { result = tryConvertToInt(strOpt.value()); } return result; } -std::optional TelegramParser::tryExtractFieldOptions(const std::string& message) +bool TelegramParser::tryExtractFieldOptions(const std::string& message, std::optional& result) { - return tryExtractJsonValueStr(message, comm::KEY_OPTIONS); + return tryExtractJsonValueStr(message, comm::KEY_OPTIONS, result); } -std::optional TelegramParser::tryExtractFieldData(const std::string& message) +bool TelegramParser::tryExtractFieldData(const std::string& message, std::optional& result) { - return tryExtractJsonValueStr(message, comm::KEY_DATA); + return tryExtractJsonValueStr(message, comm::KEY_DATA, result); } std::optional TelegramParser::tryExtractFieldStatus(const std::string& message) { std::optional result; - if (std::optional strOpt = tryExtractJsonValueStr(message, comm::KEY_STATUS)) { + std::optional strOpt; + if (tryExtractJsonValueStr(message, comm::KEY_STATUS, strOpt)) { result = tryConvertToInt(strOpt.value()); } return result; diff --git a/vpr/src/server/telegramparser.h b/vpr/src/server/telegramparser.h index a27c737eec1..6f9eca4f37b 100644 --- a/vpr/src/server/telegramparser.h +++ b/vpr/src/server/telegramparser.h @@ -16,12 +16,12 @@ class TelegramParser { public: static std::optional tryExtractFieldJobId(const std::string& message); static std::optional tryExtractFieldCmd(const std::string& message); - static std::optional tryExtractFieldOptions(const std::string& message); - static std::optional tryExtractFieldData(const std::string& message); + static bool tryExtractFieldOptions(const std::string& message, std::optional& result); + static bool tryExtractFieldData(const std::string& message, std::optional& result); static std::optional tryExtractFieldStatus(const std::string& message); private: - static std::optional tryExtractJsonValueStr(const std::string& jsonString, const std::string& key); + static bool tryExtractJsonValueStr(const std::string& jsonString, const std::string& key, std::optional& result); }; } // namespace comm diff --git a/vpr/src/server/zlibutils.cpp b/vpr/src/server/zlibutils.cpp new file mode 100644 index 00000000000..0cddc55a183 --- /dev/null +++ b/vpr/src/server/zlibutils.cpp @@ -0,0 +1,79 @@ +#include "zlibutils.h" + +#include // Include cstring for memset +#include + +std::string tryCompress(const std::string& decompressed) +{ + z_stream zs; + memset(&zs, 0, sizeof(zs)); + + if (deflateInit(&zs, Z_BEST_COMPRESSION) != Z_OK) { + return ""; + } + + zs.next_in = (Bytef*)decompressed.data(); + zs.avail_in = decompressed.size(); + + int retCode; + char resultBuffer[32768]; + std::string result; + + do { + zs.next_out = reinterpret_cast(resultBuffer); + zs.avail_out = sizeof(resultBuffer); + + retCode = deflate(&zs, Z_FINISH); + + if (result.size() < zs.total_out) { + result.append(resultBuffer, zs.total_out - result.size()); + } + } while (retCode == Z_OK); + + deflateEnd(&zs); + + if (retCode != Z_STREAM_END) { + return ""; + } + + return result; +} + +std::string tryDecompress(const std::string& compressed) +{ + z_stream zs; + memset(&zs, 0, sizeof(zs)); + + if (inflateInit(&zs) != Z_OK) { + return ""; + } + + zs.next_in = (Bytef*)compressed.data(); + zs.avail_in = compressed.size(); + + int retCode; + char resultBuffer[32768]; + std::string result; + + do { + zs.next_out = reinterpret_cast(resultBuffer); + zs.avail_out = sizeof(resultBuffer); + + retCode = inflate(&zs, 0); + + if (result.size() < zs.total_out) { + result.append(resultBuffer, zs.total_out - result.size()); + } + + } while (retCode == Z_OK); + + inflateEnd(&zs); + + if (retCode != Z_STREAM_END) { + return ""; + } + + return result; +} + + diff --git a/vpr/src/server/zlibutils.h b/vpr/src/server/zlibutils.h new file mode 100644 index 00000000000..7ef1873e6d1 --- /dev/null +++ b/vpr/src/server/zlibutils.h @@ -0,0 +1,10 @@ +#ifndef ZLIBUTILS_H +#define ZLIBUTILS_H + +#include +#include + +std::string tryCompress(const std::string& decompressed); +std::string tryDecompress(const std::string& compressed); + +#endif diff --git a/vpr/test/test_server_convertutils.cpp b/vpr/test/test_server_convertutils.cpp new file mode 100644 index 00000000000..3431cede2d6 --- /dev/null +++ b/vpr/test/test_server_convertutils.cpp @@ -0,0 +1,18 @@ +#include "convertutils.h" + +#include "catch2/catch_test_macros.hpp" +#include "catch2/matchers/catch_matchers_all.hpp" + +TEST_CASE("test_server_convert_utils_to_int", "[vpr]") +{ + REQUIRE(std::optional{-2} == tryConvertToInt("-2")); + REQUIRE(std::optional{0} == tryConvertToInt("0")); + REQUIRE(std::optional{2} == tryConvertToInt("2")); + REQUIRE(std::nullopt == tryConvertToInt("2.")); + REQUIRE(std::nullopt == tryConvertToInt("2.0")); + REQUIRE(std::nullopt == tryConvertToInt("two")); + REQUIRE(std::nullopt == tryConvertToInt("2k")); + REQUIRE(std::nullopt == tryConvertToInt("k2")); +} + + diff --git a/vpr/test/test_server_taskresolver.cpp b/vpr/test/test_server_taskresolver.cpp new file mode 100644 index 00000000000..7b76f81a885 --- /dev/null +++ b/vpr/test/test_server_taskresolver.cpp @@ -0,0 +1,82 @@ +// #include "catch2/catch_test_macros.hpp" +// #include "catch2/matchers/catch_matchers_all.hpp" + +// #include "taskresolver.h" +// #include + +// namespace { + +// TEST_CASE("test_server_taskresolver_cmdSpamFilter", "[vpr]") { +// server::TaskResolver resolver; +// resolver.addTask(std::make_unique(1,1)); +// resolver.addTask(std::make_unique(2,1)); +// resolver.addTask(std::make_unique(3,1)); +// resolver.addTask(std::make_unique(4,1)); +// resolver.addTask(std::make_unique(5,1)); + +// std::vector finished; +// resolver.takeFinished(finished); + +// REQUIRE(finished.size() == 4); + +// for (const auto& task: finished) { +// REQUIRE(task.isFinished()); +// REQUIRE(task.hasError()); +// REQUIRE(task.jobId() != 1); +// } +// REQUIRE(resolver.tasksNum() == 1); +// server::Task task = resolver.tasks()[0]; +// REQUIRE(task.jobId()==1); +// REQUIRE(task.cmd()==1); +// } + +// TEST_CASE("test_server_taskresolver_cmdOverrideFilter", "[vpr]") { +// server::TaskResolver resolver; +// resolver.addTask(server::Task{1,2,"1"}); +// resolver.addTask(server::Task{2,2,"11"}); +// resolver.addTask(server::Task{3,2,"222"}); + +// std::vector finished; +// resolver.takeFinished(finished); + +// REQUIRE(finished.size() == 2); + +// for (const auto& task: finished) { +// REQUIRE(task.isFinished()); +// REQUIRE(task.hasError()); +// REQUIRE(task.jobId() != 3); +// } +// REQUIRE(resolver.tasksNum() == 1); +// server::Task task = resolver.tasks()[0]; +// REQUIRE(task.jobId()==3); +// REQUIRE(task.cmd()==2); +// REQUIRE(task.options()=="222"); +// } + +// TEST_CASE("test_server_taskresolver_cmdSpamAndOverrideOptions", "[vpr]") { +// server::TaskResolver resolver; +// resolver.addTask(server::Task{1,2,"1"}); +// resolver.addTask(server::Task{2,2,"11"}); +// resolver.addTask(server::Task{3,2,"222"}); +// resolver.addTask(server::Task{4,2,"222"}); +// resolver.addTask(server::Task{5,1}); +// resolver.addTask(server::Task{6,1}); +// resolver.addTask(server::Task{7,1}); + +// std::vector finished; +// resolver.takeFinished(finished); + +// REQUIRE(resolver.tasksNum() == 2); +// server::TaskPtr task0 = resolver.tasks()[0]; +// server::TaskPtr task1 = resolver.tasks()[1]; + +// REQUIRE(task0.jobId()==3); +// REQUIRE(task0.cmd()==2); +// REQUIRE(task0.options()=="222"); + +// REQUIRE(task1.jobId()==5); +// REQUIRE(task1.cmd()==1); +// REQUIRE(task1.options()==""); +// } + +// } // namespace \ No newline at end of file diff --git a/vpr/test/test_server_telegrambuffer.cpp b/vpr/test/test_server_telegrambuffer.cpp new file mode 100644 index 00000000000..2504689caa4 --- /dev/null +++ b/vpr/test/test_server_telegrambuffer.cpp @@ -0,0 +1,89 @@ +#include "catch2/catch_test_macros.hpp" +#include "catch2/matchers/catch_matchers_all.hpp" + +#include "telegrambuffer.h" + +namespace { + +TEST_CASE("test_server_bytearray", "[vpr]") { + comm::ByteArray array1{"111"}; + comm::ByteArray array2{"222"}; + comm::ByteArray array{array1}; + array.append(array2); + + REQUIRE(array.at(0) == '1'); + REQUIRE(array.at(1) == '1'); + REQUIRE(array.at(2) == '1'); + REQUIRE(array.at(3) == '2'); + REQUIRE(array.at(4) == '2'); + REQUIRE(array.at(5) == '2'); + + REQUIRE(array.to_string() == "111222"); + + REQUIRE(array.size() == 6); + + array.append('3'); + + REQUIRE(array.size() == 7); + REQUIRE(array.to_string() == "1112223"); + + REQUIRE(array.at(6) == '3'); + + array.clear(); + + REQUIRE(array.size() == 0); + REQUIRE(array.to_string() == ""); +} + +// TEST_CASE("test_server_telegrambuffer_oneOpened", "[vpr]") { +// comm::TelegramBuffer buff{1024}; +// buff.append(comm::ByteArray{"111"}); +// buff.append(comm::ByteArray{"222"}); + +// auto frames = buff.takeTelegramFrames(); +// REQUIRE(frames.size() == 0); + +// REQUIRE(buff.data().to_string() == "111222"); +// } + +// TEST_CASE("test_server_telegrambuffer_oneFinishedOneOpened", "[vpr]") { +// comm::TelegramBuffer buff{1024}; +// buff.append(comm::ByteArray{"111\x17"}); +// buff.append(comm::ByteArray{"222"}); + +// auto frames = buff.takeTelegramFrames(); +// REQUIRE(frames.size() == 1); + +// REQUIRE(frames[0]->data.to_string() == "111"); + +// REQUIRE(buff.data().to_string() == "222"); +// } + +// TEST_CASE("test_server_telegrambuffer_twoFinished", "[vpr]") { +// comm::TelegramBuffer buff{1024}; +// buff.append(comm::ByteArray{"111\x17"}); +// buff.append(comm::ByteArray{"222\x17"}); + +// auto frames = buff.takeTelegramFrames(); +// REQUIRE(frames.size() == 2); + +// REQUIRE(frames[0]->data.to_string() == "111"); +// REQUIRE(frames[1]->data.to_string() == "222"); + +// REQUIRE(buff.data().to_string() == ""); +// } + +// TEST_CASE("test_server_telegrambuffer_twoCleared", "[vpr]") { +// comm::TelegramBuffer buff{1024}; +// buff.append(comm::ByteArray{"111\x17"}); +// buff.append(comm::ByteArray{"222\x17"}); + +// buff.clear(); + +// auto frames = buff.takeTelegramFrames(); +// REQUIRE(frames.size() == 0); + +// REQUIRE(buff.data().to_string() == ""); +// } + +// } // namespace diff --git a/vpr/test/test_server_telegramoptions.cpp b/vpr/test/test_server_telegramoptions.cpp new file mode 100644 index 00000000000..36ff80157f1 --- /dev/null +++ b/vpr/test/test_server_telegramoptions.cpp @@ -0,0 +1,19 @@ +#include "catch2/catch_test_macros.hpp" +#include "catch2/matchers/catch_matchers_all.hpp" + +#include "telegramoptions.h" + +namespace { + +TEST_CASE("test_server_telegramoptions", "[vpr]") { + server::TelegramOptions options{"int:path_num:11;string:path_type:debug;int:details_level:3;bool:is_flat_routing:0", {"path_num", "path_type", "details_level", "is_flat_routing"}}; + + REQUIRE(options.errorsStr() == ""); + + REQUIRE(options.getString("path_type") == "debug"); + REQUIRE(options.getInt("path_num", -1) == 11); + REQUIRE(options.getInt("details_level", -1) == 3); + REQUIRE(options.getBool("is_flat_routing", true) == false); +} + +} // namespace diff --git a/vpr/test/test_server_telegramparser.cpp b/vpr/test/test_server_telegramparser.cpp new file mode 100644 index 00000000000..41f0df55e7d --- /dev/null +++ b/vpr/test/test_server_telegramparser.cpp @@ -0,0 +1,21 @@ +#include "telegramparser.h" + +#include "catch2/catch_test_macros.hpp" +#include "catch2/matchers/catch_matchers_all.hpp" + +TEST_CASE("test_server_telegram_parser_base", "[vpr]") +{ + const std::string tdata{R"({"JOB_ID":"7","CMD":"2","OPTIONS":"type1:name1:value1;type2:name2:v a l u e 2;t3:n3:v3","DATA":"some_data...","STATUS":"1"})"}; + + REQUIRE(std::optional{7} == comm::TelegramParser::tryExtractFieldJobId(tdata)); + REQUIRE(std::optional{2} == comm::TelegramParser::tryExtractFieldCmd(tdata)); + std::optional optionsOpt; + REQUIRE(comm::TelegramParser::tryExtractFieldOptions(tdata, optionsOpt) == true); + REQUIRE(std::optional{"type1:name1:value1;type2:name2:v a l u e 2;t3:n3:v3"} == optionsOpt); + std::optional dataOpt; + REQUIRE(comm::TelegramParser::tryExtractFieldData(tdata, dataOpt) == true); + REQUIRE(std::optional{"some_data..."} == dataOpt); + REQUIRE(std::optional{1} == comm::TelegramParser::tryExtractFieldStatus(tdata)); +} + + diff --git a/vpr/thirdparty/sockpp b/vpr/thirdparty/sockpp new file mode 160000 index 00000000000..e6c4688a576 --- /dev/null +++ b/vpr/thirdparty/sockpp @@ -0,0 +1 @@ +Subproject commit e6c4688a576d95f42dd7628cefe68092f6c5cd0f From afc7661affd9f9fd6848be661d11a679c2504f02 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Thu, 15 Feb 2024 07:29:30 +0200 Subject: [PATCH 27/31] fix compilation error --- vpr/test/test_server_telegrambuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vpr/test/test_server_telegrambuffer.cpp b/vpr/test/test_server_telegrambuffer.cpp index 2504689caa4..a9199dc2c23 100644 --- a/vpr/test/test_server_telegrambuffer.cpp +++ b/vpr/test/test_server_telegrambuffer.cpp @@ -86,4 +86,4 @@ TEST_CASE("test_server_bytearray", "[vpr]") { // REQUIRE(buff.data().to_string() == ""); // } -// } // namespace +} // namespace From daa4d09c7ca6e72eb7d7ba464b3e85b66d89802e Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Thu, 15 Feb 2024 08:01:52 +0200 Subject: [PATCH 28/31] rename DISABLE_ZLIB_TELEGRAM_DATA_FIELD_COMPRESSION to FORCE_DISABLE_ZLIB_TELEGRAM_COMPRESSION --- vpr/src/server/task.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vpr/src/server/task.cpp b/vpr/src/server/task.cpp index 52a70479eeb..0ad89688ac5 100644 --- a/vpr/src/server/task.cpp +++ b/vpr/src/server/task.cpp @@ -85,7 +85,7 @@ void Task::bakeResponse() std::optional bodyOpt; uint8_t compressorId = comm::NONE_COMPRESSOR_ID; -#ifndef DISABLE_ZLIB_TELEGRAM_DATA_FIELD_COMPRESSION +#ifndef FORCE_DISABLE_ZLIB_TELEGRAM_COMPRESSION bodyOpt = tryCompress(ss.str()); if (bodyOpt) { compressorId = comm::ZLIB_COMPRESSOR_ID; From c3c56c90aba17089b14f6052d71af4c593d98918 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Thu, 15 Feb 2024 08:02:13 +0200 Subject: [PATCH 29/31] backport changes from hotfixes --- vpr/src/base/vpr_api.cpp | 2 +- vpr/src/draw/draw_basic.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index 3e545f15af8..00117567972 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -370,6 +370,7 @@ void vpr_show_resource_usage(const t_vpr_setup& vpr_setup, const t_arch& Arch) { vtr::ScopedStartFinishTimer timer("Build Device Grid"); /* Read in netlist file for placement and routing */ + auto& cluster_ctx = g_vpr_ctx.clustering(); auto& device_ctx = g_vpr_ctx.mutable_device(); device_ctx.arch = &Arch; @@ -853,7 +854,6 @@ RouteStatus vpr_route_flow(const Netlist<>& net_list, //Initialize the delay calculator std::shared_ptr timing_info = nullptr; - std::shared_ptr routing_delay_calc = nullptr; if (vpr_setup.Timing.timing_analysis_enabled) { auto& atom_ctx = g_vpr_ctx.atom(); diff --git a/vpr/src/draw/draw_basic.cpp b/vpr/src/draw/draw_basic.cpp index c52f5e9dffe..7bd1940cf87 100644 --- a/vpr/src/draw/draw_basic.cpp +++ b/vpr/src/draw/draw_basic.cpp @@ -4,6 +4,7 @@ #include #include #include +#define _USE_MATH_DEFINES // ensure (non-standard) value of M_PI is brought in from math.h #include #include #include From 4479c5e480c8215f6ebe5471ba3d5ad20c1fcf55 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Wed, 28 Feb 2024 06:33:51 +0200 Subject: [PATCH 30/31] return commented test_server_* test cases, add few new telegramparser test cases --- vpr/test/test_server_taskresolver.cpp | 192 ++++++++++++++---------- vpr/test/test_server_telegrambuffer.cpp | 125 +++++++++++---- vpr/test/test_server_telegramparser.cpp | 22 +++ 3 files changed, 230 insertions(+), 109 deletions(-) diff --git a/vpr/test/test_server_taskresolver.cpp b/vpr/test/test_server_taskresolver.cpp index 7b76f81a885..c1c5dc4011c 100644 --- a/vpr/test/test_server_taskresolver.cpp +++ b/vpr/test/test_server_taskresolver.cpp @@ -1,82 +1,110 @@ -// #include "catch2/catch_test_macros.hpp" -// #include "catch2/matchers/catch_matchers_all.hpp" - -// #include "taskresolver.h" -// #include - -// namespace { - -// TEST_CASE("test_server_taskresolver_cmdSpamFilter", "[vpr]") { -// server::TaskResolver resolver; -// resolver.addTask(std::make_unique(1,1)); -// resolver.addTask(std::make_unique(2,1)); -// resolver.addTask(std::make_unique(3,1)); -// resolver.addTask(std::make_unique(4,1)); -// resolver.addTask(std::make_unique(5,1)); - -// std::vector finished; -// resolver.takeFinished(finished); - -// REQUIRE(finished.size() == 4); - -// for (const auto& task: finished) { -// REQUIRE(task.isFinished()); -// REQUIRE(task.hasError()); -// REQUIRE(task.jobId() != 1); -// } -// REQUIRE(resolver.tasksNum() == 1); -// server::Task task = resolver.tasks()[0]; -// REQUIRE(task.jobId()==1); -// REQUIRE(task.cmd()==1); -// } - -// TEST_CASE("test_server_taskresolver_cmdOverrideFilter", "[vpr]") { -// server::TaskResolver resolver; -// resolver.addTask(server::Task{1,2,"1"}); -// resolver.addTask(server::Task{2,2,"11"}); -// resolver.addTask(server::Task{3,2,"222"}); - -// std::vector finished; -// resolver.takeFinished(finished); - -// REQUIRE(finished.size() == 2); - -// for (const auto& task: finished) { -// REQUIRE(task.isFinished()); -// REQUIRE(task.hasError()); -// REQUIRE(task.jobId() != 3); -// } -// REQUIRE(resolver.tasksNum() == 1); -// server::Task task = resolver.tasks()[0]; -// REQUIRE(task.jobId()==3); -// REQUIRE(task.cmd()==2); -// REQUIRE(task.options()=="222"); -// } - -// TEST_CASE("test_server_taskresolver_cmdSpamAndOverrideOptions", "[vpr]") { -// server::TaskResolver resolver; -// resolver.addTask(server::Task{1,2,"1"}); -// resolver.addTask(server::Task{2,2,"11"}); -// resolver.addTask(server::Task{3,2,"222"}); -// resolver.addTask(server::Task{4,2,"222"}); -// resolver.addTask(server::Task{5,1}); -// resolver.addTask(server::Task{6,1}); -// resolver.addTask(server::Task{7,1}); - -// std::vector finished; -// resolver.takeFinished(finished); - -// REQUIRE(resolver.tasksNum() == 2); -// server::TaskPtr task0 = resolver.tasks()[0]; -// server::TaskPtr task1 = resolver.tasks()[1]; - -// REQUIRE(task0.jobId()==3); -// REQUIRE(task0.cmd()==2); -// REQUIRE(task0.options()=="222"); - -// REQUIRE(task1.jobId()==5); -// REQUIRE(task1.cmd()==1); -// REQUIRE(task1.options()==""); -// } - -// } // namespace \ No newline at end of file +#include "catch2/catch_test_macros.hpp" +#include "catch2/matchers/catch_matchers_all.hpp" + +#include "taskresolver.h" +#include + +namespace { + +TEST_CASE("test_server_taskresolver_cmdSpamFilter", "[vpr]") { + server::TaskResolver resolver; + + { + const int cmd = 10; + server::TaskPtr task0 = std::make_unique(1,cmd); + server::TaskPtr task1 = std::make_unique(2,cmd); + server::TaskPtr task2 = std::make_unique(3,cmd); + server::TaskPtr task3 = std::make_unique(4,cmd); + server::TaskPtr task4 = std::make_unique(5,cmd); + + resolver.addTask(task0); + resolver.addTask(task1); + resolver.addTask(task2); + resolver.addTask(task3); + resolver.addTask(task4); + } + + std::vector finished; + resolver.takeFinished(finished); + + REQUIRE(finished.size() == 4); + + for (const auto& task: finished) { + REQUIRE(task->isFinished()); + REQUIRE(task->hasError()); + REQUIRE(task->jobId() != 1); + } + REQUIRE(resolver.tasksNum() == 1); + const server::TaskPtr& task = resolver.tasks().at(0); + REQUIRE(task->jobId() == 1); +} + +TEST_CASE("test_server_taskresolver_cmdOverrideFilter", "[vpr]") { + server::TaskResolver resolver; + const int cmd = 10; + + { + server::TaskPtr task0 = std::make_unique(1,cmd,"1"); + server::TaskPtr task1 = std::make_unique(2,cmd,"11"); + server::TaskPtr task2 = std::make_unique(3,cmd,"222"); + + resolver.addTask(task0); + resolver.addTask(task1); + resolver.addTask(task2); + } + + std::vector finished; + resolver.takeFinished(finished); + + REQUIRE(finished.size() == 2); + + for (const server::TaskPtr& task: finished) { + REQUIRE(task->isFinished()); + REQUIRE(task->hasError()); + REQUIRE(task->jobId() != 3); + } + REQUIRE(resolver.tasksNum() == 1); + const server::TaskPtr& task = resolver.tasks().at(0); + REQUIRE(task->jobId() == 3); + REQUIRE(task->cmd() == 2); + REQUIRE(task->options() == "222"); +} + +TEST_CASE("test_server_taskresolver_cmdSpamAndOverrideOptions", "[vpr]") { + server::TaskResolver resolver; + + { + server::TaskPtr task0 = std::make_unique(1,2,"1"); + server::TaskPtr task1 = std::make_unique(2,2,"11"); + server::TaskPtr task2 = std::make_unique(3,2,"222"); + server::TaskPtr task3 = std::make_unique(4,2,"222"); + server::TaskPtr task4 = std::make_unique(5,1); + server::TaskPtr task5 = std::make_unique(6,1); + server::TaskPtr task6 = std::make_unique(7,1); + + resolver.addTask(task0); + resolver.addTask(task1); + resolver.addTask(task2); + resolver.addTask(task3); + resolver.addTask(task4); + resolver.addTask(task5); + resolver.addTask(task6); + } + + std::vector finished; + resolver.takeFinished(finished); + + REQUIRE(resolver.tasksNum() == 2); + const server::TaskPtr& task0 = resolver.tasks().at(0); + const server::TaskPtr& task1 = resolver.tasks().at(1); + + REQUIRE(task0->jobId() == 3); + REQUIRE(task0->cmd() == 2); + REQUIRE(task0->options() == "222"); + + REQUIRE(task1->jobId() == 5); + REQUIRE(task1->cmd() == 1); + REQUIRE(task1->options() == ""); +} + +} // namespace \ No newline at end of file diff --git a/vpr/test/test_server_telegrambuffer.cpp b/vpr/test/test_server_telegrambuffer.cpp index a9199dc2c23..ff0e311b85e 100644 --- a/vpr/test/test_server_telegrambuffer.cpp +++ b/vpr/test/test_server_telegrambuffer.cpp @@ -35,43 +35,89 @@ TEST_CASE("test_server_bytearray", "[vpr]") { REQUIRE(array.to_string() == ""); } -// TEST_CASE("test_server_telegrambuffer_oneOpened", "[vpr]") { -// comm::TelegramBuffer buff{1024}; -// buff.append(comm::ByteArray{"111"}); -// buff.append(comm::ByteArray{"222"}); +TEST_CASE("test_server_telegrambuffer_oneOpened", "[vpr]") { + comm::TelegramBuffer buff; + buff.append(comm::ByteArray{"111"}); + buff.append(comm::ByteArray{"222"}); -// auto frames = buff.takeTelegramFrames(); -// REQUIRE(frames.size() == 0); + auto frames = buff.takeTelegramFrames(); + REQUIRE(frames.size() == 0); -// REQUIRE(buff.data().to_string() == "111222"); -// } + REQUIRE(buff.data().to_string() == "111222"); +} -// TEST_CASE("test_server_telegrambuffer_oneFinishedOneOpened", "[vpr]") { -// comm::TelegramBuffer buff{1024}; -// buff.append(comm::ByteArray{"111\x17"}); -// buff.append(comm::ByteArray{"222"}); +TEST_CASE("test_server_telegrambuffer_notFilledTelegramButWithPrependedRubish", "[vpr]") +{ + comm::TelegramBuffer tBuff; -// auto frames = buff.takeTelegramFrames(); -// REQUIRE(frames.size() == 1); + const comm::ByteArray rubish{"#@!"}; + const comm::ByteArray msgBody{"some message"}; + const comm::TelegramHeader msgHeader{comm::TelegramHeader::constructFromData(msgBody)}; -// REQUIRE(frames[0]->data.to_string() == "111"); + tBuff.append(rubish); + tBuff.append(msgHeader.buffer()); -// REQUIRE(buff.data().to_string() == "222"); -// } + auto frames = tBuff.takeTelegramFrames(); + REQUIRE(0 == frames.size()); -// TEST_CASE("test_server_telegrambuffer_twoFinished", "[vpr]") { -// comm::TelegramBuffer buff{1024}; -// buff.append(comm::ByteArray{"111\x17"}); -// buff.append(comm::ByteArray{"222\x17"}); + REQUIRE(msgHeader.buffer() == tBuff.data()); // the rubish prefix fragment will be absent here +} -// auto frames = buff.takeTelegramFrames(); -// REQUIRE(frames.size() == 2); +TEST_CASE("test_server_telegrambuffer__oneFinishedOneOpened", "[vpr]") +{ + comm::TelegramBuffer tBuff; -// REQUIRE(frames[0]->data.to_string() == "111"); -// REQUIRE(frames[1]->data.to_string() == "222"); + const comm::ByteArray msgBody1{"message1"}; + const comm::ByteArray msgBody2{"message2"}; -// REQUIRE(buff.data().to_string() == ""); -// } + const comm::TelegramHeader msgHeader1{comm::TelegramHeader::constructFromData(msgBody1)}; + const comm::TelegramHeader msgHeader2{comm::TelegramHeader::constructFromData(msgBody2)}; + + comm::ByteArray t1(msgHeader1.buffer()); + t1.append(msgBody1); + + comm::ByteArray t2(msgHeader2.buffer()); + t2.append(msgBody2); + t2.resize(t2.size()-2); // drop 2 last elements + + tBuff.append(t1); + tBuff.append(t2); + + auto frames = tBuff.takeTelegramFrames(); + REQUIRE(1 == frames.size()); + + REQUIRE(msgBody1 == frames[0]->data); + + REQUIRE(t2 == tBuff.data()); +} + +TEST_CASE("test_server_telegrambuffer_twoFinished", "[vpr]") +{ + comm::TelegramBuffer tBuff; + + const comm::ByteArray msgBody1{"message1"}; + const comm::ByteArray msgBody2{"message2"}; + + const comm::TelegramHeader msgHeader1{comm::TelegramHeader::constructFromData(msgBody1)}; + const comm::TelegramHeader msgHeader2{comm::TelegramHeader::constructFromData(msgBody2)}; + + comm::ByteArray t1(msgHeader1.buffer()); + t1.append(msgBody1); + + comm::ByteArray t2(msgHeader2.buffer()); + t2.append(msgBody2); + + tBuff.append(t1); + tBuff.append(t2); + + auto frames = tBuff.takeTelegramFrames(); + REQUIRE(2 == frames.size()); + + REQUIRE(msgBody1 == frames[0]->data); + REQUIRE(msgBody2 == frames[1]->data); + + REQUIRE(comm::ByteArray{} == tBuff.data()); +} // TEST_CASE("test_server_telegrambuffer_twoCleared", "[vpr]") { // comm::TelegramBuffer buff{1024}; @@ -86,4 +132,29 @@ TEST_CASE("test_server_bytearray", "[vpr]") { // REQUIRE(buff.data().to_string() == ""); // } + +TEST_CASE("test_server_telegrambuffer_clear", "[vpr]") +{ + comm::TelegramBuffer tBuff; + + const comm::ByteArray msgBody1{"message1"}; + const comm::ByteArray msgBody2{"message2"}; + + const comm::TelegramHeader msgHeader1{comm::TelegramHeader::constructFromData(msgBody1)}; + const comm::TelegramHeader msgHeader2{comm::TelegramHeader::constructFromData(msgBody2)}; + + comm::ByteArray t1(msgHeader1.buffer()); + t1.append(msgBody1); + + comm::ByteArray t2(msgHeader2.buffer()); + t2.append(msgBody2); + + tBuff.clear(); + + auto frames = tBuff.takeTelegramFrames(); + REQUIRE(0 == frames.size()); + + REQUIRE(comm::ByteArray{} == tBuff.data()); +} + } // namespace diff --git a/vpr/test/test_server_telegramparser.cpp b/vpr/test/test_server_telegramparser.cpp index 41f0df55e7d..092d44fd620 100644 --- a/vpr/test/test_server_telegramparser.cpp +++ b/vpr/test/test_server_telegramparser.cpp @@ -18,4 +18,26 @@ TEST_CASE("test_server_telegram_parser_base", "[vpr]") REQUIRE(std::optional{1} == comm::TelegramParser::tryExtractFieldStatus(tdata)); } +TEST_CASE("test_server_telegram_parser_invalid_keys", "[vpr]") +{ + const std::string tBadData{R"({"_JOB_ID":"7","_CMD":"2","_OPTIONS":"type1:name1:value1;type2:name2:v a l u e 2;t3:n3:v3","_DATA":"some_data...","_STATUS":"1"})"}; + + REQUIRE(std::nullopt == comm::TelegramParser::tryExtractFieldJobId(tBadData)); + REQUIRE(std::nullopt == comm::TelegramParser::tryExtractFieldCmd(tBadData)); + std::optional optionsOpt; + REQUIRE_FALSE(comm::TelegramParser::tryExtractFieldOptions(tBadData, optionsOpt)); + REQUIRE(std::nullopt == optionsOpt); + std::optional dataOpt; + REQUIRE_FALSE(comm::TelegramParser::tryExtractFieldData(tBadData, dataOpt)); + REQUIRE(std::nullopt == dataOpt); + REQUIRE(std::nullopt == comm::TelegramParser::tryExtractFieldStatus(tBadData)); +} +TEST_CASE("test_server_telegram_parser_invalid_types", "[vpr]") +{ + const std::string tBadData{R"({"JOB_ID":"x","CMD":"y","STATUS":"z"})"}; + + REQUIRE(std::nullopt == comm::TelegramParser::tryExtractFieldJobId(tBadData)); + REQUIRE(std::nullopt == comm::TelegramParser::tryExtractFieldCmd(tBadData)); + REQUIRE(std::nullopt == comm::TelegramParser::tryExtractFieldStatus(tBadData)); +} From 687d917d070c00fec239666495eeb3d8732a4d71 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Wed, 28 Feb 2024 06:58:10 +0200 Subject: [PATCH 31/31] sync with aurora --- vpr/CMakeLists.txt | 4 +- vpr/src/base/vpr_api.cpp | 2 +- vpr/src/draw/draw.cpp | 23 ++++++++ vpr/src/draw/draw_basic.cpp | 70 ++++++++----------------- vpr/src/draw/draw_basic.h | 1 - vpr/src/server/bytearray.h | 24 +++++---- vpr/src/server/convertutils.h | 3 +- vpr/src/server/gateio.cpp | 24 ++++++--- vpr/src/server/gateio.h | 4 +- vpr/src/server/taskresolver.cpp | 4 +- vpr/src/server/telegrambuffer.cpp | 52 ++++++++++-------- vpr/test/test_server_taskresolver.cpp | 6 ++- vpr/test/test_server_telegrambuffer.cpp | 14 ----- 13 files changed, 120 insertions(+), 111 deletions(-) diff --git a/vpr/CMakeLists.txt b/vpr/CMakeLists.txt index d2903730b02..49668cd4216 100644 --- a/vpr/CMakeLists.txt +++ b/vpr/CMakeLists.txt @@ -16,6 +16,8 @@ set(VPR_PGO_DATA_DIR "." CACHE PATH "Where to store and retrieve PGO data") set(GRAPHICS_DEFINES "") #sockpp +set(SOCKPP_BUILD_SHARED OFF CACHE BOOL "Override default value" FORCE) +set(SOCKPP_BUILD_STATIC ON CACHE BOOL "Override default value" FORCE) add_subdirectory(thirdparty/sockpp) set(THIRDPARTY_INCLUDE_DIRS thirdparty/sockpp/include) @@ -88,7 +90,7 @@ target_link_libraries(libvpr libargparse libpugixml librrgraph - sockpp + sockpp-static -lz ) diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index 00117567972..3e545f15af8 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -370,7 +370,6 @@ void vpr_show_resource_usage(const t_vpr_setup& vpr_setup, const t_arch& Arch) { vtr::ScopedStartFinishTimer timer("Build Device Grid"); /* Read in netlist file for placement and routing */ - auto& cluster_ctx = g_vpr_ctx.clustering(); auto& device_ctx = g_vpr_ctx.mutable_device(); device_ctx.arch = &Arch; @@ -854,6 +853,7 @@ RouteStatus vpr_route_flow(const Netlist<>& net_list, //Initialize the delay calculator std::shared_ptr timing_info = nullptr; + std::shared_ptr routing_delay_calc = nullptr; if (vpr_setup.Timing.timing_analysis_enabled) { auto& atom_ctx = g_vpr_ctx.atom(); diff --git a/vpr/src/draw/draw.cpp b/vpr/src/draw/draw.cpp index 5e5a45c8333..4c9ce8a5fd7 100644 --- a/vpr/src/draw/draw.cpp +++ b/vpr/src/draw/draw.cpp @@ -403,6 +403,29 @@ void update_screen(ScreenUpdatePriority priority, const char* msg, enum pic_type * continue. Saves the pic_on_screen_val to allow pan and zoom redraws. */ t_draw_state* draw_state = get_draw_state_vars(); +#ifdef _WIN32 + // why use _WIN32 as rest of VPR uses WIN32 ? https://groups.google.com/g/fltkcoredev/c/BwKvvtJ_Mog + // For Windows, analysis stage needs to be run along with routing stage always. + // However, from Aurora, we don't want the VPR Viewer to run in the routing stage, + // and *only in the analysis stage* - so, we enable the viewer to run *only in the analysis stage*. + // NOTE: root cause of needing route and analysis together on Windows needs to be fixed + // and this has been discussed previously with VPR, but as Windows is not their primary + // target, there has not been any updates on this. Once the root cause is fixed (by us) + // we can remove this workaround + bool enable_display_graphics = false; + if (draw_state->pic_on_screen != pic_on_screen_val) { + if (pic_on_screen_val == ROUTING + && draw_state->pic_on_screen == NO_PICTURE) { + if(setup_timing_info) { + enable_display_graphics = true; + } + } + } + if(!enable_display_graphics) { + return; + } +#endif // #ifdef _WIN32 + if (!draw_state->show_graphics) ezgl::set_disable_event_loop(true); else diff --git a/vpr/src/draw/draw_basic.cpp b/vpr/src/draw/draw_basic.cpp index 7bd1940cf87..5b74a3996e1 100644 --- a/vpr/src/draw/draw_basic.cpp +++ b/vpr/src/draw/draw_basic.cpp @@ -1011,53 +1011,15 @@ void draw_concrete_crit_path(const tatum::TimingPath& path, ezgl::renderer* g) { } } -void draw_concrete_crit_path_DEBUG(const tatum::TimingPath& path, ezgl::renderer* g) { - t_draw_state* draw_state = get_draw_state_vars(); - - //Walk through the timing path drawing each edge - tatum::NodeId prev_node; - float prev_arr_time = std::numeric_limits::quiet_NaN(); - int i = 0; - for (tatum::TimingPathElem elem : path.data_arrival_path().elements()) { - tatum::NodeId node = elem.node(); - float arr_time = elem.tag().time(); - if (prev_node) { - //We draw each 'edge' in a different color, this allows users to identify the stages and - //any routing which corresponds to the edge - // - ezgl::color color{0,0,0,40}; - - float delay = arr_time - prev_arr_time; - if (draw_state->show_crit_path == DRAW_CRIT_PATH_FLYLINES - || draw_state->show_crit_path - == DRAW_CRIT_PATH_FLYLINES_DELAYS) { - g->set_color(color); - g->set_line_dash(ezgl::line_dash::none); - g->set_line_width(1); - draw_flyline_timing_edge(tnode_draw_coord(prev_node), - tnode_draw_coord(node), delay, g, /*skipDrawDelays*/true); - } else { - VTR_ASSERT(draw_state->show_crit_path != DRAW_NO_CRIT_PATH); - - //Draw the routed version of the timing edge - draw_routed_timing_edge(prev_node, node, delay, color, g, /*skipDrawDelays*/true); - } - } - prev_node = node; - prev_arr_time = arr_time; - } -} - void draw_crit_path_elements(const std::vector& paths, const std::map>& crit_path_element_indexes, ezgl::renderer* g) { t_draw_state* draw_state = get_draw_state_vars(); + static ezgl::color contour_color{0,0,0,40}; + const bool draw_crit_path_contour = g_vpr_ctx.server().draw_crit_path_contour(); for (const auto& [pathIndex, elementIndexes]: crit_path_element_indexes) { if (pathIndex < paths.size()) { const tatum::TimingPath& path = paths[pathIndex]; - if (g_vpr_ctx.server().draw_crit_path_contour()) { - draw_concrete_crit_path_DEBUG(path, g); // debug - } //Walk through the timing path drawing each edge tatum::NodeId prev_node; @@ -1078,24 +1040,36 @@ void draw_crit_path_elements(const std::vector& paths, const ezgl::color color = kelly_max_contrast_colors[i++ % kelly_max_contrast_colors.size()]; - if (prev_node && drawCurrentElement) { + if (prev_node) { float delay = arr_time - prev_arr_time; if (draw_state->show_crit_path == DRAW_CRIT_PATH_FLYLINES || draw_state->show_crit_path == DRAW_CRIT_PATH_FLYLINES_DELAYS) { - g->set_color(color); - g->set_line_dash(ezgl::line_dash::none); - g->set_line_width(4); - draw_flyline_timing_edge(tnode_draw_coord(prev_node), - tnode_draw_coord(node), delay, g); + if (drawCurrentElement) { + g->set_color(color); + g->set_line_dash(ezgl::line_dash::none); + g->set_line_width(4); + draw_flyline_timing_edge(tnode_draw_coord(prev_node), + tnode_draw_coord(node), delay, g); + } else if (draw_crit_path_contour) { + g->set_color(contour_color); + g->set_line_dash(ezgl::line_dash::none); + g->set_line_width(1); + draw_flyline_timing_edge(tnode_draw_coord(prev_node), + tnode_draw_coord(node), delay, g, /*skipDrawDelays*/true); + } } else { VTR_ASSERT(draw_state->show_crit_path != DRAW_NO_CRIT_PATH); //Draw the routed version of the timing edge - draw_routed_timing_edge(prev_node, node, delay, color, g); + if (drawCurrentElement) { + draw_routed_timing_edge(prev_node, node, delay, color, g); + } else if (draw_crit_path_contour) { + draw_routed_timing_edge(prev_node, node, delay, contour_color, g, /*skipDrawDelays*/true); + } } } - + prev_node = node; prev_arr_time = arr_time; // end draw element diff --git a/vpr/src/draw/draw_basic.h b/vpr/src/draw/draw_basic.h index 196755ecf3c..145dee1a85c 100644 --- a/vpr/src/draw/draw_basic.h +++ b/vpr/src/draw/draw_basic.h @@ -89,7 +89,6 @@ void draw_crit_path(ezgl::renderer* g); * b) during routing, critical path is shown by both flylines and routed net connections. */ void draw_concrete_crit_path(const tatum::TimingPath& path, ezgl::renderer* g); -void draw_concrete_crit_path_DEBUG(const tatum::TimingPath& path, ezgl::renderer* g); void draw_crit_path_elements(const std::vector& paths, const std::map>& indexes, ezgl::renderer* g); diff --git a/vpr/src/server/bytearray.h b/vpr/src/server/bytearray.h index 164c6ed8c5b..d532c33b66a 100644 --- a/vpr/src/server/bytearray.h +++ b/vpr/src/server/bytearray.h @@ -1,6 +1,7 @@ #ifndef BYTEARRAY_H #define BYTEARRAY_H +#include #include #include #include @@ -39,17 +40,20 @@ class ByteArray : public std::vector { push_back(b); } - std::size_t findSequence(const char* sequence, std::size_t n) { - for (std::size_t i = 0; i <= size() - n; ++i) { - bool found = true; - for (std::size_t j = 0; j < n; ++j) { - if (at(i + j) != sequence[j]) { - found = false; - break; + std::size_t findSequence(const char* sequence, std::size_t sequenceSize) { + const std::size_t mSize = size(); + if (mSize >= sequenceSize) { + for (std::size_t i = 0; i <= mSize - sequenceSize; ++i) { + bool found = true; + for (std::size_t j = 0; j < sequenceSize; ++j) { + if (at(i + j) != sequence[j]) { + found = false; + break; + } + } + if (found) { + return i; } - } - if (found) { - return i; } } return std::size_t(-1); diff --git a/vpr/src/server/convertutils.h b/vpr/src/server/convertutils.h index 93d4574bc09..f06f84114f0 100644 --- a/vpr/src/server/convertutils.h +++ b/vpr/src/server/convertutils.h @@ -1,10 +1,11 @@ #ifndef CONVERTUTILS_H #define CONVERTUTILS_H +#include #include #include -const std::size_t DEFAULT_PRINT_STRING_MAX_NUM = 50; +const std::size_t DEFAULT_PRINT_STRING_MAX_NUM = 100; std::optional tryConvertToInt(const std::string&); std::string getPrettyDurationStrFromMs(int64_t durationMs); diff --git a/vpr/src/server/gateio.cpp b/vpr/src/server/gateio.cpp index ff134e68c45..4c8c2fe9278 100644 --- a/vpr/src/server/gateio.cpp +++ b/vpr/src/server/gateio.cpp @@ -65,8 +65,13 @@ void GateIO::moveTasksToSendQueue(std::vector& tasks) void GateIO::startListening() { +#ifdef ENABLE_CLIENT_ALIVE_TRACKER std::unique_ptr clientAliveTrackerPtr = std::make_unique(std::chrono::milliseconds{5000}, std::chrono::milliseconds{20000}); +#else + std::unique_ptr clientAliveTrackerPtr; +#endif + static const std::string echoData{comm::ECHO_DATA}; comm::TelegramBuffer telegramBuff; @@ -76,7 +81,7 @@ void GateIO::startListening() sockpp::tcp6_acceptor tcpServer(m_portNum); tcpServer.set_non_blocking(true); - const std::size_t chunkMaxBytesNum = 0.5*1024*1024; // 0.5Mb + const std::size_t chunkMaxBytesNum = 2*1024*1024; // 2Mb if (tcpServer) { m_logger.queue(LogLevel::Info, "open server, port=", m_portNum); @@ -111,16 +116,18 @@ void GateIO::startListening() /// handle sending response { std::unique_lock lock(m_tasksMutex); - for (const TaskPtr& task: m_sendTasks) { + + if (!m_sendTasks.empty()) { + const TaskPtr& task = m_sendTasks.at(0); try { std::size_t bytesToSend = std::min(chunkMaxBytesNum, task->responseBuffer().size()); std::size_t bytesActuallyWritten = client.write_n(task->responseBuffer().data(), bytesToSend); if (bytesActuallyWritten <= task->origReponseBytesNum()) { task->chopNumSentBytesFromResponseBuffer(bytesActuallyWritten); m_logger.queue(LogLevel::Detail, - "sent chunk:", getPrettySizeStrFromBytesNum(bytesActuallyWritten), - "from", getPrettySizeStrFromBytesNum(task->origReponseBytesNum()), - "left:", getPrettySizeStrFromBytesNum(task->responseBuffer().size())); + "sent chunk:", getPrettySizeStrFromBytesNum(bytesActuallyWritten), + "from", getPrettySizeStrFromBytesNum(task->origReponseBytesNum()), + "left:", getPrettySizeStrFromBytesNum(task->responseBuffer().size())); if (clientAliveTrackerPtr) { clientAliveTrackerPtr->onClientActivity(); } @@ -137,10 +144,15 @@ void GateIO::startListening() } // remove reported tasks + std::size_t tasksBeforeRemoving = m_sendTasks.size(); + auto partitionIter = std::partition(m_sendTasks.begin(), m_sendTasks.end(), [](const TaskPtr& task) { return !task->isResponseFullySent(); }); m_sendTasks.erase(partitionIter, m_sendTasks.end()); - + bool removingTookPlace = tasksBeforeRemoving != m_sendTasks.size(); + if (!m_sendTasks.empty() && removingTookPlace) { + m_logger.queue(LogLevel::Detail, "left tasks num to send ", m_sendTasks.size()); + } } // release lock /// handle receiving diff --git a/vpr/src/server/gateio.h b/vpr/src/server/gateio.h index f23948fc657..0cc299023dc 100644 --- a/vpr/src/server/gateio.h +++ b/vpr/src/server/gateio.h @@ -81,7 +81,7 @@ class GateIO class TLogger { public: TLogger() { - m_logLevel = static_cast(LogLevel::Debug); + m_logLevel = static_cast(LogLevel::Info); } ~TLogger() {} @@ -115,7 +115,7 @@ class GateIO explicit GateIO(); ~GateIO(); - const int LOOP_INTERVAL_MS = 1; + const int LOOP_INTERVAL_MS = 100; bool isRunning() const { return m_isRunning.load(); } diff --git a/vpr/src/server/taskresolver.cpp b/vpr/src/server/taskresolver.cpp index ca602162e8a..56729dbcf0f 100644 --- a/vpr/src/server/taskresolver.cpp +++ b/vpr/src/server/taskresolver.cpp @@ -17,10 +17,10 @@ void TaskResolver::addTask(TaskPtr& newTask) for (const auto& task: m_tasks) { if (task->cmd() == newTask->cmd()) { if (task->optionsMatch(newTask)) { - std::string msg = "similar task is already in execution, reject new " + task->info()+ " and waiting for old " + newTask->info() + " execution"; + std::string msg = "similar task is already in execution, reject new " + newTask->info()+ " and waiting for old " + task->info() + " execution"; newTask->fail(msg); } else { - // handle case when task has same jobId but different options + // handle case when task has same cmd but different options if (newTask->jobId() > task->jobId()) { std::string msg = "old " + task->info() + " is overriden by a new " + newTask->info(); task->fail(msg); diff --git a/vpr/src/server/telegrambuffer.cpp b/vpr/src/server/telegrambuffer.cpp index 6eab588a339..fa811a9e667 100644 --- a/vpr/src/server/telegrambuffer.cpp +++ b/vpr/src/server/telegrambuffer.cpp @@ -11,9 +11,8 @@ bool TelegramBuffer::checkRawBuffer() { std::size_t signatureStartIndex = m_rawBuffer.findSequence(TelegramHeader::SIGNATURE, TelegramHeader::SIGNATURE_SIZE); if (signatureStartIndex != std::size_t(-1)) { - std::size_t index = signatureStartIndex; - if (index != 0) { - m_rawBuffer.erase(m_rawBuffer.begin(), m_rawBuffer.begin()+index); + if (signatureStartIndex != 0) { + m_rawBuffer.erase(m_rawBuffer.begin(), m_rawBuffer.begin()+signatureStartIndex); } return true; } @@ -22,32 +21,39 @@ bool TelegramBuffer::checkRawBuffer() void TelegramBuffer::takeTelegramFrames(std::vector& result) { - if (m_rawBuffer.size() < TelegramHeader::size()) { + if (m_rawBuffer.size() <= TelegramHeader::size()) { return; } - if (!m_headerOpt) { - if (checkRawBuffer()) { - TelegramHeader header(m_rawBuffer); - if (header.isValid()) { - m_headerOpt = std::move(header); + + bool mayContainFullTelegram = true; + while(mayContainFullTelegram) { + mayContainFullTelegram = false; + if (!m_headerOpt) { + if (checkRawBuffer()) { + TelegramHeader header(m_rawBuffer); + if (header.isValid()) { + m_headerOpt = std::move(header); + } } } - } - if (m_headerOpt) { - const TelegramHeader& header = m_headerOpt.value(); - if (m_rawBuffer.size() >= TelegramHeader::size() + header.bodyBytesNum()) { - ByteArray data(m_rawBuffer.begin() + TelegramHeader::size(), m_rawBuffer.begin() + TelegramHeader::size() + header.bodyBytesNum()); - uint32_t actualCheckSum = data.calcCheckSum(); - if (actualCheckSum == header.bodyCheckSum()) { - TelegramFramePtr telegramFramePtr = std::make_shared(TelegramFrame{header, std::move(data)}); - data.clear(); - result.push_back(telegramFramePtr); - } else { - m_errors.push_back("wrong checkSums " + std::to_string(actualCheckSum) +" for " + header.info() + " , drop this chunk"); + if (m_headerOpt) { + const TelegramHeader& header = m_headerOpt.value(); + std::size_t wholeTelegramSize = TelegramHeader::size() + header.bodyBytesNum(); + if (m_rawBuffer.size() >= wholeTelegramSize) { + ByteArray data(m_rawBuffer.begin() + TelegramHeader::size(), m_rawBuffer.begin() + wholeTelegramSize); + uint32_t actualCheckSum = data.calcCheckSum(); + if (actualCheckSum == header.bodyCheckSum()) { + TelegramFramePtr telegramFramePtr = std::make_shared(TelegramFrame{header, std::move(data)}); + data.clear(); + result.push_back(telegramFramePtr); + } else { + m_errors.push_back("wrong checkSums " + std::to_string(actualCheckSum) +" for " + header.info() + " , drop this chunk"); + } + m_rawBuffer.erase(m_rawBuffer.begin(), m_rawBuffer.begin() + wholeTelegramSize); + m_headerOpt.reset(); + mayContainFullTelegram = true; } - m_rawBuffer.erase(m_rawBuffer.begin(), m_rawBuffer.begin()+TelegramHeader::size()+header.bodyBytesNum()); - m_headerOpt.reset(); } } } diff --git a/vpr/test/test_server_taskresolver.cpp b/vpr/test/test_server_taskresolver.cpp index c1c5dc4011c..4527e85a776 100644 --- a/vpr/test/test_server_taskresolver.cpp +++ b/vpr/test/test_server_taskresolver.cpp @@ -8,9 +8,9 @@ namespace { TEST_CASE("test_server_taskresolver_cmdSpamFilter", "[vpr]") { server::TaskResolver resolver; + const int cmd = 10; { - const int cmd = 10; server::TaskPtr task0 = std::make_unique(1,cmd); server::TaskPtr task1 = std::make_unique(2,cmd); server::TaskPtr task2 = std::make_unique(3,cmd); @@ -33,10 +33,12 @@ TEST_CASE("test_server_taskresolver_cmdSpamFilter", "[vpr]") { REQUIRE(task->isFinished()); REQUIRE(task->hasError()); REQUIRE(task->jobId() != 1); + REQUIRE(task->cmd() == cmd); } REQUIRE(resolver.tasksNum() == 1); const server::TaskPtr& task = resolver.tasks().at(0); REQUIRE(task->jobId() == 1); + REQUIRE(task->cmd() == cmd); } TEST_CASE("test_server_taskresolver_cmdOverrideFilter", "[vpr]") { @@ -66,7 +68,7 @@ TEST_CASE("test_server_taskresolver_cmdOverrideFilter", "[vpr]") { REQUIRE(resolver.tasksNum() == 1); const server::TaskPtr& task = resolver.tasks().at(0); REQUIRE(task->jobId() == 3); - REQUIRE(task->cmd() == 2); + REQUIRE(task->cmd() == cmd); REQUIRE(task->options() == "222"); } diff --git a/vpr/test/test_server_telegrambuffer.cpp b/vpr/test/test_server_telegrambuffer.cpp index ff0e311b85e..d02746e5815 100644 --- a/vpr/test/test_server_telegrambuffer.cpp +++ b/vpr/test/test_server_telegrambuffer.cpp @@ -119,20 +119,6 @@ TEST_CASE("test_server_telegrambuffer_twoFinished", "[vpr]") REQUIRE(comm::ByteArray{} == tBuff.data()); } -// TEST_CASE("test_server_telegrambuffer_twoCleared", "[vpr]") { -// comm::TelegramBuffer buff{1024}; -// buff.append(comm::ByteArray{"111\x17"}); -// buff.append(comm::ByteArray{"222\x17"}); - -// buff.clear(); - -// auto frames = buff.takeTelegramFrames(); -// REQUIRE(frames.size() == 0); - -// REQUIRE(buff.data().to_string() == ""); -// } - - TEST_CASE("test_server_telegrambuffer_clear", "[vpr]") { comm::TelegramBuffer tBuff;