Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,8 @@ public class CEV1RangeOfMotionStepViewController: ORK1RangeOfMotionStepViewContr
result.flexed = lowestAngle
result.extended = highestAngle
}
result.fileResult = self.fileResult

stepResult?.results = (self.addedResults ?? []) + [result]
stepResult?.results = (stepResult?.results ?? []) + [result]

return stepResult
}
Expand Down
6 changes: 6 additions & 0 deletions ORK1Kit/ORK1Kit/ActiveTasks/ORK1ActiveStep.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,12 @@ The default value of this property is `NO`.
*/
@property (nonatomic, copy, nullable) NSArray<ORK1RecorderConfiguration *> *recorderConfigurations;

/**
A Boolean value that indicates whether the step must wait for all recorders to complete, returning a result
or error before allowing the step to proceed.
*/
@property (nonatomic) BOOL recordersMustCompleteBeforeAdvancingStep;

@end

NS_ASSUME_NONNULL_END
6 changes: 5 additions & 1 deletion ORK1Kit/ORK1Kit/ActiveTasks/ORK1ActiveStep.m
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ - (instancetype)copyWithZone:(NSZone *)zone {
step.spokenInstruction = self.spokenInstruction;
step.finishedSpokenInstruction = self.finishedSpokenInstruction;
step.recorderConfigurations = [self.recorderConfigurations copy];
step.recordersMustCompleteBeforeAdvancingStep = self.recordersMustCompleteBeforeAdvancingStep;
step.image = self.image;
step.imageAltText = self.imageAltText;
return step;
Expand All @@ -126,6 +127,7 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder {
ORK1_DECODE_IMAGE(aDecoder, image);
ORK1_DECODE_OBJ_CLASS(aDecoder, imageAltText, NSString);
ORK1_DECODE_OBJ_ARRAY(aDecoder, recorderConfigurations, ORK1RecorderConfiguration);
ORK1_DECODE_BOOL(aDecoder, recordersMustCompleteBeforeAdvancingStep);
}
return self;
}
Expand All @@ -148,6 +150,7 @@ - (void)encodeWithCoder:(NSCoder *)aCoder {
ORK1_ENCODE_OBJ(aCoder, spokenInstruction);
ORK1_ENCODE_OBJ(aCoder, finishedSpokenInstruction);
ORK1_ENCODE_OBJ(aCoder, recorderConfigurations);
ORK1_ENCODE_BOOL(aCoder, recordersMustCompleteBeforeAdvancingStep);
}

- (BOOL)isEqual:(id)object {
Expand All @@ -170,7 +173,8 @@ - (BOOL)isEqual:(id)object {
(self.shouldVibrateOnStart == castObject.shouldVibrateOnStart) &&
(self.shouldVibrateOnFinish == castObject.shouldVibrateOnFinish) &&
(self.shouldContinueOnFinish == castObject.shouldContinueOnFinish) &&
(self.shouldUseNextAsSkipButton == castObject.shouldUseNextAsSkipButton));
(self.shouldUseNextAsSkipButton == castObject.shouldUseNextAsSkipButton) &&
(self.recordersMustCompleteBeforeAdvancingStep == castObject.recordersMustCompleteBeforeAdvancingStep));
}

