Utilizing Mission Control - swift

recently I've gotten into trying to utilize the DJI Mobile SDK as a little side project of mine. I've been working in Swift utilizing the samples provided by DJI on their Github but I've been having an issue utilizing the missionControl and honestly I'm not even sure if missionControl is the best way to go about doing it.
Goal: Simply what I'm trying to achieve is that I just want the drone to fly up to a specified height and take video footage of what is below it. I'm not even concerned with horizontal motion right now, just vertical motion.
What I've Done: Utilizing their sample, I've gotten the drone to record video footage, but I am unable to get it to truly fly. My first approach was to use flightController and I've succeeded in getting it to take off and land, but I do not believe their is a method such as goToLocation() or something of the sort. This is the functioning methods that do take off and landing.
#IBAction func takeOffPressed(_ sender: Any) {
if let aircraft = DJISDKManager.product() as? DJIAircraft {
aircraft.flightController?.startTakeoff(completion: nil)
}else{
self.showAlertViewWithTitle(title:"Action", withMessage: "Product Connection Not Found")
}
}
#IBAction func landPressed(_ sender: Any) {
if let aircraft = DJISDKManager.product() as? DJIAircraft {
aircraft.flightController?.startLanding(completion: nil)
}else{
self.showAlertViewWithTitle(title:"Action", withMessage: "Product Connection Not Found")
}
}
Although I got the drone to takeOff, that is not truly flight so instead I started looking into the missionControl class where there seems to be missions to achieve the functionality that I want, however my code seems to not be correct because it does not cause the drone to perform any action. If there is someone here more experienced with the DJI SDK, I would appreciate any help that you may be able to give. My non functioning code is below.
#IBAction func doMission(_ sender: Any) {
var elements = [DJIMissionControlTimelineElement]()
elements.append(DJITakeOffAction())
elements.append(DJIGoToAction(altitude: 1)!)
elements.append(DJIGoHomeAction())
DJISDKManager.missionControl()?.scheduleElements(elements)
DJISDKManager.missionControl()?.startTimeline()
}

The timeline mission needs a few adjustments, including the altitude of the go to action and the addition of a DJIRecordVideoAction to record video for your desired duration. Try the modified code below:
#IBAction func doMission(_ sender: Any) {
var elements = [DJIMissionControlTimelineElement]()
elements.append(DJITakeOffAction())
elements.append(DJIGoToAction(altitude: 5)!)
elements.append(DJIRecordVideoAction(duration: 10)!) // this assumes a desired duration of 10 seconds
elements.append(DJIGoHomeAction())
DJISDKManager.missionControl()?.scheduleElements(elements)
DJISDKManager.missionControl()?.startTimeline()
}
You can also make use of DJIMissionControl addListener:toTimelineProgressWithBlock: method to add additional diagnostics to your code as it will be called for each timeline event including start and finish of all elements.

Related

Is there away to completely override a line of code based on a condition set?

I creating this app for my church and one feature is a prayer wall. I am using firebase as my database integrated with google sign in. When a user post a prayer request it displays their display names amongst other things. I implemented a switch and some line of code to change the display name to anonymous if the switch is on. That works however it adds anonymous after the display name. Please see the examples below.
I am really new to swift and I am self taught this is my first app, any help would be greatly appreciated.
#IBAction func didPostPrayerRequest(_ sender: Any) {
var userInfo = Auth.auth().currentUser?.displayName
if privacyFilter.isOn {
userInfo?.append("anonymous")
}
let prayerPosted:[String: Any] = ["praydate": [".sv":"timestamp"], "prayer": prayerPostText.text!,"username":userInfo!]
prayerRef?.child("Prayers").childByAutoId().setValue(prayerPosted)
print("Any")
//Dismiss popover
presentingViewController?.dismiss(animated: true, completion: nil)
}
Screenshot
Simply overwrite the value using userInfo = "anonymous" instead of appending to it.

how to call multiple operations at the same time

