Skip to content

Commit 5376189

Browse files
committed
runtime: Fix Vehicle error handling
1 parent fc75ea1 commit 5376189

File tree

2 files changed

+123
-41
lines changed

2 files changed

+123
-41
lines changed

runtime/include/cloe/vehicle.hpp

+69-36
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,55 @@
3838

3939
namespace cloe {
4040

41+
/**
42+
* This error is thrown when an unknown component is accessed at a Vehicle.
43+
*/
44+
class UnknownComponent : public Error {
45+
public:
46+
UnknownComponent(const std::string& vehicle,
47+
const std::string& key,
48+
const std::vector<std::string>& available_components);
49+
50+
const std::string& vehicle() const { return vehicle_; }
51+
const std::string& unknown_component() const { return unknown_; }
52+
const std::vector<std::string>& available_components() const;
53+
54+
private:
55+
std::string vehicle_;
56+
std::string unknown_;
57+
std::vector<std::string> available_;
58+
};
59+
60+
/**
61+
* This error is thrown when a component is cast to an incorrect type.
62+
*/
63+
class BadComponentCast : public Error {
64+
public:
65+
BadComponentCast(const std::string& vehicle, const std::string& key);
66+
67+
const std::string& vehicle() const { return vehicle_; }
68+
const std::string& component_name() const { return component_; }
69+
70+
private:
71+
std::string vehicle_;
72+
std::string component_;
73+
};
74+
75+
/**
76+
* This error is thrown when a component cannot be added.
77+
*/
78+
class CannotAddComponent : public Error {
79+
public:
80+
CannotAddComponent(const std::string& msg, const std::string& vehicle, const std::string& key);
81+
82+
const std::string& vehicle() const { return vehicle_; }
83+
const std::string& component_name() const { return component_; }
84+
85+
private:
86+
std::string vehicle_;
87+
std::string component_;
88+
};
89+
4190
/**
4291
* A Vehicle is a collection of sensor and actuator components.
4392
*
@@ -91,36 +140,40 @@ class Vehicle : public Model {
91140
}
92141

93142
/**
94-
* Return the component with the given key if it exists.
143+
* Return the component associated with the key.
95144
*
96-
* This may throw std::out_of_range.
145+
* This may throw one of:
146+
* - UnknownComponent
147+
* - BadComponentCast
97148
*/
149+
template <typename T>
150+
std::shared_ptr<const T> get(const std::string& key) const {
151+
auto ptr = std::dynamic_pointer_cast<const T>(at(key));
152+
if (ptr == nullptr) {
153+
throw BadComponentCast(name(), key);
154+
}
155+
return ptr;
156+
}
157+
98158
template <typename T>
99159
std::shared_ptr<T> get(const std::string& key) {
100-
return std::dynamic_pointer_cast<T>(at(key));
160+
return std::const_pointer_cast<T>(const_cast<const Vehicle&>(*this).get<T>(key));
101161
}
102162

103163
/**
104-
* Return the component associated with the standard Cloe enum value.
164+
* Return the component associated with the enum value.
105165
*
106166
* Under-the-hood, the enum is translated to a string, which is used to fetch
107167
* the correct component.
108-
*
109-
* This may throw std::out_of_range.
110168
*/
111169
template <typename T, typename Enum, std::enable_if_t<std::is_enum<Enum>::value, int> = 0>
112-
std::shared_ptr<T> get(Enum c) {
113-
return std::dynamic_pointer_cast<T>(at(to_string(c)));
114-
}
115-
116-
template <typename T>
117-
std::shared_ptr<const T> get(const std::string& key) const {
118-
return std::dynamic_pointer_cast<const T>(at(key));
170+
std::shared_ptr<const T> get(Enum c) const {
171+
return get<T>(to_string(c));
119172
}
120173

121174
template <typename T, typename Enum, std::enable_if_t<std::is_enum<Enum>::value, int> = 0>
122-
std::shared_ptr<const T> get(Enum c) const {
123-
return std::dynamic_pointer_cast<T>(at(to_string(c)));
175+
std::shared_ptr<T> get(Enum c) {
176+
return get<T>(to_string(c));
124177
}
125178

126179
public: // Component Management
@@ -144,8 +197,7 @@ class Vehicle : public Model {
144197

145198
void add_component(std::shared_ptr<Component> sp, const std::string& alias) {
146199
if (this->has(alias)) {
147-
// TODO(ben): Add better error type here with explanation
148-
throw std::runtime_error("component already exists in the vehicle");
200+
throw CannotAddComponent("component already exists", name(), alias);
149201
}
150202
this->set_component(alias, sp);
151203
}
@@ -230,25 +282,6 @@ class Vehicle : public Model {
230282
std::map<std::string, std::shared_ptr<Component>> components_;
231283
};
232284

233-
/**
234-
* This error is thrown when an unknown component is accessed at a Vehicle.
235-
*/
236-
class UnknownComponentError : public Error {
237-
public:
238-
UnknownComponentError(const std::string& vehicle,
239-
const std::string& key,
240-
const std::vector<std::string>& available_components);
241-
242-
const std::string& vehicle() const { return vehicle_; }
243-
const std::string& unknown_component() const { return unknown_; }
244-
const std::vector<std::string>& available_components() const;
245-
246-
private:
247-
std::string vehicle_;
248-
std::string unknown_;
249-
std::vector<std::string> available_;
250-
};
251-
252285
} // namespace cloe
253286

254287
#endif // CLOE_VEHICLE_HPP_

runtime/src/cloe/vehicle.cpp

+54-5
Original file line numberDiff line numberDiff line change
@@ -99,17 +99,17 @@ std::shared_ptr<const Component> Vehicle::at(const std::string& key) const {
9999
try {
100100
return components_.at(key);
101101
} catch (std::out_of_range& e) {
102-
throw UnknownComponentError(name(), key, utility::map_keys(components_));
102+
throw UnknownComponent(name(), key, utility::map_keys(components_));
103103
}
104104
}
105105

106106
std::shared_ptr<Component> Vehicle::at(const std::string& key) {
107107
return std::const_pointer_cast<Component>(const_cast<const Vehicle&>(*this).at(key));
108108
}
109109

110-
UnknownComponentError::UnknownComponentError(const std::string& vehicle,
111-
const std::string& key,
112-
const std::vector<std::string>& available_components)
110+
UnknownComponent::UnknownComponent(const std::string& vehicle,
111+
const std::string& key,
112+
const std::vector<std::string>& available_components)
113113
: Error("vehicle {}: no component available with name: {}", vehicle, key)
114114
, vehicle_(vehicle)
115115
, unknown_(key)
@@ -132,9 +132,58 @@ UnknownComponentError::UnknownComponentError(const std::string& vehicle,
132132
i) configuring it in the stackfile at /vehicles/<index>/components/<key>, or
133133
ii) adding it to the vehicle in the simulator binding plugin.
134134
135-
Note: You can also use the web API to inspect a vehicle during runtime.
135+
Note: You can also use the web API to inspect a vehicle during run-time.
136136
)", key, utility::join_vector(available_, "\n "));
137137
// clang-format on
138138
}
139139

