ArDrone SDK 1.8 added
[mardrone] / mardrone / ARDrone_SDK_Version_1_8_20110726 / Examples / iPhone / FreeFlight / Classes / Menus / MenuUpdater.m
1 //
2 //  ARUpdaterViewController.m
3 //  AR.Updater
4 //
5 //  Created by Robert Ryll on 10-05-14.
6 //  Copyright Playsoft 2010. All rights reserved.
7 //
8
9 #import "MenuUpdater.h"
10 #include <sys/socket.h>
11 #include <netinet/in.h>
12 #include <arpa/inet.h>
13 #include <unistd.h>
14 #include "plf.h"
15
16 #define UPDATER_ASSERT(var, value)                                                                      \
17 if (var != value)                                                                                                       \
18 {                                                                                                                                       \
19         NSLog(@"oO ! "#var" is #%u (should be #%u)", (var), (value));   \
20         return;                                                                                                                 \
21 }
22
23 static NSArray *documentPath = nil;
24
25 static const NSString *stepKeys[] =
26 {
27         @"Connecting",
28         @"Checking/updating bootloader",
29         @"Checking version",
30         @"Sending file",
31         @"Restarting AR.Drone",
32         @"Installing",
33 };
34
35 enum
36 {
37         STEP_IMAGE_EMPTY,
38         STEP_IMAGE_PASS,
39         STEP_IMAGE_FAIL,
40         STEP_IMAGE_PROBLEM
41 };
42
43 #define LINE_H                   22
44 #define STATUS_LINE_NR    2
45 #define INDICATOR_S              10
46
47 #define IPAD_LINE_H                       50
48 #define IPAD_INDICATOR_S          20
49 #define IPAD_OFFSET_X             50
50
51 #define OFFSET_Y                (SCREEN_H - (STATUS_LINE_NR + NUMBER_OF_UPDATER_STEPS + 3) * LINE_H)
52 #define IMG_H                   (OFFSET_Y - LINE_H)
53 #define STATUS_Y                (OFFSET_Y + LINE_H)
54 #define STEP_LINE_Y             (STATUS_Y + LINE_H * STATUS_LINE_NR + LINE_H)
55 #define INDICATOR_M             ((LINE_H - INDICATOR_S) / 2)
56
57 #define IPAD_OFFSET_Y           ((IPAD_SCREEN_H - (STATUS_LINE_NR + NUMBER_OF_UPDATER_STEPS) * IPAD_LINE_H) / 2)
58 #define IPAD_IMG_H                      (IPAD_OFFSET_Y - IPAD_LINE_H)
59 #define IPAD_STATUS_Y           (IPAD_OFFSET_Y + IPAD_LINE_H)
60 #define IPAD_STEP_LINE_Y        (IPAD_STATUS_Y + IPAD_LINE_H * STATUS_LINE_NR + IPAD_LINE_H)
61 #define IPAD_INDICATOR_M        ((IPAD_LINE_H - IPAD_INDICATOR_S) / 2)
62
63 #define MAX_RETRIES 5
64
65 #define VERSION_TXT @"version.txt"
66 #define REPAIR_VERSION_TXT @"repair_version.txt"
67
68 @implementation MenuUpdater
69 @synthesize firmwareVersion;
70 @synthesize firmwarePath;
71 @synthesize firmwareFileName;
72 @synthesize repairPath;
73 @synthesize repairFileName;
74 @synthesize repairVersion;
75 @synthesize bootldrPath;
76 @synthesize bootldrFileName;
77 @synthesize droneFirmwareVersion;
78 @synthesize localIP;
79
80 @synthesize ftp;
81 @synthesize repairFtp;
82 @synthesize fsm;
83
84 #pragma mark init
85 - (id) initWithController:(MenuController*)menuController
86 {
87         NSArray *targetArray = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"UIDeviceFamily"];
88         compiledForIPad = NO;
89         self.localIP = nil;
90         
91         for (int i = 0; i < [targetArray count]; i++)
92         {
93                 NSNumber* num = (NSNumber*)[targetArray objectAtIndex:i];
94                 int value;
95                 [num getValue:&value];
96                 if (2 == value)
97                 {
98                         compiledForIPad = YES;
99                         break;
100                 }
101         }
102         
103         usingIPad = NO;
104         if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
105         {
106                 usingIPad = YES;
107         }
108         
109         using2xInterface = NO;
110         if ([UIScreen instancesRespondToSelector:@selector(scale)]) {
111                 CGFloat scale = [[UIScreen mainScreen] scale];
112                 if (scale > 1.0) {
113                         using2xInterface = YES;
114                 }
115         }
116         
117         if (!documentPath)
118                 documentPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] copy];
119         
120         if (usingIPad && compiledForIPad)
121         {
122                 self = [super initWithNibName:@"MenuUpdater-iPad" bundle:nil];
123         }
124         else
125         {
126                 self = [super initWithNibName:@"MenuUpdater" bundle:nil];
127         }
128         
129         if (self)
130         {
131                 controller = menuController;
132         ftp = nil;
133         }
134         return self;
135 }
136
137 - (void) viewDidLoad
138 {
139         NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"Firmware" ofType:@"plist"];
140         NSDictionary *plistDict = [NSDictionary dictionaryWithContentsOfFile:plistPath];
141         self.firmwareFileName = [plistDict objectForKey:@"FirmwareFileName"];
142         self.repairFileName = [plistDict objectForKey:@"RepairFileName"];
143         self.bootldrFileName = [plistDict objectForKey:@"BootldrFileName"];
144         self.repairVersion = [plistDict objectForKey:@"RepairVersion"];
145         
146         self.firmwarePath = [[NSBundle mainBundle] pathForResource:firmwareFileName ofType:@"plf"];
147         self.repairPath = [[NSBundle mainBundle] pathForResource:repairFileName ofType:@"bin"];
148         self.bootldrPath = [[NSBundle mainBundle] pathForResource:bootldrFileName ofType:@"bin"];
149         plf_phdr plf_header;
150         
151
152         if(plf_get_header([self.firmwarePath cStringUsingEncoding:NSUTF8StringEncoding], &plf_header) != 0)
153                 memset(&plf_header, 0, sizeof(plf_phdr));
154         
155         self.firmwareVersion = [NSString stringWithFormat:@"%d.%d.%d", plf_header.p_ver, plf_header.p_edit, plf_header.p_ext];
156         
157 #ifdef DEBUG
158         firmwareVersionLabel.text = [NSString stringWithFormat:@"v%s_D", [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] cStringUsingEncoding:NSUTF8StringEncoding]];
159 #else
160         firmwareVersionLabel.text = [NSString stringWithFormat:@"v%s", [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] cStringUsingEncoding:NSUTF8StringEncoding]];
161 #endif
162         
163         if (usingIPad && compiledForIPad)
164         {
165                 for (NSInteger i = 0; i < NUMBER_OF_UPDATER_STEPS ; i++)
166                 {
167                         stepLabel[i] = [UILabel new];
168                         stepLabel[i].backgroundColor = [UIColor clearColor];
169                         stepLabel[i].textColor = NORMAL_COLOR;
170                         stepLabel[i].frame = CGRectMake(IPAD_LINE_H + IPAD_OFFSET_X, IPAD_STEP_LINE_Y + IPAD_LINE_H * i, IPAD_SCREEN_H - IPAD_LINE_H, IPAD_LINE_H);
171                         stepLabel[i].text = NSLocalizedString((NSString *)stepKeys[i],);
172                         [self.view addSubview:stepLabel[i]];
173                         
174                         stepImageView[i] = [UIImageView new];
175                         stepImageView[i].frame = CGRectMake(IPAD_OFFSET_X, IPAD_STEP_LINE_Y + i * IPAD_LINE_H, IPAD_LINE_H, IPAD_LINE_H);
176                         stepImageView[i].image = [UIImage imageNamed:@"Empty-iPad.png"];
177                         [self.view addSubview:stepImageView[i]];
178                         
179                         if(i > UPDATER_STEP_CHECK)
180                         {
181                                 stepLabel[i].hidden = YES;
182                                 stepImageView[i].hidden = YES;  
183                         }
184                 }
185         }
186         else
187         {
188                 for (NSInteger i = 0; i < NUMBER_OF_UPDATER_STEPS ; i++)
189                 {
190                         stepLabel[i] = [UILabel new];
191                         stepLabel[i].backgroundColor = [UIColor clearColor];
192                         stepLabel[i].textColor = NORMAL_COLOR;
193                         stepLabel[i].frame = CGRectMake(LINE_H, STEP_LINE_Y + LINE_H * i, SCREEN_H - LINE_H, LINE_H);
194                         stepLabel[i].text = NSLocalizedString((NSString *)stepKeys[i],);
195                         [self.view addSubview:stepLabel[i]];
196                         
197                         stepImageView[i] = [UIImageView new];
198                         stepImageView[i].frame = CGRectMake(0, STEP_LINE_Y + i * LINE_H, LINE_H, LINE_H);
199                         stepImageView[i].image = [UIImage imageNamed:@"Empty.png"];
200                         [self.view addSubview:stepImageView[i]];
201                         
202                         if(i > UPDATER_STEP_CHECK)
203                         {
204                                 stepLabel[i].hidden = YES;
205                                 stepImageView[i].hidden = YES;  
206                         }
207                 }
208         }
209         
210         [okButton setTitle:NSLocalizedString(@"Ok",) forState:UIControlStateNormal];
211         [cancelButton setTitle:NSLocalizedString(@"Cancel",) forState:UIControlStateNormal];
212
213         stepIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleWhite];
214         stepIndicator.hidesWhenStopped = YES;
215         [self.view addSubview:stepIndicator];
216         [stepIndicator release];
217         
218         okButton.hidden = YES;
219         cancelButton.hidden = YES;
220         sendProgressView.hidden = YES;
221         
222         self.fsm = [FiniteStateMachine fsmWithXML:[[NSBundle mainBundle] pathForResource:@"updater_fsm" ofType:@"xml"]];
223         fsm.delegate = self;
224         fsm.currentState = UPDATER_STATE_WAITING_CONNECTION;
225         
226         [self performSelector:@selector(checkFtp) withObject:nil afterDelay:5.];
227 }
228
229 - (void)didReceiveMemoryWarning
230 {
231         [super didReceiveMemoryWarning];
232 }
233
234 - (void)dealloc
235 {
236         for (NSInteger i = 0 ; i < NUMBER_OF_UPDATER_STEPS ; i++) 
237         {
238                 [stepLabel[i] release];
239                 [stepImageView[i] release];
240         }
241         
242         self.firmwarePath = nil;
243         self.firmwareFileName = nil;
244         self.repairPath = nil;
245         self.repairFileName = nil;
246         self.repairVersion = nil;
247         self.bootldrPath = nil;
248         self.bootldrFileName = nil;
249         self.firmwareVersion = nil;
250         self.droneFirmwareVersion = nil;
251         self.localIP = nil;
252         
253         self.ftp = nil;
254         self.repairFtp = nil;
255         self.fsm = nil;
256         
257         if (documentPath)
258                 [documentPath release];
259         
260         [super dealloc];
261 }
262
263 // NSURLConnection needed to ping the ARDrone before creating the FTP.
264 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
265 {
266         NSLog(@"%@", response);
267         [connection release];
268         
269         self.ftp = [ARDroneFTP anonymousUpdateFTPwithDelegate:self withDefaultCallback:nil];
270         
271         if ([ftp keepConnexionAlive])
272                 [fsm doAction:UPDATER_ACTION_SUCCESS];
273         else
274                 [fsm doAction:UPDATER_ACTION_FAIL];
275 }
276
277 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
278 {
279         NSLog(@"%@", error);
280         [connection release];
281         
282         [fsm doAction:UPDATER_ACTION_FAIL];
283 }
284
285 - (void)executeCommandOut:(ARDRONE_COMMAND_OUT)commandId withParameter:(void*)parameter fromSender:(id)sender
286 {
287         switch(commandId)
288         {
289                 case ARDRONE_COMMAND_RUN:
290                         self.localIP = [NSString stringWithCString:(char*)parameter encoding:NSUTF8StringEncoding];
291                         
292                         NSURLConnection *ping = [[NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:FTP_ADDRESS, localIP, FTP_PORT, @""]]] delegate:self] retain];
293                         [ping start];
294                         
295                         break;
296                         
297                 default:
298                         break;
299         }
300 }
301
302 - (void)checkFtp
303 {
304         if (fsm.currentState == UPDATER_STATE_WAITING_CONNECTION)
305                 [fsm doAction:UPDATER_ACTION_FAIL];
306 }
307
308 // Updates the display with the FSM object.
309 - (void) updateStatusLabel
310 {
311         NSString *key = [(NSString *)fsm.currentObject stringByReplacingOccurrencesOfString:@"\\n" withString:@"\n"];
312         
313         switch (fsm.currentState)
314         {
315                 case UPDATER_STATE_NOT_CONNECTED:
316                 case UPDATER_STATE_NOT_REPAIRED:
317                 case UPDATER_STATE_NOT_UPDATED:
318                         statusLabel.text = [NSString stringWithFormat:NSLocalizedString(key,), [[UIDevice currentDevice] model]];
319                         break;
320                 case UPDATER_STATE_CHECK_VERSION:
321                 {
322                         switch ([firmwareVersion compare:droneFirmwareVersion options:NSNumericSearch])
323                         {
324                                 case NSOrderedAscending:
325                                         statusLabel.text = [NSString stringWithFormat:NSLocalizedString(key,), droneFirmwareVersion, NSLocalizedString(@"Please update this application",), NSLocalizedString(@"Launching Application...",)];
326                                         break;
327                                 case NSOrderedSame:
328                                         statusLabel.text = [NSString stringWithFormat:NSLocalizedString(key,), droneFirmwareVersion, NSLocalizedString(@"AR.Drone up to date",), NSLocalizedString(@"Launching Application...",)];
329                                         break;
330                                 default:
331                                         statusLabel.text = [NSString stringWithFormat:NSLocalizedString(key,), droneFirmwareVersion, NSLocalizedString(@"Firmware update available",), NSLocalizedString(@"Do you want to update the AR.Drone ?",)];
332                                         break;
333                         }
334                 }
335                         break;
336                 default:
337                         statusLabel.text = NSLocalizedString(key,);
338                         break;
339         }
340 }
341
342 - (void) updateStepIndicator:(unsigned int)index
343 {
344         stepIndicator.frame =   (usingIPad && compiledForIPad) ?
345         /* iPad */                              CGRectMake(IPAD_OFFSET_X + IPAD_INDICATOR_M, IPAD_STEP_LINE_Y + IPAD_INDICATOR_M + IPAD_LINE_H * index, IPAD_INDICATOR_S, IPAD_INDICATOR_S) :
346         /* iPhone */                    CGRectMake(INDICATOR_M, STEP_LINE_Y + INDICATOR_M + LINE_H * index, INDICATOR_S, INDICATOR_S);
347 }
348
349 - (void) updateStepImage:(unsigned int)index withImage:(unsigned int)image
350 {
351         switch (image)
352         {
353                 case STEP_IMAGE_EMPTY:
354                         stepImageView[index].image = [UIImage imageNamed:(usingIPad && compiledForIPad) ? @"Empty-iPad.png" : @"Empty.png"];
355                         break;
356                         
357                 case STEP_IMAGE_PASS:
358                         stepImageView[index].image = [UIImage imageNamed:(usingIPad && compiledForIPad) ? @"Pass-iPad.png" : @"Pass.png"];
359                         break;
360                         
361                 case STEP_IMAGE_FAIL:
362                         stepImageView[index].image = [UIImage imageNamed:(usingIPad && compiledForIPad) ? @"Fail-iPad.png" : @"Fail.png"];
363                         break;
364                         
365                 case STEP_IMAGE_PROBLEM:
366                         stepImageView[index].image = [UIImage imageNamed:(usingIPad && compiledForIPad) ? @"Problem-iPad.png" : @"Problem.png"];
367                         break;
368                         
369                 default:
370                         break;
371         }
372 }
373
374 /*
375  * Waiting Connection State:
376  *
377  *
378  */
379
380 // Enter
381 - (void)enterWaitingConnection:(id)_fsm
382 {
383         [self updateStatusLabel];
384         
385         [self updateStepIndicator:UPDATER_STEP_CONNECTING];
386         [stepIndicator startAnimating];
387 }
388
389 // Quit
390 - (void)quitWaitingConnection:(id)_fsm
391 {
392         [stepIndicator stopAnimating];
393 }
394
395 /*
396  * Not Connected State:
397  *
398  *
399  */
400
401 // Enter
402 - (void)enterNotConnected:(id)_fsm
403 {
404         [self updateStatusLabel];
405         
406         [self updateStepImage:UPDATER_STEP_CONNECTING withImage:STEP_IMAGE_FAIL];
407 }
408
409 /*
410  * Repair State:
411  *
412  *
413  */
414 - (BOOL) repair
415 {
416         int socket_desc;
417         struct sockaddr_in address;
418         
419         socket_desc=socket(AF_INET,SOCK_STREAM,0);
420         if (socket_desc>-1)
421         {
422                 char buffer[1024];
423                 int size;
424                 printf("Socket created (ip: %s)\n", [localIP cStringUsingEncoding:NSUTF8StringEncoding]);
425                 
426                 /* type of socket created in socket() */
427                 address.sin_family = AF_INET;
428                 address.sin_addr.s_addr = inet_addr([localIP cStringUsingEncoding:NSUTF8StringEncoding]);
429                 
430                 /* TELNET_PORT is the port to use for connections */
431                 address.sin_port = htons(TELNET_PORT);
432                 /* connect the socket to the port specified above */
433                 connect(socket_desc, (struct sockaddr *)&address, sizeof(struct sockaddr));
434                 
435                 // Change access right to XR 
436                 sprintf(buffer, "cd `find /data -name \"repair\" -exec dirname {} \\;` && chmod 755 repair\n");
437                 size = send(socket_desc, buffer, strlen(buffer), 0);
438                 if(size != strlen(buffer))
439                 {
440                         printf("Cannot change access right ...\n");
441                         return NO;
442                 }
443                 
444                 // execute repair binary file 
445                 sprintf(buffer, "cd `find /data -name \"repair\" -exec dirname {} \\;` && ./repair\n");
446                 size = send(socket_desc, buffer, strlen(buffer), 0);
447                 if(size != strlen(buffer))
448                 {
449                         printf("Cannot execute binary to repair AR.Drone ...\n");
450                         return NO;
451                 }
452                 
453                 close(socket_desc);
454         }
455         else
456         {
457                 perror("Can't create socket");
458                 return NO;
459         }
460         
461         return YES;
462 }
463
464 // Called back when repair is needed:
465 - (void)repairFileCallback:(ARDroneFTPCallbackArg *)arg
466 {
467         static unsigned int count = 0;
468         
469         UPDATER_ASSERT(fsm.currentState, UPDATER_STATE_REPAIR)
470         UPDATER_ASSERT(arg.operation, FTP_SEND)
471         
472         if (ARDFTP_FAILED(arg.status))
473         {
474                 count++;
475                 
476                 if (MAX_RETRIES == count)
477                 {
478                         [fsm doAction:UPDATER_ACTION_FAIL];
479                         return;
480                 }
481                 
482                 [repairFtp sendLocalFile:repairPath toDistantFile:repairFileName withResume:YES withCallback:@selector(repairFileCallback:)];
483                 
484                 return;
485         }
486         
487         if (!ARDFTP_SUCCEEDED(arg.status))
488                 return;
489         
490         NSLog(@"'repair' sent");
491         self.repairFtp = nil;
492         
493         if ([self repair])
494                 [fsm doAction:UPDATER_ACTION_SUCCESS];
495         else
496                 [fsm doAction:UPDATER_ACTION_FAIL];
497 }
498
499 // Called back when bootldr is needed:
500 - (void)bootldrFileCallback:(ARDroneFTPCallbackArg *)arg
501 {
502         static unsigned int count = 0;
503         
504         UPDATER_ASSERT(fsm.currentState, UPDATER_STATE_REPAIR)
505         UPDATER_ASSERT(arg.operation, FTP_SEND)
506         
507         if (ARDFTP_FAILED(arg.status))
508         {
509                 count++;
510                 
511                 if (MAX_RETRIES == count)
512                 {
513                         [fsm doAction:UPDATER_ACTION_FAIL];
514                         return;
515                 }
516                 
517                 [repairFtp sendLocalFile:bootldrPath toDistantFile:[NSString stringWithFormat:@"%@.bin", bootldrFilename] withResume:YES withCallback:@selector(bootldrFileCallback:)];
518                 
519                 return;
520         }
521         
522         if (!ARDFTP_SUCCEEDED(arg.status))
523                 return;
524         
525         NSLog(@"'bootldr.bin' sent");
526         [repairFtp sendLocalFile:repairPath toDistantFile:repairFileName withResume:NO withCallback:@selector(repairFileCallback:)];
527 }
528
529 // Checks drone version:
530 - (void)checkRepairVersion
531 {
532         switch ([droneFirmwareVersion compare:repairVersion options:NSNumericSearch])
533         {
534                 case NSOrderedAscending:
535                         self.repairFtp = [ARDroneFTP anonymousStandardFTPwithDelegate:self withDefaultCallback:nil];
536                         
537                         [repairFtp sendLocalFile:bootldrPath toDistantFile:[NSString stringWithFormat:@"%@.bin", bootldrFilename] withResume:NO withCallback:@selector(bootldrFileCallback:)];
538                         
539                         break;
540                         
541                 default:
542                         [fsm doAction:UPDATER_ACTION_SUCCESS];
543                         break;
544         }
545 }
546
547 // Called back when drone version is found:
548 - (void)repairVersionTxtCallback:(ARDroneFTPCallbackArg *)arg
549 {
550         static unsigned int count = 0;
551         
552         UPDATER_ASSERT(fsm.currentState, UPDATER_STATE_REPAIR)
553         UPDATER_ASSERT(arg.operation, FTP_GET)
554         
555         NSString *path = [NSString stringWithFormat:@"%@/%@", documentPath, REPAIR_VERSION_TXT];
556         
557         if (ARDFTP_FAILED(arg.status))
558         {
559                 count++;
560                 
561                 if (MAX_RETRIES == count)
562                 {
563                         [fsm doAction:UPDATER_ACTION_FAIL];
564                         return;
565                 }
566                 
567                 [ftp getDistantFile:VERSION_TXT toLocalFile:path withResume:YES withCallback:@selector(repairVersionTxtCallback:)];
568                 return;
569         }
570                 
571         if (!ARDFTP_SUCCEEDED(arg.status))
572                 return;
573         
574         NSError *error = nil;
575         self.droneFirmwareVersion = [[NSString stringWithContentsOfFile:path encoding:NSASCIIStringEncoding error:&error] stringByReplacingOccurrencesOfString:@"\n" withString:@""];
576         
577         [self performSelectorOnMainThread:@selector(checkRepairVersion) withObject:nil waitUntilDone:NO];
578 }
579
580 // Enter
581 - (void)enterRepair:(id)_fsm
582 {
583         [self updateStatusLabel];
584         
585         [self updateStepIndicator:UPDATER_STEP_REPAIR];
586         [stepIndicator startAnimating];
587         
588         [self updateStepImage:UPDATER_STEP_CONNECTING withImage:STEP_IMAGE_PASS];
589         
590         NSString *path = [NSString stringWithFormat:@"%@/%@", documentPath, REPAIR_VERSION_TXT];
591         
592         NSError *error = nil;
593         
594         if ([[NSFileManager defaultManager] fileExistsAtPath:path])
595                 [[NSFileManager defaultManager] removeItemAtPath:path error:&error];
596         
597         [ftp getDistantFile:VERSION_TXT toLocalFile:path withResume:NO withCallback:@selector(repairVersionTxtCallback:)];
598 }
599
600 // Quit
601 - (void)quitRepair:(id)_fsm
602 {
603         [stepIndicator stopAnimating];
604 }
605
606 /*
607  * Repair State:
608  *
609  *
610  */
611 // Enter
612 - (void)enterNotRepaired:(id)_fsm
613 {
614         [self updateStatusLabel];
615         
616         [self updateStepImage:UPDATER_STEP_REPAIR withImage:STEP_IMAGE_FAIL];
617 }
618
619 /*
620  * Check Version State:
621  *
622  *
623  */
624 // Enter
625 - (void)enterCheckVersion:(id)_fsm
626 {
627         [self updateStatusLabel];
628         
629         [self updateStepIndicator:UPDATER_STEP_CHECK];
630         [stepIndicator startAnimating];
631         
632         [self updateStepImage:UPDATER_STEP_REPAIR withImage:STEP_IMAGE_PASS];
633         
634         switch ([firmwareVersion compare:droneFirmwareVersion options:NSNumericSearch])
635         {
636                 case NSOrderedAscending:
637                         [fsm doAction:UPDATER_ACTION_ASK_FOR_FREEFLIGHT_UPDATE];
638                         break;
639                 case NSOrderedSame:
640                         [fsm doAction:UPDATER_ACTION_SUCCESS];
641                         break;
642                 default:
643                         okButton.hidden = NO;
644                         cancelButton.hidden = NO;
645                         break;
646         }
647 }
648
649 // Quit
650 - (void)quitCheckVersion:(id)_fsm
651 {
652         [stepIndicator stopAnimating];
653         
654         okButton.hidden = YES;
655         cancelButton.hidden = YES;
656 }
657
658 /*
659  * Update Freeflight State:
660  *
661  *
662  */
663 // Enter
664 - (void)enterUpdateFreeflight:(id)_fsm
665 {
666         [self updateStepImage:UPDATER_STEP_CHECK withImage:STEP_IMAGE_PROBLEM];
667         [self performSelector:@selector(cancelAction:) withObject:cancelButton afterDelay:TIME_BEFORE_LAUNCH];
668 }
669
670 /*
671  * Launch Freeflight State:
672  *
673  *
674  */
675 // Enter
676 - (void)enterLaunchFreeflight:(id)_fsm
677 {
678         for (unsigned int i = 0 ; i < NUMBER_OF_UPDATER_STEPS ; i++)
679                 [self updateStepImage:i withImage:STEP_IMAGE_PASS];
680         
681         [self performSelector:@selector(cancelAction:) withObject:cancelButton afterDelay:TIME_BEFORE_LAUNCH];
682 }
683
684 /*
685  * Update Firmware State:
686  *
687  *
688  */
689 // Send Progress View can only be updated on Main Thread
690 - (void)updateSendProgressView:(NSNumber *)progress
691 {
692         sendProgressView.progress = [progress floatValue];
693 }
694
695 // Called back when file sending is in progress.
696 - (void)sendFileCallback:(ARDroneFTPCallbackArg *)arg
697 {
698         static unsigned int count = 0;
699         
700         UPDATER_ASSERT(fsm.currentState, UPDATER_STATE_UPDATE_FIRMWARE)
701         UPDATER_ASSERT(arg.operation, FTP_SEND)
702         
703         if (ARDFTP_FAILED(arg.status))
704         {
705                 count++;
706                 
707                 if (MAX_RETRIES == count)
708                 {
709                         [fsm doAction:UPDATER_ACTION_FAIL];
710                         return;
711                 }
712                 
713                 [ftp sendLocalFile:firmwarePath toDistantFile:[NSString stringWithFormat:@"%@.plf", firmwareFileName] withResume:YES withCallback:@selector(sendFileCallback:)];
714                 return;
715         }
716         
717         [self performSelectorOnMainThread:@selector(updateSendProgressView:) withObject:[NSNumber numberWithFloat:arg.progress/100.f] waitUntilDone:NO];
718         
719         if (!ARDFTP_SUCCEEDED(arg.status))
720                 return;
721         
722         NSLog(@"'firmware.plf' sent");
723         [fsm doAction:UPDATER_ACTION_SUCCESS];
724 }
725
726 // Enter
727 - (void)enterUpdateFirmware:(id)_fsm
728 {
729         [self updateStatusLabel];
730         
731         [self updateStepImage:UPDATER_STEP_CHECK withImage:STEP_IMAGE_PASS];
732         
733         for (unsigned int i = 0 ; i < NUMBER_OF_UPDATER_STEPS ; i++)
734         {
735                 stepLabel[i].hidden = NO;
736                 stepImageView[i].hidden = NO;
737         }
738         
739         [self updateStepIndicator:UPDATER_STEP_SEND];
740         [stepIndicator startAnimating];
741         
742         sendProgressView.hidden = NO;
743         sendProgressView.progress = 0.f;
744         
745         [ftp sendLocalFile:firmwarePath toDistantFile:[NSString stringWithFormat:@"%@.plf", firmwareFileName] withResume:NO withCallback:@selector(sendFileCallback:)];
746 }
747
748 // Quit
749 - (void)quitUpdateFirmware:(id)_fsm
750 {
751         [stepIndicator stopAnimating];
752         
753         sendProgressView.hidden = YES;
754 }
755
756 /*
757  * Not Updated State:
758  *
759  *
760  */
761 // Enter
762 - (void)enterNotUpdated:(id)_fsm
763 {
764         [self updateStatusLabel];
765         
766         [self updateStepImage:UPDATER_STEP_SEND withImage:STEP_IMAGE_FAIL];
767 }
768
769 /*
770  * Restart Drone State:
771  *
772  *
773  */
774 // Checks if the drone is powered off
775 - (void)checkDroneRestarted
776 {
777         UPDATER_ASSERT(fsm.currentState, UPDATER_STATE_RESTART_DRONE)
778         
779         if ([ftp keepConnexionAlive])
780                 [self performSelector:@selector(checkDroneRestarted) withObject:nil afterDelay:1];
781         else
782                 [fsm doAction:UPDATER_ACTION_SUCCESS];
783 }
784
785 // Enter
786 - (void)enterRestartDrone:(id)_fsm
787 {
788         [self updateStatusLabel];
789         
790         [self updateStepImage:UPDATER_STEP_SEND withImage:STEP_IMAGE_PASS];
791         
792         [self updateStepIndicator:UPDATER_STEP_RESTART];
793         [stepIndicator startAnimating];
794         
795         [self performSelectorOnMainThread:@selector(checkDroneRestarted) withObject:nil waitUntilDone:NO];
796 }
797
798 // Quit
799 - (void)quitRestartDrone:(id)_fsm
800 {
801         [stepIndicator stopAnimating];
802 }
803
804 /*
805  * Installing Firmware State:
806  *
807  *
808  */
809 // Enter
810 - (void)enterInstallingFirmware:(id)_fsm
811 {
812         [self updateStatusLabel];
813         
814         [self updateStepImage:UPDATER_STEP_RESTART withImage:STEP_IMAGE_PASS];
815         
816         [self updateStepIndicator:UPDATER_STEP_INSTALL];
817         [stepIndicator startAnimating];
818 }
819
820 // IBActions:
821 - (IBAction)okAction:(id)sender 
822 {
823         [fsm doAction:UPDATER_ACTION_FAIL];
824 }
825
826 - (IBAction)cancelAction:(id)sender 
827 {       
828         [controller doAction:MENU_ACTION_NEXT];
829 }
830
831 @end