I know I can use GCD or an OperationQueue to do concurrent workload but I've been trying to look for a way to call multiple functions and have them start at the same time.
My project here is to trigger multiple webcams at once to have synchronised photos coming from different cameras.
I had found this post about how to display multiple feeds from multiple webcams at once here: Run multiple AVCaptureSessions or add multiple inputs
I am unsure of how I will be able to trigger a photo capture from these as of yet, but I first wanted to see how I would go about synchronising the call to the function.
My theoretical solution would be something like this:
create concurrent operation queue
make it so I can start the operation queue manually rather than start each operation automatically as it would be added to the queue (how ?)
add an operation that would take a picture from the associated video input. Once for each camera
start operation queue
wait for operations to finish
continue workflow
Is this possible ?
If not is calling multiple methods truly at once even possible ?
If even this isn't possible how would I go about synchronising the photo capture, maybe recording a short video, use timestamp right before the start of the recording to adjust delay and capture frames at a specific time in the resulting videos ?
Also in the comments, what is the tag for a MacOS application built in swift ? It's my first time asking for this rather than iOS so it would help find people who might be able to help.
Thanks for any insight you might be able to give me !
You are on the right track (NS)OperationQueue is made for this. Below is an example:
func operation1() {
print("\(#function) starts")
sleep(1)
print("\(#function) ends")
}
func operation2() {
print("\(#function) starts")
sleep(2)
print("\(#function) ends")
}
func operation3() {
print("\(#function) starts")
sleep(3)
print("\(#function) ends")
}
#IBAction func start(_ sender: Any) {
let operation = BlockOperation(block: {})
operation.addExecutionBlock { self.operation1() }
operation.addExecutionBlock { self.operation2() }
operation.addExecutionBlock { self.operation3() }
let endOperation = BlockOperation { self.allFinished() }
endOperation.addDependency(operation)
let queue = OperationQueue()
queue.addOperations([operation, endOperation], waitUntilFinished: false)
}
func allFinished() {
print("all finished")
}
operation1, 2, 3 can start in arbitrary order but the finishing order is always 1, 2, 3, and then allFinished is triggered.

Why text field can not be refresh in a method?

I try to refresh a text field to provide information. But i realize i can not update this display till the code ends. I tried to make it async but no success. Can someone to explain me with a simple example?
I show you this simple code. The text field named txtLog only show "Nothing else" when count of i (100000) is ended. Why?
#IBAction func elimineDoublons(_ sender: Any) {
DispatchQueue.main.async {
self.txtLog.stringValue="Nothing else"
}
for i in 0...100000{
print(i)
}
}
Can you explain me or show me a simple example? Please...
PS: Sorry for my english, i'm french...
You get this behaviour because your for loop is running in the main queue: #IBAction functions are always running in the main queue. So, you are blocking the queue in which you want to run self.txtLog.stringValue="Nothing else". The UI is always updated in the main thread, so GCD (Grand Central Dispatch) waits for the end of your loop before running this code.
Keep in mind that UI components can only be properly manipulated on the main thread.
Therefore, the for loop must be running in a background queue. For instance, replace your code by this one:
#IBAction func elimineDoublons(_ sender: Any) {
txtLog.stringValue = "Nothing else"
DispatchQueue.global(qos: .background).async(elimineDoublons)
}
private func elimineDoublons() {
for i in 0...100000 {
print(i)
}
}
Note that we have defined two functions with the same name, but their signatures are not the same, this is why it works correctly. Do not change the signatures, it may not compile anymore.

Writing better Swift code

The title is on how to write better Swift code but, my real question is really what is better if I create a function, then call it when the button is clicked vs I write what I want to happen once the button is clicked .
Eg.
var thing = 0
func hi(){
// Do something
thing++
}
#IBAction func somethingHi(sender: AnyObject) {
println(hi)
}
vs
var thing = 0
#IBAction func othersomethingHI(sender: AnyObject) {
thing++
println(thing)
}
I know both do the same thing but, is one "better" written than the other?
If an IBAction does something that you might want to do at some other time then it should call a function that performs that action, so that "others" can effect the same thing without duplicating code. If not, implement it solely in the action.
If you're code is short and won't be reused, you can just put it inside the #IBAction func function.

Sync two web views

I have a need to sync two web views, so that anything that happens in one web view happens simultaneously in the other.
I have tried various ways, without success, and the more I try the more convoluted and likely bug ridden this is getting.
I feel there maybe a very simple way to do this, but can not figure it out.
One of the things that I note is not allowed is having the same NSTextField as the "takeStringURLFrom"
override func controlTextDidEndEditing(obj: NSNotification) {
webViewLeft.takeStringURLFrom(hiddenTextField)
webViewRight.takeStringURLFrom(urlField)
}
override func webView(sender: WebView!, didCommitLoadForFrame frame: WebFrame!) {
if frame == sender.mainFrame {
urlField.stringValue = sender.mainFrameURL
hiddenTextField.stringValue = sender.mainFrameURL
webViewRight.takeStringURLFrom(urlField)
webViewLeft.takeStringURLFrom(hiddenTextField)
printLn("realised just creating an infinite loop here")
}
}
I don't like this but it appears to work as needed. I think it needs some refinement. Using a hidden text field to mimic the url for the second web view and tie each web view to there respective text fields via the web view's referenced action "takeStringUrlFrom"
override func webView(sender: WebView!, didStartProvisionalLoadForFrame frame: WebFrame!) {
if frame == sender.mainFrame {
urlField.stringValue = sender.mainFrameURL
hiddenTextField.stringValue = sender.mainFrameURL
window.makeFirstResponder(nil)
window.makeFirstResponder(hiddenTextField)
window.makeFirstResponder(nil)
window.makeFirstResponder(urlField)
window.makeFirstResponder(nil)
}
}