Flash/AIR で FeliCa へアクセス

2010年4月5日 加藤 雄亮 この記事をはてなブックマーク この記事をクリップ! twitter Twitterに投稿

最近、ソニーが開発した非接触ICカード技術“FeliCa”への関心が高まっています。先日、SDK for FeliCa AIR/Flash というFlash/AIRからFeliCaを操作出来るSDKがリリースされました。このSDKには、Basic版(無償)とStandard版(有償)があり、機能が制限されていますが、簡単な動作確認であれば、Basic版で十分です。
SDK for FeliCa & Adobe® AIR® / Adobe® Flash®

今回は新しくFlash Builber 4を使用してFlex4で簡単にサンプルを作成してみました。

FeliCaのIDmとPMmを読み取るサンプル

下記の動作には、FeliCaポート/パソリが接続されている事、またFeliCaポートソフトウェアがインストールされている必要があります。

FeliCaサンプル

MXMLソースコード

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
               xmlns:s="library://ns.adobe.com/flex/spark" 
               xmlns:mx="library://ns.adobe.com/flex/mx" 
               minWidth="300" minHeight="200">
    <s:TextArea id="textArea" lineHeight="150%" visible="false"/>
    <s:Button id="button" label="FeliCa読み込み開始" click="{this._onButtonClick()}"/>
    <fx:Script>
        <![CDATA[
            import com.sony.jp.felica.FeliCaCloseReaderWriterRequest;
            import com.sony.jp.felica.FeliCaCloseReaderWriterResponse;
            import com.sony.jp.felica.FeliCaControl;
            import com.sony.jp.felica.FeliCaOpenReaderWriterAutoRequest;
            import com.sony.jp.felica.FeliCaOpenReaderWriterAutoResponse;
            import com.sony.jp.felica.FeliCaPollingAndGetCardInformationRequest;
            import com.sony.jp.felica.FeliCaPollingAndGetCardInformationResponse;
            import com.sony.jp.felica.FeliCaSessionRequest;
            import com.sony.jp.felica.FeliCaSessionResponse;
            import com.sony.jp.felica.error.FeliCaAccessError;
            import com.sony.jp.felica.event.FeliCaStatusEvent;
            import com.sony.jp.felica.event.OpenStatusEvent;
            
            import mx.utils.ObjectUtil;
            
            /** FeliCaProxy ポート番号 */
            private static const FELICA_PORT:uint = 10250;
            /** FeliCaProxy 通信タイムアウト秒数 */
            private static const FELICA_TIMEOUT:uint = 10;
            
            /** FeliCaProxy と通信を行うクラス */
            private var _felica:FeliCaControl = new FeliCaControl();
            
            /** ポーリング用 Timer (500ミリ秒毎, 20回 = 5秒間) */
            private var _pollingTimer:Timer = new Timer(500, 10);
            
            private function _onButtonClick():void
            {
                this.textArea.width   = this.width;
                this.textArea.height  = this.height;
                this.textArea.visible = true;
                this.button.visible   = false;
                this._open();
            }
            
            private function _open():void
            {
                this._felica.responseTimeout = FELICA_TIMEOUT * 1000;
                this._felica.addEventListener(OpenStatusEvent.OPEN_COMPLETE, this._onOpenComplete);
                this._felica.addEventListener(OpenStatusEvent.OPEN_FAILURE,  this._onOpenFailure);
                this._felica.open(FELICA_PORT);
            }
            
            private function _close():void
            {
                if (this._pollingTimer.running) {
                    this._pollingTimer.reset();
                }
                if (!this._felica.close()) {
                    this._log('[ NG ] Disconnect FeliCa Proxy Error!');
                    this._close();
                    return;
                }
                this._log('[ OK ] Disconnect FeliCa Proxy.');
            }
            
            private function _onOpenComplete(event:OpenStatusEvent):void
            {
                this._log('[ OK ] Connect FeliCa Proxy.');
                this._felica.addEventListener(FeliCaStatusEvent.FELICA_ACCESS_COMPLETE, this._onFelicaAccessComplete);
                this._felica.addEventListener(FeliCaStatusEvent.FELICA_ACCESS_FAILURE,  this._onFelicaAccessFailure);
                this._lock();
            }
            
            private function _onOpenFailure(event:OpenStatusEvent):void
            {
                this._log('[ NG ] Open FeliCa Proxy Error!');
                this._log(ObjectUtil.toString(event));
            }
            
            private function _lock():void
            {
                var request:FeliCaSessionRequest = new FeliCaSessionRequest();
                request.type          = FeliCaSessionRequest.HOLD;
                request.lockTimeout   = 10;
                request.unlockTimeout = 30;
                this._felica.access(request);
            }
            
            private function _unlock():void
            {
                var request:FeliCaSessionRequest = new FeliCaSessionRequest();
                request.type = FeliCaSessionRequest.RELEASE;
                this._felica.access(request);
            }
            
            private function _polling():void
            {
                if (!this._pollingTimer.hasEventListener(TimerEvent.TIMER)) {
                    this._onPollingTimer();
                }
                this._pollingTimer.addEventListener(TimerEvent.TIMER,          this._onPollingTimer);
                this._pollingTimer.addEventListener(TimerEvent.TIMER_COMPLETE, this._onPollingTimerComplete);
                this._pollingTimer.start();
            }
            
            private function _onPollingTimer(event:TimerEvent = null):void
            {
                this._log('Polling ...');
                var request:FeliCaPollingAndGetCardInformationRequest = new FeliCaPollingAndGetCardInformationRequest();
                request.systemCode = 'FFFF';
                this._felica.access(request);
            }
            
            private function _onPollingTimerComplete(event:TimerEvent):void
            {
                this._log('[ OK ] Stop Polling.');
                this._close();
            }
            
            private function _onFelicaAccessComplete(event:FeliCaStatusEvent):void
            {
                if (event.object is FeliCaSessionResponse) {
                    var session:FeliCaSessionResponse = event.object as FeliCaSessionResponse;
                    if (session.type == FeliCaSessionResponse.HOLD) {
                        this._log('[ OK ] FeliCa Session Hold.');
                        this._felica.access(new FeliCaOpenReaderWriterAutoRequest());
                    } else if (session.type == FeliCaSessionResponse.RELEASE) {
                        this._log('[ OK ] FeliCa Session Release.');
                        this._close();
                    }
                } else if (event.object is FeliCaOpenReaderWriterAutoResponse) {
                    this._log('[ OK ] Start Polling.');
                    this._polling();
                } else if (event.object is FeliCaPollingAndGetCardInformationResponse) {
                    this._log('[ OK ] Open FeliCa Reader/Writer.');
                    if (this._pollingTimer.running) {
                        this._pollingTimer.reset();
                    }
                    var pgcResponse:FeliCaPollingAndGetCardInformationResponse = event.object as FeliCaPollingAndGetCardInformationResponse;
                    this._log('\nIDm: ' + pgcResponse.idm);
                    this._log('PMm: ' + pgcResponse.pmm + "\n");
                    this._felica.access(new FeliCaCloseReaderWriterRequest());
                } else if (event.object is FeliCaCloseReaderWriterResponse) {
                    this._log('[ OK ] Close FeliCa Reader/Writer.');
                    this._unlock();
                }
            }
            
            private function _onFelicaAccessFailure(event:FeliCaStatusEvent):void
            {
                if (event.object is FeliCaAccessError) {
                    var feliCaAccessError:FeliCaAccessError = event.object as FeliCaAccessError;
                    if (feliCaAccessError.rwError == 157) {  // Error Code: No FeliCa
                        return;
                    }
                }
                if (event.object is Error) {
                    var error:Error = event.object as Error;
                    if (error.errorID == 7011) {
                        this._log('[ NG ] FeliCa Proxy Timeout Error!');
                    }
                }
                this._log('[ NG ] FeliCa Access Error!');
                this._log(ObjectUtil.toString(event));
                this._close();
            }
            
            private function _log(message:String):void
            {
                this.textArea.appendText(message + "\n");
            }
        ]]>
    </fx:Script>
</s:Application>

ダウンロード

Flash Builder 4 プロジェクトダウンロード: FeliCaBasic.zip



Standard版(有償)を利用すれば、データ領域にアクセス出来るため、様々な事に応用出来そうな気がします。また、読み取り側の普及次第となりますが、ECサイトの決済サービス等も増えてきそうな気がします。

コメント / トラックバック2件

  1. [...] « Flash/AIR で FeliCa へアクセス [...]

  2. 通りすがりで試した人 より:

    処理が止まらなくメモリを食いつぶされました。
    (タスクマネージャで確認したときにはメモリ1.2Gつかってました。)
    OSはWindowsXPProffesional ブラウザはIE8です。

コメントをどうぞ