- (NSSet<HKObjectType *> *)requestedHealthKitTypesForReading {
Expand Down
6 changes: 6 additions & 0 deletions ORK1Kit/ORK1Kit/ActiveTasks/ORK1ActiveStepViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,12 @@ ORK1_CLASS_AVAILABLE
*/
- (void)finish;

/**
Triggers the step to advance once the recorders have completed;
- Parameter stepDidFinishOnly: do not explicity call goForward on self when recorders are done (for steps that goForward without calling finish)
*/
- (void)goForwardOnceRecordersHaveCompleted:(BOOL)stepDidFinishOnly;

/// @name Recorder life cycle

/**
Expand Down
43 changes: 42 additions & 1 deletion ORK1Kit/ORK1Kit/ActiveTasks/ORK1ActiveStepViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ @interface ORK1ActiveStepViewController () {

NSArray *_recorderResults;

NSMutableSet *_recordersRunning; // only accessed on the main queue
NSInteger _recordersCheckLoopCount; // only accessed on the main queue

SystemSoundID _alertSound;
NSURL *_alertSoundURL;
BOOL _hasSpokenHalfwayCountdown;
Expand All @@ -76,6 +79,8 @@ - (instancetype)initWithStep:(ORK1Step *)step {
self = [super initWithStep:step];
if (self) {
_recorderResults = [NSArray new];
_recordersRunning = [NSMutableSet new];
_recordersCheckLoopCount = 0;

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
Expand Down Expand Up @@ -292,6 +297,9 @@ - (void)startRecorders {
for (ORK1Recorder *recorder in self.recorders) {
[recorder viewController:self willStartStepWithView:self.customViewContainer];
[recorder start];
dispatch_async(dispatch_get_main_queue(), ^{
[_recordersRunning addObject:recorder];
});
}
}

Expand Down Expand Up @@ -381,12 +389,39 @@ - (void)finish {
}
if (!self.activeStep.startsFinished) {
if (self.activeStep.shouldContinueOnFinish) {
[self goForward];
if (self.activeStep.recordersMustCompleteBeforeAdvancingStep) {
[self goForwardOnceRecordersHaveCompleted:NO];
} else {
[self goForward];
}
}
}
[self stepDidFinish];
}

- (void)goForwardOnceRecordersHaveCompleted:(BOOL)stepDidFinishOnly {
dispatch_async(dispatch_get_main_queue(), ^{
if ([_recordersRunning count] == 0) {
if (!stepDidFinishOnly) {
[self goForward];
}
[self stepDidFinish];
} else if (_recordersCheckLoopCount > 10) {
NSLog(@"Recorders are still not complete after 10 wait cycles, data may be lost in final result.");
if (!stepDidFinishOnly) {
[self goForward];
}
[self stepDidFinish];
} else {
_recordersCheckLoopCount += 1;
NSLog(@"Waiting for [%lu] recorders to complete...", [_recordersRunning count]);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self goForwardOnceRecordersHaveCompleted:stepDidFinishOnly];
});
}
});
}

- (void)dealloc {
AudioServicesDisposeSystemSoundID(_alertSound);
NSNotificationCenter *nfc = [NSNotificationCenter defaultCenter];
Expand Down Expand Up @@ -470,6 +505,9 @@ - (void)stepDidFinish {

- (void)recorder:(ORK1Recorder *)recorder didCompleteWithResult:(ORK1Result *)result {
_recorderResults = [_recorderResults arrayByAddingObject:result];
dispatch_async(dispatch_get_main_queue(), ^{
[_recordersRunning removeObject:recorder];
});
[self notifyDelegateOnResultChange];
}

Expand All @@ -488,6 +526,9 @@ - (void)recorder:(ORK1Recorder *)recorder didFailWithError:(NSError *)error {
self.outputDirectory == nil) {
[strongDelegate stepViewControllerDidFail:self withError:error];
}
dispatch_async(dispatch_get_main_queue(), ^{
[_recordersRunning removeObject:recorder];
});
}
}

Expand Down
1 change: 1 addition & 0 deletions ORK1Kit/ORK1Kit/ActiveTasks/ORK1RangeOfMotionStep.m
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ - (instancetype)initWithIdentifier:(NSString *)identifier limbOption:(ORK1Predef
self.shouldContinueOnFinish = YES;
self.shouldStartTimerAutomatically = YES;
self.limbOption = limbOption;
self.recordersMustCompleteBeforeAdvancingStep = YES;
}
return self;
}
Expand Down
20 changes: 6 additions & 14 deletions ORK1Kit/ORK1Kit/ActiveTasks/ORK1RangeOfMotionStepViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -175,12 +175,6 @@ - (void)deviceMotionRecorderDidUpdateWithMotion:(CMDeviceMotion *)motion {
_lastAngle = angle;
}

#pragma mark - ORK1RecorderDelegate

- (void)recorder:(ORK1Recorder *)recorder didCompleteWithResult:(ORK1Result *)result {
self.fileResult = (ORK1FileResult *)result;
}

/*
When the device is in Portrait mode, we need to get the attitude's pitch
to determine the device's angle. attitude.pitch doesn't return all
Expand Down Expand Up @@ -209,14 +203,12 @@ - (double)getDeviceAngleInDegreesFromAttitude:(CMAttitude *)attitude {

- (ORK1Result *)result {
ORK1StepResult *stepResult = [super result];

ORK1RangeOfMotionResult *result = [[ORK1RangeOfMotionResult alloc] initWithIdentifier:self.step.identifier];
result.flexed = _flexedAngle;
result.extended = result.flexed - _rangeOfMotionAngle;
result.fileResult = _fileResult;

stepResult.results = [self.addedResults arrayByAddingObject:result] ? : @[result];

NSMutableArray *results = [NSMutableArray arrayWithArray:stepResult.results];
ORK1RangeOfMotionResult *rangeOfMotionResult = [[ORK1RangeOfMotionResult alloc] initWithIdentifier:self.step.identifier];
rangeOfMotionResult.flexed = _flexedAngle;
rangeOfMotionResult.extended = rangeOfMotionResult.flexed - _rangeOfMotionAngle;
[results addObject:rangeOfMotionResult];
stepResult.results = [results copy];
return stepResult;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,14 @@ @implementation ORK1ShoulderRangeOfMotionStepViewController
- (ORK1Result *)result {
ORK1StepResult *stepResult = [super result];

ORK1RangeOfMotionResult *result = [[ORK1RangeOfMotionResult alloc] initWithIdentifier:self.step.identifier];
result.flexed = 90.0 - _flexedAngle;
result.extended = result.flexed + _rangeOfMotionAngle;
NSMutableArray *results = [NSMutableArray arrayWithArray:stepResult.results];

stepResult.results = [self.addedResults arrayByAddingObject:result] ? : @[result];
ORK1RangeOfMotionResult *rangeOfMotionResult = [[ORK1RangeOfMotionResult alloc] initWithIdentifier:self.step.identifier];
rangeOfMotionResult.flexed = 90.0 - _flexedAngle;
rangeOfMotionResult.extended = rangeOfMotionResult.flexed + _rangeOfMotionAngle;
[results addObject:rangeOfMotionResult];

stepResult.results = [results copy];

return stepResult;
}
Expand Down
11 changes: 8 additions & 3 deletions ORK1Kit/ORK1Kit/ActiveTasks/ORK1TimedWalkStepViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,15 @@ - (void)viewDidLoad {
self.timerUpdateInterval = 0.1f;
}

- (void)finish {
// The ORK1TimedWalkStepViewController expects the Next button to trigger goForward and doesn't call finish explicitly
- (void)goForward {
[super stopRecorders];
[super goForwardOnceRecordersHaveCompleted:YES];
}

- (void)stepDidFinish {
[super finish];

[self goForward];
[super goForward];
}

- (void)countDownTimerFired:(ORK1ActiveStepTimer *)timer finished:(BOOL)finished {
Expand Down
5 changes: 0 additions & 5 deletions ORK1Kit/ORK1Kit/Common/ORK1Result.h
Original file line number Diff line number Diff line change
Expand Up @@ -327,11 +327,6 @@ ORK1_CLASS_AVAILABLE
*/
@property (nonatomic, assign) double extended;

/**
The recorder output - usually the device motion recorder.
*/
@property (nonatomic, strong) ORK1FileResult *fileResult;

@end


Expand Down
12 changes: 2 additions & 10 deletions ORK1Kit/ORK1Kit/Common/ORK1Result.m
Original file line number Diff line number Diff line change
Expand Up @@ -258,15 +258,13 @@ - (void)encodeWithCoder:(NSCoder *)aCoder {
[super encodeWithCoder:aCoder];
ORK1_ENCODE_DOUBLE(aCoder, flexed);
ORK1_ENCODE_DOUBLE(aCoder, extended);
ORK1_ENCODE_OBJ(aCoder, fileResult);
}

- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
ORK1_DECODE_DOUBLE(aDecoder, flexed);
ORK1_DECODE_DOUBLE(aDecoder, extended);
ORK1_DECODE_OBJ_CLASS(aDecoder, fileResult, ORK1FileResult);
}
return self;
}
Expand All @@ -280,24 +278,18 @@ - (BOOL)isEqual:(id)object {
__typeof(self) castObject = object;
return (isParentSame &&
self.flexed == castObject.flexed &&
self.extended == castObject.extended &&
ORK1EqualObjects(self.fileResult, castObject.fileResult));
}

