1use crate::chain::CuJointAxis;
4use crate::chain::CuJointSpec;
5use crate::chain::CuKinematicChain;
6use crate::joint::CuJointLimits;
7use cu29::prelude::*;
8use cu29::units::si::angle::radian;
9use cu29::units::si::angular_velocity::radian_per_second;
10use cu29::units::si::f64::Angle;
11use cu29::units::si::f64::AngularVelocity;
12use cu29::units::si::f64::Length;
13use cu29::units::si::length::meter;
14use serde::Deserialize;
15use serde::Deserializer;
16use serde::Serialize;
17use serde::Serializer;
18
19fn m(v: f64) -> Length {
20 Length::new::<meter>(v)
21}
22
23fn rad(v: f64) -> Angle {
24 Angle::new::<radian>(v)
25}
26
27fn rebel_joint(xyz: [f64; 3], rpy_z: f64, axis: CuJointAxis, lo: f64, hi: f64) -> CuJointSpec {
28 CuJointSpec::revolute(
29 [m(xyz[0]), m(xyz[1]), m(xyz[2])],
30 [rad(0.0), rad(0.0), rad(rpy_z)],
31 axis,
32 CuJointLimits::revolute(
33 rad(lo),
34 rad(hi),
35 AngularVelocity::new::<radian_per_second>(2.16),
36 ),
37 )
38}
39
40pub fn igus_rebel_chain() -> CuKinematicChain<6> {
46 CuKinematicChain::new([
47 rebel_joint(
49 [0.0, 0.0, 0.1462],
50 core::f64::consts::PI,
51 CuJointAxis::PosZ,
52 -3.1241,
53 3.1241,
54 ),
55 rebel_joint(
57 [0.0, 0.0265, 0.106],
58 0.0,
59 CuJointAxis::NegY,
60 -1.3500,
61 2.4435,
62 ),
63 rebel_joint(
65 [0.0, -0.0265, 0.24152],
66 0.0,
67 CuJointAxis::NegY,
68 -1.39626,
69 2.4000,
70 ),
71 rebel_joint(
73 [0.0013449814287804, 0.0, 0.157479407401356],
74 0.0,
75 CuJointAxis::PosZ,
76 -3.12414,
77 3.12414,
78 ),
79 rebel_joint([0.0, 0.0, 0.142], 0.0, CuJointAxis::NegY, -1.65806, 1.65806),
81 rebel_joint(
83 [0.0, 0.0, 0.0768],
84 0.0,
85 CuJointAxis::PosZ,
86 -3.12414,
87 3.12414,
88 ),
89 ])
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Reflect)]
94#[serde(rename_all = "snake_case")]
95pub enum CuRobotPreset {
96 IgusRebel,
98}
99
100impl CuRobotPreset {
101 pub const fn name(self) -> &'static str {
103 match self {
104 Self::IgusRebel => "igus_rebel",
105 }
106 }
107
108 pub const fn dof(self) -> usize {
110 match self {
111 Self::IgusRebel => 6,
112 }
113 }
114
115 pub fn chain<const N: usize>(self) -> CuResult<CuKinematicChain<N>> {
117 let joints = match self {
118 Self::IgusRebel => *igus_rebel_chain().joints(),
119 };
120 let joints: [CuJointSpec; N] = joints.as_slice().try_into().map_err(|_| {
121 CuError::from(alloc::format!(
122 "Robot preset `{}` has {} joints but the task expects N = {N}",
123 self.name(),
124 self.dof()
125 ))
126 })?;
127 Ok(CuKinematicChain::new(joints))
128 }
129}
130
131#[derive(Debug, Clone, Copy, PartialEq)]
138pub enum CuChainSpec<const N: usize> {
139 Preset(CuRobotPreset),
141 Inline(CuKinematicChain<N>),
143}
144
145impl<const N: usize> From<CuKinematicChain<N>> for CuChainSpec<N> {
146 fn from(chain: CuKinematicChain<N>) -> Self {
147 Self::Inline(chain)
148 }
149}
150
151impl<const N: usize> From<CuRobotPreset> for CuChainSpec<N> {
152 fn from(preset: CuRobotPreset) -> Self {
153 Self::Preset(preset)
154 }
155}
156
157impl<const N: usize> CuChainSpec<N> {
158 pub fn resolve(&self) -> CuResult<CuKinematicChain<N>> {
160 let chain = match *self {
161 Self::Preset(preset) => preset.chain()?,
162 Self::Inline(chain) => chain,
163 };
164 chain.validate()?;
165 Ok(chain)
166 }
167}
168
169impl<const N: usize> Serialize for CuChainSpec<N> {
170 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
171 match self {
172 Self::Preset(preset) => preset.serialize(serializer),
173 Self::Inline(chain) => chain.serialize(serializer),
174 }
175 }
176}
177
178impl<'de, const N: usize> Deserialize<'de> for CuChainSpec<N> {
179 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
180 use serde::de::IntoDeserializer;
181 use serde::de::MapAccess;
182 use serde::de::Visitor;
183 use serde::de::value::MapAccessDeserializer;
184
185 struct SpecVisitor<const M: usize>;
186 impl<'de, const M: usize> Visitor<'de> for SpecVisitor<M> {
187 type Value = CuChainSpec<M>;
188 fn expecting(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
189 write!(
190 f,
191 "a robot preset name (\"igus_rebel\") or an inline chain {{\"joints\": [...]}}"
192 )
193 }
194 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
195 CuRobotPreset::deserialize(v.into_deserializer()).map(CuChainSpec::Preset)
196 }
197 fn visit_map<A: MapAccess<'de>>(self, map: A) -> Result<Self::Value, A::Error> {
198 CuKinematicChain::deserialize(MapAccessDeserializer::new(map))
199 .map(CuChainSpec::Inline)
200 }
201 }
202 deserializer.deserialize_any(SpecVisitor::<N>)
203 }
204}