From 84a121892b0e96f10f38403544dacf0522248b8d Mon Sep 17 00:00:00 2001 From: WS Date: Mon, 30 Mar 2026 10:40:46 -0500 Subject: [PATCH 1/4] changed CSV writer to write strings new syntax for at --- docs/source/overview/logging.rst | 8 +- docs/source/tutorials/test.rst | 4 +- include/scrimmage/common/CSV.h | 53 +++- src/common/CSV.cpp | 228 ++++++++++++------ .../TrajectoryRecordPlayback.cpp | 14 +- test/test_collisions.cpp | 2 +- test/test_csv.cpp | 71 +++--- 7 files changed, 254 insertions(+), 126 deletions(-) diff --git a/docs/source/overview/logging.rst b/docs/source/overview/logging.rst index f48b5aaf1f..189cc1edae 100644 --- a/docs/source/overview/logging.rst +++ b/docs/source/overview/logging.rst @@ -145,10 +145,10 @@ Print out the data from the CSV file: :linenos: for (int r = 0; r < csv.rows(); r++) { - cout << "t: " << csv.at(r, "t") << endl; - cout << "x: " << csv.at(r, "x") << endl; - cout << "y: " << csv.at(r, "y") << endl; - cout << "z: " << csv.at(r, "z") << endl; + cout << "t: " << csv.at(r, "t") << endl; + cout << "x: " << csv.at(r, "x") << endl; + cout << "y: " << csv.at(r, "y") << endl; + cout << "z: " << csv.at(r, "z") << endl; } Real-time Plotting from CSV diff --git a/docs/source/tutorials/test.rst b/docs/source/tutorials/test.rst index a9927b67e0..aa790442f9 100644 --- a/docs/source/tutorials/test.rst +++ b/docs/source/tutorials/test.rst @@ -38,7 +38,7 @@ mission level functionality. A sample can be found in if (!summary_found) return; const int row = csv.rows() - 1; - double collisions = csv.at(row, "team_coll"); + double collisions = csv.at(row, "team_coll"); EXPECT_GT(collisions, 0); // expect collisions } @@ -71,5 +71,5 @@ that there was at least one collision in the simulation by reading the if (!summary_found) return; const int row = csv.rows() - 1; - double collisions = csv.at(row, "team_coll"); + double collisions = csv.at(row, "team_coll"); EXPECT_GT(collisions, 0); // expect collisions diff --git a/include/scrimmage/common/CSV.h b/include/scrimmage/common/CSV.h index 12ba3197c5..fa10c8a38c 100644 --- a/include/scrimmage/common/CSV.h +++ b/include/scrimmage/common/CSV.h @@ -34,18 +34,56 @@ #define INCLUDE_SCRIMMAGE_COMMON_CSV_H_ #include +#include +#include #include #include #include +#include #include #include +#include + +#include "scrimmage/parse/ParseUtils.h" + +namespace { + +struct StringifyVisitor { + + bool double_is_fixed = true; + bool double_is_scientific = true; + int double_precision = 13; + + std::string operator()(bool value) const { return value ? "true" : "false"; } + std::string operator()(uint32_t value) const { return std::to_string(value); } + std::string operator()(uint64_t value) const { return std::to_string(value); } + std::string operator()(int32_t value) const { return std::to_string(value); } + std::string operator()(int64_t value) const { return std::to_string(value); } + std::string operator()(const std::string& value) const { return value; } + std::string operator()(double value) const { + // default precision values for double are not enough in many cases + std::ostringstream conv; + if (double_is_fixed) { + conv << std::fixed; + } + if (double_is_scientific) { + conv << std::scientific; + } + conv << std::setprecision(double_precision) << value; + return conv.str(); + } +}; + +} // namespace namespace scrimmage { class CSV { public: - typedef std::list Headers; - typedef std::list> Pairs; + typedef std::vector Headers; + typedef std::variant + PossibleVariantTypes; + typedef std::vector> Pairs; ~CSV(); @@ -75,7 +113,11 @@ class CSV { size_t rows(); - double at(int row, const std::string& header); + template + T1 at(int row, const std::string& header) { + const int column = column_headers_.at(header); + return convert(table_.at(row).at(column)); + } friend std::ostream& operator<<(std::ostream& os, const CSV& csv); @@ -87,7 +129,7 @@ class CSV { void set_double_scientific(bool is_scientific) { double_is_scientific_ = is_scientific; } protected: - std::list get_csv_line_elements(const std::string& str); + std::string get_csv_string(const PossibleVariantTypes& val) const; void write_headers(); @@ -100,7 +142,7 @@ class CSV { // Key 1 : Row Index // Key 2 : Column Index // Value : Cell Value - std::map> table_; + std::map> table_; int next_row_ = 0; std::ofstream file_out_; @@ -110,7 +152,6 @@ class CSV { int double_precision_ = 13; bool double_is_fixed_ = true; bool double_is_scientific_ = false; - std::string headers_to_string() const; std::string rows_to_string() const; std::string row_to_string(const int& i) const; diff --git a/src/common/CSV.cpp b/src/common/CSV.cpp index 68769f6390..9a65489dc9 100644 --- a/src/common/CSV.cpp +++ b/src/common/CSV.cpp @@ -32,21 +32,132 @@ #include "scrimmage/common/CSV.h" -#include -#include -#include #include #include #include #include -#include using std::cout; using std::endl; namespace scrimmage { +// Function to escape special characters in a CSV field +std::string escape_csv_field(const std::string& field) { + std::string escapedField = field; + + // If the field contains a comma, a quote, or a newline, wrap it in quotes + if (field.find(',') != std::string::npos || field.find('"') != std::string::npos + || field.find('\n') != std::string::npos) { + + std::ostringstream escaped; + escaped << '"'; + for (char c : field) { + if (c == '"') { + escaped << "\"\""; // Escape double quotes as "" + } else { + escaped << c; + } + } + escaped << '"'; + escapedField = escaped.str(); + } + + return escapedField; +} + +// Function to unescape a single CSV field +std::string unescape_csv_field(const std::string& field) { + // If the field starts and ends with a quote, it is escaped + if (field.size() >= 2 && field.front() == '"' && field.back() == '"') { + std::ostringstream unescaped; + bool isQuotePair = false; + + // Process the part within the quotes + for (size_t i = 1; i < field.size() - 1; ++i) { + char c = field[i]; + if (isQuotePair) { + if (c == '"') { // This is the double quote -> unescape it + unescaped << '"'; + isQuotePair = false; + } else { + // Invalid situation (should not happen in a valid CSV): ignore + throw std::runtime_error("Invalid CSV: unexpected character after quote"); + } + } else { + if (c == '"') { + // First quote of a pair + isQuotePair = true; + } else { + unescaped << c; // Regular character + } + } + } + + // If `isQuotePair` is still true here, it means the quotes were improperly escaped + if (isQuotePair) { + throw std::runtime_error("Invalid CSV: unbalanced quotes in field"); + } + + return unescaped.str(); + } + + // If there are no surrounding quotes, return the field as is + return field; +} + +// Function to split a CSV string by some token while respecting escape sequences of +// 2 double quotes """" +std::vector split_csv_string(const std::string& row, const char& token) { + std::vector fields; // Stores the extracted fields + std::ostringstream currentField; // Accumulates characters for the current field + bool insideQuotes = false; // Tracks if we're inside a quoted field + + for (size_t i = 0; i < row.size(); ++i) { + char c = row[i]; + + if (insideQuotes) { + if (c == '"') { + // If we see a double-quote while inside quotes, check for escape (i.e., `""`) + if (i + 1 < row.size() && row[i + 1] == '"') { + currentField << c; + currentField << c; + ++i; // Skip the next quote (because this is an escape sequence) + } else { + // Exiting the quoted field + insideQuotes = false; + currentField << c; + } + } else { + // Append any other character inside quotes + // This is where newlines and commas can appear + // as valid text inside a cell + currentField << c; + } + } else { + if (c == token) { + // token outside quotes: field delimiter + fields.push_back(currentField.str()); // Add the current field to the list + currentField.str(""); // Reset for the next field + currentField.clear(); // Reset for the next field + } else if (c == '"') { + // Start of a quoted field + currentField << c; + insideQuotes = true; + } else { + // Regular character outside quotes + currentField << c; + } + } + } + + // Add the last field (if any) + fields.push_back(currentField.str()); + + return fields; +} + CSV::~CSV() { this->close_output(); } @@ -70,18 +181,27 @@ void CSV::set_column_headers(const Headers& headers, bool write) { } void CSV::set_column_headers(const std::string& headers, bool write) { - std::list headers_vec = get_csv_line_elements(headers); + std::vector headers_vec = split_csv_string(headers, ','); set_column_headers(headers_vec, write); } +std::string CSV::get_csv_string(const PossibleVariantTypes& v) const { + return std::visit( + StringifyVisitor{ + .double_is_fixed = double_is_fixed_, + .double_is_scientific = double_is_scientific_, + .double_precision = double_precision_}, + v); +} + bool CSV::append(const Pairs& pairs, bool write, bool keep_in_memory) { - for (std::pair pair : pairs) { + for (const auto& pair : pairs) { auto it = column_headers_.find(pair.first); if (it == column_headers_.end()) { cout << "Warning: column header doesn't exist: " << pair.first << endl; } - table_[next_row_][it->second] = pair.second; + table_[next_row_][it->second] = escape_csv_field(get_csv_string(pair.second)); } if (write) { @@ -124,7 +244,7 @@ std::ostream& operator<<(std::ostream& os, const CSV& csv) { } std::string CSV::headers_to_string() const { - std::string result = ""; + std::ostringstream result; // Get an ordered vector of headers std::vector headers(column_headers_.size()); @@ -134,65 +254,47 @@ std::string CSV::headers_to_string() const { unsigned int i = 0; for (std::string header : headers) { - result += header; + result << escape_csv_field(header); if (i + 1 < headers.size()) { - result += ","; + result << ","; } i++; } - return result; + return result.str(); } std::string CSV::rows_to_string() const { - std::string result = ""; + std::ostringstream result; // Write all the rows out for (unsigned int i = 0; i < table_.size(); i++) { - result += this->row_to_string(i); + result << this->row_to_string(i); if (i < table_.size() - 1) { - result += "\n"; + result << "\n"; } } - return result; + return result.str(); } std::string CSV::row_to_string(const int& row) const { - std::string result = ""; + std::ostringstream result; - // Initialize a vector with no value string. Iterate over - // column_headers, use column index to fill in column for values. - std::vector values(column_headers_.size(), no_value_str_); auto it_row = table_.find(row); - for (auto& kv : it_row->second) { - if (static_cast(kv.second) == kv.second) { - values[kv.first] = std::to_string(static_cast(kv.second)); - } else if (static_cast(kv.second) == kv.second) { - // default precision values for double are not enough in many cases - std::ostringstream conv; - if (double_is_fixed_) { - conv << std::fixed; - } - if (double_is_scientific_) { - conv << std::scientific; - } - conv << std::setprecision(double_precision_) << kv.second; - values[kv.first] = conv.str(); - } else { - values[kv.first] = std::to_string(kv.second); - } + if (it_row == table_.end()) { + return ""; } // Append the rows to the resultant string unsigned int i = 0; - for (std::string str : values) { - result += str; + for (auto& kv : it_row->second) { + result << escape_csv_field(kv.second); - if (i + 1 < values.size()) { - result += ","; + if (i + 1 < it_row->second.size()) { + result << ","; } i++; } - return result; + return result.str(); } bool CSV::to_csv(const std::string& filename) { @@ -218,34 +320,31 @@ bool CSV::read_csv_from_string(const std::string& csv_str, const bool& contains_ table_.clear(); column_headers_.clear(); - std::vector line_tokens; - boost::split(line_tokens, csv_str, boost::is_any_of("\n")); + std::vector line_tokens = split_csv_string(csv_str, '\n'); int row_num = 0; for (unsigned int line_num = 0; line_num < line_tokens.size(); line_num++) { - // Strip "whitespace" characters. - std::string line = remove_whitespace(line_tokens[line_num]); + std::string line = line_tokens[line_num]; if (line == "") continue; // Ignore lines that are empty if (line_num == 0 && contains_header) { - std::list headers = get_csv_line_elements(line); + std::vector headers = split_csv_string(line, ','); int i = 0; - for (auto& str : headers) { - column_headers_[str] = i; + for (const auto& str : headers) { + column_headers_[unescape_csv_field(str)] = i; i++; } } else { - std::vector tokens; - boost::split(tokens, line, boost::is_any_of(",")); + std::vector tokens = split_csv_string(line, ','); for (unsigned int i = 0; i < tokens.size(); i++) { - table_[row_num][i] = std::stod(tokens[i]); + table_[row_num][i] = unescape_csv_field(tokens[i]); } // If this is the first line and the file doesn't contain a header, // populate the column headers with indices based on the number of // comma separated values - if (row_num == 0 && not contains_header) { + if (row_num == 0 && !contains_header) { for (unsigned int i = 0; i < tokens.size(); i++) { column_headers_[std::to_string(i)] = i; } @@ -285,31 +384,6 @@ size_t CSV::rows() { return table_.size(); } -double CSV::at(int row, const std::string& header) { - const int column = column_headers_.at(header); - return table_.at(row).at(column); -} - -std::list CSV::get_csv_line_elements(const std::string& str) { - std::list elems; - std::vector tokens; - boost::split(tokens, str, boost::is_any_of(",")); - for (unsigned int i = 0; i < tokens.size(); i++) { - // Remove whitespace - tokens[i].erase( - std::remove_if( - tokens[i].begin(), - tokens[i].end(), - [](unsigned char x) { return std::isspace(x); }), - tokens[i].end()); - - if (tokens[i] != "") { - elems.push_back(tokens[i]); - } - } - return elems; -} - void CSV::write_headers() { file_out_ << headers_to_string() << endl; } diff --git a/src/plugins/autonomy/TrajectoryRecordPlayback/TrajectoryRecordPlayback.cpp b/src/plugins/autonomy/TrajectoryRecordPlayback/TrajectoryRecordPlayback.cpp index 529e3f2510..e3f0f2f5a4 100644 --- a/src/plugins/autonomy/TrajectoryRecordPlayback/TrajectoryRecordPlayback.cpp +++ b/src/plugins/autonomy/TrajectoryRecordPlayback/TrajectoryRecordPlayback.cpp @@ -90,19 +90,21 @@ void TrajectoryRecordPlayback::init(std::map& params) for (size_t r = 0; r < csv_.rows(); r++) { scrimmage::State desired_state; - desired_state.pos() << csv_.at(r, "x_d"), csv_.at(r, "y_d"), csv_.at(r, "z_d"); + desired_state.pos() << csv_.at(r, "x_d"), csv_.at(r, "y_d"), + csv_.at(r, "z_d"); - desired_state.vel() << csv_.at(r, "vx_d"), csv_.at(r, "vy_d"), csv_.at(r, "vz_d"); + desired_state.vel() << csv_.at(r, "vx_d"), csv_.at(r, "vy_d"), + csv_.at(r, "vz_d"); scrimmage::Quaternion quat( - csv_.at(r, "roll_d"), - csv_.at(r, "pitch_d"), - csv_.at(r, "yaw_d")); + csv_.at(r, "roll_d"), + csv_.at(r, "pitch_d"), + csv_.at(r, "yaw_d")); desired_state.quat() = quat; TrajectoryPoint traj; - traj.set_t(csv_.at(r, "t")); + traj.set_t(csv_.at(r, "t")); traj.set_desired_state(desired_state); trajs_.push_back(traj); } diff --git a/test/test_collisions.cpp b/test/test_collisions.cpp index 5abd95730a..7ccaab07c0 100644 --- a/test/test_collisions.cpp +++ b/test/test_collisions.cpp @@ -54,6 +54,6 @@ TEST(test_collisions, collisions) { return; const int row = csv.rows() - 1; - int collisions = csv.at(row, "team_coll"); + int collisions = csv.at(row, "team_coll"); EXPECT_GT(collisions, 0); // expect collisions } diff --git a/test/test_csv.cpp b/test/test_csv.cpp index 3e5debf57f..231e18011c 100644 --- a/test/test_csv.cpp +++ b/test/test_csv.cpp @@ -37,7 +37,6 @@ #include #include "scrimmage/common/CSV.h" -#include "scrimmage/parse/ParseUtils.h" namespace sc = scrimmage; @@ -51,35 +50,48 @@ class CSVTest : public ::testing::Test { } std::string csv_str = ""; std::vector csv_lines = { - "my_test_bool,my_test_int,my_test_float,my_test_double", - "1,1,2.100000,99.987000", - "0,1,2.100000,99.987000", - "0,2,2.100000,99.987000", - "0,2,3.200000,99.987000", - "0,2,3.200000,98.987000"}; + "my_test_bool,my_test_int,my_test_float,my_test_double,\"my,_test_string\"", + "1,1,2.100000,99.987000,\",test with \"\"quotes\"\", newline \n, and comma,\"", + "0,1,2.100000,99.987000,\",test with \"\"quotes\"\", newline \n, and comma,\"", + "0,2,2.100000,99.987000,\",test with \"\"quotes\"\", newline \n, and comma,\"", + "0,2,3.200000,99.987000,\",test with \"\"quotes\"\", newline \n, and comma,\"", + "0,2,3.200000,98.987000,\",test with \"\"quotes\"\", newline \n, and comma,\""}; + + std::vector csv_headers = { + "my_test_bool", + "my_test_int", + "my_test_float", + "my_test_double", + "my,_test_string", + }; + std::vector list_of_pairs = { + {{"my_test_bool", 1}, + {"my_test_int", 1}, + {"my_test_float", 2.100000}, + {"my_test_double", 99.987000}, + {"my,_test_string", ",test with \"quotes\", newline \n, and comma,"}}, + {{"my_test_bool", 0}, + {"my_test_int", 1}, + {"my_test_float", 2.100000}, + {"my_test_double", 99.987000}, + {"my,_test_string", ",test with \"quotes\", newline \n, and comma,"}}, + {{"my_test_bool", 0}, + {"my_test_int", 2}, + {"my_test_float", 2.100000}, + {"my_test_double", 99.987000}, + {"my,_test_string", ",test with \"quotes\", newline \n, and comma,"}}, + {{"my_test_bool", 0}, + {"my_test_int", 2}, + {"my_test_float", 3.200000}, + {"my_test_double", 99.987000}, + {"my,_test_string", ",test with \"quotes\", newline \n, and comma,"}}, + {{"my_test_bool", 0}, + {"my_test_int", 2}, + {"my_test_float", 3.200000}, + {"my_test_double", 99.987000}, + {"my,_test_string", ",test with \"quotes\", newline \n, and comma,"}}}; }; -std::list construct_list_of_pairs(const std::vector& csv_lines) { - EXPECT_GT(csv_lines.size(), static_cast(0)); - - // Use the csv_lines list of strings to append to the CSV object - std::list list_of_pairs; - std::vector headers = - sc::str2container>(csv_lines[0], ","); - for (unsigned int i = 1; i < csv_lines.size(); i++) { - std::vector items = - sc::str2container>(csv_lines[i], ","); - EXPECT_EQ(headers.size(), items.size()); - - sc::CSV::Pairs pairs; - for (unsigned int k = 0; k < headers.size(); k++) { - pairs.push_back(std::make_pair(headers[k], std::stod(items[k]))); - } - list_of_pairs.push_back(pairs); - } - return list_of_pairs; -} - TEST_F(CSVTest, from_string) { std::string filename = "from_string.csv"; @@ -105,10 +117,9 @@ TEST_F(CSVTest, from_append) { std::string filename("from_append.csv"); sc::CSV csv; csv.open_output(filename); - csv.set_column_headers(csv_lines[0]); // Write the column headers + csv.set_column_headers(csv_headers); // Write the column headers // Create a list of pairs to pass to CSV - auto list_of_pairs = construct_list_of_pairs(csv_lines); for (auto& pairs : list_of_pairs) { csv.append(pairs, true, true); } From 1a23abae4740d960b762396b0ba388df649b6d27 Mon Sep 17 00:00:00 2001 From: WS Date: Tue, 31 Mar 2026 17:05:58 -0400 Subject: [PATCH 2/4] readme formatting --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a1c6364688..a7a6b9451f 100644 --- a/README.md +++ b/README.md @@ -259,18 +259,18 @@ If you want to see the scrimmage mission on your host, you can do the following: 1. Allow docker to connect to your local display 2. Run the mission in the docker container - xhost +local:docker - docker run -it -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix scrimmage/ubuntu:latest - source ~/.scrimmage/setup.bash - scrimmage ~/scrimmage/scrimmage/missions/straight.xml + xhost +local:docker + docker run -it -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix scrimmage/ubuntu:latest + source ~/.scrimmage/setup.bash + scrimmage ~/scrimmage/scrimmage/missions/straight.xml If you want gpu access for your docker container You must do the following: 1. Install the container tools for your OS/GPU to allow the container to access your host's resources properly. - a. For NVidia GPUS this is the [Nvidia container toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html) + a. For Nvidia GPUs this is the [Nvidia container toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html) 2. include the --gpus all flag in your docker run invocation - docker run -it --gpus all -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix scrimmage/ubuntu:latest + docker run -it --gpus all -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix scrimmage/ubuntu:latest ## Building SCRIMMAGE for CentOS or RedHat From 96558b5afb15dbe409ab62d4a9b98f0a6638b1f7 Mon Sep 17 00:00:00 2001 From: WS Date: Wed, 1 Apr 2026 19:29:06 -0400 Subject: [PATCH 3/4] fix typo in docs --- docs/source/tutorials/parameters.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/tutorials/parameters.rst b/docs/source/tutorials/parameters.rst index 922f0e221b..6944ff96c4 100644 --- a/docs/source/tutorials/parameters.rst +++ b/docs/source/tutorials/parameters.rst @@ -34,7 +34,7 @@ During the simulation, another plugin can call the ``set_param()`` function: set_param("desired_z", 100.0) -while will have the affect of directly modifying the value of ``desired_z_`` in +which will have the affect of directly modifying the value of ``desired_z_`` in the plugin that registered that parameter. If you require a callback function when the parameter value is updated, you can use the optional callback form of the ``register_param()`` function: From 88491c5cf1714ba2c2e565a137b4bba4f8db2a92 Mon Sep 17 00:00:00 2001 From: WS Date: Thu, 2 Apr 2026 11:32:10 -0400 Subject: [PATCH 4/4] switch docker docs to github package registry --- README.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a7a6b9451f..65a67e46f8 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,8 @@ # SCRIMMAGE Multi-Agent Simulator -[![Build Status](https://travis-ci.org/gtri/scrimmage.png?branch=master)](https://travis-ci.org/gtri/scrimmage) +[![CI](https://github.com/gtri/scrimmage/actions/workflows/ubuntu-24.04.yml/badge.svg)](https://github.com/gtri/scrimmage/actions/workflows/ubuntu-24.04.yml) [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/gtri/scrimmage/issues) -[![Docker Pulls](https://img.shields.io/docker/pulls/syllogismrxs/scrimmage.svg?maxAge=2592000)](https://hub.docker.com/r/syllogismrxs/scrimmage) -[![Join the chat at https://gitter.im/gtri-scrimmage/community](https://badges.gitter.im/gtri-scrimmage/community.svg)](https://gitter.im/gtri-scrimmage/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Docker Pulls](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fghcr-badge.elias.eu.org%2Fapi%2Fgtri%2Fscrimmage%2Fscrimmage-24.04&query=downloadCount&label=Docker%20Pulls)](ghcr.io/gtri/scrimmage-24.04:latest) ![SCRIMMAGE Logo](./docs/source/images/scrimmage_vert_black_clean_medium.png) @@ -222,7 +221,7 @@ The SCRIMMAGE docker image is pushed to a public repository after a successful build on Travis. If docker is installed on your machine, you can obtain the SCRIMMAGE docker image by running the following command: - docker pull syllogismrxs/scrimmage:latest + docker pull ghcr.io/gtri/scrimmage-24.04:latest You can pass mission files from your host machine to the `scrimmage` executable inside of the docker container with the following command: @@ -230,7 +229,7 @@ inside of the docker container with the following command: cd /path/to/scrimmage/missions docker run --name my-scrimmage \ -v ${PWD}/straight_jsbsim.xml:/straight_jsbsim.xml \ - syllogismrxs/scrimmage:latest /straight_jsbsim.xml + ghcr.io/gtri/scrimmage-24.04:latest /straight_jsbsim.xml The previous command mounts the `straight_jsbsim.xml` mission file on your host machine into the scrimmage container and then the `/straight_jsbsim.xml` @@ -246,7 +245,7 @@ log files from the docker container: If you need to drop into a shell inside of the scrimmage container, you will need to overwrite the docker image's ENTRYPOINT. - docker run -it --entrypoint="/bin/bash" syllogismrxs/scrimmage:latest + docker run -it --entrypoint="/bin/bash" ghcr.io/gtri/scrimmage-24.04:latest Once inside of the container, you will need to source the `setup.bash` file manually before running a mission.