// // PlayGroundViewController.m // PlayGround // // Created by Simon Urbanek on 10/8/10. // Copyright Simon Urbanek 2010. All rights reserved. // #import "PlayGroundViewController.h" #import #include #include #include #include #include #include #include //--- configurable variables #define dataCapacity (512*1024) #define commitDataWatermark (dataCapacity - 10000) #define DST_PORT 8120 #define DST_IP "207.140.168.142" // iX //--- shared instance static PlayGroundViewController *sharedPGVCtrl; //--- on-disk data format (header) typedef struct record_header { int ts, lat, lon, dill, res, len; } record_header_t; //--- implementation @implementation PlayGroundViewController + (PlayGroundViewController*) sharedController { return sharedPGVCtrl; } /* // The designated initializer. Override to perform setup that is required before the view is loaded. - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) { // Custom initialization } return self; } */ /* // Implement loadView to create a view hierarchy programmatically, without using a nib. - (void)loadView { } */ - (void) append: (NSString*) text { [textView setText:[[textView text] stringByAppendingString:text]]; [infoLabel setText:[NSString stringWithFormat:@"in-memory: %d, last: %d, udp: %d", [data length], lastLen, udp_seq]]; } - (NSString*) dataFilePath { return [NSString stringWithFormat:@"%@/data.bin", dataDir]; } - (void) commitData { if (data && [data length]) { const char *fn = [[self dataFilePath] UTF8String]; FILE *f = fopen(fn, "ab"); if (f) { int n = fwrite([data bytes], 1, [data length], f); fclose(f); [self performSelectorOnMainThread:@selector(append:) withObject:[NSString stringWithFormat:@"Committed %d of %d bytes.\n", n, [data length]] waitUntilDone:NO]; // flush the in-memory part [data setLength:0]; } } } static char sendBuf[4096]; - (void) uploadData { // make sure all the data is on disk [self commitData]; // then upload the disk contents const char *fn = [[self dataFilePath] UTF8String]; FILE *f = fopen(fn, "rb"); if (f) { NSError *err = nil; BOOL succ = NO; int up_total = 0; TCPConnection *tc = [TCPConnection connectToHost:@"207.140.168.142" port:1174 error:&err]; if (err || !tc) { // if it happens the first time, try using URL connection to plow the way NSURLResponse *res; [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://207.140.168.142/"] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:5.0] returningResponse:&res error:&err]; err = nil; tc = [TCPConnection connectToHost:@"207.140.168.142" port:1174 error:&err]; } if (!err && tc) { succ = YES; int n; while (!feof(f) && ((n = fread(sendBuf, 1, sizeof(sendBuf), f)) > 0)) { int rn; if ((rn = [tc write:(unsigned char*) sendBuf maxLength:n]) != n) { [self performSelectorOnMainThread:@selector(append:) withObject:[NSString stringWithFormat:@"UPLOAD ERROR: @%d got %d instad of %d\n", up_total, rn, n] waitUntilDone:NO]; succ = NO; break; } up_total += n; } [tc close]; } else [self performSelectorOnMainThread:@selector(append:) withObject:[NSString stringWithFormat:@"UPLOAD ERROR: %@", err] waitUntilDone:NO]; fclose(f); if (succ) { [self performSelectorOnMainThread:@selector(append:) withObject:[NSString stringWithFormat:@" - data upload success (%d bytes)\n", up_total] waitUntilDone:NO]; unlink(fn); } } } - (void) networkActivityThread: (id) dummy { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; int s = socket(AF_INET, SOCK_DGRAM, 0); udp_seq = 0; while (active) { if (udpPing) { if (s == -1) s = socket(AF_INET, SOCK_DGRAM, 0); if (s != -1) { struct sockaddr_in sa; record_header_t h; h.ts = time(NULL); h.lat = lat; h.lon = lon; h.dill = dill; h.res = udp_seq; h.len = 0; memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_port = htons(DST_PORT); sa.sin_len = sizeof(sa); if (inet_aton(DST_IP, &sa.sin_addr)) { int n; if ((n = sendto(s, &h, sizeof(h), 0, (const struct sockaddr *) &sa, sizeof(sa))) != sizeof(h)) { close(s); s = -1; [self performSelectorOnMainThread:@selector(append:) withObject:[NSString stringWithFormat:@"UDP[!%d]", n] waitUntilDone:NO]; } else udp_seq++; } } } [NSThread sleepForTimeInterval:2]; } [pool release]; } static char lastInfoCache[32768]; - (void) monitorThread: (id) dummy { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; lastLen = -1; [self performSelectorOnMainThread:@selector(append:) withObject:@" - entering monitor thread\n" waitUntilDone:NO]; while (active) { NSAutoreleasePool *loop_pool = [[NSAutoreleasePool alloc] init]; // let's clean up our stuff on each pass [NSThread sleepForTimeInterval:0.5]; if (monitor) { void *dataPtr = 0; int len = 0; if ([monitor getRawFieldTestInfo:&dataPtr length:&len] && dataPtr && (len != lastLen || memcmp(dataPtr, lastInfoCache, len))) { record_header_t h; h.ts = time(NULL); h.lat = lat; h.lon = lon; h.dill = dill; h.res = 0; h.len = len; [data appendBytes:&h length:sizeof(h)]; [data appendBytes:dataPtr length:len]; // copy the content into the cache as well lastLen = len; if (lastLen > sizeof(lastInfoCache)) lastLen = sizeof(lastInfoCache); memcpy(lastInfoCache, dataPtr, lastLen); // free the memory vm_deallocate(mach_task_self(), (vm_address_t)dataPtr, len); if ([data length] > commitDataWatermark) [self commitData]; // [self performSelectorOnMainThread:@selector(append:) withObject:[NSString stringWithFormat:@" + new: %p [%d:%d]\n %d,%d %d\n", data, len, [data length], lat, lon, dill] waitUntilDone:NO]; [self performSelectorOnMainThread:@selector(append:) withObject:[NSString stringWithFormat:@"(%d)", len] waitUntilDone:NO]; } else if (locationUpdated) { // just log GPS locationUpdated = NO; record_header_t h; h.ts = time(NULL); h.lat = lat; h.lon = lon; h.dill = dill; h.res = 0; h.len = 0; [data appendBytes:&h length:sizeof(h)]; [self performSelectorOnMainThread:@selector(append:) withObject:@"." waitUntilDone:NO]; } } [loop_pool release]; } [pool release]; } - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation { lat = (int) (newLocation.coordinate.latitude * 1000000.0); lon = (int) (newLocation.coordinate.longitude * 1000000.0); dill = (newLocation.horizontalAccuracy < 0) ? -1 : ((int)(newLocation.horizontalAccuracy * 1000000.0)); locationUpdated = YES; } // Implement viewDidLoad to do additional setup after loading the view, typically from a nib. - (void)viewDidLoad { [super viewDidLoad]; sharedPGVCtrl = self; data = [[NSMutableData alloc] initWithCapacity:dataCapacity]; location = [[CLLocationManager alloc] init]; location.delegate = self; lat = 0; lon = 0; dill = -1; lastLen = -1; [location startUpdatingLocation]; // get the Documents directory and make sure it exists dataDir = @"."; NSArray *docDirs = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); if (docDirs && [docDirs count]) dataDir = (NSString*) [[docDirs objectAtIndex:0] retain]; NSFileManager *fm = [NSFileManager defaultManager]; if (![fm fileExistsAtPath:dataDir]) [fm createDirectoryAtPath:dataDir withIntermediateDirectories:YES attributes:nil error:nil]; [textView setText:[NSString stringWithFormat:@"Welcome, v%@.\n\n", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]]]; NSDictionary *fa; if ([fm fileExistsAtPath:[self dataFilePath]] && (fa = [fm fileAttributesAtPath:[self dataFilePath] traverseLink:YES])) [self append:[NSString stringWithFormat:@"Existing data: %u bytes.\n", [[fa objectForKey:NSFileSize] unsignedIntValue]]]; monitor = [[NetworkMonitor alloc] init]; [monitor start]; active = YES; [self append:@"Network monitor started.\n"]; [NSThread detachNewThreadSelector:@selector(monitorThread:) toTarget:self withObject:nil]; [NSThread detachNewThreadSelector:@selector(networkActivityThread:) toTarget:self withObject:nil]; // [self append:[NSString stringWithFormat:@"uid=%d, gid=%d\n", getuid(), getgid()]]; } - (IBAction) udpChange: (id) sender { udpPing = udpSwitch.on; } - (IBAction) start: (id) sender { [location startUpdatingLocation]; [monitor start]; [self append:@"Network monitor+location started.\n"]; } - (IBAction) stop: (id) sender { [location stopUpdatingLocation]; [monitor stop]; [self append:@"Network monitor+location stopped.\n"]; lat = 0; lon = 0; dill = -1; } - (IBAction) upload: (id) sender { [self uploadData]; } - (IBAction) reset: (id) sender { [data setLength:0]; [textView setText:@"In-memory data reset.\n"]; } - (IBAction) purge: (id) sender { NSFileManager *fm = [NSFileManager defaultManager]; if (![fm fileExistsAtPath:[self dataFilePath]]) [self append:@"NOTE: nothing to purge\n"]; else { [fm removeItemAtPath:[self dataFilePath] error:nil]; [self append:@"INFO: data file purged\n"]; } } - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { return YES; // (interfaceOrientation == UIInterfaceOrientationPortrait); } - (void)didReceiveMemoryWarning { // Releases the view if it doesn't have a superview. [super didReceiveMemoryWarning]; // Release any cached data, images, etc that aren't in use. } - (void)viewDidUnload { // Release any retained subviews of the main view. // e.g. self.myOutlet = nil; } - (void)dealloc { [super dealloc]; } @end // old stuff just as a historical note ----------- #if 0 { #if 0 NSError *err = nil; TCPConnection *c = [TCPConnection connectToHost:@"127.0.0.1" port:750 error:&err]; if (err) [self append:[NSString stringWithFormat:@"FAILED to connect: %@\n", err]]; else { [self append:@"Connected to ipsh\n"]; #if 0 BufferedConnection *bc = [[BufferedConnection alloc] initWithConnection:c bufferSize:1024]; [c writeString:@"help\n"]; NSString *s = nil; while ([bc hasBytesAvailable] && (s = [bc readLine])) [self append:[NSString stringWithFormat:@"ipsh> %@\n", s]]; [self append:@"Done, closing connection\n"]; [bc close]; [bc release]; #else [NSThread detachNewThreadSelector:@selector(readThread:) toTarget:self withObject:c]; [c writeString:@"help\n"]; #endif } #else CTServerConnectionRef conn = _CTServerConnectionCreate(kCFAllocatorDefault, CreateCallback, NULL); if (conn == 0) { [self append:@"CTServerConnectionCreate FAILED.\n"]; return; } // from the write-up FieldTest does: // CTNetworkMonitorUpdateNotification() ? // CTServerConnectionRegisterForNotification #if 0 int err = 0; int *res = _CTServerConnectionRegisterForNotification(kCTNetworkMonitorUpdateNotification, conn, &err); [self append:[NSString stringWithFormat:@" - register notif. res=%p, err=%p\n", res, err]]; #else int *res = 0; #endif int port = _CTServerConnectionGetPort(conn); [self append:[NSString stringWithFormat:@" - got port: %d\n", port]]; CFMachPortRef mach_port = CFMachPortCreateWithPort(kCFAllocatorDefault, port, NULL, NULL, NULL); [self append:[NSString stringWithFormat:@" - mach_port: 0x%x\n", CFMachPortGetPort(mach_port)]]; #if 0 // create a source for the port and add to the run loop CFRunLoopSourceRef src = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, mach_port, 0); CFRunLoopAddSource(CFRunLoopGetCurrent(), src, kCFRunLoopCommonModes); #endif res = _CTServerConnectionNetworkMonitorStart(mach_port, conn); [self append:[NSString stringWithFormat:@" - monitor start returned %p\n", res]]; sleep(8); { int succ = 0, len = 0; void *data = 0; #ifndef DIRECT_API _CTServerConnectionNetworkMonitorCopyFieldTestInfo(mach_port, conn, &succ, &data); [self append:[NSString stringWithFormat:@" - info result=%d, data=%p\n", succ, data]]; if (data) [self append:[NSString stringWithFormat:@" - data: %@\n", (NSObject*) data]]; #else _CTCellMonitorCopyFieldTestInfo(CFMachPortGetPort(conn->myport), &succ, &data, &len); [self append:[NSString stringWithFormat:@" - info result=%d, data length=%d\n", succ, len]]; if (len) { CFDataRef dc = CFDataCreateWithBytesNoCopy(NULL, data, len, kCFAllocatorNull); if (dc) { CFStringRef errstr = NULL; #if 0 TCPConnection *tc = [TCPConnection connectToHost:@"207.140.168.142" port:1174 error:&errstr]; if (tc) { [tc write:data maxLength:len]; [tc close]; } #endif errstr = NULL; CFPropertyListRef pl = CFPropertyListCreateFromXMLData(NULL, dc, kCFPropertyListImmutable, &errstr); if (!pl) { [self append:[NSString stringWithFormat:@" * failed to parse XML: %@\n", errstr]]; } else { [self append:[NSString stringWithFormat:@" - got plist %@\n", (NSObject*) pl]]; CFRelease(pl); } CFRelease(dc); } } #endif } #endif } #endif