-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
Rework Vehicle Joystick Handling #10758
Conversation
Yeah, good work. TBH it's funny that vehicle has awareness of Joystick. This should not happen. But this is OSS and there's always a way to improve the code. |
Yes, agreed and I'm still willing to do a deeper dive on the joystick support. This PR is just a basic fix of the current bugs which I think is needed in the short term, certainly I needed it for my custom build. Can you think of the best way to safely stop the thread when all the active vehicles are removed? I think it has to be in the joystick class rather than the vehicle class for that, since the vehicle doesn't know if it is the last one when it gets destructed. |
Instead of using activeVehicle to trigger, connect to the MultiVehicleManager::vehicles countChanged signal. When the count goes to 0 all the vehicles are gone. |
I looked through and the changes make sense to me. Would like to get this finished up and merged in. I need to make some changes above it to support a mode for Herelink which supports buttons only but no sticks. |
Other suggestion for review is the ArduSub folks since they are all about joysticks. |
@jaxxzer Any ArduSub folks to look at this? |
@DonLakeFlyer Thanks for the review.
I had a look and found MAVLinkProtocol::_vehicleCountChanged as a private slot which connects to Should I connect to MultiVehicleManager::vehicleRemoved and then check for !(_toolbox->multiVehicleManager->activeVehicle()) to know if the last vehicle has been deleted? |
In joystick manager, something along these lines:
|
7338c58
to
7492d6b
Compare
@DonLakeFlyer ahh thanks, yep, that worked perfectly. Only thing I noticed while testing this.
Previously, before my changes, the joystick thread would be stopped in the destructor of the deleted vehicle, and then started again on activeVehicleChanged in the new active vehicle. (actually the thread would also stop and start on every change of active vehicle) It seems to me better to avoid starting and stopping the thread, I can't see any danger in those brief errors in sendJoystickDataThreadSafe, but I thought I'd point it out in case I'm missing something. Anyway, I implemented stopping the thread following your suggestion, and fixed up a few typos in my comments, whoops. |
Actually now that I look at it more, if joystick::_handleAxis fell at just the right moment, its appears possible, though extremely unlikely, that it could dereference a dead pointer to the just-deleted vehicle. Perhaps it is safer to stop and start the joystick thread any time a vehicle is deleted as before, so that the joystick thread is never running while its _activeVehicle pointer is pointing to a vehicle which is being deleted. I will take a closer look later on. |
Ok after looking into this I think this can stand as is. I had a misunderstanding of how signals and slots worked.
|
Signals get queued only if they are crossing thread boundaries. |
Good, that means that by the time the vehicle is deleted, either the _activeVehicle pointer in the joystick class will have been updated, or the thread will have been stopped, depending on if it is the last vehicle or not. Either way this should be safe. |
@JMare Could I ask for your help in reviewing and a quick test of this pull : #10764? For a normal build it should be a no-op in functionality. It allows custom builds to use a joystick where only the buttons are supported. This is for the Herelink such that it can create a custom build of QGC which doesn't need to modofy mainline QGC code. I've already tested it out on my new Herelink QGC version. |
@JMare One thing I noticed which I'm not sure if I changed with my pull or not when on the Herelink: When you have a calibrated joystick and you go into the buttons page clicking buttons while on that page will execute the action for real. Seems a bit dangerous since you may just be clicking buttons just to see which is which. Not sure if that's the way it always was. |
Sorry I hit the wrong button and closed the pull. Duh! |
@DonLakeFlyer I tested your pull #10764 on my Ubuntu setup with a USB joystick. I guess nobody ever plugs an external joystick into the herelink but if someone did, it might give weird behavior due to the fact the active joystick select menu is hidden.
Yes, this behavior goes back at least to the latest stable. I have been caught out in the field by this with an unexpected arming of the vehicle while setting up buttons. Maybe a simple fix for this would be a red warning on the top of the page that appears if the joystick is enabled? |
Thanks for the review and test @JMare. I'm going to merge it in. I have the live button thing in the back of my head. I'll see what I can do about it. |
@JMare Anything left to do on this pull or should I merge? |
The changes I made yesterday for compatibility with the herelink mode caused a double call to capture joystick sometimes, so I fixed that this morning and did a force push just now. This pull is now ready to merge. The only other issue I noticed is if for some reason you have joystick enabled true saved in the settings and then switch to a build with usebuttonsonly true, the joystick will stay fully enabled with axis and buttons working, and there will be no way to disable the joystick because the menu with the tickbox is hidden. |
Oh and I have quite a lot of logging prints in VehicleLog. Change of active vehicle:
Enabling the joystick from menu:
Disconnecting the joystick:
Reconnecting the joystick:
|
Yeah, that's kind of a strange edge case. Given the use buttons only thing is pretty device specific it would have a to be a screw up somewhere which allowed a build onto the device which did not have that set correctly. So I'm not too worried about that. For the logging could you move the joystick specific logging in Vehicle to JoyStickLog instead of VehicleLog? In order to do that you will likely have to move the definition of JoystickLog out of the joystick code and make it globally available. You do that by moving it to QGCLoggingCategory.h/cc. |
Changed my mind. That's a simple change so might as well move this from probably won't happen to can't happen by fixing it. |
Thanks @DonLakeFlyer,
Sanity check, is this a sensible way to store the value in the Vehicle class? Full new function for context.
I haven't tested this properly yet, I won't be able to until Monday as I'm taking time off work and I cant get either Arducopter or PX4 sitl to work on my mac right now. I'll get to it on Monday and confirm that I haven't broken anything with this minor change. |
Since you are setting the value each time using static this doesn't really achieve anything other than move the storage location off the stack. Just remove the static. Unless I"m not quite sure what you are trying to achieve here? |
I had thought that the static keyword would cause the call to only run once and then that value would be stored as const for the rest of execution. |
Ah, ok that's what wanted. Like you say there is no need to do that. The compiler will end up doing the same thing for you by itself. |
This fixes a number of issues with the way joysticks were handled in a multi vehicle context. I reworked the way vehicle determine to enable and capture the joystick input, as well as seperating setJoystickEnable from saveJoystick to seperate runtime and saved state.
@DonLakeFlyer updated, tested the changes. Logging now goes to joystick log rather than vehicle log. This PR is ready to merge. |
@JMare Thanks for all the work on this. |
Thanks for the help and review @DonLakeFlyer |
Huge thanks and kudos @JMare! @DonLakeFlyer as usual great support not to be missed. Thanks! |
@JMare I ran into a problem with this where QGC goes into an infinite loop and blows the stack. It specifically happens when you have a disabled joystick which is uncalibrated:
I think I have the details right. It's a little tricky. |
@DonLakeFlyer, apologies, that's a bad bug that is my mistake. I thought I was being clever by having setJoystickEnabled(false) call capturejoystick so it would disconnect the signals to the vehicle once the joystick was disabled. I changed that in one of my last updates to that PR and didn't test uncalibrated joysticks since. It doesn't actually matter if the signals are disconnected, because the joystick class checks for joystickenabled before sending signals. This is the change I made which introduced this issue I believe. reverting this should be the fix. But I will try and replicate this issue today and confirm 100% that this is the fix and doesn't affect anything else. |
Update, I realized that since a default calibration is used for gamepads and all my testing was on gamepads, I never tested this with an calibrated joystick. I replicated the issue just now by disabling the loading of default calibration from game pads, and incrementing Joystick::_calibratedSettingsKey to force recalibration, and I immediately saw the unbounded loop you found. As above, the fix is the following git diff:
The only downside of this is that it will leave these signals connected if the user disables a joystick that was previously enabled.
Additionally the joystick will retain a pointer to the vehicle which now no longer wants the joystick data.
And has a similar check before it emits a button action:
Not sure how I can add commits to a pull request that is already merged. Wanted to provide a solution to this fast so I put the quick fix on my fork https://github.com/JMare/qgroundcontrol/tree/joystick-loop-fix. |
Just submit a new pull. |
If the signals are not disconnected to the vehicle when the joystick is disabled. Doesn't that means that in a multi-vehicle situation if the user re-enables a joystick the button presses will go to in-active vehicles as well as active vehicles? |
This sequence on events:
|
In the scenario above, when active vehicle changes to B, the joystick is captured by the new active vehicle, which redirects all the signals.
The reason I think we can solve this with the solution I posted last night rather than the two above is that I have aimed to:
|
I created another PR with the quick solution. I'm happy to look at different solutions to this which are neater if you like. Again my apologies I didn't catch that issue before this got merged. |
Ok, I see that now. Thanks. All ok then. |
#10648 fixed a bug identified in #10544 which had caused joystick control to get stuck on a vehicle.
However, the fix introduced two new bugs, and I think was not the ideal way to fix it.
The three new bugs introduced, are:
I investigated and found a couple of problems, the first is obvious:
The others are less obvious
My Solution:
Would appreciate review and testing - I have tested thoroughly on my enviroment. (linux using usb joystick)
Note that there is a bug in MAVProxy/MAVLink identified here: which makes this difficult to test on ardupilot.
I have been doing most of my testing on PX4 for this reason.
Leaving this as a draft until I have worked out what to do about stopping the thread when all vehicles are gone.
@zdanek @vorobotics