#import "ClientState.h" #import "Preferences.h" @implementation ClientState static ClientState *clientState = nil; // Returns the singleton, creates one if it doesn't exist + (ClientState *)clientState { if(clientState == nil) { clientState = [[[ClientState alloc] init] retain]; [NSTimer scheduledTimerWithTimeInterval:0.25 target:clientState selector:@selector(loadData:) userInfo:nil repeats:YES]; [clientState loadData:nil]; } return clientState; } - (void)dealloc { [clientStateDictionary release]; [lastKnownModification release]; [super dealloc]; } // Loads projects and credit information from file - (void)loadData:(id)userInfo { // Get the path to client_state.xml NSString *clientStatePath = [[[Preferences prefs] objectForKey:@"boincDataDirectory"] stringByAppendingString:@"client_state.xml"]; // Check to see when the client state file was last modified NSDate *lastModification = [[[NSFileManager defaultManager] fileAttributesAtPath:clientStatePath traverseLink:NO] objectForKey:NSFileModificationDate]; // If the date the file was last modified is the same last known modification date, then we're done if(lastKnownModification != nil && [lastKnownModification isEqualToDate:lastModification]) return; // Otherwise, we need to update the client state [lastKnownModification release]; lastKnownModification = [lastModification retain]; NSMutableDictionary *newClientStateDictionary = [NSMutableDictionary dictionary]; // Read contents of client_state.xml file NSString *clientStateString = [NSString stringWithContentsOfFile:clientStatePath]; // If client state file isn't available there is no need to scan for projects if(!clientStateString) return; NSScanner *scanner = [NSScanner scannerWithString:clientStateString]; // Scan for projects, urls, and credits NSString *temp; [scanner scanUpToString:@"" intoString:nil]; while(![scanner isAtEnd]) { NSMutableDictionary *projectInfo = [NSMutableDictionary dictionary]; [scanner scanUpToString:@"" intoString:nil]; [scanner scanString:@"" intoString:nil]; [scanner scanUpToString:@"" intoString:&temp]; [projectInfo setObject:temp forKey:@"url"]; [scanner scanUpToString:@"" intoString:nil]; [scanner scanString:@"" intoString:nil]; [scanner scanUpToString:@"" intoString:&temp]; [projectInfo setObject:temp forKey:@"name"]; [scanner scanUpToString:@"" intoString:nil]; [scanner scanString:@"" intoString:nil]; [scanner scanUpToString:@"" intoString:&temp]; [projectInfo setObject:[NSString stringWithFormat:@"%.3f", [temp doubleValue]] forKey:@"credit"]; [scanner scanUpToString:@"" intoString:nil]; [newClientStateDictionary setObject:projectInfo forKey:[projectInfo objectForKey:@"url"]]; } // Rescan for percentages scanner = [NSScanner scannerWithString:clientStateString]; [scanner scanUpToString:@"" intoString:nil]; while(![scanner isAtEnd]) { [scanner scanUpToString:@"" intoString:nil]; [scanner scanString:@"" intoString:nil]; [scanner scanUpToString:@"" intoString:&temp]; NSMutableDictionary *projectInfo = [newClientStateDictionary objectForKey:temp]; [scanner scanUpToString:@"" intoString:nil]; [scanner scanString:@"" intoString:nil]; [scanner scanUpToString:@"" intoString:&temp]; if([temp isEqualToString:@"1"]) temp = @"NO"; else if([temp isEqualToString:@"2"]) temp = @"YES"; NSMutableDictionary *workUnitInfo = [NSMutableDictionary dictionary]; [workUnitInfo setObject:temp forKey:@"isRunning"]; [scanner scanUpToString:@"" intoString:nil]; [scanner scanString:@"" intoString:nil]; [scanner scanUpToString:@"" intoString:&temp]; [workUnitInfo setObject:[NSString stringWithFormat:@"%.3f", ([temp doubleValue] * 100.0)] forKey:@"percentCompleted"]; [scanner scanUpToString:@"" intoString:nil]; if(![projectInfo objectForKey:@"workUnitInfo"]) [projectInfo setObject:[NSMutableArray arrayWithObject:workUnitInfo] forKey:@"workUnitInfo"]; else [[projectInfo objectForKey:@"workUnitInfo"] addObject:workUnitInfo]; } NSObject *tempPointer = clientStateDictionary; clientStateDictionary = [newClientStateDictionary retain]; [tempPointer release]; [[NSNotificationCenter defaultCenter] postNotificationName:@"clientStateChanged" object:nil]; } - (NSArray *)projects { NSMutableArray *projectURLS = [NSMutableArray array]; NSMutableArray *projectNames = [NSMutableArray array]; NSEnumerator *keyEnumerator = [clientStateDictionary keyEnumerator]; NSString *key; while ((key = [keyEnumerator nextObject])) { [projectURLS addObject:key]; [projectNames addObject:[[clientStateDictionary objectForKey:key] objectForKey:@"name"]]; } return [NSArray arrayWithObjects:projectNames, projectURLS, nil]; } - (NSString *)totalCredit { NSEnumerator *keyEnumeratior = [clientStateDictionary keyEnumerator]; NSString *key; double sum = 0; while ((key = [keyEnumeratior nextObject])) { sum += [[[clientStateDictionary objectForKey:key] objectForKey:@"credit"] doubleValue]; } return [NSString stringWithFormat:@"%.3f", sum]; } - (NSArray *)infoForActiveWorkUnits { NSEnumerator *keyEnumeratior = [clientStateDictionary keyEnumerator]; NSString *key; NSMutableArray *activeWorkUnitInfoArray = [NSMutableArray array]; while ((key = [keyEnumeratior nextObject])) { NSDictionary *projectInfo = [clientStateDictionary objectForKey:key]; NSArray *workUnitInfoArray = [projectInfo objectForKey:@"workUnitInfo"]; int i; for(i = 0; i < [workUnitInfoArray count]; i++) if([[[workUnitInfoArray objectAtIndex:i] objectForKey:@"isRunning"] isEqualToString:@"YES"]) { NSMutableDictionary *extendedWorkUnitInfo = [NSMutableDictionary dictionary]; [extendedWorkUnitInfo addEntriesFromDictionary:[workUnitInfoArray objectAtIndex:i]]; [extendedWorkUnitInfo setObject:[projectInfo objectForKey:@"name"] forKey:@"name"]; [extendedWorkUnitInfo setObject:[projectInfo objectForKey:@"credit"] forKey:@"credit"]; [activeWorkUnitInfoArray addObject:extendedWorkUnitInfo]; } } return activeWorkUnitInfoArray; } @end