diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b6042d9..1818962 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -50,6 +50,9 @@ git submodule update --init --recursive The HiGHS library must be installed on your system. +>[!NOTE] +>If CMake cannot find a system-installed version of HiGHS, it will automatically download and build it as part of the project configuration process. While this simplifies setup, a pre-installed version is recommended for faster build times. + #### macOS (Homebrew) This is the easiest method for macOS: @@ -117,7 +120,7 @@ We use a standard out-of-source CMake build. This will create the main products in the `build/` directory: - * `libjres_solver.a` (or `.lib` on Windows): The static library. + * `libjres_solver.so` or `libjres_solver.a` (or `.dll`/`.lib`): The shared or static library. The type depends on the `BUILD_SHARED_LIBS` CMake option (defaults to ON). * `jres_solver` (or `jres_solver.exe`): The solver CLI executable. * `jres_formatter` (or `jres_formatter.exe`): The formatter CLI executable. diff --git a/README.md b/README.md index dcd5fb8..e3984aa 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,8 @@ These structs are used to represent the availability of team members. | `schedule_len` | `int` | The number of schedule entries. | | `diagnosis` | `const char**` | An array of strings with diagnostic information. Empty on success. | | `diagnosis_len` | `int` | The number of diagnosis strings. | +| `stats` | `JresSolverStats*` | Solver performance and complexity metrics. | +| `options` | `JresSolverOptions*` | The options used to generate this solution. | | `teamMembers` | `JresTeamMember*` | A pointer to an array of team members, including their tzOffset. | | `teamMembers_len` | `int` | The number of team members. | @@ -80,7 +82,9 @@ These structs are used to represent the availability of team members. | Field | Type | Description | | :--- | :--- | :--- | -| `stintId` | `int` | The ID of the stint. | +| `id` | `int` | The ID of the stint. | +| `startTime` | `const char*` | ISO 8601 timestamp for the start of the stint. | +| `endTime` | `const char*` | ISO 8601 timestamp for the end of the stint. | | `driver` | `const char*` | Name of the assigned driver. | | `spotter` | `const char*` | Name of the assigned spotter. | @@ -255,7 +259,9 @@ The `jres_output_to_json` function returns a JSON string containing the solution | Field | Type | Description | | :--- | :--- | :--- | -| `stintId` | Integer | The ID of the stint. | +| `id` | Integer | The ID of the stint. | +| `startTime` | String | ISO 8601 timestamp for the start of the stint. | +| `endTime` | String | ISO 8601 timestamp for the end of the stint. | | `driver` | String | Name of the assigned driver. | | `spotter` | String | Name of the assigned spotter (if Spotter Mode is active). | @@ -268,9 +274,27 @@ When the solver fails, the `schedule` array will be empty, and the `diagnosis` a ```json { "schedule": [ - { "stintId": 1, "driver": "Niki", "spotter": "Alain" }, - { "stintId": 2, "driver": "Niki", "spotter": "Alain" }, - { "stintId": 3, "driver": "Alain", "spotter": "Niki" } + { + "id": 1, + "startTime": "2024-06-15T15:00:00Z", + "endTime": "2024-06-15T16:00:00Z", + "driver": "Niki", + "spotter": "Alain" + }, + { + "id": 2, + "startTime": "2024-06-15T16:00:00Z", + "endTime": "2024-06-15T17:00:00Z", + "driver": "Niki", + "spotter": "Alain" + }, + { + "id": 3, + "startTime": "2024-06-15T17:00:00Z", + "endTime": "2024-06-15T18:00:00Z", + "driver": "Alain", + "spotter": "Niki" + } ], "diagnosis": [] } diff --git a/TOOLS.md b/TOOLS.md index d04a80f..8e9dca7 100644 --- a/TOOLS.md +++ b/TOOLS.md @@ -48,11 +48,11 @@ jres_solver.exe [options] | :--- | :------------------- | :-------------------------------------------------------------------------------------------- | :------ | | `-i` | `--input` | Path to the race data `.json` file. Reads from `stdin` if omitted. | `stdin` | | `-o` | `--output` | Path to save the calculated schedule (JSON). **Required for the Formatter.** | `stdout`| -| `-t` | `--time-limit` | Maximum time (in seconds) to let the optimizer run. | `30` | +| `-t` | `--time-limit` | Maximum time (in seconds) to let the optimizer run. | `5` | | `-q` | `--quiet` | Suppress INFO logs and the printed schedule summary. | `false` | | `-s` | `--spotter-mode` | Strategy for assigning spotters. Options: `none`, `integrated`, `sequential`. | `none` | | | `--allow-no-spotter` | Allow specific stints to have no spotter assigned (if spotter mode is active). | `false` | -| `-g` | `--optimality-gap` | Stop solver when the solution is within this gap of perfection (e.g., `0.01` for 1%). | `0.0` | +| `-g` | `--optimality-gap` | Stop solver when the solution is within this gap of perfection (e.g., `0.2` for 20%). | `0.2` | | `-d` | `--diagnose` | Run in **Diagnostic Mode** to explain why a schedule is infeasible. | `false` | | `-h` | `--help` | Print usage instructions. | | diff --git a/include/jres_solver/jres_json_converter.hpp b/include/jres_solver/jres_json_converter.hpp deleted file mode 100644 index 6d758f6..0000000 --- a/include/jres_solver/jres_json_converter.hpp +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef JRES_JSON_CONVERTER_HPP -#define JRES_JSON_CONVERTER_HPP - -#include "jres_solver.hpp" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Converts a JSON string to a JresSolverInput struct. - * - * The caller is responsible for freeing the memory allocated for the - * JresSolverInput struct and its members by calling free_jres_solver_input(). - * - * @param jsonData The JSON data as a string. - * @return A pointer to the converted JresSolverInput struct, or nullptr on failure. - */ -JresSolverInput* jres_input_from_json(const char* jsonData); - -/** - * @brief Converts a JresSolverOutput struct to a JSON string. - * - * The caller is responsible for freeing the memory allocated for the - * returned JSON string. - * - * @param output The JresSolverOutput struct to convert. - * @return A JSON string representation of the output, or nullptr on failure. - */ -char* jres_output_to_json(const JresSolverOutput* output); - -/** - * @brief Frees the memory allocated for a JresSolverInput struct. - * - * @param input The JresSolverInput struct to free. - */ -void free_jres_solver_input(JresSolverInput* input); - -/** - * @brief Frees the memory allocated for a JresSolverOutput struct. - * - * @param output The JresSolverOutput struct to free. - */ -void free_jres_solver_output(JresSolverOutput* output); - -/** - * @brief Retrieves the last error message that occurred in a C-API function. - * - * The caller does NOT own the returned string and must NOT free it. - * The message is thread-local and valid until the next C-API call on the same thread. - * - * @return A C-string containing the last error message, or an empty string if no error. - */ -const char* jres_get_last_error(); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // JRES_JSON_CONVERTER_HPP diff --git a/include/jres_solver/jres_solver.hpp b/include/jres_solver/jres_solver.hpp index bd9f043..234f29a 100644 --- a/include/jres_solver/jres_solver.hpp +++ b/include/jres_solver/jres_solver.hpp @@ -200,6 +200,7 @@ JRES_SOLVER_API char* jres_output_to_json(const JresSolverOutput* output); JRES_SOLVER_API void free_jres_solver_input(JresSolverInput* input); JRES_SOLVER_API void free_jres_solver_output(JresSolverOutput* output); +JRES_SOLVER_API const char* jres_get_last_error(); JRES_SOLVER_API void free_json_string(char* json_string); #ifdef __cplusplus diff --git a/src/jres_json_converter.cpp b/src/jres_json_converter.cpp index 93b729c..7739332 100644 --- a/src/jres_json_converter.cpp +++ b/src/jres_json_converter.cpp @@ -3,7 +3,7 @@ * @file src/jres_json_converter.cpp * @brief JSON conversion functions for the JRES Solver library. */ -#include "jres_solver/jres_json_converter.hpp" +#include "jres_solver/jres_solver.hpp" #include "nlohmann/json.hpp" #include #include @@ -57,7 +57,7 @@ char* allocate_and_copy(const std::string& s) { return cstr; } -JresSolverInput* jres_input_from_json(const char* jsonData) { +JRES_SOLVER_API JresSolverInput* jres_input_from_json(const char* jsonData) { last_error_message.clear(); // Clear previous error try { json j = json::parse(jsonData); @@ -125,7 +125,7 @@ JresSolverInput* jres_input_from_json(const char* jsonData) { } -char* jres_output_to_json(const JresSolverOutput* output) { +JRES_SOLVER_API char* jres_output_to_json(const JresSolverOutput* output) { last_error_message.clear(); // Clear previous error if (!output) { last_error_message = "Output is nullptr."; @@ -194,11 +194,11 @@ char* jres_output_to_json(const JresSolverOutput* output) { } } -const char* jres_get_last_error() { +JRES_SOLVER_API const char* jres_get_last_error() { return last_error_message.c_str(); } -void free_jres_solver_output(JresSolverOutput* output) { +JRES_SOLVER_API void free_jres_solver_output(JresSolverOutput* output) { if (!output) { return; } @@ -232,7 +232,7 @@ void free_jres_solver_output(JresSolverOutput* output) { delete output; } -void free_jres_solver_input(JresSolverInput* input) { +JRES_SOLVER_API void free_jres_solver_input(JresSolverInput* input) { if (!input) { return; } diff --git a/src/jres_solver.cpp b/src/jres_solver.cpp index 15a0a63..264ecf8 100644 --- a/src/jres_solver.cpp +++ b/src/jres_solver.cpp @@ -7,9 +7,8 @@ #include "jres_internal_types.hpp" #include "jres_standard_solver.hpp" #include "jres_diagnostic_solver.hpp" -#include "jres_solver/jres_json_converter.hpp" -JresSolverOutput* solve_race_schedule(const JresSolverInput* input, const JresSolverOptions* options) { +JRES_SOLVER_API JresSolverOutput* solve_race_schedule(const JresSolverInput* input, const JresSolverOptions* options) { try { jres::internal::SolverInput internal_input = jres::internal::from_c_input(input); JresStandardSolver solver(internal_input, *options); @@ -26,7 +25,7 @@ JresSolverOutput* solve_race_schedule(const JresSolverInput* input, const JresSo } } -JresSolverOutput* diagnose_race_schedule(const JresSolverInput* input, const JresSolverOptions* options) { +JRES_SOLVER_API JresSolverOutput* diagnose_race_schedule(const JresSolverInput* input, const JresSolverOptions* options) { try { jres::internal::SolverInput internal_input = jres::internal::from_c_input(input); JresDiagnosticSolver solver(internal_input, *options); @@ -43,6 +42,6 @@ JresSolverOutput* diagnose_race_schedule(const JresSolverInput* input, const Jre } } -void free_json_string(char* json_string) { +JRES_SOLVER_API void free_json_string(char* json_string) { delete[] json_string; }