140+
BadComponentCast::BadComponentCast(const std::string& vehicle, const std::string& key)
141+
: Error("vehicle {}: bad component cast with name: {}", vehicle, key)
142+
, vehicle_(vehicle)
143+
, component_(key) {
144+
// clang-format off
145+
this->set_explanation(R"(
146+
The dynamic pointer cast returned a nullptr.
147+
148+
It looks like you are trying to cast a component to a type that does not match
149+
the component's actual type.
150+
151+
This error can have several causes, in order of likelihood:
152+
153+
a) The component key is incorrect (see the stackfile).
154+
b) The developer wrote the wrong type (see the controller source code).
155+
c) The component has the correct type, but is a nullptr.
156+
157+
Note: You can use the web API to inspect a vehicle during run-time.
158+
)");
159+
// clang-format on
160+
}
161+
162+
CannotAddComponent::CannotAddComponent(const std::string& msg,
163+
const std::string& vehicle,
164+
const std::string& key)
165+
: Error("vehicle {}: {}: {}", vehicle, msg, key), vehicle_(vehicle), component_(key) {
166+
// clang-format off
167+
this->set_explanation(R"(
168+
It looks like you are trying to add a component to a vehicle, but the component
169+
already exists:
170+
171+
{}
172+
173+
This error can have several causes, in order of likelihood:
174+
175+
a) The component name has been copy-pasted and should be modified (see the
176+
stackfile).
177+
178+
b) The component being added should use the set_component() method to allow
179+
overwriting existing components (see the plugin source code).
180+
181+
Note: The component that already exists could have been added by the simulator
182+
binding that originally created the vehicle (see /simulators section), added
183+
by the component configuration (see /vehicles section), or even added by a
184+
controller added to the vehicle (see /controllers section).
185+
)", key);
186+
// clang-format on
187+
}
188+
140189
} // namespace cloe

0 commit comments

Comments
 (0)