phoxal 0.10.0

Phoxal — production-oriented autonomous robot framework (engine, model, typed bus, contracts).
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
use crate::bus::topic::{PubSub, Query, Topic};

use super::*;

fn assert_pubsub<T>(topic: Topic<PubSub<T>>, key: &str, schema: &str) {
    assert_eq!(topic.key(), key);
    assert_eq!(topic.schema(), schema);
}

fn assert_query<Req, Resp>(topic: Topic<Query<Req, Resp>>, key: &str, schema: &str) {
    assert_eq!(topic.key(), key);
    assert_eq!(topic.schema(), schema);
}

#[test]
fn topic_tree_keys_and_schemas_cover_api_domains() {
    assert_pubsub::<crate::api::drive::Target>(
        topic::new().drive().target(),
        "drive/target",
        "drive/target",
    );
    assert_pubsub::<crate::api::drive::State>(
        topic::new().drive().state(),
        "drive/state",
        "drive/state",
    );
    assert_pubsub::<crate::api::drive::ActuatorCommands>(
        topic::new().drive().actuator_commands(),
        "drive/actuator_commands",
        "drive/actuator_commands",
    );
    assert_pubsub::<crate::api::drive::Saturation>(
        topic::new().drive().saturation(),
        "drive/saturation",
        "drive/saturation",
    );
    assert_pubsub::<crate::api::drive::Watchdog>(
        topic::new().drive().watchdog(),
        "drive/watchdog",
        "drive/watchdog",
    );
    assert_pubsub::<crate::api::drive::Kinematics>(
        topic::new().drive().kinematics(),
        "drive/kinematics",
        "drive/kinematics",
    );
    assert_pubsub::<crate::api::odometry::OdometryEstimate>(
        topic::new().odometry().estimate(),
        "odometry/estimate",
        "odometry/estimate",
    );
    assert_pubsub::<crate::api::odometry::Status>(
        topic::new().odometry().status(),
        "odometry/status",
        "odometry/status",
    );
    assert_pubsub::<crate::api::odometry::SourceHealth>(
        topic::new().odometry().source_health(),
        "odometry/source_health",
        "odometry/source_health",
    );
    assert_pubsub::<crate::api::odometry::Residuals>(
        topic::new().odometry().residuals(),
        "odometry/residuals",
        "odometry/residuals",
    );
    assert_pubsub::<crate::api::odometry::Integration>(
        topic::new().odometry().integration(),
        "odometry/integration",
        "odometry/integration",
    );
    assert_pubsub::<crate::api::joint::JointState>(
        topic::new().joint("left_wheel").data(),
        "joint/left_wheel/data",
        "joint/data",
    );
    assert_pubsub::<crate::api::frame::Tree>(
        topic::new().frame().tree(),
        "frame/tree",
        "frame/tree",
    );
    assert_pubsub::<crate::api::frame::Static>(
        topic::new().frame().r#static(),
        "frame/static",
        "frame/static",
    );
    assert_pubsub::<crate::api::frame::FrameTransform>(
        topic::new().frame().data(),
        "frame/data",
        "frame/data",
    );
    assert_pubsub::<crate::api::power::Command>(
        topic::new().power().command(),
        "power/command",
        "power/command",
    );
    assert_pubsub::<crate::api::power::State>(
        topic::new().power().state(),
        "power/state",
        "power/state",
    );
    assert_pubsub::<crate::api::presence::Heartbeat>(
        topic::new().presence().heartbeat(),
        "presence/heartbeat",
        "presence/heartbeat",
    );
    assert_pubsub::<crate::api::presence::Summary>(
        topic::new().presence().summary(),
        "presence/summary",
        "presence/summary",
    );
    assert_pubsub::<crate::api::presence::DebugReadiness>(
        topic::new().presence().readiness(),
        "presence/readiness",
        "presence/readiness",
    );
    assert_pubsub::<crate::api::motion::State>(
        topic::new().motion().state(),
        "motion/state",
        "motion/state",
    );
    assert_pubsub::<crate::api::motion::ManualCommand>(
        topic::new().motion().manual(),
        "motion/manual",
        "motion/manual",
    );
    assert_pubsub::<crate::api::motion::Arbitration>(
        topic::new().motion().arbitration(),
        "motion/arbitration",
        "motion/arbitration",
    );
    assert_pubsub::<crate::api::motion::SourceFreshness>(
        topic::new().motion().source_freshness(),
        "motion/source_freshness",
        "motion/source_freshness",
    );
    assert_pubsub::<crate::api::follow::Target>(
        topic::new().follow().target(),
        "follow/target",
        "follow/target",
    );
    assert_pubsub::<crate::api::follow::State>(
        topic::new().follow().state(),
        "follow/state",
        "follow/state",
    );
    assert_pubsub::<crate::api::follow::TrackingError>(
        topic::new().follow().tracking_error(),
        "follow/tracking_error",
        "follow/tracking_error",
    );
    assert_pubsub::<crate::api::follow::Candidates>(
        topic::new().follow().candidates(),
        "follow/candidates",
        "follow/candidates",
    );
    assert_pubsub::<crate::api::follow::Costs>(
        topic::new().follow().costs(),
        "follow/costs",
        "follow/costs",
    );
    assert_pubsub::<crate::api::follow::RevisionInputs>(
        topic::new().follow().revision_inputs(),
        "follow/revision_inputs",
        "follow/revision_inputs",
    );
    assert_pubsub::<crate::api::explore::Frontiers>(
        topic::new().explore().frontiers(),
        "explore/frontiers",
        "explore/frontiers",
    );
    assert_pubsub::<crate::api::explore::GoalCandidates>(
        topic::new().explore().goal_candidates(),
        "explore/goal_candidates",
        "explore/goal_candidates",
    );
    assert_pubsub::<crate::api::explore::State>(
        topic::new().explore().state(),
        "explore/state",
        "explore/state",
    );
    assert_pubsub::<crate::api::explore::Scoring>(
        topic::new().explore().scoring(),
        "explore/scoring",
        "explore/scoring",
    );
    assert_pubsub::<crate::api::explore::RejectedCandidates>(
        topic::new().explore().rejected_candidates(),
        "explore/rejected_candidates",
        "explore/rejected_candidates",
    );
    assert_pubsub::<crate::api::plan::Path>(topic::new().plan().path(), "plan/path", "plan/path");
    assert_pubsub::<crate::api::plan::State>(
        topic::new().plan().state(),
        "plan/state",
        "plan/state",
    );
    assert_pubsub::<crate::api::plan::SearchGraph>(
        topic::new().plan().search_graph(),
        "plan/search_graph",
        "plan/search_graph",
    );
    assert_pubsub::<crate::api::plan::CostLayers>(
        topic::new().plan().cost_layers(),
        "plan/cost_layers",
        "plan/cost_layers",
    );
    assert_pubsub::<crate::api::plan::RejectedPaths>(
        topic::new().plan().rejected_paths(),
        "plan/rejected_paths",
        "plan/rejected_paths",
    );
    assert_pubsub::<crate::api::plan::RevisionInputs>(
        topic::new().plan().revision_inputs(),
        "plan/revision_inputs",
        "plan/revision_inputs",
    );
    assert_pubsub::<crate::api::perception::Detections>(
        topic::new().perception().detections(),
        "perception/detections",
        "perception/detections",
    );
    assert_pubsub::<crate::api::perception::PerceptionState>(
        topic::new().perception().state(),
        "perception/state",
        "perception/state",
    );
    assert_pubsub::<crate::api::safety::SafetyAuthorization>(
        topic::new().safety().authorization(),
        "safety/authorization",
        "safety/authorization",
    );
    assert_pubsub::<crate::api::safety::State>(
        topic::new().safety().state(),
        "safety/state",
        "safety/state",
    );
    assert_pubsub::<crate::api::safety::EmergencyStopRequest>(
        topic::new().safety().emergency_stop_request(),
        "safety/emergency_stop_request",
        "safety/emergency_stop_request",
    );
    assert_pubsub::<crate::api::safety::Evidence>(
        topic::new().safety().evidence(),
        "safety/evidence",
        "safety/evidence",
    );
    assert_pubsub::<crate::api::safety::StopSet>(
        topic::new().safety().stop_set(),
        "safety/stop_set",
        "safety/stop_set",
    );
    assert_pubsub::<crate::api::safety::LatencyBudget>(
        topic::new().safety().latency_budget(),
        "safety/latency_budget",
        "safety/latency_budget",
    );
    assert_pubsub::<crate::api::safety::SourceHealth>(
        topic::new().safety().source_health(),
        "safety/source_health",
        "safety/source_health",
    );
    assert_pubsub::<crate::api::mission::MissionCommand>(
        topic::new().mission().command(),
        "mission/command",
        "mission/command",
    );
    assert_pubsub::<crate::api::mission::State>(
        topic::new().mission().state(),
        "mission/state",
        "mission/state",
    );
    assert_pubsub::<crate::api::mission::Goal>(
        topic::new().mission().goal(),
        "mission/goal",
        "mission/goal",
    );
    assert_pubsub::<crate::api::mission::DecisionTrace>(
        topic::new().mission().decision_trace(),
        "mission/decision_trace",
        "mission/decision_trace",
    );
    assert_pubsub::<crate::api::mission::Goal>(
        topic::new().mission().debug().goal_record(),
        "mission/debug/goal_record",
        "mission/debug/goal_record",
    );
    assert_pubsub::<crate::api::video::StreamEvent>(
        topic::new().video().stream("front").event(),
        "video/stream/front/event",
        "video/stream/event",
    );
    assert_query::<crate::api::asset::GetRequest, crate::api::asset::GetResponse>(
        topic::new().asset().get(),
        "asset/get",
        "asset/get",
    );
    assert_pubsub::<crate::api::simulation::clock::Clock>(
        topic::new().simulation().clock(),
        "simulation/clock",
        "simulation/clock",
    );
    assert_pubsub::<crate::api::simulation::status::Status>(
        topic::new().simulation().status(),
        "simulation/status",
        "simulation/status",
    );
    assert_pubsub::<crate::api::localize::LocalizationState>(
        topic::new().localize().state(),
        "localize/state",
        "localize/state",
    );
    assert_pubsub::<crate::api::localize::PoseEstimate>(
        topic::new().localize().pose(),
        "localize/pose",
        "localize/pose",
    );
    assert_pubsub::<crate::api::localize::LocalizationRevision>(
        topic::new().localize().revision(),
        "localize/revision",
        "localize/revision",
    );
    assert_pubsub::<crate::api::localize::Keyframe>(
        topic::new().localize().keyframe(),
        "localize/keyframe",
        "localize/keyframe",
    );
    assert_pubsub::<crate::api::localize::PoseGraphCorrection>(
        topic::new().localize().correction(),
        "localize/correction",
        "localize/correction",
    );
    assert_pubsub::<crate::api::map::MapRevision>(
        topic::new().map().revision(),
        "map/revision",
        "map/revision",
    );
    assert_pubsub::<crate::api::map::Summary>(
        topic::new().map().summary(),
        "map/summary",
        "map/summary",
    );
    assert_pubsub::<crate::api::map::LocalCost>(
        topic::new().map().local_cost(),
        "map/local_cost",
        "map/local_cost",
    );
    assert_pubsub::<crate::api::map::Traversability>(
        topic::new().map().traversability(),
        "map/traversability",
        "map/traversability",
    );
    assert_pubsub::<crate::api::map::TraversabilitySummary>(
        topic::new().map().traversability_summary(),
        "map/traversability_summary",
        "map/traversability_summary",
    );
}

