Skip to main content

RobotBuilder

Struct RobotBuilder 

Source
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

Source

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);
Source

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);
Source

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.

Source

pub fn kinematics(self, kinematics: Kinematics<'_>) -> Self

Drive this robot with the given geometry.

Source

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.

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);
Source

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.

Source

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);
Source

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.

Source

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);
Source

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, .. })
));

Trait Implementations§

Source§

impl Debug for RobotBuilder

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.