Skip to content

Commit 05e8fe6

Browse files
authored
[choreolib] Port Choreo factory functions from Java to C++ (#919)
1 parent c024826 commit 05e8fe6

File tree

8 files changed

+266
-137
lines changed

8 files changed

+266
-137
lines changed

choreolib/src/main/java/choreo/Choreo.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -296,25 +296,25 @@ public void clear() {
296296
* Create a factory that can be used to create {@link AutoRoutine} and {@link AutoTrajectory}.
297297
*
298298
* @param <SampleType> The type of samples in the trajectory.
299-
* @param driveSubsystem The drive {@link Subsystem} to require for {@link AutoTrajectory} {@link
300-
* Command}s.
301299
* @param poseSupplier A function that returns the current field-relative {@link Pose2d} of the
302300
* robot.
303301
* @param controller A {@link BiConsumer} to follow the current {@link Trajectory}&lt;{@link
304302
* SampleType}&gt;.
305303
* @param mirrorTrajectory If this returns true, the path will be mirrored to the opposite side,
306304
* while keeping the same coordinate system origin. This will be called every loop during the
307305
* command.
306+
* @param driveSubsystem The drive {@link Subsystem} to require for {@link AutoTrajectory} {@link
307+
* Command}s.
308308
* @param bindings Universal trajectory event bindings.
309309
* @return An {@link AutoFactory} that can be used to create {@link AutoRoutine} and {@link
310310
* AutoTrajectory}.
311311
* @see AutoChooser using this factory with AutoChooser to generate auto routines.
312312
*/
313313
public static <SampleType extends TrajectorySample<SampleType>> AutoFactory createAutoFactory(
314-
Subsystem driveSubsystem,
315314
Supplier<Pose2d> poseSupplier,
316315
BiConsumer<Pose2d, SampleType> controller,
317316
BooleanSupplier mirrorTrajectory,
317+
Subsystem driveSubsystem,
318318
AutoBindings bindings) {
319319
return new AutoFactory(
320320
requireNonNullParam(poseSupplier, "poseSupplier", "Choreo.createAutoFactory"),
@@ -329,15 +329,15 @@ public static <SampleType extends TrajectorySample<SampleType>> AutoFactory crea
329329
* Create a factory that can be used to create {@link AutoRoutine} and {@link AutoTrajectory}.
330330
*
331331
* @param <SampleType> The type of samples in the trajectory.
332-
* @param driveSubsystem The drive {@link Subsystem} to require for {@link AutoTrajectory} {@link
333-
* Command}s.
334332
* @param poseSupplier A function that returns the current field-relative {@link Pose2d} of the
335333
* robot.
336334
* @param controller A {@link BiConsumer} to follow the current {@link Trajectory}&lt;{@link
337335
* SampleType}&gt;.
338336
* @param mirrorTrajectory If this returns true, the path will be mirrored to the opposite side,
339337
* while keeping the same coordinate system origin. This will be called every loop during the
340338
* command.
339+
* @param driveSubsystem The drive {@link Subsystem} to require for {@link AutoTrajectory} {@link
340+
* Command}s.
341341
* @param bindings Universal trajectory event bindings.
342342
* @param trajectoryLogger A {@link TrajectoryLogger} to log {@link Trajectory} as they start and
343343
* finish.
@@ -346,10 +346,10 @@ public static <SampleType extends TrajectorySample<SampleType>> AutoFactory crea
346346
* @see AutoChooser using this factory with AutoChooser to generate auto routines.
347347
*/
348348
public static <SampleType extends TrajectorySample<SampleType>> AutoFactory createAutoFactory(
349-
Subsystem driveSubsystem,
350349
Supplier<Pose2d> poseSupplier,
351350
BiConsumer<Pose2d, SampleType> controller,
352351
BooleanSupplier mirrorTrajectory,
352+
Subsystem driveSubsystem,
353353
AutoBindings bindings,
354354
TrajectoryLogger<SampleType> trajectoryLogger) {
355355
return new AutoFactory(

choreolib/src/main/native/cpp/choreo/trajectory/DifferentialSample.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#include "choreo/Choreo.h"
99
#include "choreo/trajectory/TrajectorySample.h"
1010

11-
using namespace choreo;
11+
namespace choreo {
1212

1313
units::second_t DifferentialSample::GetTimestamp() const {
1414
return timestamp;
@@ -53,8 +53,7 @@ DifferentialSample DifferentialSample::Interpolate(
5353
};
5454
}
5555

56-
void choreo::to_json(wpi::json& json,
57-
const DifferentialSample& trajectorySample) {
56+
void to_json(wpi::json& json, const DifferentialSample& trajectorySample) {
5857
json = wpi::json{{"t", trajectorySample.timestamp.value()},
5958
{"x", trajectorySample.x.value()},
6059
{"y", trajectorySample.y.value()},
@@ -67,8 +66,7 @@ void choreo::to_json(wpi::json& json,
6766
{"fr", trajectorySample.fr.value()}};
6867
}
6968

70-
void choreo::from_json(const wpi::json& json,
71-
DifferentialSample& trajectorySample) {
69+
void from_json(const wpi::json& json, DifferentialSample& trajectorySample) {
7270
trajectorySample.timestamp = units::second_t{json.at("t").get<double>()};
7371
trajectorySample.x = units::meter_t{json.at("x").get<double>()};
7472
trajectorySample.y = units::meter_t{json.at("y").get<double>()};
@@ -82,3 +80,5 @@ void choreo::from_json(const wpi::json& json,
8280
trajectorySample.fl = units::newton_t{json.at("fl").get<double>()};
8381
trajectorySample.fr = units::newton_t{json.at("fr").get<double>()};
8482
}
83+
84+
} // namespace choreo

choreolib/src/main/native/include/choreo/Choreo.h

Lines changed: 214 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,33 @@
22

33
#pragma once
44

5+
#include <functional>
6+
#include <optional>
57
#include <string>
68
#include <string_view>
9+
#include <unordered_map>
710
#include <vector>
811

912
#include <fmt/format.h>
1013
#include <frc/Errors.h>
1114
#include <frc/Filesystem.h>
15+
#include <frc/geometry/Pose2d.h>
16+
#include <frc2/command/Subsystem.h>
1217
#include <wpi/MemoryBuffer.h>
1318
#include <wpi/json.h>
1419

1520
#include "choreo/SpecVersion.h"
21+
#include "choreo/auto/AutoTrajectory.h"
1622
#include "choreo/trajectory/ProjectFile.h"
1723
#include "choreo/trajectory/Trajectory.h"
24+
#include "choreo/trajectory/TrajectorySample.h"
25+
#include "choreo/util/AllianceFlipperUtil.h"
1826

1927
namespace choreo {
2028

29+
template <TrajectorySample SampleType, int Year>
30+
class AutoFactory;
31+
2132
/**
2233
* A class that handles loading choreo and caching choreo trajectories.
2334
*/
@@ -31,7 +42,7 @@ class Choreo {
3142
*
3243
* @return the project file
3344
*/
34-
static choreo::ProjectFile GetProjectFile() {
45+
static ProjectFile GetProjectFile() {
3546
if (LAZY_PROJECT_FILE.has_value()) {
3647
return LAZY_PROJECT_FILE.value();
3748
}
@@ -67,8 +78,8 @@ class Choreo {
6778
throw fmt::format(".chor project file: Wrong version {}. Expected {}",
6879
version, kSpecVersion);
6980
}
70-
choreo::ProjectFile resultProjectFile;
71-
choreo::from_json(json, resultProjectFile);
81+
ProjectFile resultProjectFile;
82+
from_json(json, resultProjectFile);
7283
LAZY_PROJECT_FILE = resultProjectFile;
7384
} catch (const std::filesystem::filesystem_error&) {
7485
FRC_ReportError(frc::warn::Warning, "Error finding choreo directory!");
@@ -88,8 +99,8 @@ class Choreo {
8899
* @return The loaded trajectory, or `empty std::optional` if the trajectory
89100
* could not be loaded.
90101
*/
91-
template <choreo::TrajectorySample SampleType>
92-
static std::optional<choreo::Trajectory<SampleType>> LoadTrajectory(
102+
template <TrajectorySample SampleType>
103+
static std::optional<Trajectory<SampleType>> LoadTrajectory(
93104
std::string_view trajectoryName) {
94105
if (trajectoryName.ends_with(TRAJECTORY_FILE_EXTENSION)) {
95106
trajectoryName = trajectoryName.substr(
@@ -130,28 +141,221 @@ class Choreo {
130141
* @return The loaded trajectory, or `empty std::optional` if the trajectory
131142
* could not be loaded.
132143
*/
133-
template <choreo::TrajectorySample SampleType>
134-
static std::optional<choreo::Trajectory<SampleType>> LoadTrajectoryString(
144+
template <TrajectorySample SampleType>
145+
static std::optional<Trajectory<SampleType>> LoadTrajectoryString(
135146
std::string_view trajectoryJsonString, std::string_view trajectoryName) {
136147
wpi::json json = wpi::json::parse(trajectoryJsonString);
137148
std::string version = json["version"];
138149
if (kSpecVersion != version) {
139150
throw fmt::format("{}.traj: Wrong version {}. Expected {}",
140151
trajectoryName, version, kSpecVersion);
141152
}
142-
choreo::Trajectory<SampleType> trajectory;
143-
choreo::from_json(json, trajectory);
153+
Trajectory<SampleType> trajectory;
154+
from_json(json, trajectory);
144155
return trajectory;
145156
}
146157

158+
/**
159+
* A utility for caching loaded trajectories. This allows for loading
160+
* trajectories only once, and then reusing them.
161+
*/
162+
template <TrajectorySample SampleType>
163+
class TrajectoryCache {
164+
public:
165+
/**
166+
* Load a trajectory from the deploy directory. Choreolib expects .traj
167+
* files to be placed in src/main/deploy/choreo/[trajectoryName].traj.
168+
*
169+
* This method will cache the loaded trajectory and reused it if it is
170+
* requested again.
171+
*
172+
* @param trajectoryName the path name in Choreo, which matches the file
173+
* name in the deploy directory, file extension is optional.
174+
* @return the loaded trajectory, or `empty std::optional` if the trajectory
175+
* could not be loaded.
176+
* @see Choreo#LoadTrajectory(std::string_view)
177+
*/
178+
static std::optional<Trajectory<SampleType>> LoadTrajectory(
179+
std::string_view trajectoryName) {
180+
if (cache.contains(std::string{trajectoryName})) {
181+
return cache[std::string{trajectoryName}];
182+
} else {
183+
cache[std::string{trajectoryName}] =
184+
Choreo::LoadTrajectory<SampleType>(trajectoryName);
185+
return cache[std::string{trajectoryName}];
186+
}
187+
}
188+
189+
/**
190+
* Load a section of a split trajectory from the deploy directory. Choreolib
191+
* expects .traj files to be placed in
192+
* src/main/deploy/choreo/[trajectoryName].traj.
193+
*
194+
* This method will cache the loaded trajectory and reused it if it is
195+
* requested again. The trajectory that is split off of will also be cached.
196+
*
197+
* @param trajectoryName the path name in Choreo, which matches the file
198+
* name in the deploy directory, file extension is optional.
199+
* @param splitIndex the index of the split trajectory to load
200+
* @return the loaded trajectory, or `empty std::optional` if the trajectory
201+
* could not be loaded.
202+
* @see Choreo#LoadTrajectory(std::string_view)
203+
*/
204+
static std::optional<Trajectory<SampleType>> LoadTrajectory(
205+
std::string_view trajectoryName, int splitIndex) {
206+
std::string key = fmt::format("{}.:.{}", trajectoryName, splitIndex);
207+
208+
if (!cache.contains(key)) {
209+
if (cache.contains(std::string{trajectoryName})) {
210+
cache[key] =
211+
cache[std::string{trajectoryName}].value().GetSplit(splitIndex);
212+
} else {
213+
auto possibleTrajectory = LoadTrajectory(trajectoryName);
214+
cache[std::string{trajectoryName}] = possibleTrajectory;
215+
216+
if (possibleTrajectory.has_value()) {
217+
cache[key] = possibleTrajectory.value().GetSplit(splitIndex);
218+
}
219+
}
220+
}
221+
222+
return cache[key];
223+
}
224+
225+
/**
226+
* Clears the trajectory cache.
227+
*/
228+
static void Clear() { cache.clear(); }
229+
230+
private:
231+
static inline std::unordered_map<std::string,
232+
std::optional<Trajectory<SampleType>>>
233+
cache;
234+
};
235+
236+
/**
237+
* Create a factory that can be used to create AutoRoutine and AutoTrajectory.
238+
*
239+
* @tparam SampleType The type of samples in the trajectory.
240+
* @tparam Year The field year. Defaults to the current year.
241+
* @param poseSupplier A function that returns the current field-relative
242+
* Pose2d of the robot.
243+
* @param controller A function for following the current trajectory.
244+
* @param mirrorTrajectory If this returns true, the path will be mirrored to
245+
* the opposite side, while keeping the same coordinate system origin.
246+
* This will be called every loop during the command.
247+
* @param driveSubsystem The drive Subsystem to require for AutoTrajectory
248+
* Commands.
249+
* @return An AutoFactory that can be used to create AutoRoutines and
250+
* AutoTrajectories.
251+
* @see AutoChooser using this factory with AutoChooser to generate auto
252+
* routines.
253+
*/
254+
template <TrajectorySample SampleType, int Year = util::kDefaultYear>
255+
static AutoFactory<SampleType, Year> CreateAutoFactory(
256+
std::function<frc::Pose2d()> poseSupplier,
257+
std::function<void(frc::Pose2d, SampleType)> controller,
258+
std::function<bool()> mirrorTrajectory, frc2::Subsystem driveSubsystem);
259+
260+
/**
261+
* Create a factory that can be used to create AutoRoutines and
262+
* AutoTrajectories.
263+
*
264+
* @tparam SampleType The type of samples in the trajectory.
265+
* @tparam Year The field year. Defaults to the current year.
266+
* @param poseSupplier A function that returns the current field-relative
267+
* Pose2d of the robot.
268+
* @param controller A function for following the current trajectory.
269+
* @param mirrorTrajectory If this returns true, the path will be mirrored to
270+
* the opposite side, while keeping the same coordinate system origin.
271+
* This will be called every loop during the command.
272+
* @param driveSubsystem The drive Subsystem to require for AutoTrajectory
273+
* Commands.
274+
* @param trajectoryLogger A TrajectoryLogger to log trajectories as they
275+
* start and finish.
276+
* @return An AutoFactory that can be used to create AutoRoutines and
277+
* AutoTrajectories.
278+
* @see AutoChooser using this factory with AutoChooser to generate auto
279+
* routines.
280+
*/
281+
template <TrajectorySample SampleType, int Year = util::kDefaultYear>
282+
static AutoFactory<SampleType, Year> CreateAutoFactory(
283+
std::function<frc::Pose2d()> poseSupplier,
284+
std::function<void(frc::Pose2d, SampleType)> controller,
285+
std::function<bool()> mirrorTrajectory, frc2::Subsystem driveSubsystem,
286+
TrajectoryLogger<SampleType> trajectoryLogger);
287+
147288
private:
148289
static constexpr std::string_view TRAJECTORY_FILE_EXTENSION = ".traj";
149290

150-
static inline std::optional<choreo::ProjectFile> LAZY_PROJECT_FILE = {};
291+
static inline std::optional<ProjectFile> LAZY_PROJECT_FILE = {};
151292

152293
static inline const std::string CHOREO_DIR =
153294
frc::filesystem::GetDeployDirectory() + "/choreo";
154295

155296
Choreo();
156297
};
298+
299+
} // namespace choreo
300+
301+
#include "choreo/auto/AutoFactory.h"
302+
303+
namespace choreo {
304+
305+
/**
306+
* Create a factory that can be used to create AutoRoutine and AutoTrajectory.
307+
*
308+
* @tparam SampleType The type of samples in the trajectory.
309+
* @param poseSupplier A function that returns the current field-relative
310+
* Pose2d of the robot.
311+
* @param controller A function for following the current trajectory.
312+
* @param mirrorTrajectory If this returns true, the path will be mirrored to
313+
* the opposite side, while keeping the same coordinate system origin.
314+
* This will be called every loop during the command.
315+
* @param driveSubsystem The drive Subsystem to require for AutoTrajectory
316+
* Commands.
317+
* @return An AutoFactory that can be used to create AutoRoutines and
318+
* AutoTrajectories.
319+
* @see AutoChooser using this factory with AutoChooser to generate auto
320+
* routines.
321+
*/
322+
template <TrajectorySample SampleType>
323+
static AutoFactory<SampleType> CreateAutoFactory(
324+
std::function<frc::Pose2d()> poseSupplier,
325+
std::function<void(frc::Pose2d, SampleType)> controller,
326+
std::function<bool()> mirrorTrajectory, frc2::Subsystem driveSubsystem) {
327+
return AutoFactory{poseSupplier, controller, mirrorTrajectory, driveSubsystem,
328+
std::nullopt};
329+
}
330+
331+
/**
332+
* Create a factory that can be used to create AutoRoutines and
333+
* AutoTrajectories.
334+
*
335+
* @tparam SampleType The type of samples in the trajectory.
336+
* @param poseSupplier A function that returns the current field-relative
337+
* Pose2d of the robot.
338+
* @param controller A function for following the current trajectory.
339+
* @param mirrorTrajectory If this returns true, the path will be mirrored to
340+
* the opposite side, while keeping the same coordinate system origin.
341+
* This will be called every loop during the command.
342+
* @param driveSubsystem The drive Subsystem to require for AutoTrajectory
343+
* Commands.
344+
* @param trajectoryLogger A TrajectoryLogger to log trajectories as they
345+
* start and finish.
346+
* @return An AutoFactory that can be used to create AutoRoutines and
347+
* AutoTrajectories.
348+
* @see AutoChooser using this factory with AutoChooser to generate auto
349+
* routines.
350+
*/
351+
template <TrajectorySample SampleType>
352+
static AutoFactory<SampleType> CreateAutoFactory(
353+
std::function<frc::Pose2d()> poseSupplier,
354+
std::function<void(frc::Pose2d, SampleType)> controller,
355+
std::function<bool()> mirrorTrajectory, frc2::Subsystem driveSubsystem,
356+
TrajectoryLogger<SampleType> trajectoryLogger) {
357+
return AutoFactory{poseSupplier, controller, mirrorTrajectory, driveSubsystem,
358+
trajectoryLogger};
359+
}
360+
157361
} // namespace choreo

choreolib/src/main/native/include/choreo/auto/AutoBindings.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,5 @@ class AutoBindings {
6969

7070
std::unordered_map<std::string, std::function<frc2::CommandPtr()>> bindings;
7171
};
72+
7273
} // namespace choreo

0 commit comments

Comments
 (0)