Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[choreo] Document Updater #926

Merged
merged 29 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
de3a836
added document upgrading
oh-yes-0-fps Nov 15, 2024
da1a88a
Merge branch 'main' of https://github.com/SleipnirGroup/Choreo into d…
oh-yes-0-fps Nov 15, 2024
320e05c
i tested and it works but i still have to add unit tests
oh-yes-0-fps Nov 15, 2024
fc2ca50
tested on paths, needs tests 2
oh-yes-0-fps Nov 15, 2024
0e35265
Merge branch 'main' into document-updater
calcmogul Nov 19, 2024
3daed8e
Run formatter
calcmogul Nov 19, 2024
4cfd00b
Merge branch 'main' into document-updater
calcmogul Nov 22, 2024
85d9413
Use integer for spec version
calcmogul Nov 22, 2024
2aa257b
Cast version to int
calcmogul Nov 22, 2024
b930c6d
Gracefully catch string versions
calcmogul Nov 22, 2024
aa7f526
Merge branch 'main' into document-updater
shueja Dec 2, 2024
5f0ac17
Create unit tests in upgraders.rs for document updater
shueja Dec 4, 2024
4e96a74
Format Rust
shueja Dec 4, 2024
a56669b
Reorganize test-jsons directory
shueja Dec 4, 2024
3b72712
Renamed beta6 to version number 0
shueja Dec 4, 2024
c4cfd2b
Add cargo tests to CI
shueja Dec 4, 2024
cf8332f
Move document spec into DocumentTypes directly, and split project and…
shueja Dec 4, 2024
1324f33
Merge branch 'main' into document-updater
calcmogul Dec 4, 2024
1cacc71
Added backlinks to upgrade docs from MobX stores
shueja Dec 4, 2024
164f7bf
Merge branch 'document-updater' of https://github.com/oh-yes-0-fps/Ch…
shueja Dec 4, 2024
b22555b
Add choreolib schema version docs
shueja Dec 4, 2024
f738344
Run formatter
calcmogul Dec 4, 2024
81acc01
Revert 0->1 upgrade function for trajectory (trackwidth)
shueja Dec 4, 2024
ead0712
Merge branch 'document-updater' of https://github.com/oh-yes-0-fps/Ch…
shueja Dec 4, 2024
b192527
Run formatter
calcmogul Dec 4, 2024
30a8669
Fix CI tests
shueja Dec 4, 2024
332d450
Merge branch 'document-updater' of https://github.com/oh-yes-0-fps/Ch…
shueja Dec 4, 2024
bcba64d
Format
shueja Dec 4, 2024
556efbc
Add new docs page to mkdocs.yml
shueja Dec 4, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/choreo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ jobs:

- name: Set up sccache
uses: mozilla-actions/[email protected]

- name: Cargo test
run: cargo test
- name: Build package
run: pnpm run tauri build ${{ matrix.tauri-build-flags }}
env:
Expand Down
25 changes: 18 additions & 7 deletions choreolib/py/choreo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
load_event_marker,
)

SPEC_VERSION = "v2025.0.0"
TRAJ_SCHEMA_VERSION = 0
PROJECT_SCHEMA_VERSION = 1


