iOS External Accessories: iMag Pro IIで磁気カードを読む



# iMag Pro II 入手

- [ID Tech Japan](http://www.idtech-japan.co.jp/)さんにメールで連絡しました
- 見積書を作ってもらって発注しました
- SDKのCDを焼いてもらえて、一緒に納品されます。
- iMag.hとlibiMagSDK.a がお目当てになります。

# プロジェクト作成

- 空ののソリューションつくります

![image](https://lh5.googleusercontent.com/-VQANjUuysdo/VCPIE-mWP7I/AAAAAAAAA4I/hqc8cME1skw/w806-h490-no/1.%2BNew%2BSolution.png)



# バインディングライブラリ作成(IdTechBind)

- Binding Libaryを作成

![image](https://lh5.googleusercontent.com/-vKVPuV8jAog/VCPIF0RcG8I/AAAAAAAAA4c/PV8QcK0iVVU/w806-h485-no/2.%2BBinding%2BProject.png)


~~~
Peeko:CardScanSample hide$ tree .
.
├── CardScanSample.sln
└── IdTechBind
    ├── ApiDefinition.cs
    ├── IdTechBind.csproj
    ├── Resources
    └── StructsAndEnums.cs

2 directories, 4 files
~~~

## libMagSDK.linkwith.cs 作成

- libiMagSDK.a をプロジェクトルートにドラッグ&ドロップ

![image](https://lh4.googleusercontent.com/-MxK1sPLq2S4/VCPIFxS4SNI/AAAAAAAAA4s/k-pUx9uDR4w/w619-h161-no/3.%2BAdd%2BObjective-C%2Blibrary.png)

![image](https://lh3.googleusercontent.com/-JML6rTKJJ2M/VCPIGdFImAI/AAAAAAAAA4k/36Z8Rp6MaBk/w533-h228-no/4.%2BCopy.png)


~~~
Peeko:CardScanSample hide$ tree .
.
├── CardScanSample.sln
└── IdTechBind
    ├── ApiDefinition.cs
    ├── IdTechBind.csproj
    ├── Resources
    ├── StructsAndEnums.cs
    ├── bin
    │   └── Debug
    ├── libiMagSDK.a
    ├── libiMagSDK.linkwith.cs
    └── obj
        └── Debug

6 directories, 6 files
~~~


## ApiDefinition.cs 作成

- [Objective Sharpie](http://developer.xamarin.com/guides/ios/advanced_topics/binding_objective-c/objective_sharpie/)を実行してコピる

![image](https://lh6.googleusercontent.com/-owDsyXA2fyQ/VCPIGyiR0MI/AAAAAAAAA44/QKsiOVC1QZE/w740-h549-no/5.%2BRun%2BObjective%2BSharpie.png)

![image](https://lh3.googleusercontent.com/-hMDPrl2KqrY/VCPIHdW4PFI/AAAAAAAAA5A/TNOZTqLGG8s/w740-h549-no/6.%2BiOS8.png)

![image](https://lh6.googleusercontent.com/-4Ia2x6kngNc/VCPIHjQiJ1I/AAAAAAAAA5I/3j5D6VLkHEQ/w740-h549-no/7.%2BTrage%2BHeader.png)

![image](https://lh4.googleusercontent.com/-Mjck_E16NBE/VCPIIF-KVOI/AAAAAAAAA5M/CWlyo1ZhF2I/w740-h549-no/8.%2BNamespace.png)



- 実行エラー(Xcode6になってXamarimも更新したら出るようになった.... XCode5の時はでなかったのに....)

![image](https://lh5.googleusercontent.com/-WFLrNGfeIoI/VCPIIV-oNfI/AAAAAAAAA5Q/hu98ge_o7Jw/w740-h549-no/9.%2BHuge%2BError.png)


~~~
In file included from /Users/hide/Documents/80097802-006_iMag_SDK_2.7_A/SDK/iMag.h:10:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIKit.h:9:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIAccelerometer.h:8:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/Foundation.framework/Headers/Foundation.h:10:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSArray.h:7:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSRange.h:5:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSValue.h:12:1: error: 'objc_returns_inner_pointer' attribute only applies to methods
@property (readonly) const char *objCType NS_RETURNS_INNER_POINTER;
^                                         ~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /Users/hide/Documents/80097802-006_iMag_SDK_2.7_A/SDK/iMag.h:10:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIKit.h:9:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIAccelerometer.h:8:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/Foundation.framework/Headers/Foundation.h:15:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSCharacterSet.h:8:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSString.h:248:1: error: 'objc_returns_inner_pointer' attribute only applies to methods
@property (readonly) __strong const char *UTF8String NS_RETURNS_INNER_POINTER;  // Convenience to return null-terminated UTF8 representation
^                                                    ~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /Users/hide/Documents/80097802-006_iMag_SDK_2.7_A/SDK/iMag.h:10:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIKit.h:9:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIAccelerometer.h:8:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/Foundation.framework/Headers/Foundation.h:17:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSData.h:76:1: error: 'objc_returns_inner_pointer' attribute only applies to methods
@property (readonly) const void *bytes NS_RETURNS_INNER_POINTER;
^                                      ~~~~~~~~~~~~~~~~~~~~~~~~
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSData.h:161:1: error: 'objc_returns_inner_pointer' attribute only applies to methods
@property (readonly) void *mutableBytes NS_RETURNS_INNER_POINTER;
^                                       ~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /Users/hide/Documents/80097802-006_iMag_SDK_2.7_A/SDK/iMag.h:10:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIKit.h:9:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIAccelerometer.h:8:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/Foundation.framework/Headers/Foundation.h:25:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSDecimalNumber.h:101:1: error: 'objc_returns_inner_pointer' attribute only applies to methods
@property (readonly) const char *objCType NS_RETURNS_INNER_POINTER;
^                                         ~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /Users/hide/Documents/80097802-006_iMag_SDK_2.7_A/SDK/iMag.h:10:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIKit.h:9:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIAccelerometer.h:8:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/Foundation.framework/Headers/Foundation.h:31:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSFileManager.h:8:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSPathUtilities.h:34:1: error: 'objc_returns_inner_pointer' attribute only applies to methods
@property (readonly) __strong const char *fileSystemRepresentation NS_RETURNS_INNER_POINTER;
^                                                                  ~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /Users/hide/Documents/80097802-006_iMag_SDK_2.7_A/SDK/iMag.h:10:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIKit.h:9:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIAccelerometer.h:8:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/Foundation.framework/Headers/Foundation.h:41:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSKeyValueObserving.h:183:1: error: 'objc_returns_inner_pointer' attribute only applies to methods
@property void *observationInfo NS_RETURNS_INNER_POINTER;
^                               ~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /Users/hide/Documents/80097802-006_iMag_SDK_2.7_A/SDK/iMag.h:10:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIKit.h:9:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIAccelerometer.h:8:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/Foundation.framework/Headers/Foundation.h:46:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSMethodSignature.h:22:1: error: 'objc_returns_inner_pointer' attribute only applies to methods
@property (readonly) const char *methodReturnType NS_RETURNS_INNER_POINTER;
^                                                 ~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /Users/hide/Documents/80097802-006_iMag_SDK_2.7_A/SDK/iMag.h:10:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIKit.h:9:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIAccelerometer.h:8:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/Foundation.framework/Headers/Foundation.h:74:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSURL.h:86:1: error: 'objc_returns_inner_pointer' attribute only applies to methods
@property (readonly) __strong const char *fileSystemRepresentation NS_RETURNS_INNER_POINTER NS_AVAILABLE(10_9, 7_0);
^                                                                  ~~~~~~~~~~~~~~~~~~~~~~~~
9 errors generated.
Error while processing /Users/hide/Documents/80097802-006_iMag_SDK_2.7_A/SDK/iMag.h.
sharpie-clang-parser exited with error code: 1
System.Exception: sharpie-clang-parser exited with error code: 1
  at Sharpie.ObjC.Parser.Parse (System.IO.TextWriter sexpWriter, System.Collections.Generic.List`1 stderrLines) [0x00000] in <filename unknown>:0 
  at Sharpie.Mac.MainWindowController+<GenerateBinding>c__AnonStorey3.<>m__B (System.Object o) [0x00000] in <filename unknown>:0 

~~~

- [報告されている](http://forums.xamarin.com/discussion/24692/objective-sharpies-ns-returns-inner-pointer). Object Sharpieのパーサー古いってよ！
- Xcode5だと以下の用に解析される

~~~
using System;

using MonoTouch.Foundation;

namespace IdTechBind {

	[BaseType (typeof (NSObject))]
	public partial interface IMag : EAAccessoryDelegate, NSStreamDelegate {

		[Export ("init")]
		IntPtr Constructor ();

		[Export ("iMagConnected")]
		bool IMagConnected { get; }

		[Export ("session")]
		EASession Session { get; set; }

		[Export ("SendCMD:")]
		NSData SendCMD (NSData writeBuf);

		[Export ("SendCMDGetVersion"), Verify ("ObjC method massaged into getter property", "/Users/hide/Documents/80097802-006_iMag_SDK_2.7_A/SDK/iMag2.h", Line = 29)]
		NSData SendCMDGetVersion { get; }

		[Export ("SendCMDGetSerialNumber"), Verify ("ObjC method massaged into getter property", "/Users/hide/Documents/80097802-006_iMag_SDK_2.7_A/SDK/iMag2.h", Line = 30)]
		NSData SendCMDGetSerialNumber { get; }

		[Static, Export ("enableLogging:")]
		void EnableLogging (bool enable);
	}
}
~~~

- このファイルを元にAppDefinition.cs を作るのですが、以下のように修正

~~~
using System;
using System.Drawing;

using MonoTouch.ObjCRuntime;
using MonoTouch.Foundation;
using MonoTouch.UIKit;

using MonoTouch.ExternalAccessory;


namespace IdTechBind
{

	[BaseType (typeof (NSObject))]
	public partial interface iMag 		// Must be "iMag" as same as in *.h file. NOT IMag...	{
		[Export ("session")]
		EASession Session { get; set; }

		[Export ("iMagConnected")]
		bool IMagConnected { get; }

		[Export ("SendCMD:")]
		NSData SendCMD (NSData writeBuf);

		[Export ("SendCMDGetVersion")]
		NSData SendCMDGetVersion();

		[Export ("SendCMDGetSerialNumber")]
		NSData SendCMDGetSerialNumber();

		[Static, Export ("enableLogging:")]
		void EnableLogging (bool enable);

	}
}
~~~

- IMagと生成されますが、iMagにすること。 Objective-Cの名前に合わせないと new できないっぽい。
- EAAccessoryDelegate, NSStreamDelegate は無視してよいです。これは、 EAAccessory, NSStreameのプロトコルを実装しろということで、コンクリートクラスでなんかすればいいのではないでしょうか。
- EAAccessoryDelegate,NSStreamDelegateともにXamarinではabstractクラスで定義されているので、C#的には多重継承できないので、やるにしても別名でプロトコル用のinterfaceを定義するなど必要です。

## リンクオプション(libiMagSDK.linkwith.cs)

- 自動生成されるlibiMagSDK.linkwith.csを編集
-  デバイスにアクセスするので、ExternalAccessory は必須

~~~

using System;
using MonoTouch.ObjCRuntime;

// ArmV7s : iPhone5
[assembly: LinkWith (
        "libiMagSDK.a", 
        LinkTarget.ArmV6 | LinkTarget.ArmV7 | LinkTarget.ArmV7s | LinkTarget.Simulator,
        Frameworks = "ExternalAccessory UIKit Foundation CoreGraphics",
        ForceLoad = true)]
~~~

## ビルド

- バインディングライブラリをビルドすると中間ファイルなどが自動生成されて、ターゲットコードにトランスレートされたC#プログラム Objective-C ライブラリがリンクされる。

~~~
Peeko:CardScanSample hide$ tree IdTechBind/obj/
IdTechBind/obj/
└── Debug
    ├── IdTechBind.csproj.FilesWrittenAbsolute.txt
    ├── IdTechBind.dll
    ├── IdTechBind.dll.mdb
    ├── MonoTouch,Version=v1.0.AssemblyAttribute.cs
    └── ios
        ├── IdTechBind.IdTechBind
        │   └── iMag.g.cs
        ├── ObjCRuntime
        │   └── Messaging.g.cs
        ├── sources.list
        ├── temp.dll
        └── temp.dll.mdb

4 directories, 9 files
~~~

# 中間ライブラリを作成（IdTechCardReader）

## 準備

- Libraryプロジェクトを作成
- MyClassをCardReaderにリファクタリング
- IdTechBindを参照

## CardReaderを実装

~~~

using System;
using System.Drawing;
using System.Collections.Generic;

using MonoTouch.Foundation;
using MonoTouch.UIKit;
using MonoTouch.ExternalAccessory;
using System.Threading.Tasks;
using System.CodeDom.Compiler;

namespace IdTechCardReader
{
	public class CardReader 
	{
        // カードリーダーからの通知をエベントで返す
		public delegate void ConnectedEvent(NSNotification notification);
		public delegate void DisconnectedEvent(NSNotification notification);
		public delegate void ScannedEvent(NSNotification notification);

		public event ConnectedEvent Connected;	
		public event DisconnectedEvent Disconnected;
		public event ScannedEvent Scanned;

        // 通知センター用のオブザーバー
		List<NSObject> _observers ;

		public CardReader ()
		{
		    // 通知センターでデバイスから通知がきたらC#イベントでアプリにコールバックする
			_observers = new List<NSObject>(){
				NSNotificationCenter.DefaultCenter.AddObserver(
					"iMagDidConnectNotification",
					delegate(NSNotification notification) {
						this.Connected(notification);
					}),
				NSNotificationCenter.DefaultCenter.AddObserver(
					"iMagDidDisconnectNotification",
					delegate (NSNotification notification){
						this.Disconnected(notification);
					}),

				NSNotificationCenter.DefaultCenter.AddObserver(
					"iMagDidReceiveDataNotification",
					delegate(NSNotification notfication) {
						this.Scanned(notfication);
					}
				)
			};
		}

        // ID Tech iMag II カードリーダー
		IdTechBind.iMag _mag =null;

        // 終了処理
		public void Dispose()
		{
			NSNotificationCenter.DefaultCenter.RemoveObservers (_observers);
		}

        // カードリーダーを利用できるようにする
		public void Connect()
		{
			if (_mag == null) {
			    // コンストラクタでイニシャライズまで行われる
				_mag = new IdTechBind.iMag ();             
				IdTechBind.iMag.EnableLogging (true);
			}
		}
	}
}

~~~

# iOSアプリ作成(Shopper)

## Single Viewアプリで作成

- 作成

![image](https://lh4.googleusercontent.com/-VybgmrRKFM4/VCPIE-C6CPI/AAAAAAAAA4U/tICnYj9kYcA/w805-h481-no/10.%2BiPhone%2Bapplication.png)


## Info.plistで通知センターを受け取るようにする

- 「ソース」で編集する
- com.idtechproducts.reader を Supported external accessory protocolsで指定

![image](https://lh6.googleusercontent.com/-65cNgyJCnfQ/VCPIFBh1_mI/AAAAAAAAA4M/4TkL7LIjfiE/w604-h53-no/11.%2BPlist.png)


## IdTechCardReaderから C#のイベント受け取る

- メインのビューコントローラで、StartCardReaderをコールしたら(ボタンとかViewDidLoadとかで)イベントを受け取って処理できるようにする
 
~~~
		IdTechCardReader.CardReader _magpro = null;

		void StartCardReader()
		{
			_magpro = new IdTechCardReader.CardReader();

            // リーダーが接続された
			_magpro.Connected += async(notification) => {
				//TODO: Card Reader Status
				await MessageBox ("Connected", notification.Description);
			};
			
			// リーダが取り外された
			_magpro.Disconnected +=async (notification) => {
				await MessageBox ("DisConnected", notification.Description);
			};

            // スキャンが発生
            
			_magpro.Scanned += async(notification) => {
			
			    // バイト列をパースします
				var data = _magpro.GetScannedData(notification);
				
				if( this.CardType.SelectedSegment == 0 ){
				    // クレジットカード: ISOとかの規格でフォーマット決まってる
					await MessageBox ("Credit:",
						(new IdTechCardReader.PlainCreditCard(data)).ToString());
				}
				if( this.CardType.SelectedSegment == 1 ){
				    // T-POINTカードは規格とは違う
					await MessageBox ("T-CARD:",
						(new IdTechCardReader.TCard(data)).ToString());
				}
				if( this.CardType.SelectedSegment == 2 ){
					// どこのかわからん
					await MessageBox ("Other:",
						(new IdTechCardReader.Data(data)).ToString());
				}
			};

            // 開始
			_magpro.Connect();
		}
~~~		

# iMag Pro II

- 情報少なめ
- 暗号化は[DUKPT](https://en.wikipedia.org/wiki/Derived_unique_key_per_transaction)でセッションごとに派生キーを発生させてそれで読み取りデータを暗号化する模様
- 永続キーは購入する時に設定してもらうのかも
- 設定コマンドを実行してみたが、常にNAK(0x15)が返るので、何も出来ないかあるいは何か実装がまちがえているのかも