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}