From 266fee24b62109784093ab8eabf341caef76cf30 Mon Sep 17 00:00:00 2001 From: Jing <128339540+Jng468@users.noreply.github.com> Date: Sat, 18 Jan 2025 14:10:32 -0800 Subject: [PATCH] [NET] Local Transceiver - receive function and testing (#465) * current changes (debug) * Local transceiver receive function updates * Fixes for half of receive function * Fix stoi error * fixes * Data is received but not decoded * Waypoints received now B) * cleanup * lint fixes * Fixes * Merge conflict fixes --------- Co-authored-by: lross03 Co-authored-by: Sam Dai <89489298+samdai01@users.noreply.github.com> Co-authored-by: Akshanjay Kompelli --- src/local_pathfinding/.gitignore | 3 + .../projects/local_transceiver/inc/at_cmds.h | 6 +- .../local_transceiver/inc/local_transceiver.h | 5 + .../src/local_transceiver.cpp | 98 +++++++++++++- .../src/local_transceiver_ros_intf.cpp | 2 +- .../test/test_local_transceiver.cpp | 61 +++++++++ .../components/DropDown/dropdown.module.css | 126 ++++++------------ 7 files changed, 211 insertions(+), 90 deletions(-) diff --git a/src/local_pathfinding/.gitignore b/src/local_pathfinding/.gitignore index 5ad2f1cf4..433cb7cb7 100644 --- a/src/local_pathfinding/.gitignore +++ b/src/local_pathfinding/.gitignore @@ -6,3 +6,6 @@ __pycache__/ # global paths with exceptions /global_paths/*.csv !/global_paths/mock_global_path.csv + +# Python Bindings +src/local_pathfinding/src/build/pyompl.cpython-310-x86_64-linux-gnu.so diff --git a/src/network_systems/projects/local_transceiver/inc/at_cmds.h b/src/network_systems/projects/local_transceiver/inc/at_cmds.h index b6d39dc61..88af36422 100644 --- a/src/network_systems/projects/local_transceiver/inc/at_cmds.h +++ b/src/network_systems/projects/local_transceiver/inc/at_cmds.h @@ -15,8 +15,10 @@ const std::string DELIMITER = "\r\n"; const std::string STATUS_OK = "OK"; const std::string RSP_READY = "READY"; -const std::string CHECK_CONN = "AT"; -const std::string SBD_SESSION = "AT+SBDIX"; // 5.144 +const std::string CHECK_CONN = "AT"; +const std::string SBD_SESSION = "AT+SBDIX"; // 5.144 +const std::string DSBL_CTRLFLOW = "AT&K0"; +const std::string DNLD_TO_QUEUE = "AT+SBDRB"; namespace write_bin // 5.154 { diff --git a/src/network_systems/projects/local_transceiver/inc/local_transceiver.h b/src/network_systems/projects/local_transceiver/inc/local_transceiver.h index 2ad94a623..2fbeb94ee 100644 --- a/src/network_systems/projects/local_transceiver/inc/local_transceiver.h +++ b/src/network_systems/projects/local_transceiver/inc/local_transceiver.h @@ -25,6 +25,8 @@ constexpr unsigned int SATELLITE_BAUD_RATE = 19200; class LocalTransceiver { friend class TestLocalTransceiver_parseInMsgValid_Test; + friend class TestLocalTransceiver_SendAndReceiveMessage; + friend class TestLocalTransceiver_testMailboxBlackbox_Test; public: /** @@ -124,6 +126,9 @@ class LocalTransceiver */ custom_interfaces::msg::Path receive(); + // TEST + bool checkMailbox(); + private: // Serial port read/write timeout constexpr static const struct timeval TIMEOUT diff --git a/src/network_systems/projects/local_transceiver/src/local_transceiver.cpp b/src/network_systems/projects/local_transceiver/src/local_transceiver.cpp index da53e3d8e..1a55264f8 100644 --- a/src/network_systems/projects/local_transceiver/src/local_transceiver.cpp +++ b/src/network_systems/projects/local_transceiver/src/local_transceiver.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -223,8 +224,101 @@ std::optional LocalTransceiver::debugSend(const std::string & cmd) custom_interfaces::msg::Path LocalTransceiver::receive() { - std::string receivedData = readRsp().value(); - custom_interfaces::msg::Path to_publish = parseInMsg(receivedData); + static constexpr int MAX_NUM_RETRIES = 20; + for (int i = 0; i <= MAX_NUM_RETRIES; i++) { + if (i == MAX_NUM_RETRIES) { + return parseInMsg("-1"); + } + static const AT::Line check_conn_cmd = AT::Line(AT::CHECK_CONN); + if (!send(check_conn_cmd)) { + continue; + } + + if (!rcvRsps({check_conn_cmd, AT::Line(AT::DELIMITER), AT::Line(AT::STATUS_OK), AT::Line("\n")})) { + continue; + } + + static const AT::Line disable_ctrlflow_cmd = AT::Line(AT::DSBL_CTRLFLOW); + if (!send(disable_ctrlflow_cmd)) { + continue; + } + + if (!rcvRsps({disable_ctrlflow_cmd, AT::Line(AT::DELIMITER), AT::Line(AT::STATUS_OK), AT::Line("\n")})) { + continue; + } + + static const AT::Line sbdix_cmd = AT::Line(AT::SBD_SESSION); + if (!send(sbdix_cmd)) { + continue; + } + + auto opt_rsp = readRsp(); + if (!opt_rsp) { + continue; + } + + std::string opt_rsp_val = opt_rsp.value(); + std::vector sbd_status_vec; + boost::algorithm::split(sbd_status_vec, opt_rsp_val, boost::is_any_of(AT::DELIMITER)); + + std::string sbdix_value; + for (const auto & element : sbd_status_vec) { + if (element.find("SBDIX:") != std::string::npos) { + sbdix_value = element; + break; + } + } + + AT::SBDStatusRsp rsp(sbdix_value); + + if (rsp.MO_status_ == 0) { + if (rsp.MT_status_ == 0) { + return parseInMsg("-1"); + } else if (rsp.MT_status_ == 1) { //NOLINT + break; + } else if (rsp.MT_status_ == 2) { + continue; + } + } else { + continue; + } + } + + std::string receivedDataBuffer; + for (int i = 0; i < MAX_NUM_RETRIES; i++) { + static const AT::Line message_to_queue_cmd = AT::Line(AT::DNLD_TO_QUEUE); + if (!send(message_to_queue_cmd)) { + continue; + } + + if (!rcvRsps({message_to_queue_cmd, AT::Line("\n")})) { + continue; + } + + auto buffer_data = readRsp(); + if (!buffer_data) { + continue; + } + + std::regex re( + R"(name=\"data\"; filename=\"[^\"]*\"\r?\n(?:.*\r?\n)*\r?\n([\s\S]*?)\r?\n--)", std::regex::ECMAScript); + + std::smatch match; + + if (std::regex_search(*buffer_data, match, re)) { + *buffer_data = match[1]; + std::stringstream ss; + ss << *buffer_data; + std::cout << ss.str() << std::endl; + } else { + std::cout << "No match found." << std::endl; + } + + receivedDataBuffer = buffer_data.value(); + break; + } + + custom_interfaces::msg::Path to_publish = parseInMsg(receivedDataBuffer); return to_publish; } diff --git a/src/network_systems/projects/local_transceiver/src/local_transceiver_ros_intf.cpp b/src/network_systems/projects/local_transceiver/src/local_transceiver_ros_intf.cpp index d742ac133..1c2ce7542 100644 --- a/src/network_systems/projects/local_transceiver/src/local_transceiver_ros_intf.cpp +++ b/src/network_systems/projects/local_transceiver/src/local_transceiver_ros_intf.cpp @@ -57,7 +57,7 @@ class LocalTransceiverIntf : public NetNode lcl_trns_ = std::make_unique(port, SATELLITE_BAUD_RATE); static constexpr int ROS_Q_SIZE = 5; - static constexpr auto TIMER_INTERVAL = std::chrono::milliseconds(500); + static constexpr auto TIMER_INTERVAL = std::chrono::milliseconds(300000); timer_ = this->create_wall_timer(TIMER_INTERVAL, std::bind(&LocalTransceiverIntf::pub_cb, this)); pub_ = this->create_publisher(ros_topics::GLOBAL_PATH, ROS_Q_SIZE); diff --git a/src/network_systems/projects/local_transceiver/test/test_local_transceiver.cpp b/src/network_systems/projects/local_transceiver/test/test_local_transceiver.cpp index 0c6b57162..a49cbfae8 100644 --- a/src/network_systems/projects/local_transceiver/test/test_local_transceiver.cpp +++ b/src/network_systems/projects/local_transceiver/test/test_local_transceiver.cpp @@ -1,5 +1,6 @@ /* IMPORTANT: Make sure only one instance of sailbot_workspace/scripts/run_virtual_iridium.sh is running */ +#include #include #include @@ -12,6 +13,7 @@ #include #include #include +#include #include #include "at_cmds.h" @@ -255,3 +257,62 @@ TEST_F(TestLocalTransceiver, parseInMsgValid) EXPECT_EQ(parsed_test.waypoints[1].latitude, holder); EXPECT_EQ(parsed_test.waypoints[1].longitude, holder); } + +std::mutex port_mutex; + +TEST_F(TestLocalTransceiver, testMailboxBlackbox) +{ + std::lock_guard lock(port_mutex); // because same port is being used + + std::string holder = "curl -X POST -F \"test=1234\" http://localhost:8080"; + std::string holder2 = "printf \"at+sbdix\r\" > $LOCAL_TRANSCEIVER_TEST_PORT"; + + system(holder.c_str()); //NOLINT + system(holder2.c_str()); //NOLINT + + std::optional response = lcl_trns_->readRsp(); + std::cout << *response << std::endl; +} + +TEST_F(TestLocalTransceiver, parseReceiveMessageBlackbox) +{ + std::lock_guard lock(port_mutex); + + constexpr float holder = 14.3; + Polaris::GlobalPath sample_data; + + Polaris::Waypoint * waypoint_a = sample_data.add_waypoints(); + waypoint_a->set_latitude(holder); + waypoint_a->set_longitude(holder); + Polaris::Waypoint * waypoint_b = sample_data.add_waypoints(); + waypoint_b->set_latitude(holder); + waypoint_b->set_longitude(holder); + + std::string serialized_data; + ASSERT_TRUE(sample_data.SerializeToString(&serialized_data)); + + std::ofstream outfile("/tmp/serialized_data.bin", std::ios::binary); + outfile.write(serialized_data.data(), static_cast(serialized_data.size())); + outfile.close(); + + std::string holder2 = "curl -X POST -F \"data=@/tmp/serialized_data.bin\" http://localhost:8080"; + std::system(holder2.c_str()); //NOLINT + + custom_interfaces::msg::Path received_data = lcl_trns_->receive(); + + Polaris::GlobalPath global_path; + for (const auto & waypoint : received_data.waypoints) { + Polaris::Waypoint * new_waypoint = global_path.add_waypoints(); + new_waypoint->set_latitude(waypoint.latitude); + new_waypoint->set_longitude(waypoint.longitude); + } + + if (global_path.waypoints_size() > 0) { + ASSERT_EQ(global_path.waypoints_size(), sample_data.waypoints_size()) + << "Mismatch in number of waypoints received."; + ASSERT_EQ(global_path.waypoints(0).latitude(), holder); + ASSERT_EQ(global_path.waypoints(0).longitude(), holder); + } else { + std::cout << "No waypoints received." << std::endl; + } +} diff --git a/src/website/views/components/DropDown/dropdown.module.css b/src/website/views/components/DropDown/dropdown.module.css index 25b4eca98..5973bf849 100644 --- a/src/website/views/components/DropDown/dropdown.module.css +++ b/src/website/views/components/DropDown/dropdown.module.css @@ -1,102 +1,58 @@ -@import 'https://fonts.googleapis.com/css2?family=Raleway:ital,wght@0,100..900;1,100..900&display=swap'; +@import 'https://fonts.googleapis.com/css2?family=Switzer:wght@400;700&display=swap'; -.iconButton { +.flexItemContainer { + flex: 1; display: flex; justify-content: center; - align-items: center; - font-family: Verdana, sans-serif; - padding: 3px; - border: 2px solid white; - border-radius: 5px; - width: 130px; - height: 50px; - color: white; - float: right; - margin-right: 20px; - box-sizing: border-box; - transition: 0.3s ease; - transform: none; - user-select: none; + font-family: Switzer, sans-serif; + color: #092e4e; + font-weight: bold; } -.iconButton:hover { - background-color: white; - color: #3498db; - cursor: pointer; - transition: 0.3s ease; +.detailsFlexContainer { + display: flex; + justify-content: space-between; + width: 100%; + position: relative; + padding-bottom: 8px; + margin-top: 2px; } -.iconButtonOpen { - font-family: Verdana, Geneva, sans-serif; - padding: 3px; - border: 2px solid white; - border-radius: 5px; - width: 130px; - height: 50px; - float: right; - margin-right: 20px; - box-sizing: border-box; - text-align: center; - background-color: white; - color: #3498db; - cursor: pointer; - transition: 0.3s ease; - transform: none; - user-select: none; +.detailsFlexContainer::after { + content: ''; + display: block; + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 1px; + background-color: black; } -.dropdownActive { - opacity: 1; - visibility: visible; - transform: translateY(0); - transition: 0.3s ease; +.contentContainer { + display: flex; + justify-content: space-between; + width: 100%; + padding-top: 8px; } -.dropdownInactive { - opacity: 0; - visibility: hidden; - transform: translateY(-45px); - transition: 0.3s; - pointer-events: none; +.accordionCustom { + background-color: #7a8b99; + border: 1px solid #bdc3c7; + box-shadow: none; } -.dropdownMenu { - margin: 5px; - position: absolute; - right: 3px; - top: 60px; - z-index: 1000; - background-color: white; - border-bottom: 2px #3498db solid; - border-radius: 5px; +.accordionSummaryCustom { + background-color: #d9dddc; + color: #00263e; + font-family: Switzer, sans-serif; + font-weight: bold; } -.dropdownItem { - text-transform: capitalize; - display: grid; - grid-template-columns: 90px auto; - font-size: 18px; - font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif; - width: 150px; - padding: 10px; - height: 40px; +.accordionDetailsCustom { background-color: white; - border: 2px white solid; - border-radius: 5px; - color: #3498db; - align-items: center; - transform: none; - user-select: none; - cursor: pointer; -} - -.dropdownItem:hover { - background-color: whitesmoke; -} - -.dragDropIndicator { - font-size: 20px; - font-weight: bold; - position: absolute; - right: 15px; + flex-direction: column; + padding: 16px; + font-family: Switzer, sans-serif; + color: #092e4e; }