#[test]
fn topic_tree_query_leaves_omit_query_path_segment() {
    assert_query::<crate::api::frame::FrameLookupRequest, crate::api::frame::FrameLookupResponse>(
        topic::new().frame().lookup(),
        "frame/lookup",
        "frame/lookup",
    );
    assert_query::<crate::api::video::OpenRequest, crate::api::video::OpenResponse>(
        topic::new().video().open(),
        "video/open",
        "video/open",
    );
    assert_query::<crate::api::asset::GetRequest, crate::api::asset::GetResponse>(
        topic::new().asset().get(),
        "asset/get",
        "asset/get",
    );
    assert_query::<crate::api::localize::PoseGraphRequest, crate::api::localize::PoseGraphResponse>(
        topic::new().localize().pose_graph(),
        "localize/pose_graph",
        "localize/pose_graph",
    );
    assert_query::<crate::api::localize::KeyframeRequest, crate::api::localize::KeyframeResponse>(
        topic::new().localize().keyframe_query(),
        "localize/keyframe_query",
        "localize/keyframe_query",
    );
    assert_query::<
        crate::api::localize::CorrectionsRequest,
        crate::api::localize::CorrectionsResponse,
    >(
        topic::new().localize().corrections(),
        "localize/corrections",
        "localize/corrections",
    );
    assert_query::<
        crate::api::map::TraversabilityTileRequest,
        crate::api::map::TraversabilityTileResponse,
    >(
        topic::new().map().traversability_tile(),
        "map/traversability_tile",
        "map/traversability_tile",
    );
    assert_query::<crate::api::map::SubmapRequest, crate::api::map::SubmapResponse>(
        topic::new().map().submap(),
        "map/submap",
        "map/submap",
    );
    assert_query::<crate::api::map::EsdfTileRequest, crate::api::map::EsdfTileResponse>(
        topic::new().map().esdf_tile(),
        "map/esdf_tile",
        "map/esdf_tile",
    );
    assert_query::<crate::api::map::LocalGridRequest, crate::api::map::LocalGridResponse>(
        topic::new().map().local_grid(),
        "map/local_grid",
        "map/local_grid",
    );
    assert_query::<crate::api::map::GlobalGridRequest, crate::api::map::GlobalGridResponse>(
        topic::new().map().global_grid(),
        "map/global_grid",
        "map/global_grid",
    );
    assert_query::<crate::api::map::SnapshotRequest, crate::api::map::SnapshotResponse>(
        topic::new().map().snapshot(),
        "map/snapshot",
        "map/snapshot",
    );
    assert_query::<crate::api::simulation::reset::Request, crate::api::simulation::reset::Response>(
        topic::new().simulation().reset(),
        "simulation/reset",
        "simulation/reset",
    );
}

