How to get "Area under aircraft unsuitable for landing" message? - swift

When I initiate auto-landing in the DJI Fly app I sometimes get the following message, especially under bad lighting conditions:
Now, in my own code, when I call DJIFlightController.startLandingWithCompletion, the drone would not land and the completion block gets executed without any error.
My question is, how can I intercept the equivalent to DJIs error message as shown above? What code is relevant for that?
EDIT 1:
I am also checking if a landing confirmation is needed with the following code:
func observeConfirmLanding() {
guard let confirmLandingKey = DJIFlightControllerKey(param: DJIFlightControllerParamConfirmLanding) else { return }
DJISDKManager.keyManager()?.startListeningForChanges(on: confirmLandingKey, withListener: self) { (oldValue: DJIKeyedValue?, newValue: DJIKeyedValue?) in
DispatchQueue.main.async {
if let oldBoolValue = oldValue?.boolValue,
let newBoolValue = newValue?.boolValue,
oldBoolValue != newBoolValue {
self.landingConfirmationNeeded = newBoolValue
self.logger.debug("Landing confirmation is needed")
}
}
}
}
It never enters the closure.
As I understood the landing confirmation might be needed at a height of 0.3m, but in my case, the landing process gets interrupted at different heights that are more than 0.3m, e.g. already at 2m or 1.5m
EDIT 2:
I have changed the surface below the drone in my basement by adding a bright carpet with a distinct pattern. This improves the whole stability of the drone AND even more important: The drone just lands without being interrupted. I do not get the warning message in the DJI Fly app any more.
I check for isLandingConfirmation the way Brien suggests in his comment, I finally get true when testing this in the simulator.
extension FlightControllerObserver: DJIFlightControllerDelegate {
func flightController(_ fc: DJIFlightController, didUpdate state: DJIFlightControllerState) {
if (landingConfirmationNeeded != state.isLandingConfirmationNeeded) {
landingConfirmationNeeded = state.isLandingConfirmationNeeded
}
}
But, when I test this in my basement (flight mode "OPTI") and outside (flight mode "GPS") the drone just lands without waiting for any confirmation.
While I learned a lot, it is still a miracle to me which class in the DJI Mobile SDK is responsible for "throwing" that warning message.

If the completion handler completes without any error you might need to check if isLandingConfirmationNeeded in DJIFlightControllerState is set to true. If thats the case then you will need to implement the function confirmLandingWithCompletion.
Sounds relevant to your experience looking at the documentation
(void)confirmLandingWithCompletion:(DJICompletionBlock)completion Confirms continuation of landing action. When the clearance between
the aircraft and the ground is less than 0.3m, the aircraft will pause
landing and wait for user's confirmation. Can use
isLandingConfirmationNeeded in DJIFlightControllerState to check if
confirmation is needed. It is supported by flight controller firmware
3.2.0.0 and above.
The landingProtectionState property of the DJIVisionControlState class could be a good place to look for the cause of that error message. One of the potential states that sounds relevant is
DJIVisionLandingProtectionStateNotSafeToLand -> Landing area is not flat
enough to be considered safe for landing. The aircraft should be moved
to an area that is more flat and an auto-land should be attempted
again or the user should land the aircraft manually.
Also within a section of DJI's documentation there is a section on an article about flight control that talks about landing protection and forcing a landing. I couldn't see any functions in the SDK to force a landing.

You should be aware of that the fly app does not use the sdk internally. It uses the middlelayer directly.
You often get different behaivor when using the SDK compared to the app. Some functions are not available at all.
I usually disable it completly, I want it to land when I say so :-)
(exit_landing_ground_not_smooth_enable g_config.landing.exit_landing_ground_not_smooth_enable)

Related

Saving a score to firebase with attached values

