You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
218 lines
7.4 KiB
218 lines
7.4 KiB
/*
|
|
* Copyright (C) 2016 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#import "ScreenResponseController.h"
|
|
|
|
#include <stdatomic.h>
|
|
|
|
#import "NSArray+Extensions.h"
|
|
#import "UIAlertView+Extensions.h"
|
|
#import "WALTAppDelegate.h"
|
|
#import "WALTClient.h"
|
|
#import "WALTLogger.h"
|
|
|
|
static const NSUInteger kMaxFlashes = 20; // TODO(pquinn): Make this user-configurable.
|
|
static const NSTimeInterval kFlashingInterval = 0.1;
|
|
static const char kWALTScreenTag = 'S';
|
|
|
|
@interface ScreenResponseController ()
|
|
- (void)setFlashTimer;
|
|
- (void)flash:(NSTimer *)timer;
|
|
@end
|
|
|
|
@implementation ScreenResponseController {
|
|
WALTClient *_client;
|
|
WALTLogger *_logger;
|
|
|
|
NSTimer *_flashTimer;
|
|
NSOperationQueue *_readOperations;
|
|
|
|
// Statistics
|
|
NSUInteger _initiatedFlashes;
|
|
NSUInteger _detectedFlashes;
|
|
|
|
_Atomic NSTimeInterval _lastFlashTime;
|
|
NSMutableArray<NSNumber *> *_deltas;
|
|
}
|
|
|
|
- (void)dealloc {
|
|
[_readOperations cancelAllOperations];
|
|
[_flashTimer invalidate];
|
|
}
|
|
|
|
- (void)viewDidLoad {
|
|
[super viewDidLoad];
|
|
|
|
_client = ((WALTAppDelegate *)[UIApplication sharedApplication].delegate).client;
|
|
_logger = [WALTLogger sessionLogger];
|
|
}
|
|
|
|
- (void)viewWillAppear:(BOOL)animated {
|
|
[super viewWillAppear:animated];
|
|
|
|
[_logger appendString:@"SCREENRESPONSE\n"];
|
|
[self reset:nil];
|
|
}
|
|
|
|
- (IBAction)start:(id)sender {
|
|
[self reset:nil];
|
|
|
|
// Clear the screen trigger on the WALT.
|
|
NSError *error = nil;
|
|
if (![_client sendCommand:WALTSendLastScreenCommand error:&error]) {
|
|
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"WALT Connection Error" error:error];
|
|
[alert show];
|
|
return;
|
|
}
|
|
|
|
WALTTrigger trigger = [_client readTriggerWithTimeout:kWALTReadTimeout];
|
|
if (trigger.tag != kWALTScreenTag) {
|
|
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"WALT Response Error"
|
|
message:@"Failed to read last screen trigger."
|
|
delegate:nil
|
|
cancelButtonTitle:@"Dismiss"
|
|
otherButtonTitles:nil];
|
|
[alert show];
|
|
return;
|
|
}
|
|
|
|
// Create a queue for work blocks to read WALT trigger responses.
|
|
_readOperations = [[NSOperationQueue alloc] init];
|
|
_readOperations.maxConcurrentOperationCount = 1;
|
|
|
|
// Start the flash timer and spawn a thread to check for responses.
|
|
[self setFlashTimer];
|
|
}
|
|
|
|
- (void)setFlashTimer {
|
|
_flashTimer = [NSTimer scheduledTimerWithTimeInterval:kFlashingInterval
|
|
target:self
|
|
selector:@selector(flash:)
|
|
userInfo:nil
|
|
repeats:NO];
|
|
}
|
|
|
|
- (IBAction)computeStatistics:(id)sender {
|
|
self.flasherView.hidden = YES;
|
|
self.responseLabel.hidden = NO;
|
|
|
|
NSMutableString *results = [[NSMutableString alloc] init];
|
|
for (NSNumber *delta in _deltas) {
|
|
[results appendFormat:@"%.3f s\n", delta.doubleValue];
|
|
}
|
|
|
|
[results appendFormat:@"Median: %.3f s\n", [_deltas medianValue].doubleValue];
|
|
self.responseLabel.text = results;
|
|
}
|
|
|
|
- (IBAction)reset:(id)sender {
|
|
_initiatedFlashes = 0;
|
|
_detectedFlashes = 0;
|
|
_deltas = [[NSMutableArray<NSNumber *> alloc] init];
|
|
|
|
[_readOperations cancelAllOperations];
|
|
[_flashTimer invalidate];
|
|
|
|
self.flasherView.hidden = NO;
|
|
self.flasherView.backgroundColor = [UIColor whiteColor];
|
|
self.responseLabel.hidden = YES;
|
|
|
|
NSError *error = nil;
|
|
if (![_client syncClocksWithError:&error]) {
|
|
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"WALT Connection Error" error:error];
|
|
[alert show];
|
|
}
|
|
|
|
[_logger appendString:@"RESET\n"];
|
|
}
|
|
|
|
- (void)flash:(NSTimer *)timer {
|
|
if (_initiatedFlashes == 0) {
|
|
// First flash.
|
|
// Turn on brightness change notifications.
|
|
NSError *error = nil;
|
|
if (![_client sendCommand:WALTScreenOnCommand error:&error]) {
|
|
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"WALT Connection Error" error:error];
|
|
[alert show];
|
|
return;
|
|
}
|
|
|
|
NSData *response = [_client readResponseWithTimeout:kWALTReadTimeout];
|
|
if (![_client checkResponse:response forCommand:WALTScreenOnCommand]) {
|
|
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"WALT Response Error"
|
|
message:@"Failed to start screen probe."
|
|
delegate:nil
|
|
cancelButtonTitle:@"Dismiss"
|
|
otherButtonTitles:nil];
|
|
[alert show];
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (_initiatedFlashes != kMaxFlashes) {
|
|
// Swap the background colour and record the time.
|
|
self.flasherView.backgroundColor =
|
|
([self.flasherView.backgroundColor isEqual:[UIColor blackColor]] ?
|
|
[UIColor whiteColor] :
|
|
[UIColor blackColor]);
|
|
atomic_store(&_lastFlashTime, _client.currentTime);
|
|
++_initiatedFlashes;
|
|
|
|
// Queue an operation to read the trigger.
|
|
[_readOperations addOperationWithBlock:^{
|
|
// NB: The timeout here should be much greater than the expected screen response time.
|
|
WALTTrigger response = [_client readTriggerWithTimeout:kWALTReadTimeout];
|
|
if (response.tag == kWALTScreenTag) {
|
|
++_detectedFlashes;
|
|
|
|
// Record the delta between the trigger and the flash time.
|
|
NSTimeInterval lastFlash = atomic_load(&_lastFlashTime);
|
|
NSTimeInterval delta = response.t - lastFlash;
|
|
if (delta > 0) { // Sanity check
|
|
[_deltas addObject:[NSNumber numberWithDouble:delta]];
|
|
[_logger appendFormat:@"O\t%f\n", delta];
|
|
} else {
|
|
[_logger appendFormat:@"X\tbogus delta\t%f\t%f\n", lastFlash, response.t];
|
|
}
|
|
|
|
// Queue up another flash.
|
|
[self performSelectorOnMainThread:@selector(setFlashTimer)
|
|
withObject:nil
|
|
waitUntilDone:NO];
|
|
} else {
|
|
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"WALT Response Error"
|
|
message:@"Failed to read screen probe."
|
|
delegate:nil
|
|
cancelButtonTitle:@"Dismiss"
|
|
otherButtonTitles:nil];
|
|
[alert show];
|
|
}
|
|
}];
|
|
}
|
|
|
|
if (_initiatedFlashes == kMaxFlashes) {
|
|
// Queue an operation (after the read trigger above) to turn off brightness notifications.
|
|
[_readOperations addOperationWithBlock:^{
|
|
[_client sendCommand:WALTScreenOffCommand error:nil];
|
|
[_client readResponseWithTimeout:kWALTReadTimeout];
|
|
[self performSelectorOnMainThread:@selector(computeStatistics:)
|
|
withObject:nil
|
|
waitUntilDone:NO];
|
|
}];
|
|
}
|
|
}
|
|
@end
|