From 58a7e8165e87d445e0ce99701b2ffb964da3d510 Mon Sep 17 00:00:00 2001 From: Antoine Rosset Date: Mon, 20 May 2013 20:32:23 +0200 Subject: [PATCH] MD-234 --- Binaries/English.lproj/Send.xib | 310 +++++++++++++++++++++++++------- DefaultsOsiriX.m | 1 + DicomDatabase.mm | 19 +- SendController.h | 1 - SendController.m | 170 ++++++++++++++---- 5 files changed, 389 insertions(+), 112 deletions(-) diff --git a/Binaries/English.lproj/Send.xib b/Binaries/English.lproj/Send.xib index da9f68da6b..2cb2255970 100644 --- a/Binaries/English.lproj/Send.xib +++ b/Binaries/English.lproj/Send.xib @@ -1,29 +1,29 @@ - 1060 - 11E53 - 2182 - 1138.47 - 569.00 + 1070 + 12E52 + 3084 + 1187.39 + 626.00 com.apple.InterfaceBuilder.CocoaPlugin - 2182 + 3084 - NSUserDefaultsController - NSPopUpButton NSButton - NSMenu - NSTextFieldCell NSButtonCell - NSMenuItem - NSMatrix NSCustomObject + NSMatrix + NSMenu + NSMenuItem + NSPopUpButton + NSPopUpButtonCell + NSTextField + NSTextFieldCell + NSUserDefaultsController NSView NSWindowTemplate - NSTextField - NSPopUpButtonCell com.apple.InterfaceBuilder.CocoaPlugin @@ -45,7 +45,7 @@ 1 2 - {{329, 257}, {531, 214}} + {{329, 257}, {531, 292}} 1886912512 DICOM Send NSWindow @@ -55,21 +55,22 @@ {213, 107} - + 256 268 - {{79, 40}, {343, 58}} + {{178, 85}, {343, 58}} - + YES + NO 3 1 - -2080244224 + -2080374784 0 All images @@ -78,7 +79,7 @@ 1044 - 1211912703 + 1211912448 0 NSRadioButton @@ -91,13 +92,13 @@ 25 - 67239424 + 67108864 0 Only key images 1 - 1211912703 + 1211912448 0 @@ -106,13 +107,13 @@ 25 - 67239424 + 67108864 0 Only Secondary Captures (SC) 2 - 1211912703 + 1211912448 0 @@ -126,11 +127,11 @@ 1143472128 NSActionCell - 67239424 + 67108864 0 Radio - 1211912703 + 1211912448 0 549453824 @@ -197,19 +198,19 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA 256 - {{420, 57}, {97, 32}} + {{420, 13}, {97, 32}} - + 1 YES - 67239424 + 67108864 134217728 Send 1 - -2038284033 + -2038284288 1 @@ -217,21 +218,22 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA 200 25 + NO 256 - {{323, 57}, {97, 32}} + {{323, 13}, {97, 32}} YES - 67239424 + 67108864 134217728 Cancel - -2038284033 + -2038284288 1 @@ -239,16 +241,17 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA 200 25 + NO 256 - {{6, 187}, {161, 17}} + {{6, 265}, {161, 17}} YES - 67239424 + 67108864 71303168 Number of files: @@ -264,16 +267,17 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA + NO 256 - {{175, 187}, {339, 17}} + {{175, 265}, {339, 17}} YES - 67239424 + 67108864 4194304 @@ -281,16 +285,17 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA + NO 256 - {{6, 157}, {161, 17}} + {{6, 235}, {161, 17}} YES - 67239424 + 67108864 71303168 Select destination: @@ -298,16 +303,17 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA + NO 256 - {{50, 107}, {117, 17}} + {{50, 185}, {117, 17}} YES - 67239424 + 67108864 71303168 Transfer Syntax: @@ -315,16 +321,17 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA + NO 256 - {{-3, 81}, {70, 17}} + {{-3, 126}, {170, 17}} YES - 67239424 + 67108864 71303168 Send: @@ -332,21 +339,22 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA + NO 256 - {{169, 101}, {345, 26}} + {{169, 179}, {345, 26}} - + 6 YES - -2076049856 + -2076180416 2048 - 109199615 + 109199360 1 LucidaGrande @@ -551,16 +559,17 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA YES 1 + NO 256 - {{175, 133}, {333, 17}} + {{175, 211}, {333, 17}} YES - 67239424 + 67108864 4194304 129.195.163.200 : 4096 @@ -568,20 +577,22 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA + NO 256 - {{79, 11}, {428, 18}} + {{178, 59}, {428, 18}} + YES - 67239424 + 67108864 0 Include ROIs, Comments, Reports, ... - 1211912703 + 1211912448 2 NSSwitch @@ -591,16 +602,17 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA 200 25 + NO 256 - {{-47, 12}, {114, 17}} + {{6, 60}, {161, 17}} YES - 67239424 + 67108864 71303168 ROIs: @@ -608,20 +620,39 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA + NO + + + + 256 + {{-3, 151}, {170, 21}} + + + YES + + 67108864 + 71303168 + Number of associations: + + + + + + NO 268 - {{169, 151}, {345, 26}} + {{169, 229}, {345, 26}} YES - -2076049856 + -2076180416 2048 - 109199615 + 109199360 129 @@ -673,9 +704,82 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA YES 2 + NO + + + + 268 + {{170, 148}, {344, 26}} + + + _NS:9 + YES + + -2076180416 + 2048 + + _NS:9 + + 109199360 + 129 + + + 400 + 75 + + YES + + OtherViews + + + + Single association + + 2147483647 + + + _popUpItemAction: + 1 + + + + + 2 simultaneous associations + + 1048576 + 2147483647 + + + _popUpItemAction: + 2 + + + + + 4 simultaneous associations + + 1048576 + 2147483647 + + + _popUpItemAction: + 4 + + + + + + -1 + 1 + YES + YES + 2 + + NO - {531, 214} + {531, 292} + {{0, 0}, {2560, 1418}} @@ -837,6 +941,22 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA 93 + + + selectedTag: values.SendControllerConcurrentThreads + + + + + + selectedTag: values.SendControllerConcurrentThreads + selectedTag + values.SendControllerConcurrentThreads + 2 + + + 160 + @@ -873,15 +993,17 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA - - + + + - + + @@ -1208,6 +1330,60 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA + + 152 + + + + + + + + 153 + + + + + 154 + + + + + + + + 155 + + + + + + + + 156 + + + + + + + + + + 157 + + + + + 158 + + + + + 159 + + + @@ -1239,6 +1415,14 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -1271,14 +1455,14 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA - 151 + 160 0 IBCocoaFramework com.apple.InterfaceBuilder.CocoaPlugin.macosx - + com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 diff --git a/DefaultsOsiriX.m b/DefaultsOsiriX.m index 730797157e..603503a11c 100644 --- a/DefaultsOsiriX.m +++ b/DefaultsOsiriX.m @@ -1043,6 +1043,7 @@ + (NSMutableDictionary*) getDefaults [defaultValues setObject:@"30" forKey: @"WebPortalMaximumNumberOfRecentStudies"]; [defaultValues setObject:@"10" forKey: @"WebPortalMaximumNumberOfDaysForRecentStudies"]; [defaultValues setObject:@"2" forKey:@"yearOldDatabaseDisplay"]; + [defaultValues setObject:@"4" forKey:@"SendControllerConcurrentThreads"]; #ifdef MACAPPSTORE [defaultValues setObject:@"1" forKey:@"MACAPPSTORE"]; diff --git a/DicomDatabase.mm b/DicomDatabase.mm index 75b43ae474..49ca845984 100644 --- a/DicomDatabase.mm +++ b/DicomDatabase.mm @@ -1275,21 +1275,14 @@ -(void)_processFilesAtPaths_processChunk:(NSArray*)io { -(void)processFilesAtPaths:(NSArray*)paths intoDirAtPath:(NSString*)destDir mode:(int)mode { + NSThread* thread = [NSThread currentThread]; + NSString* nameFormat = nil; - if (mode == Compress) { - if (paths.count != 1) - nameFormat = NSLocalizedString(@"Compressing %d files...", @"plural"); - else nameFormat = NSLocalizedString(@"Compressing %d file...", @"singular"); - } else { - if (paths.count != 1) - nameFormat = NSLocalizedString(@"Decompressing %d files...", @"plural"); - else nameFormat = NSLocalizedString(@"Decompressing %d file...", @"singular"); - } + if (mode == Compress) + thread.name = [NSString stringWithFormat: @"Compressing %@", N2LocalizedSingularPluralCount( paths.count, NSLocalizedString(@"file", nil), NSLocalizedString(@"files", nil))]; + else + thread.name = [NSString stringWithFormat: @"Decompressing %@", N2LocalizedSingularPluralCount( paths.count, NSLocalizedString(@"file", nil), NSLocalizedString(@"files", nil))]; - NSThread* thread = [NSThread currentThread]; - -// [thread pushLevel]; - thread.name = [NSString stringWithFormat:nameFormat, paths.count]; thread.status = NSLocalizedString(@"Waiting for similar threads to complete...", nil); thread.progress = -1; diff --git a/SendController.h b/SendController.h index 0217249183..134430d706 100644 --- a/SendController.h +++ b/SendController.h @@ -55,7 +55,6 @@ enum SendServerType { osirixServer, offisServer }; BOOL _readyForRelease; BOOL _abort; NSRecursiveLock *_lock; - DCMTKStoreSCU *storeSCU; NSDictionary *_destinationServer; IBOutlet NSPopUpButton *newServerList; diff --git a/SendController.m b/SendController.m index 4d4aa8872a..54d612f4ca 100644 --- a/SendController.m +++ b/SendController.m @@ -31,6 +31,84 @@ static volatile int sendControllerObjects = 0; +@interface DCMTKStoreSCUOperation: NSOperation +{ + NSArray *files; + NSDictionary *server; + NSThread *thread; +} +@property (retain) NSArray *files; +@property (retain) NSDictionary *server; +@property (retain) NSThread *thread; + +- (id) initWithFiles:(NSArray*) a server: (NSDictionary*) s; + +@end + +@implementation DCMTKStoreSCUOperation +@synthesize files, server, thread; + +- (id) initWithFiles:(NSArray*) a server:(NSDictionary*) s +{ + self = [super init]; + + self.files = a; + self.server = s; + + return self; +} + +- (void) showErrorMessage:(NSException*) ne +{ + NSString *message = [NSString stringWithFormat:@"%@\r\r%@\r%@", NSLocalizedString( @"DICOM StoreSCU operation failed.", nil), [ne name], [ne reason]]; + + NSRunCriticalAlertPanel(NSLocalizedString(@"DICOM Send Error",nil), message, NSLocalizedString( @"OK",nil), nil, nil); +} + +- (void) main +{ + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + + if( self.isCancelled) + return; + + self.thread = [NSThread currentThread]; + + DCMTKStoreSCU *storeSCU = [[DCMTKStoreSCU alloc] initWithCallingAET: [NSUserDefaults defaultAETitle] + calledAET: [server objectForKey:@"AETitle"] + hostname: [server objectForKey:@"Address"] + port: [[server objectForKey:@"Port"] intValue] + filesToSend: files + transferSyntax: [[NSUserDefaults standardUserDefaults] integerForKey:@"syntaxListOffis"] + compression: 1.0 + extraParameters: server]; + + @try + { + [storeSCU run:self]; + } + + @catch( NSException *ne) + { + [self performSelectorOnMainThread:@selector(showErrorMessage:) withObject:ne waitUntilDone: NO]; + } + + [storeSCU release]; + storeSCU = nil; + + [pool release]; +} + +- (void) dealloc +{ + self.files = nil; + self.server = nil; + self.thread = nil; + [super dealloc]; +} + +@end + @implementation SendController +(int) sendControllerObjects @@ -391,13 +469,6 @@ - (void) sendToNode: (NSDictionary*) node objects:(NSArray*) objects #pragma mark Sending functions -- (void) showErrorMessage:(NSException*) ne -{ - NSString *message = [NSString stringWithFormat:@"%@\r\r%@\r%@", NSLocalizedString( @"DICOM StoreSCU operation failed.", nil), [ne name], [ne reason]]; - - NSRunCriticalAlertPanel(NSLocalizedString(@"DICOM Send Error",nil), message, NSLocalizedString( @"OK",nil), nil, nil); -} - - (void) executeSend:(NSArray*) files patientName: (NSString*) patientName { if( [NSThread currentThread].isCancelled) @@ -406,35 +477,64 @@ - (void) executeSend:(NSArray*) files patientName: (NSString*) patientName [NSThread currentThread].name = [NSString stringWithFormat: @"%@ %@", NSLocalizedString( @"Sending...", nil), patientName]; // Send the collected files from the same patient - - NSString *calledAET = [[self server] objectForKey:@"AETitle"]; - NSString *hostname = [[self server] objectForKey:@"Address"]; - NSString *destPort = [[self server] objectForKey:@"Port"]; - - NSMutableDictionary* xp = [NSMutableDictionary dictionaryWithDictionary:[self server]]; -// [xp setObject:database forKey:@"DicomDatabase"]; - storeSCU = [[DCMTKStoreSCU alloc] initWithCallingAET:[NSUserDefaults defaultAETitle] - calledAET:calledAET - hostname:hostname - port:[destPort intValue] - filesToSend:files - transferSyntax: [[NSUserDefaults standardUserDefaults] integerForKey:@"syntaxListOffis"] - compression: 1.0 - extraParameters:[self server]]; - - @try - { - [storeSCU run:self]; - } - - @catch( NSException *ne) - { - [self performSelectorOnMainThread:@selector(showErrorMessage:) withObject:ne waitUntilDone: NO]; - } - - [storeSCU release]; - storeSCU = nil; + NSMutableArray *operations = [NSMutableArray array]; + NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease]; + queue.name = [NSString stringWithFormat: @"%@ %@", NSLocalizedString( @"Sending...", nil), patientName]; + + unsigned int maxThreads = [[NSUserDefaults standardUserDefaults] integerForKey: @"SendControllerConcurrentThreads"]; + if( maxThreads <= 0) + maxThreads = 1; + unsigned long loc = 0; + do + { + NSRange range = NSMakeRange( loc, ceil( (float)files.count / (float)maxThreads)); + if( operations.count == maxThreads-1) + range.length = files.count - range.location; + + if( range.length) + { + loc += range.length; + + DCMTKStoreSCUOperation *op = [[[DCMTKStoreSCUOperation alloc] initWithFiles: [files subarrayWithRange: range] server: [self server]] autorelease]; + + [operations addObject: op]; + [queue addOperation: op]; + } + + } + while( loc < files.count); + + NSUInteger initialOpCount = queue.operationCount; + while (queue.operationCount) + { + if( [[NSThread currentThread] isCancelled]) + { + for( DCMTKStoreSCUOperation *o in operations) + [o.thread cancel]; + + [queue cancelAllOperations]; + } + + float progress = 0; + for( DCMTKStoreSCUOperation *o in operations) + { + if( [o.thread isFinished]) + progress += 1; + else if( o.thread.progress >= 0) + progress += o.thread.progress; + } + + progress /= operations.count; + [NSThread currentThread].progress = progress; + + int remainingFiles = (files.count - files.count * progress); + [NSThread currentThread].status = N2LocalizedSingularPluralCount( remainingFiles, NSLocalizedString(@"file", nil), NSLocalizedString(@"files", nil)); + + [NSThread sleepForTimeInterval:0.1]; + } + + [queue waitUntilAllOperationsAreFinished]; } - (void) sendDICOMFilesOffis:(NSDictionary *) dict