iPhoneのサンプルプログラム

ジャイロの記録とアップロード その2

upload1c.cgi(サーバー側のCGI)
#!/usr/local/bin/perl
use CGI;
use File::Copy;
use File::Basename;



my ($sec, $min, $hour, $day, $mon, $year) = localtime(time);
$year += 1900;
$mon += 1;

my $q = new CGI;

my $fh = $q->upload('filename'); # ファイルハンドル兼ファイル名
my $temp_path = $q->tmpFileName($fh); # アップロードされた
                                      #ファイルのフルパス
fileparse_set_fstype('MSDOS');   # WinIE用パス文字設定
# my $filename    = basename($fh); # アップロードされたファイルの
                                 # ファイル名
my $upload_dir  = "../../work3";

my $filename = sprintf("%d%02d%02d_%02d%02d%02d.csv", $year, $mon, $day, $hour, $min, $sec);
my $upload_path = "$upload_dir/$filename";

move ($temp_path, $upload_path)  # File::Copy の moveメソッドで
  or die $!;                     # 移動
close($fh);                      # おまじない

print $q->header( -type=>'text/html', -charset=>'euc-jp', );
print <<"END_OF_HTML";

完了!

END_OF_HTML exit;

Gyro_Recorder.h
#import <Foundation/Foundation.h>

@interface Gyro_Recorder : NSObject {
    NSMutableArray *arrayData;
}

- (id)init;

- (void)addGyro_Values: (float)roll gyroP:(float)pitch gyroY:(float)yaw;

- (void)upload: (NSString *)url
  dataFilename:(NSString *)data_filename delegate:(id)dg;

- (void)dealloc;

@end

Gyro_Recorder.m
#import "Gyro_Recorder.h"

@implementation Gyro_Recorder

- (id)init
{
    if (self = [super init]) {
        arrayData = [[NSMutableArray alloc] initWithCapacity:100];
    }

    return self;
}

- (void)dealloc
{
    [arrayData release];
    [super dealloc];
}

- (void)addGyro_Values: (float)roll gyroP:(float)pitch gyroY:(float)yaw
{
    [arrayData addObject:[NSNumber numberWithFloat:roll]];
    [arrayData addObject:[NSNumber numberWithFloat:pitch]];
    [arrayData addObject:[NSNumber numberWithFloat:yaw]];
}

- (void)upload:(NSString *)url
  dataFilename:(NSString *)data_filename delegate:(id)dg
{
    NSString *boundary = @"___boundary___";

    // ジャイロデータのとりまとめ
    NSMutableString *buffer = [NSMutableString stringWithCapacity:120];
    int i = 0;
    for (NSNumber *num in arrayData)
    {
        i++;
        if (i == 3) {
            [buffer appendFormat:@"%f\n", [num floatValue]];
            i = 0;
        }
        else {
            [buffer appendFormat:@"%f,", [num floatValue]];
        }
    }
    NSData *data = [buffer dataUsingEncoding:NSASCIIStringEncoding];

    // 送信先の設定
    NSURL *cgiUrl = [NSURL URLWithString:url];
    NSMutableURLRequest *postReq = [NSMutableURLRequest requestWithURL:cgiUrl];
    [postReq setHTTPMethod:@"POST"];

    // 送信データ準備
    NSMutableData *result = [NSMutableData dataWithCapacity:(NSUInteger)120];

    [result appendData:
     [[NSString stringWithFormat:@"--%@\r\n", boundary]
      dataUsingEncoding:NSASCIIStringEncoding]];

    [result appendData:
     [[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n",
       @"filename", data_filename]
      dataUsingEncoding:NSASCIIStringEncoding]];

    [result appendData:
     [[NSString stringWithString:@"Content-Type: text/csv\r\n\r\n"]
      dataUsingEncoding:NSASCIIStringEncoding]];

    [result appendData:data];

    [result appendData:
     [[NSString stringWithString:@"\r\n"]
      dataUsingEncoding:NSASCIIStringEncoding]];

    // user
    [result appendData:
     [[NSString stringWithFormat:@"--%@\r\n", boundary]
      dataUsingEncoding:NSASCIIStringEncoding]];

    // マルチパート終端
    [result appendData:
     [[NSString stringWithFormat:@"--%@--\r\n\r\n", boundary]
      dataUsingEncoding:NSASCIIStringEncoding]];

    // POST実行
    NSString *s1 = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
    [postReq addValue:s1 forHTTPHeaderField:@"Content-Type"];
    [postReq setHTTPBody:result];

    NSLog(@"通信開始");

    NSURLConnection *conn = [NSURLConnection connectionWithRequest:postReq delegate:dg];

    if (! conn) {
        NSLog(@"送信失敗!");
    }
}