def load_differential_trajectory_string(
Expand All @@ -22,10 +23,15 @@ def load_differential_trajectory_string(
"""
data = json.loads(trajectory_json_string)
name = data["name"]
version = data["version"]
if version != SPEC_VERSION:
try:
version = int(data["version"])
if version != TRAJ_SCHEMA_VERSION:
raise ValueError(
f"{name}.traj: Wrong version {version}. Expected {TRAJ_SCHEMA_VERSION}"
)
except ValueError:
raise ValueError(
f"{name}.traj: Wrong version {version}. Expected {SPEC_VERSION}"
f"{name}.traj: Wrong version {data['version']}. Expected {TRAJ_SCHEMA_VERSION}"
)
samples = [
DifferentialSample(
Expand Down Expand Up @@ -76,10 +82,15 @@ def load_swerve_trajectory_string(trajectory_json_string: str) -> SwerveTrajecto
"""
data = json.loads(trajectory_json_string)
name = data["name"]
version = data["version"]
if version != SPEC_VERSION:
try:
version = int(data["version"])
if version != TRAJ_SCHEMA_VERSION:
raise ValueError(
f"{name}.traj: Wrong version {version}. Expected {TRAJ_SCHEMA_VERSION}"
)
except ValueError:
raise ValueError(
f"{name}.traj: Wrong version {version}. Expected {SPEC_VERSION}"
f"{name}.traj: Wrong version {data['version']}. Expected {TRAJ_SCHEMA_VERSION}"
)
samples = [
SwerveSample(
Expand Down
2 changes: 1 addition & 1 deletion choreolib/py/choreo/test/choreolib_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
TRAJECTORY = """
{
"name":"New Path",
"version":"v2025.0.0",
"version":0,
"snapshot":{
"waypoints":[
{"x":0.0, "y":0.0, "heading":0.0, "intervals":9, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false},
Expand Down
2 changes: 1 addition & 1 deletion choreolib/py/choreo/test/resources/swerve_test.traj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name":"test",
"version":"v2025.0.0",
"version":0,
"snapshot":{
"waypoints":[
{"x":2.6185336112976074, "y":6.034867286682129, "heading":0.0, "intervals":16, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false},
Expand Down
13 changes: 9 additions & 4 deletions choreolib/py/choreo/trajectory/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,18 +130,23 @@ def get_chassis_speeds(self) -> ChassisSpeeds:
"""
Returns the field-relative chassis speeds of this state.
"""
from choreo import SPEC_VERSION
from choreo import PROJECT_SCHEMA_VERSION
from wpilib import getDeployDirectory

# Get only .chor file in deploy directory
chor = [f for f in os.listdir(getDeployDirectory()) if f.endswith(".chor")][0]

with open(chor, "r", encoding="utf-8") as project_file:
data = json.load(project_file)
version = data["version"]
if version != SPEC_VERSION:
try:
version = int(data["version"])
if version != PROJECT_SCHEMA_VERSION:
raise ValueError(
f".chor project file: Wrong version {version}. Expected {PROJECT_SCHEMA_VERSION}"
)
except ValueError:
raise ValueError(
f".chor project file: Wrong version {version}. Expected {SPEC_VERSION}"
f".chor project file: Wrong version {data['version']}. Expected {PROJECT_SCHEMA_VERSION}"
)
trackwidth = float(data["config"]["differentialTrackWidth"]["val"])

Expand Down
37 changes: 30 additions & 7 deletions choreolib/src/main/java/choreo/Choreo.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ public final class Choreo {
.registerTypeAdapter(EventMarker.class, new EventMarker.Deserializer())
.create();
private static final String TRAJECTORY_FILE_EXTENSION = ".traj";
private static final String SPEC_VERSION = "v2025.0.0";
private static final int TRAJ_SCHEMA_VERSION = 0;
private static final int PROJECT_SCHEMA_VERSION = 1;

private static File CHOREO_DIR = new File(Filesystem.getDeployDirectory(), "choreo");

Expand Down Expand Up @@ -84,10 +85,22 @@ public static ProjectFile getProjectFile() {
String str = reader.lines().reduce("", (a, b) -> a + b);
reader.close();
JsonObject json = GSON.fromJson(str, JsonObject.class);
String version = json.get("version").getAsString();
if (!SPEC_VERSION.equals(version)) {
int version;
try {
version = json.get("version").getAsInt();
if (version != PROJECT_SCHEMA_VERSION) {
throw new RuntimeException(
".chor project file: Wrong version "
+ version
+ ". Expected "
+ PROJECT_SCHEMA_VERSION);
}
} catch (ClassCastException e) {
throw new RuntimeException(
".chor project file: Wrong version " + version + ". Expected " + SPEC_VERSION);
".chor project file: Wrong version "
+ json.get("version").getAsString()
+ ". Expected "
+ PROJECT_SCHEMA_VERSION);
}
LAZY_PROJECT_FILE = Optional.of(GSON.fromJson(str, ProjectFile.class));
} catch (JsonSyntaxException ex) {
Expand Down Expand Up @@ -162,10 +175,20 @@ static Trajectory<? extends TrajectorySample<?>> loadTrajectoryString(
String trajectoryJsonString, ProjectFile projectFile) {
JsonObject wholeTrajectory = GSON.fromJson(trajectoryJsonString, JsonObject.class);
String name = wholeTrajectory.get("name").getAsString();
String version = wholeTrajectory.get("version").getAsString();
if (!SPEC_VERSION.equals(version)) {
int version;
try {
version = wholeTrajectory.get("version").getAsInt();
if (version != TRAJ_SCHEMA_VERSION) {
throw new RuntimeException(
name + ".traj: Wrong version: " + version + ". Expected " + TRAJ_SCHEMA_VERSION);
}
} catch (ClassCastException e) {
throw new RuntimeException(
name + ".traj: Wrong version: " + version + ". Expected " + SPEC_VERSION);
name
+ ".traj: Wrong version: "
+ wholeTrajectory.get("version").getAsString()
+ ". Expected "
+ TRAJ_SCHEMA_VERSION);
}
// Filter out markers with negative timestamps or empty names
List<EventMarker> unfilteredEvents =
Expand Down
4 changes: 2 additions & 2 deletions choreolib/src/main/java/choreo/trajectory/ProjectFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public static class Config {
public final String name;

/** The version of the project. */
public final String version;
public final int version;

/** The sample type for the project */
public final String type;
Expand All @@ -132,7 +132,7 @@ public static class Config {
public final List<String> generationFeatures;

ProjectFile(
String name, String version, String type, Config config, List<String> generationFeatures) {
String name, int version, String type, Config config, List<String> generationFeatures) {
this.name = name;
this.version = version;
this.type = type;
Expand Down
8 changes: 4 additions & 4 deletions choreolib/src/main/native/include/choreo/Choreo.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

namespace choreo {

inline constexpr std::string_view kSpecVersion = "v2025.0.0";
inline constexpr uint32_t kSpecVersion = 1;

template <TrajectorySample SampleType, int Year>
class AutoFactory;
Expand Down Expand Up @@ -77,7 +77,7 @@ class Choreo {
}

wpi::json json = wpi::json::parse(fileBuffer.value()->GetCharBuffer());
std::string version = json["version"];
uint32_t version = json["version"];
if (kSpecVersion != version) {
throw fmt::format(".chor project file: Wrong version {}. Expected {}",
version, kSpecVersion);
Expand Down Expand Up @@ -155,8 +155,8 @@ class Choreo {
}

wpi::json json = wpi::json::parse(trajectoryJsonString);
std::string version = json["version"];
if (kSpecVersion != version) {
uint32_t version = json["version"];
if (version != kSpecVersion) {
throw fmt::format("{}.traj: Wrong version {}. Expected {}",
trajectoryName, version, kSpecVersion);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ struct ProjectFile {
std::string name;

/// The version of the project.
std::string version;
uint32_t version;

/// The sample type for the project.
std::string type;
Expand Down
4 changes: 2 additions & 2 deletions choreolib/src/test/java/choreo/ChoreoTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class ChoreoTests {
"""
{
"name":"New Path",
"version":"v2025.0.0",
"version":0,
"snapshot":{
"waypoints":[
{"x":0.0, "y":0.0, "heading":0.0, "intervals":9, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false},
Expand Down Expand Up @@ -59,7 +59,7 @@ public class ChoreoTests {
public static final String PROJ =
"{"
+ " \"name\": \"idk\","
+ " \"version\": \"v2025.0.0\","
+ " \"version\": 1,"
+ " \"type\": \"Swerve\","
+ " \"variables\": {"
+ " \"expressions\": {},"
Expand Down
4 changes: 2 additions & 2 deletions choreolib/src/test/native/cpp/ProjectFileTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ using namespace choreo;

constexpr std::string_view projectJsonString = R"({
"name":"test",
"version":"v2025.0.0",
"version":1,
"type":"Swerve",
"variables":{
"expressions":{
Expand Down Expand Up @@ -116,7 +116,7 @@ const wpi::json projectJson = wpi::json::parse(projectJsonString);

const ProjectFile correctProjFile{
"test",
"v2025.0.0",
1,
"Swerve",
{{"test", Variable{"Number", Expression{"2", 2.0}}}},
{{"test2", Pose{Expression{"2 m", 2.0}, Expression{"3 m", 3.0},
Expand Down
2 changes: 1 addition & 1 deletion choreolib/src/test/native/cpp/TrajectoryFileTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ using namespace choreo;
constexpr std::string_view swerveTrajectoryString =
R"({
"name":"New Path",
"version":"v2025.0.0",
"version":0,
"snapshot":{
"waypoints":[
{"x":0.0, "y":0.0, "heading":0.0, "intervals":9, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false},
Expand Down
110 changes: 110 additions & 0 deletions docs/contributing/schema-upgrade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Changing the Choreo Data Objects

Occasionally it is necessary to add something to the Choreo data objects. This document serves as a checklist for defining a new schema version.

> This document only discusses adding new fields to the existing objects. For example, adding new constraints uses a system not entirely described by this document.
This document is also not meant to capture every place that depends on a given struct.

## Capturing .chor and .traj from before the change

### Add project and trajectory files pre-change to `./test-jsons`.

To facilitate unit testing, Choreo keeps a copy of project and trajectory files from each schema version. These are stored in the test-jsons directory and organized first by file type, then by version.

For a version number `n`, the structure is
```
test-jsons/
project/
n/
differential.chor
swerve.chor
trajectory/
n/
differential.traj
swerve.traj

```
The contents of these files should include enough of Choreo's features to test the upgrader thoroughly, since version upgrade changes that don't apply to the test file don't get well-tested. The trajectories should be generated.


## src-core
### Make the desired change in `src-core/src/spec/project.rs` and/or `src-core/src/spec/trajectory.rs` as needed.
Note that in Rust, the `#[serde(rename_all = "camelCase")]` macros mean that the Rust struct name is `snake_case`, while in JSON serialization and in the TypeScript mirrors of the Rust structs, the name is `camelCase`.

### If editing the project schema, update the default file
This is in `src-core/src/spec/project.rs::default()`. Update it by manually making the change with reasonable default values. Rust may enforce this step with an error, depending on the change being made.

### Increment the correct schema version
This is either `PROJECT_SCHEMA_VERSION` or `TRAJ_SCHEMA_VERSION`, in `src-core/src/spec/mod.rs`. The previous and new versions shall be referred to as `PREV_VERSION` and `NEW_VERSION` in documentation below.

### Write an upgrade function for the new schema.

Upgrade functions are Rust functions that operate on the JSON object of the file. The upgrade functions and their tests are in `src-core/src/spec/upgraders.rs`. Refer to existing upgrade functions or `src-core/src/file_management/upgrader.rs::Editor` for details. Here are two examples:

```rs
// Naming convention: up_[PREV_VERSION]_[NEW_VERSION]
// Function signature (&mut Editor) -> ChoreoResult<()>
fn up_0_1(editor: &mut Editor) -> ChoreoResult<()> {
// Check if the file has a particular sub-object
if editor.has_path("trajectory") {
// Set an existing or nonexisting field
editor.set_path("trajectory.trackwidth", 1.0)
} else {
Ok(())
}
}

fn up_0_1(editor: &mut Editor) -> ChoreoResult<()> {
// Set the value of some field with the serialization of the argument
editor.set_path_serialize("config.cof", Expr::new("1.5", 1.5))
}
```

Ensure that any errors returned from the `Editor` calls are returned from the upgrader function before proceeding with the rest of the function.
### Add the upgrader function to the upgrader

In `upgraders.rs`, after adding the new upgrader function, add a line like
```rs
upgrader.add_version_action(up_[PREV_VERSION]_[NEW_VERSION]);
```
in `make_upgrader()` AFTER all existing `upgrader.add_version_action(...)` calls.

### Make tests for the new upgrade function
Create unit tests that load the files previously captured in `test-jsons` and upgrade them to the current version.
* In `upgraders.rs`, tests for the upgrade functions are within each sub-module (`project_file` and `traj_file`).
* Copy and paste an existing block of two tests and change the version number.
* Run tests to ensure the upgrader is working correctly.

## src (Typescript UI source)

### Make changes to the data objects in `src/document/2025/DocumentTypes.ts`

There is a mirror for every Rust struct that gets serialized. Note again that in TS, the fields are in `camelCase`.

### Update any usages of those objects in the Typescript source

Some objects have default values stored throughout the code.

### Update the relevant schema version in `src/document/2025/DocumentTypes.ts`

### Update the Mobx store for the modified data struct.

These are in `src/document/`.

#### Properties
Each store has a `.model` section showing the properties of the Mobx object. Usually these should match the data objects, but this is up to the needs of the implementation.

#### `get serialize()`
Each store has a `get serialize()` computed property. This needs to be updated to populate the data struct with the new field. Usually this is a straightforward assignment operator for primitive fields or a call of `serialize()` on child stores, but some stores have more bespoke serialization.

#### `deserialize()`
Each store has a `deserialize()` which populates the Mobx store from a data object.

## choreolib

Update TRAJ_SCHEMA_VERSION and PROJECT_SCHEMA_VERSION in the following:
* Python: `choreolib/py/choreo/__init__.py`
* Java: `choreolib/src/main/java/choreo/Choreo.java`
* C++ currently does not do version validation.

Make any functional changes to the trajectory classes and loading methods in all three languages, according to the schema change being made.
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,4 @@ nav:
- Building: contributing/building-choreo.md
- Contributing Guide: contributing/contributing-guide.md
- Release Process: contributing/release-process.md
- Upgrading File Schemas: contributing/schema-upgrade.md
1 change: 1 addition & 0 deletions src-core/src/file_management/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type TrajectoryFileName = String;

mod diagnostics;
mod formatter;
pub mod upgrader;

pub use diagnostics::{create_diagnostic_file, get_log_lines};

Expand Down
Loading
Loading