robot_description_builder/
cluster_objects.rs

1use std::{
2	collections::HashMap,
3	sync::{PoisonError, RwLockWriteGuard},
4};
5
6use crate::{
7	cluster_objects::kinematic_data_errors::AddTransmissionError,
8	joint::{Joint, JointBuilder},
9	link::{builder::LinkBuilder, Link},
10	material::{data::MaterialData, Material},
11	transmission::{
12		transmission_builder_state::{WithActuator, WithJoints},
13		Transmission, TransmissionBuilder,
14	},
15	utils::{ArcLock, ArcRW, WeakLock},
16	yank_errors::{YankJointError, YankLinkError},
17	Chained,
18};
19
20pub mod kinematic_data_errors;
21pub(crate) mod kinematic_data_tree;
22mod kinematic_tree;
23mod robot;
24
25pub use kinematic_tree::KinematicTree;
26pub use robot::Robot;
27
28type PoisonWriteIndexError<'a, K, V> = PoisonError<RwLockWriteGuard<'a, HashMap<K, V>>>;
29
30/// A trait which allows for the existance of multiple different Kinematic Structures, such as `Robot` and `KinematicTree`.
31pub trait KinematicInterface: Sized {
32	/// Returns the root link of the Kinematic Tree
33	///
34	/// # Example
35	/// ```
36	/// # use robot_description_builder::{KinematicInterface, Link, JointBuilder, JointType, linkbuilding::LinkBuilder};
37	/// let tree = Link::builder("the root link").build_tree();
38	///
39	/// /// This is equivalent to `get_root_link` in this case, since this is a new tree/Link.
40	/// tree.get_newest_link().try_write().unwrap().try_attach_child(
41	///     JointBuilder::new("just a joint", JointType::Fixed),
42	///     LinkBuilder::new("his one and only child")
43	/// ).unwrap();
44	///
45	/// assert_eq!(tree.get_root_link().try_read().unwrap().name(), "the root link")
46	/// ```
47	fn get_root_link(&self) -> ArcLock<Link>;
48
49	/// Returns the newest link of the Kinematic Tree
50	///
51	/// # Example
52	/// ```
53	/// # use robot_description_builder::{KinematicInterface, Link, JointBuilder, JointType, linkbuilding::LinkBuilder};
54	/// let tree = Link::builder("the root link").build_tree();
55	///
56	/// assert_eq!(tree.get_newest_link().try_read().unwrap().name(), "the root link");
57	///
58	/// tree.get_newest_link().try_write().unwrap().try_attach_child(
59	///     JointBuilder::new("just a joint", JointType::Fixed),
60	///     LinkBuilder::new("his one and only child")
61	/// ).unwrap();
62	///
63	/// assert_eq!(tree.get_newest_link().try_read().unwrap().name(), "his one and only child");
64	///
65	/// let long_sub_tree = LinkBuilder::new("the other child").build_tree();
66	///
67	/// long_sub_tree.get_newest_link().try_write().unwrap().try_attach_child(
68	///     JointBuilder::new("second joint", JointType::Fixed),
69	///     Link::builder("the latest child")
70	/// ).unwrap();
71	///
72	/// tree.get_root_link().try_write().unwrap().try_attach_child(
73	///     JointBuilder::new("third joint", JointType::Fixed),
74	///     long_sub_tree
75	/// ).unwrap();
76	///
77	/// assert_eq!(tree.get_newest_link().try_read().unwrap().name(), "the latest child");
78	/// ```
79	fn get_newest_link(&self) -> ArcLock<Link>;
80
81	/// Retrieves the `Link`-index of the Kinematic structure.
82	// TODO: EXPAND?
83	fn get_links(&self) -> ArcLock<HashMap<String, WeakLock<Link>>>;
84	/// Retrieves the `Joint`-index of the Kinematic structure.
85	// TODO: EXPAND?
86	fn get_joints(&self) -> ArcLock<HashMap<String, WeakLock<Joint>>>;
87	// FIXME: This not Ok end-user should not interact wiht MaterialData
88	fn get_materials(&self) -> ArcLock<HashMap<String, ArcLock<MaterialData>>>;
89	#[doc(hidden)]
90	// FIXME: Hidden until implemented
91	fn get_transmissions(&self) -> ArcLock<HashMap<String, ArcLock<Transmission>>>;
92
93	// TODO: EXAMPLE
94	/// Get the `Link` with the specified `name`.
95	///
96	/// If the [`Link`] does not exist `None` is returned.
97	fn get_link(&self, name: &str) -> Option<ArcLock<Link>>;
98	// TODO: EXAMPLE
99	/// Get the `Joint` with the specified `name`.
100	///
101	/// If the [`Joint`] does not exist `None` is returned.
102	fn get_joint(&self, name: &str) -> Option<ArcLock<Joint>>;
103	// TODO: EXAMPLE
104	/// Get the `Material` with the specified `name`.
105	///
106	/// If the [`Material`] does not exist `None` is returned.
107	fn get_material(&self, name: &str) -> Option<Material>;
108	#[doc(hidden)]
109	// FIXME: Hidden until implemented transmissions
110	fn get_transmission(&self, name: &str) -> Option<ArcLock<Transmission>>;
111
112	#[doc(hidden)]
113	// FIXME: Hidden until implemented
114	// TODO: NOT FINAL
115	fn try_add_transmission(
116		&self,
117		transmission: TransmissionBuilder<WithJoints, WithActuator>,
118	) -> Result<(), AddTransmissionError>;
119
120	// TODO: Maybe depricate
121	/// Cleans up orphaned/broken `Link` entries from the `links` HashMap.
122	///
123	/// This mostly happens automatically, but is exposed for use in other methods.
124	///
125	/// TODO: DOCTEST/EXAMPLE
126	fn purge_links(&self);
127
128	// TODO: Maybe depricate
129	/// Cleans up orphaned/broken `Joint` entries from the `joints` HashMap.
130	///
131	/// This mostly happens automatically, but is exposed for use in other methods.
132	///
133	/// TODO: DOCTEST/EXAMPLE
134	fn purge_joints(&self);
135
136	/// Cleans up orphaned/unused `Material` entries from `material_index` HashMap
137	fn purge_materials(&self) -> Result<(), PoisonWriteIndexError<String, ArcLock<MaterialData>>>;
138
139	#[doc(hidden)]
140	// FIXME: Hidden until implemented
141	/// Cleans up orphaned/broken `Transmission` entries from the `transmissions` HashMap
142	fn purge_transmissions(
143		&self,
144	) -> Result<(), PoisonWriteIndexError<String, ArcLock<Transmission>>>;
145
146	// TODO: Maybe Error is removable
147	// NOTE: after yanking the joints parent link is the `newest_link`
148	fn yank_link(&self, name: &str) -> Option<Chained<LinkBuilder>> {
149		// Result<Option<Chained<LinkBuilder>>, YankLinkError> {
150		// Maybe the option should be on the outside.
151		let builder = self
152			.get_link(name)
153			// FIXME: UNWRAP NOT OK
154			.map(|link| -> Result<_, YankLinkError> { Ok(Chained(link.mread().unwrap().yank()?)) })
155			.transpose(); // TODO: Maybe don't transpose?
156		self.purge_joints();
157		self.purge_links();
158		builder.unwrap() // FIXME: Is unwrap ok here?
159	}
160
161	/// Cosumes the `KinematicInterface` implementor and creates a `Chained<LinkBuilder>` to rebuild it.
162	///
163	/// This has the same result as yanking the `root_link`, with the additional effect that the current tree is consumed.
164	///
165	/// # Example
166	///
167	/// ```
168	/// # use robot_description_builder::{prelude::*, Link};
169	///
170	/// let builder = Link::builder("root-link");
171	///
172	/// assert_eq!(*builder.clone().build_tree().yank_root().unwrap(), builder);
173	///
174	/// /// It is equivalent to yanking the "root_link"
175	/// assert_eq!(builder.clone().build_tree().yank_root().unwrap(), builder.build_tree().yank_link("root-link").unwrap())
176	/// ```
177	fn yank_root(self) -> Result<Chained<LinkBuilder>, YankLinkError> {
178		// FIXME: UNWRAP NOT OK ? Maybe it is removeable
179		let builder = self.get_root_link().mread().unwrap().yank()?;
180		Ok(Chained(builder))
181	}
182
183	fn yank_joint(&self, name: &str) -> Option<Chained<JointBuilder>> {
184		// Option<Result<...>> could make sense since if the joint is not found it cannot be yanked
185		// Or a custom error type
186		let builder = self
187			.get_joint(name)
188			.map(|joint| -> Result<_, YankJointError> { Ok(Chained(joint.mread()?.yank()?)) })
189			.transpose(); // TODO: Maybe don't transpose?
190		self.purge_joints();
191		self.purge_links();
192		builder.unwrap() // FIXME: Is unwrap ok here? NO
193	}
194
195	// TODO: or a rebuild?
196}