@end

ViewController.h
#import <UIKit/UIKit.h>
#import <CoreMotion/CoreMotion.h>
#import "Gyro_Recorder.h"

typedef enum {
    kInit,
    kOnRecord,
    kUpload,
    kDelete
} AppState;

@interface ViewController : UIViewController {
    IBOutlet UILabel *labelRoll;
    IBOutlet UILabel *labelPitch;
    IBOutlet UILabel *labelYaw;

    IBOutlet UIButton *btn1;

    AppState state;

    Gyro_Recorder *recorder;

    CMMotionManager *motionManager;
    CMAttitude *referenceAttitude;
    BOOL isCaptured;

}

- (IBAction)onButton1;

@end

ViewController.m
#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController


- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    state = kInit;

    motionManager = [[CMMotionManager alloc] init];
    motionManager.deviceMotionUpdateInterval = 1.0f/60.0f; // ジャイロのサンプリング間隔[s]

    isCaptured = NO;

    recorder = nil;
    referenceAttitude = nil;

    [labelRoll setText: [NSString stringWithString:@"Roll:"]];
    [labelPitch setText: [NSString stringWithString:@"Pitch:"]];
    [labelYaw setText: [NSString stringWithString:@"Yaw:"]];

    [btn1 setTitle:@"測定開始" forState: UIControlStateNormal];
}

- (void)viewDidUnload
{
    motionManager = nil;
    referenceAttitude = nil;

    [super viewDidUnload];
    // Release any retained subviews of the main view.
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

- (void)recordGyroData:(CMDeviceMotion *)motion
{
    CMAttitude *currentAttitude = motion.attitude;

    if (referenceAttitude != nil) {
        [currentAttitude multiplyByInverseOfAttitude: referenceAttitude];
    }

    // 角度を度数に変換
    float rollDegree = 180 * currentAttitude.roll / M_PI;
    float pitchDegree = 180 * currentAttitude.pitch / M_PI;
    float yawDegree = 180 * currentAttitude.yaw / M_PI;

    [recorder addGyro_Values:rollDegree gyroP:pitchDegree gyroY:yawDegree];

    [labelRoll setText: [NSString stringWithFormat:@"Roll: %f", rollDegree]];
    [labelPitch setText: [NSString stringWithFormat:@"Pitch: %f", pitchDegree]];
    [labelYaw setText: [NSString stringWithFormat:@"Yaw: %f", yawDegree]];
}

- (void)onButton1
{
    switch (state) {
        case kInit:
            recorder = [[Gyro_Recorder alloc] init];

            // デバイスのハードウェアチェック
            if (!motionManager.deviceMotionAvailable)
            {
                NSLog(@"DeviceMotion is not available.");
                return;
            }

            isCaptured = NO;

            // モーションデータ更新時のハンドラを作成
            void (^handler)(CMDeviceMotion *, NSError *) = ^(CMDeviceMotion *motion, NSError *error)
        {
            if (isCaptured == NO) {
                referenceAttitude = [motion.attitude retain];
                isCaptured = YES;
            }
            else {
                // ジャイロデータの記録
                [self recordGyroData:motion];
            }
        };

            // モーションデータの測定を開始
            NSOperationQueue *queue = [NSOperationQueue currentQueue];
            [motionManager startDeviceMotionUpdatesToQueue:queue withHandler:handler];
            state = kOnRecord;
            [btn1 setTitle:@"測定停止" forState: UIControlStateNormal];
            break;

        case kOnRecord:
            // モーションデータの測定を終了
            [motionManager stopDeviceMotionUpdates];
            [referenceAttitude release];
            referenceAttitude = nil;
            isCaptured = NO;
            state = kUpload;
            [btn1 setTitle:@"アップロード" forState: UIControlStateNormal];
            break;

        case kUpload:
            state = kInit;
            [btn1 setTitle:@"測定開始" forState: UIControlStateDisabled];
            [recorder upload:@"http://www.tosiyama.jp/personal/cgi/upload1c.cgi" dataFilename:@"gyro_2012.csv" delegate:self];
            break;

        default:
            break;
    }
}

- (void)connection:(NSURLConnection *)connection
didReceiveResponse:(NSURLResponse *)response
{

}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{

}

- (void)connection:(NSURLConnection *)connection
  didFailWithError:(NSError *)error
{

}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    [recorder release];
    recorder = nil;
    [btn1 setTitle:@"測定開始" forState: UIControlStateNormal];
}

@end

アプリのIcon(57x57)