- (NSUInteger)hash {
return super.hash ^ self.fileResult.hash;
self.extended == castObject.extended);
}

- (instancetype)copyWithZone:(NSZone *)zone {
ORK1RangeOfMotionResult *result = [super copyWithZone:zone];
result.flexed = self.flexed;
result.extended = self.extended;
result.fileResult = self.fileResult;
return result;
}

- (NSString *)descriptionWithNumberOfPaddingSpaces:(NSUInteger)numberOfPaddingSpaces {
return [NSString stringWithFormat:@"<%@: flexion: %f; extension: %f; fileResult: %@>", self.class.description, self.flexed, self.extended, self.fileResult.description];
return [NSString stringWithFormat:@"<%@: flexion: %f; extension: %f>", self.class.description, self.flexed, self.extended];
}

@end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,8 @@ public class CEVRangeOfMotionStepViewController: ORKRangeOfMotionStepViewControl
result.flexed = lowestAngle
result.extended = highestAngle
}
result.fileResult = self.fileResult

stepResult?.results = (self.addedResults ?? []) + [result]
stepResult?.results = (stepResult?.results ?? []) + [result]

return stepResult
}
Expand Down
6 changes: 6 additions & 0 deletions ResearchKit/ActiveTasks/ORKActiveStep.h
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,12 @@ The default value of this property is `NO`.
*/
@property (nonatomic, assign) BOOL isPractice;