What I'm wanting to accomplish is save a score to firebase that has two values attached to it. Here's the code that writes the score to firebase.
func writeToFirebase() {
DispatchQueue.global(qos: .userInteractive).async {
self.ref = Database.database().reference()
if GameManager.instance.getTopScores().contains(GameManager.instance.getGameScore()) {
self.ref?.child("user")
.child(GameManager.instance.getUsername())
.child(String(GameManager.instance.getGameScore()))
.updateChildValues( [
"badge":GameManager.instance.getBadgeLevel(),
"vehicle": GameManager.instance.getVehicleSelected()
]
)
}
}
}
The issue I'm having is when a new score is saved with its values it sometimes overwrites the other scores. This seems to be random and its not when they're the same score or anything like that. Sometimes it will only overwrite one score and sometimes multiple. I'm watching firebase and I can see it being overwritten, it turns red and then is deleted. Sometimes the new score being added will be red and get deleted. The score doesn't need to be a child, but I don't know how to attach values to it if it's not. Any help is appreciated
This issue seems to happen occasionally so I am going to post my comment as an answer.
There are situations where an observer may be added to a node and when data changes in that node, like a write or update, it will fire that observer which may then overwrite the existing data with nil.
You can see this visually in the console as when the write occurs, you can see the data change/update, then it turns red and then mysteriously vanishes.
As suggested in my comment, add a breakpoint to the function that performs the write and run the code. See if that function is called twice (or more). If that's the case, the first write is storing the data properly but upon calling it a second time, the values being written are probably nil, which then makes the node 'go away' as Firebase nodes cannot exist without a value.
Generally speaking if you see your data turn red and vanish, it's likely caused by nil values being written to the node.

google action conversation exit: not waiting for user response

