-
-
Notifications
You must be signed in to change notification settings - Fork 509
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Multi component handling #310
Conversation
I am wondering one thing: why do we need dronecore to handle multiple devices? Isn't it enough to create one dronecore instance for each device and keep that complexity out of the core libraries? More specifically, is it limiting for the user if, instead of doing something like:
the user has to do something more like:
|
When you create multiple DroneCore instance, you will get to a situation in binding same port number twice. If vehicle (autopilot & other components) are sending heartbeat to 14540, then first DroneCore instance will bind onto it, not allowing other one to handle other component. Also by design, DroneCore Device abstracts any MAVLink component, autopilot is a special case. We can very well handle just a standalone camera with a server support using current infrastructure. |
Its not multiple devices, but components. You may check #304 for issue details. |
Yeah sorry, bad wording on my side :-). I'll have a look at the issue ASAP. |
@shakthi-prashanth-m I think this API is too complex. I think all we want to connect to is a mavlink device and that device then happens to be e.g.:
If you now want to create an interface for all these combinations it will get messy. That's why I vote to just have a This is for the API, now for the internals, I think you're right in that we need to properly take care of the different components. |
Perhaps rambling but ... This is a case where our old plugin model made more sense from the user perspective. In that case you would have a Device (lets call it a MAVLink "system") made up of components. DroneCore could then build up a picture of the system based on the SysIDs and component IDs and you would query the system for list of available cameras, autopilot, servos etc. In our current design though the plugins are created "manually" as needed. That means that the coder will have to know about all the camera configurations on all the vehicles in order to build up the picture of the vehicle(s). I "hate" it, but I think the simplest API for both implementation and users is probably to treat every component as a separate Not sure if this works at all when you get to missions - where the system needs to be able to send camera messages. @julianoes is this what you meant in #310 (comment) by "I vote to just have a device and behind that device can be whatever is supported by plugins, etc." |
How about this ?
|
At some point you might throw everything about DroneCore out and go back to mavlink 😄 . @shakthi-prashanth-m I think a device should contain both autopilot and camera as laid out before. Therefore, you could have:
|
Yeah, lets go back to MAVLink :-) OK, so in
Is your idea than the device itself creates camera objects using a camera plugin? Or does it just know that something is a camera, hand over a component id, and you create the plugin with that? |
A camera plugin goes to the core and says hey let me talk to component ID of the camera. |
@julianoes we don't maintain components in device! We should ask to |
We should though and that's where I agree with you. But that doesn't mean we should make the user API more complex. |
Can we safely assume System ID what we get in heartbeat is unique to identify a system ? If we make System ID unique, the combination of System ID and component ID uniquely identifies a MAVLink device. |
Any system can use whatever id it likes, provided there are no conflicts (other devices on the system that use the same ID). GCS tend to use 255 (true I believe for QGC, Mission Planner, Droid Planner). The default for PX4 is "1" (set in MAV_SYS_ID). DroneCore must have some standard id set too. So in normal case if you were setting up a system of GCS and vehicle there would be no conflict. If you had multiple vehicles you'd set them up safely by giving consecutive numbers up from 1.
In a properly set up MAVLink network that would be correct. However it is quite possible for people to change the system ID, or for there to be accidental clashes. So far we haven't assumed this - because we have a UUID in most cases that we know we can trust - we only use SYSID if we don't have UUID ... and then we trust it.
For CSD this is configurable in the mavlink section of the config file. The default is actually 42, but must be over-ridden in each instance to match the associated system.
Yes. Though the UUID is more unique :-), so combo of UUID and component ID perhaps :-) |
Thanks @hamishwillee.
So, how about having
to get ALL MAVLink devices. We could maintain |
We can still keep uuid discovery as well. And we can also query autopilot using uuid.
To get access to autopilot of given UUID. |
@lbegani
EDIT: OK, so FYI all, the docs are wrong.
Upshot, please ignore this :-) |
component-id field is not used. Component ID is assigned dynamically to the cameras as they get detected. Perhaps need to clean that field or add a note
CSD will print error message and ignore additional cameras once hit the limit. It will continue running with the components already instantiated |
It's a mavlink limitation and therefore we need to assume it. It is up to the integrator of the whole system to make sure the system IDs don't clash and match up within a "system". |
@julianoes then it's good news. |
e3a96bd
to
6b0707c
Compare
We've deprecated this and using `ActionResult` type.
cmake document recommends (below note) to avoid GLOB_RECURSE: "Note: We do not recommend using GLOB to collect a list of source files from your source tree. If no CMakeLists.txt file changes when a source is added or removed then the generated build system cannot know when to ask CMake to regenerate." Ref: https://cmake.org/cmake/help/v3.9/command/file.html
0bf2acf
to
51fbcf3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good but I had a couple of small things.
core/mavlink_system.cpp
Outdated
return !is_autopilot(); | ||
} | ||
|
||
bool MAVLinkSystem::is_autopilot() const |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would change this to has_autopilot()
to make it consistent and because it's not clear if something is an autopilot if it also includes a camera.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(FYI: I am unable to use Slack temporarily, but I see your email notifications.)
OK. I added is_autopilot
thinking that we could say,
...
if (system.is_autopilot() && system.has_camera())
print "We've a drone that has a camera"
else if (system.is_autopilot())
print "We've a drone"
...
I understand your point as well, so I am ok to change as you suggested.
I am also thinking to add is_camera()
for standalone cameras.
If something is a camera, it can't be anything else; because, it is drones that contain camera and other components. What do you think ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need is_camera()
because it will be confusing to have both. And by just having has_...()
it is clear that a system can always include various components.
core/mavlink_system.cpp
Outdated
uint8_t camera_comp_id = MAV_COMP_ID_CAMERA + (camera_id - 1); | ||
|
||
for (auto compid : _components) { | ||
return compid == camera_comp_id; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is wrong and should be:
for (auto compid : _components) {
if (compid == camera_comp_id) {
return true;
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No its not. The result of the expression compid == camera_comp_id
evaluates to true
if they're equal and false
otherwise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh! ok sorry I missed the point. I'll correct.
core/mavlink_system.cpp
Outdated
std::vector<uint8_t> MAVLinkSystem::get_camera_ids() const | ||
{ | ||
std::vector<uint8_t> camera_ids; | ||
camera_ids.clear(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can just initialize it wit:
std::vector<uint8_t> camera_ids {};
{ | ||
for (auto compid : _components) | ||
if (compid == MAV_COMP_ID_GIMBAL) { | ||
return compid; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same error here, I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here, I return gimbal compid
if I find it.
core/mavlink_system.h
Outdated
class Device | ||
|
||
// This represents DroneCore client application which is a GCS. | ||
struct GCSClient { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add in the comment or name what a GCS
is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
core/system.h
Outdated
bool has_camera(uint8_t camera_id = 1) const; | ||
|
||
/** | ||
* @brief Checks whether the system has gimbal. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
has a gimbal
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
core/system.h
Outdated
bool is_standalone() const; | ||
|
||
/** | ||
* @brief Checks whether the system has camera with the given camera ID. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
has a camera
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
core/system.h
Outdated
~System(); | ||
|
||
/** | ||
* @brief Checks whether the system is an autopilot. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
has an autopilot
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
core/system.h
Outdated
*/ | ||
bool has_gimbal() const; | ||
|
||
friend class DroneCoreImpl; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are the friends needed? Please add a comment explaining why.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I had not seen that. I'm interesting in why we need friends at all. I thought they were made for operators, but that they were not good practice in other uses...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah good point.
friend class PluginImplBase
:
While we create plugins, we pass a reference toSystem
to plugin constructors. But eventually, plugins need access toMAVLinkSystem
instance. So, we keepMAVLinkSystem
as a private member of classSystem
.
By makingPluginImplBase
class a friend of classSystem
, we allow toPluginImplBase
class to request access to instance ofMAVLinkSystem
class.friend class DroneCoreImpl
:
classDroneCoreImpl
wants to perform some operations on classSystem
, but we don't want those to expose to public. Please suggest for alternative if any.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Usually we use used the pimpl scheme to solve exactly this. I guess we can leave it for now but might want to optimize this later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
@@ -56,7 +59,7 @@ class PluginImplBase | |||
const PluginImplBase &operator=(const PluginImplBase &) = delete; | |||
|
|||
protected: | |||
Device &_parent; | |||
std::shared_ptr<MAVLinkSystem> _parent; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why didn't you leave this a reference?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
class System
creates a shared_ptr
to MAVLinkSystem
during construction to finally share them to various plugins. I made it so, because each plugin holds a shared instance of the system. It made sense to me. Do you see any issue ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a reference was fine, and generally you wanted to favor references against pointers, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I favor reference over a normal (raw) pointers. I can change to references as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess the question of smart pointer vs reference is about ownership: say I have an object A
that has an object o
. If o
only lives when A
lives, then you can pass a reference to other objects using o
. You want to have a pointer when ownership can change, i.e. when o
may live longer than A
. And there you have the question between unique_ptr
and shared_ptr
.
I am not completely sure of the design here, but it seems like a reference is what you want, indeed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I create MAVLinkSystem
instance in System constructor. I've to use pointer here. So, as it is going to be shared by all the plugins, I made it as shared_ptr
. Now, while giving access to plugins, it is better to give them as shared_ptr
itself, rather than dereference and give reference. What do you think ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@shakthi-prashanth-m why do you have to create MAVLinkSystem
on the heap in the system constructor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So that System
can hide MAVLinkSystem
class. Now we have pointer to it.
|
||
auto is_standalone = system.is_standalone(); | ||
|
||
auto has_camera = system.has_camera(); // by default checks for camera 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to ask few suggestions. I'd done testing of this by attaching a V4L2 camera on Ubuntu.
But, when I tested it today with a new camera, its component ID turned out to be 101
(usually it was 100
).
The test queries whether we discovered camera 1 ? This time we discovered camera 2. As the test didn't discover camera 1, it prints as Its an Autopilot alone.
It should rather query did we discover any camera ? If yes, continue the rest of the test.
We need a method that returns true
if the system has at least one camera. We have two options:
- Add a new method
has_any_camera()
. - Overload
has_camera()
, when 0 is passed to know whether system has at least a camera.
What shall we do ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting question, but I would not change it in this PR. What about opening an issue to discuss this point and make a new PR later with a fix for it? :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Later is fine. I think has_camera
without argument should be true
if it has any camera.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JonasVautherin I agree.
@julianoes That would break our original idea. We thought that has_camera()
, by default should check for camera 1 (to avoid passing 1 every time). I think, has_camera(0)
should be OK because, by concept: Comp ID 0 indicates all IDs. So it means check for any camera on the system.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@julianoes I think what you suggested makes sense.
1. Corrected and improved `MAVLinkSystem::has_camera()` logic. 2. Use default initializer for `camera_ids` vector. 3. Add description for GCS. 4. Replace `is_autopilot()` to `has_autopilot()`. 5. Correct API descriptions of `has_camera()`, `has_gimbal()`, `has_autopilot()`. 6. Move friend decl to private of class `System` and describe them. 7. Necessary modifications in `SitlTest.MultiComponentDiscovery` test.
@julianoes @JonasVautherin |
PluginBase(), | ||
_impl(new TelemetryImpl(device)) | ||
_impl { new TelemetryImpl(system) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure of this syntax. Why {}
and not ()
? I tried to change it in a rebase, but it just came back :D.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its C++11 style to initialize class members: http://en.cppreference.com/w/cpp/language/initializer_list. See (2) in the item list.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it necessary, or is it just a different style? If it's only the style, I'd rather keep it consistent (we usually use ()
and not {}
in the code). Having it different here suggests that it requires a special treatment, making the code more difficult to read (IMO).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, {}
is the consistent style so far used in all the plugins and core.
plugins/gimbal/gimbal.cpp: _impl { new GimbalImpl(system) }
plugins/offboard/offboard.cpp: _impl { new OffboardImpl(system) }
plugins/info/info.cpp: _impl { new InfoImpl(system) }
plugins/follow_me/follow_me.cpp: _impl { new FollowMeImpl(system) }
plugins/mission/mission_item.cpp: _impl { new MissionItemImpl() }
plugins/mission/mission.cpp: _impl { new MissionImpl(system) }
plugins/logging/logging.cpp: _impl { new LoggingImpl(system) }
plugins/action/action.cpp: _impl { new ActionImpl(system) }
plugins/telemetry/telemetry.cpp: _impl { new TelemetryImpl(system) }
core/dronecore.cpp: _impl { new DroneCoreImpl() }
And it makes sense to follow C++11 style over the old as we've mentioned in README.md
as well :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, my bad :)
backend/src/backend.cpp
Outdated
Telemetry telemetry(_dc.device()); | ||
TelemetryServiceImpl<> telemetryService(telemetry); | ||
Telemetry telemetry(_dc.system()); | ||
TelemetryServiceImpl<dronecore::Telemetry> telemetryService(telemetry); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TelemetryServiceImpl<>
will take the default template. Again, it came back this way after your last rebase, I guess :-).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, sorry I'll correct that.
…re/DroneCore into multi-component-handling
c147002
to
c2ea6fa
Compare
core/mavlink_system.cpp
Outdated
bool MAVLinkSystem::has_camera(uint8_t camera_id) const | ||
{ | ||
uint8_t camera_comp_id = MAV_COMP_ID_CAMERA + (camera_id - 1); | ||
uint8_t camera_comp_id = (camera_id == 0) ? | ||
camera_id : (MAV_COMP_ID_CAMERA + (camera_id - 1)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does that mean that the first camera has index 1? If yes, that works but should be documented that it is 1 based.
Otherwise, I'd make the default int camera_id = -1
and check for that, and then use 0
input as the first component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, although it is documented, I think what you're saying is right. Users may feel index 0
as more intuitive.
1. Make `camera_id` to start from 0. 2. Minor changes to multi-comp integration test.
Awesome, thanks @shakthi-prashanth-m! |
1. Corrected and improved `MAVLinkSystem::has_camera()` logic. 2. Use default initializer for `camera_ids` vector. 3. Add description for GCS. 4. Replace `is_autopilot()` to `has_autopilot()`. 5. Correct API descriptions of `has_camera()`, `has_gimbal()`, `has_autopilot()`. 6. Move friend decl to private of class `System` and describe them. 7. Necessary modifications in `SitlTest.MultiComponentDiscovery` test.
1. Make `camera_id` to start from 0. 2. Minor changes to multi-comp integration test.
Multi component handling
Device
toSystem
System
interface pushing back all MAVLink details in a dedicatedMAVLinkSystem
class. Plugins work withMAVLinkSystem
class. Applications are forbidden to send MAVLink messages usingSystem
interface.Current design: (Details omitted)
Proposed design: (Details omitted)