#[test]
fn topic_tree_component_and_simulation_slots_elide_holes_in_schemas() {
    assert_pubsub::<crate::api::component::capability::motor::Command>(
        topic::new().component("base").motor("left_wheel").command(),
        "component/base/motor/left_wheel/command",
        "component/motor/command",
    );
    assert_pubsub::<crate::api::component::capability::encoder::Sample>(
        topic::new().component("base").encoder("left_wheel").data(),
        "component/base/encoder/left_wheel/data",
        "component/encoder/data",
    );
    assert_pubsub::<crate::api::component::capability::accelerometer::Sample>(
        topic::new()
            .component("imu_board")
            .accelerometer("accel")
            .data(),
        "component/imu_board/accelerometer/accel/data",
        "component/accelerometer/data",
    );
    assert_pubsub::<crate::api::component::capability::gyroscope::Sample>(
        topic::new().component("imu_board").gyroscope("gyro").data(),
        "component/imu_board/gyroscope/gyro/data",
        "component/gyroscope/data",
    );
    assert_pubsub::<crate::api::component::capability::magnetometer::Sample>(
        topic::new()
            .component("imu_board")
            .magnetometer("mag")
            .data(),
        "component/imu_board/magnetometer/mag/data",
        "component/magnetometer/data",
    );
    assert_pubsub::<crate::api::component::capability::imu::Sample>(
        topic::new().component("imu_board").imu("imu").data(),
        "component/imu_board/imu/imu/data",
        "component/imu/data",
    );
    assert_pubsub::<crate::api::component::capability::gnss::Sample>(
        topic::new().component("gps").gnss("gnss").data(),
        "component/gps/gnss/gnss/data",
        "component/gnss/data",
    );
    assert_pubsub::<crate::api::component::capability::camera::Frame>(
        topic::new().component("head").camera("front").data(),
        "component/head/camera/front/data",
        "component/camera/data",
    );
    assert_pubsub::<crate::api::component::capability::camera::Frame>(
        topic::new()
            .component("head")
            .camera("front")
            .profile("r640x480_h10_rgb8")
            .data(),
        "component/head/camera/front/profile/r640x480_h10_rgb8/data",
        "component/camera/profile/data",
    );
    assert_pubsub::<crate::api::component::capability::depth::Depth>(
        topic::new().component("head").depth("front_depth").data(),
        "component/head/depth/front_depth/data",
        "component/depth/data",
    );
    assert_pubsub::<crate::api::component::capability::depth::Depth>(
        topic::new()
            .component("head")
            .depth("front_depth")
            .profile("r320x240_h5_depth_mm")
            .data(),
        "component/head/depth/front_depth/profile/r320x240_h5_depth_mm/data",
        "component/depth/profile/data",
    );
    assert_pubsub::<crate::api::component::capability::range::Sample>(
        topic::new().component("base").range("front_tof").data(),
        "component/base/range/front_tof/data",
        "component/range/data",
    );
    assert_pubsub::<crate::api::component::capability::lidar::Scan>(
        topic::new().component("front_lidar").lidar("scan").data(),
        "component/front_lidar/lidar/scan/data",
        "component/lidar/data",
    );
    assert_pubsub::<crate::api::component::capability::mmwave::Scan>(
        topic::new().component("radar").mmwave("mmwave").data(),
        "component/radar/mmwave/mmwave/data",
        "component/mmwave/data",
    );
    assert_pubsub::<crate::api::component::capability::emergency_stop::State>(
        topic::new()
            .component("safety_panel")
            .emergency_stop("estop")
            .data(),
        "component/safety_panel/emergency_stop/estop/data",
        "component/emergency_stop/data",
    );
    assert_pubsub::<crate::api::component::capability::battery::State>(
        topic::new()
            .component("power_board")
            .battery("main_battery")
            .data(),
        "component/power_board/battery/main_battery/data",
        "component/battery/data",
    );
    assert_pubsub::<crate::api::component::capability::led::Command>(
        topic::new()
            .component("status_panel")
            .led("status")
            .command(),
        "component/status_panel/led/status/command",
        "component/led/command",
    );
    assert_pubsub::<crate::api::component::capability::microphone::Frame>(
        topic::new().component("head").microphone("mic").data(),
        "component/head/microphone/mic/data",
        "component/microphone/data",
    );
    assert_pubsub::<crate::api::component::capability::speaker::audio::Audio>(
        topic::new().component("head").speaker("speaker").audio(),
        "component/head/speaker/speaker/audio",
        "component/speaker/audio",
    );
    assert_pubsub::<crate::api::component::capability::speaker::command::Command>(
        topic::new().component("head").speaker("speaker").command(),
        "component/head/speaker/speaker/command",
        "component/speaker/command",
    );
    assert_pubsub::<crate::api::simulation::pose::Pose>(
        topic::new().simulation().robot("r1").pose(),
        "simulation/robot/r1/pose",
        "simulation/robot/pose",
    );
    assert_pubsub::<crate::api::simulation::contact::Contact>(
        topic::new().simulation().robot("r1").contact(),
        "simulation/robot/r1/contact",
        "simulation/robot/contact",
    );
    assert_pubsub::<crate::api::simulation::collision::Collision>(
        topic::new().simulation().robot("r1").collision(),
        "simulation/robot/r1/collision",
        "simulation/robot/collision",
    );
}