/**
A Boolean value that indicates whether the step must wait for all recorders to complete, returning a result
or error before allowing the step to proceed.
*/
@property (nonatomic) BOOL recordersMustCompleteBeforeAdvancingStep;

@end

NS_ASSUME_NONNULL_END
6 changes: 5 additions & 1 deletion ResearchKit/ActiveTasks/ORKActiveStep.m
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ - (instancetype)copyWithZone:(NSZone *)zone {
step.spokenInstruction = self.spokenInstruction;
step.finishedSpokenInstruction = self.finishedSpokenInstruction;
step.recorderConfigurations = [self.recorderConfigurations copy];
step.recordersMustCompleteBeforeAdvancingStep = self.recordersMustCompleteBeforeAdvancingStep;
step.image = self.image;
step.imageAltText = self.imageAltText;
step.isPractice = self.isPractice;
Expand All @@ -128,6 +129,7 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder {
ORK_DECODE_OBJ_CLASS(aDecoder, imageAltText, NSString);
ORK_DECODE_OBJ_ARRAY(aDecoder, recorderConfigurations, ORKRecorderConfiguration);
ORK_DECODE_BOOL(aDecoder, isPractice);
ORK_DECODE_BOOL(aDecoder, recordersMustCompleteBeforeAdvancingStep);
}
return self;
}
Expand All @@ -151,6 +153,7 @@ - (void)encodeWithCoder:(NSCoder *)aCoder {
ORK_ENCODE_OBJ(aCoder, finishedSpokenInstruction);
ORK_ENCODE_OBJ(aCoder, recorderConfigurations);
ORK_ENCODE_BOOL(aCoder, isPractice);
ORK_ENCODE_BOOL(aCoder, recordersMustCompleteBeforeAdvancingStep);
}

- (BOOL)isEqual:(id)object {
Expand All @@ -174,7 +177,8 @@ - (BOOL)isEqual:(id)object {
(self.shouldVibrateOnFinish == castObject.shouldVibrateOnFinish) &&
(self.shouldContinueOnFinish == castObject.shouldContinueOnFinish) &&
(self.shouldUseNextAsSkipButton == castObject.shouldUseNextAsSkipButton) &&
(self.isPractice == castObject.isPractice));
(self.isPractice == castObject.isPractice) &&
(self.recordersMustCompleteBeforeAdvancingStep == castObject.recordersMustCompleteBeforeAdvancingStep));
}

- (NSSet<HKObjectType *> *)requestedHealthKitTypesForReading {
Expand Down
6 changes: 6 additions & 0 deletions ResearchKit/ActiveTasks/ORKActiveStepViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,12 @@ ORK_CLASS_AVAILABLE
*/
- (void)finish;

/**
Triggers the step to advance once the recorders have completed;
- Parameter stepDidFinishOnly: do not explicity call goForward on self when recorders are done (for steps that goForward without calling finish)
*/
- (void)goForwardOnceRecordersHaveCompleted:(BOOL)stepDidFinishOnly;

/// @name Recorder life cycle

/**
Expand Down
Loading