I am trying to seek user feedback at the end of a google action session using actions sdk.
As per the Actions documentation (https://developers.google.com/actions/assistant/conversation-exits), I have set the cancel intent
"conversations" : {
...
"inDialogIntents": [
{
"name": "actions.intent.CANCEL"
},
...
and in my functions code I am calling a SimpleResponse as below:
app.intent('actions.intent.CANCEL', (conv, input) => {
conv.close(new SimpleResponse({
speech: 'Kindly rate between 1 and 5,
text: 'Rate between 1 and 5'
}));
});
When the user says goodbye, the above code gets called. The speech is heard and text displayed but the conversation immediately exits with an earcon.
The documentation mentions that the system will wait for 2 seconds before exiting but it seems to do so immediately. Is there a way to get this working? Thanks
It's not quite clear to me what you'd like to achieve. First of all, as long as you're using conv.close() method, you are ending the conversation. If you want to keep the mic open, you need to use conv.ask() method. But I'm not sure id you can use conv.ask() with actions.intent.CANCEL, because that event is there to get the user out, not to keep the conversation going. But I'm not %100 sure, you need to try it.
If what you're trying to achieve is something like this:
User: cancel
System: How would you rate our interaction?
User: Good.
earcon
Then, in my humble opinion, you probably shouldn't (and as I said you probably can't) do it. The docs clearly state that the purpose for custom exit behavior is:
to cleanup your fulfillment logic and respond to the user one last time.
Also, the docs doesn't say the system will wait for the user response for 2 second. It says the execution of the request (the time out for your fulfillment) is 2 seconds. Plus; if a user is canceling mid-conversation, chances are that the interaction was unsuccessful anyway. I'd rather create feedback dialog turns that are connected to the fallback intents and happy path(s).
Still, try to use conv.ask() instead and let me know if that works. (And please let me know by commenting.) Either case, though, I'd consider getting the feedback some other way.

Error when running coreml in the background: Error computing NN outputs error

I'm running an mlmodel that is coming from keras on an iPhone 6. The predictions often fails with the error Error computing NN outputs. Does anyone know what could be the cause and if there is anything I can do about it?
do {
return try model.prediction(input1: input)
} catch let err {
fatalError(err.localizedDescription) // Error computing NN outputs error
}
EDIT: I tried apple's sample project and that one works in the background so it seems it's specific to either our project or model type.
I got the same error myself at similar "seemingly random" times. A bit of debug tracing established that it was caused by the app sometimes trying to load its coreml model when it was sent to background, then crashing or freezing when reloaded into foreground.
The message Error computing NN outputs error was preceded by:
Execution of the command buffer was aborted due to an error during execution. Insufficient Permission (to submit GPU work from background) (IOAF code 6)
I didn't need (or want) the model to be used when the app was in background, so I detected when the app was going in / out of background, set a flag and used a guard statement before attempting to call the model.
Detect when going into background using applicationWillResignActive within the AppDelegate.swift file and set a Bool flag e.g. appInBackground = true. See this for more info: Detect iOS app entering background
Detect when app re-enters foreground using applicationDidBecomeActive in the same AppDelegate.swift file, and reset flag appInBackground = false
Then in the function where you call the model, just before calling model, use a statement such as:
guard appInBackground == false else { return } // new line to add
guard let model = try? VNCoreMLModel(for modelName.model) else { fatalError("could not load model") // original line to load model
I doubt this is the most elegant solution, but it worked for me.
I haven't established why the attempt to load the model in background only happens sometimes.
In the Apple example you link to, it looks like their app only ever calls the model in response to a user input, so it will never try to load the model when in background. Hence the difference in my case ... and possibly yours as well?
In the end it was enough for us to set the usesCPUOnly flag. Using the GPU in the background seems prohibited in iOS. Apple actually wrote about this in their documentation as well. To specify this flag we couldn't use the generated model class anymore but had to call the raw coreml classes instead. I can imagine this changing in a future version however. The snippet below is taken from the generated model class, but with the added MLPredictionOptions specified.
let options = MLPredictionOptions()
options.usesCPUOnly = true // Can't use GPU in the background
// Copied from from the generated model class
let input = model_input(input: mlMultiArray)
let output = try generatedModel.model.prediction(from: input, options: options)
let result = model_output(output: output.featureValue(for: "output")!.multiArrayValue!).output

requestAccessForMediaType doesn't ask for permission

I noticed that my app doesn't request permission to use the camera. After some experimentation I figured out that the piece of code used to check permission takes a very long time to complete. So I thought of letting that part of my viewdidload run on a serial queue (sync). Forcing the rest to wait for the auth process to complete before starting the next line. But that doesn't really work. The lines start in order, but still don't finish in order.
The strange thing is if I just call for the permission and do nothing with it, no completionHandler (like the code below) then he does run the second time I run it with a completionHandler perfectly, although it still doesn't show an alert to the user. Can this be because recording video doesn't need permission in Europe? avcapturedevice ref
If I want to have code executed line by line, do I need to use barriers? or sync serial queue's or??
AVCaptureDevice.requestAccessForMediaType(AVMediaTypeVideo, completionHandler: { nil }())
Update:
This method should be pretty fail safe, but it isn't. It returns true for granted, but 3 for the rawValue (denied)
func checkForAuthorizationStatus() {
println("auth me")
AVCaptureDevice.requestAccessForMediaType(AVMediaTypeVideo, completionHandler: {
granted in
if granted {
println("granted: \(granted)")
self.deviceAuthorized = true
println("raw value: \(AVCaptureDevice.authorizationStatusForMediaType(AVMediaTypeVideo).rawValue)")
} else {
self.deviceAuthorized = false
}
})
}
Update 2 : answer in comments below.
- Reset privacy settings on device to get the request.
rawValue for authorised is 3 not 4
If it's taking a long time to appear, then it sounds like you're not running this on the main thread, which you should be. Also, if it has prompted once before, it won't prompt again – the user has to go into Settings and enable camera access.

QApplication::processEvents never returns

In my application I need to wait until external program (using QProcess) is finished. I want to keep the application responsible so blocking methods are unacceptable.
Also I need to disallow user input. I've tried to make QEventLoop and exec it with QEventLoop::ExcludeUserInputEvents flag, but as documentation says it only delays an event handling:
the events are not discarded; they will be delivered the next time processEvents() is called without the ExcludeUserInputEvents flag.
So I implemented simple event filter and install it on qApp (the idea is took from Qt Application: Simulating modal behaviour (enable/disable user input)). It works well, but sometimes QApplication::processEvents function never returns even if I specify the maximum timeout. Could anyone help me to understand for what reasons it periodically happens?
class UserInputEater : public QObject
{
public:
bool eventFilter(QObject *object, QEvent *event)
{
switch(event->type())
{
case QEvent::UpdateRequest:
case QEvent::UpdateLater:
case QEvent::Paint:
return QObject::eventFilter(object, event);
default:
return true;
}
}
};
-
UserInputEater eventEater;
qApp->installEventFilter(&eventEater);
QProcess prc;
prc.start("...");
while(!prc.waitForFinished(10))
{
if(qApp->hasPendingEvents())
{
// Sometimes it never returns from processEvents
qApp->processEvents(QEventLoop::AllEvents, 100);
}
}
qApp->removeEventFilter(&eventEater);
UPD: Seems like it depends of the timeout value for QProcess::waitForFinished.
I guess you are filtering some useful events (for example, QEvent::SockAct could be involved). Try to add some debug output and find out which event types you're actually filtering. Or it might be better to specify the black list of events you want to block instead of white list of events you want to allow. See this answer.
Also you shouldn't use return QObject::eventFilter(object, event);. You should use return false. All other event filters will be called automatically.
This solution however seems weird and unreasonable to me because you can just call setEnabled(false) for your top level widget to block user input, and then you can use QApplication::processEvents without any flags.