pub struct RobotBuilder { /* private fields */ }Expand description
Composes a canonical Robot from stated facts.
No method here fails. A rejected value is held until Self::build, which
reports the first one as a typed ModelError, so a chain reads as one
statement rather than a sequence of fallible steps.
use phoxal_model::builder::RobotBuilder;
let robot = RobotBuilder::new("rover")
.component_type("rgbd", |camera| camera.camera("rgb", "lens"))
.component("front_camera", "rgbd")
.build()?;
assert_eq!(robot.id().as_str(), "rover");Implementations§
Source§impl RobotBuilder
impl RobotBuilder
Sourcepub fn new(id: &str) -> Self
pub fn new(id: &str) -> Self
A robot with the given id, no components and no drive.
It starts with an omnidirectional kinematic config declaring no actuators - the one geometry that describes nothing a robot without a drive would have to invent - and runs no services.
use phoxal_model::builder::RobotBuilder;
let robot = RobotBuilder::new("rover").build()?;
assert_eq!(robot.id().as_str(), "rover");
assert_eq!(robot.services().len(), 0);Sourcepub fn service(self, id: &str, config: Option<Value>) -> Self
pub fn service(self, id: &str, config: Option<Value>) -> Self
Run one service on this robot, with the given configuration.
Declaring the same service twice replaces the earlier configuration.
use phoxal_model::builder::RobotBuilder;
let robot = RobotBuilder::new("rover")
.service("drive", None)
.service("mission", Some(serde_json::json!({ "speed": 1 })))
.build()?;
assert_eq!(robot.service_config("mission"), Some(&serde_json::json!({ "speed": 1 })));
assert_eq!(robot.service_config("drive"), None);Sourcepub const fn motion_limits(self, limits: MotionLimits) -> Self
pub const fn motion_limits(self, limits: MotionLimits) -> Self
Clamp this robot’s motion to the given envelope.
The limits must be finite, positive and representable as f32, which
Self::build checks.
Sourcepub fn kinematics(self, kinematics: Kinematics<'_>) -> Self
pub fn kinematics(self, kinematics: Kinematics<'_>) -> Self
Drive this robot with the given geometry.
Sourcepub fn joint(self, joint: Joint<'_>) -> Self
pub fn joint(self, joint: Joint<'_>) -> Self
Add one joint, and its child link, to the robot’s own structure.
Use this when the robot’s link tree is part of what is being stated; a robot that says nothing still gets the conventional base frames and a mount link per instance.
Sourcepub fn link(self, link: Link<'_>) -> Self
pub fn link(self, link: Link<'_>) -> Self
Give one link of the robot’s own structure a body.
A link no stated joint attaches is added beneath base_link by a fixed
joint named <link>_joint, so this is enough on its own to put a link
on the robot. Stating the same link twice replaces the earlier body.
use phoxal_model::AssetId;
use phoxal_model::builder::{
Collision, Inertia, Inertial, Link, Material, RobotBuilder, Visual,
};
use phoxal_model::structure::Geometry;
let robot = RobotBuilder::new("rover")
.link(Link {
name: "chassis",
inertial: Inertial {
xyz: [0.0, 0.0, 0.05],
mass_kg: 12.0,
inertia: Inertia {
ixx: 0.8,
iyy: 1.2,
izz: 1.6,
..Inertia::default()
},
..Inertial::default()
},
visuals: vec![Visual {
name: Some("shell"),
material: Some(Material {
color: Some([0.2, 0.2, 0.2, 1.0]),
texture: Some(AssetId::new("textures/carbon.png")?),
..Material::new("carbon")
}),
..Visual::new(Geometry::Mesh {
asset: AssetId::new("meshes/chassis.stl")?,
scale: None,
})
}],
collisions: vec![Collision::new(Geometry::Box {
size: [0.6, 0.4, 0.2],
})],
..Link::default()
})
.build()?;
let chassis = robot.structure().link("chassis").expect("the stated link");
assert_eq!(chassis.inertial().mass_kg(), 12.0);
assert_eq!(chassis.collisions().len(), 1);Sourcepub fn material(self, material: Material<'_>) -> Self
pub fn material(self, material: Material<'_>) -> Self
Add one material to the robot structure’s own catalogue.
This is the structure-level material table, which is one of the places a bundle’s declared assets are read from; a visual states the material it is drawn with itself. Restating a name replaces the earlier material.
Sourcepub fn component_type(
self,
component_type: &str,
declare: impl FnOnce(ComponentTypeBuilder) -> ComponentTypeBuilder,
) -> Self
pub fn component_type( self, component_type: &str, declare: impl FnOnce(ComponentTypeBuilder) -> ComponentTypeBuilder, ) -> Self
Declare one component type.
Declaring the same type twice replaces the earlier declaration, so a type is stated once and mounted as many times as needed.
use phoxal_model::builder::RobotBuilder;
let robot = RobotBuilder::new("rover")
.component_type("drive_motor", |motor| {
motor.motor("spin", "axle").encoder("count", "axle")
})
.component("left_drive", "drive_motor")
.component("right_drive", "drive_motor")
.build()?;
assert_eq!(robot.capability_refs(|_| true).len(), 4);Sourcepub fn component(self, instance: &str, component_type: &str) -> Self
pub fn component(self, instance: &str, component_type: &str) -> Self
Mount one instance of component_type on a generated mount link named
<instance>_mount.
The type must be declared by Self::component_type, which
Self::build checks.
Sourcepub fn component_with(
self,
instance: &str,
component_type: &str,
mount: impl FnOnce(ComponentBuilder) -> ComponentBuilder,
) -> Self
pub fn component_with( self, instance: &str, component_type: &str, mount: impl FnOnce(ComponentBuilder) -> ComponentBuilder, ) -> Self
Mount one instance of component_type, stating where it sits and how
its actuators are turned.
Mounting the same instance twice replaces the earlier mount.
use phoxal_model::builder::RobotBuilder;
let robot = RobotBuilder::new("rover")
.component_type("drive_motor", |motor| motor.motor("spin", "axle"))
.component_with("right_drive", "drive_motor", |mounted| {
mounted
.mounted_on("right_wheel_mount")
.direction_sign("spin", -1)
})
.build()?;
let (_motor, sign) = robot.require_motor(&"right_drive.spin".parse()?)?;
assert_eq!(sign, -1);Sourcepub fn build(self) -> Result<Robot, ModelError>
pub fn build(self) -> Result<Robot, ModelError>
Normalize, assemble and validate the robot.
§Errors
Returns the first ModelError the stated robot violates: an
identifier that is not a normalized token, a capability reference that
does not resolve to the kind its kinematic role needs, a structure that
is not a single link tree, or any other invariant the canonical model
enforces on a compiled bundle.
use phoxal_model::{IdentifierKind, ModelError};
use phoxal_model::builder::RobotBuilder;
let rejected = RobotBuilder::new("Rover").build();
assert!(matches!(
rejected,
Err(ModelError::NotNormalized { kind: IdentifierKind::RobotId, .. })
));