2013年5月8日水曜日

明日のためのメモ pythonでバイナリ

pythonを使ってバイナリデータ(長さ不定のヘッダあり)を変数に読み込み特定の文字ごとに区切ってリストに入れて、区切られた要素はときどき要素数が一定でないのに対応して任意のパターンでアンパックしたいときの処理がしたいなー( ´・ω・`) というときに使えそうなプログラムのメモ

こんなバイナリ入力→「****123*****123*****123**123***」

を↓こんな感じで出力、output[]の中身を一行ずつCSVに入れたりすると幸せになれるかもしれない。

output=[[aaaa],[123,100,200],[123,20,30],[123,34,12,12],[123,0,2]]

関数にしてみた↓ conは入力(バイナリ)

def binparse(con):
    con=con.replace('123','\n123')
    con=con.split('\n')
    print "replaced\n",con
    cnt=0
    for i in con:
        if 1<cnt<(len(con)) and i[0:2] !='ab':  ←配列の修正。区切り文字が意図しないところにはいっていてもこれでたぶん修正できる。
            con[cnt-1]=con[cnt-1]+"\n"+i
            con.pop(cnt)
       
        cnt += 1

    out=[]
    cnt=0
  
    fmt1='2c6hi'  ←文字数ごとに適当なフォーマットを設定。パターンが増えそうだとインデックス
    fmt2='2c3hc'   とかリストにまとめたほうがいいかも
    for i in con:
        l=len(i)
        if l==20: ←長さが20だったら~fmt以外は同じ処理
            tmp=struct.unpack(fmt1,i)
            tmp2=[]
            for j in range(len(tmp)):
                           
                tmp2.append(tmp[j])
            print out
            out.append(tmp2)
        elif l==9:
            tmp=struct.unpack(fmt2,i)
            tmp2=[]
            for j in range(len(tmp)):
                           
                tmp2.append(tmp[j])
            print out
            out.append(tmp2)
            pass
        cnt += 1
   
    f.close()
    return out

2013年5月6日月曜日

GWの工作(2) ~androidとpythonのソケット通信に関するメモ~

raspberry piというたのしいおもちゃを買ってしまったのでxbeeは投げ捨ててwifiを使った通信を試みる今日この頃。今回はその辺のメモ

・今回はデータをandroidタブレット→(wifi)→raspberrypi(python)→(シリアル)→arduino の経路で流すシステムを考えます。

・pythonをソケットのサーバ、androidをソケットのクライアントとします。

■まずpythonのソケット通信から、これは以前もやったのでわりとスムーズだった。

 通信用のスレッド(抜粋)↓ ※メインのスレッド(main())が回ってないとおかしいことになる。main()は適当に無限ループにしておく

class tcpthread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        host = '○○○.○○○.○○○.○○○'  ←IPアドレス
        port =(ポート番号)

        tcpcom=0   ←ポート処理用のフラグ(後述)

       ↓ソケットのインスタンスを作成
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        while tcpcom==0:   ←ポートが詰まってたら別のポートを探す処理(何度もテストしてるとたまにポートが詰まる。時間経過でまた開くが面倒なので別のポートをスキャンする)
            try:
                s.bind((host,port))
                tcpcom=1   ←ポートがあったらフラグを立ててループ脱出
                print "connection port:",port  ←つながったポートを表示。android側はこれを入力             except:
                port =port +1 ←詰まってたら次のポートへ

    s.listen(1) ←受け付けるクライアントは1つのみ

    ↓通信ループ
    while True:
            print "waiting"
            conn,addr=s.accept()  ←クライアントからの接続待ちつながるまでここで止まる
            print "accept port :",conn,"addr :",addr   ←つながったIPを表示
            data=conn.recv(120)       ←クライアントからデータ受け取る()内は受け取るバイト数

      conn.send("send python\n")  ←クライアントにデータ送信


    //s.close() ←ソケットのクローズ。通信ループの外にあることに注目。今のところソケットは開けっ放しである。


 if __name__ == '__main__':
    tcp= tcpthread()
    tcp.setDaemon(True)  ←スレッドを綺麗に終わらせる呪文。
    tcp.start()
    time.sleep(1)
    main()


■次にandroid。初心者だったので辛かったが以下の参考資料などを参考にどうにか実装
http://yamato-iphone.blogspot.jp/2012/06/blog-post_5123.html

まずマルチスレッドのやり方。AsyncTaskというクラスを使うのが正道っぽいがなんだか面倒くさそうだったのでもっと楽なやり方(ハンドラを使った普通のマルチスレッド)で実装
public class MainActivity extends Activity {

↓(実質的な)グローバル変数
private Handler mHandler;  ←ハンドラを作っておく
 serverflag =0;  ←サーバーがあるかどうかのフラグ。後述

↑ここまでグローバル変数
protected void onCreate(Bundle savedInstanceState) { 

~略~

mHandler =new Handler(); ←ハンドラのインスタンス作成

/////マルチスレッド、ハンドラを使っているからGUI書き換えもできる。onCreateの中に書いてしまっている
        (new Thread(new Runnable() {

            @Override
            public void run() {
                    while(true){
                         mHandler.post(new Runnable(){
                             @Override
                             public void run(){
       ↑ここまでほぼ呪文。普通のマルチスレッドの中でハンドラを経由した処理を実行している(と思う)。GUIの更新が絡む処理がこのrun()のなかでやること。でないと例外が出る仕様
                                 socketcom("test")    ←後述するソケット通信関数
                                 textview1.setText(Integer.toString(”文字”));
                                 System.out.println("マルチスレッド 通過");

                                


                             }
                             });
                       
                        try { ←マルチスレッドのtry

                                    Thread.sleep(30); ←ウエイトを設定。別にここじゃなくてもいい。通信が絡む場合はウエイトが必要
↓以下も呪文
                            } catch (InterruptedException e) {}
                    }
            }
    })).start();
        ///////ここまでマルチスレッド

○ここからソケット通信の話↓ というか自分用テンプレート関数

public void socketcom(String str){ ←引数は別になんでもいい。無くてもいい
if (serverflag != 1){   ←サーバ無かったら以下の処理素通り
    try {
        //タイムアウトを設定。この辺は重要。タイムアウトを手動設定しないと通信途絶と同時にアプリも止まる。socket =new Socket(IP,PORT) で一発接続しないのがコツ


        InetSocketAddress addr = new InetSocketAddress(ipaddr, port); ←とりあえずアドレスだけ専用のインスタンスにまとめておく。あと当然だがアドレスとポートはサーバのものを設定する
        socket =new Socket(); ←カラのソケットを作る
        socket.connect(addr,500); ←ソケットを接続。引数の数字はタイムアウト時間(ms)。時間はお好みで
       
↓ 入力と出力のストリームを作っておく。ほぼ呪文。生の通信データ(byte)を使いやすい形に変換しているためなんか複雑になっている模様
        DataOutputStream out = new DataOutputStream(socket.getOutputStream());

        BufferedReader inp = new BufferedReader(new inputStreamReader(socket.getInputStream()));

↑入出力定義

         out.writeBytes(str);  ←ソケットへの出力。pythonに届く

        String responseLine="";    入力を入れる変数。↓のifの中で作ってもいいかも

        if ((responseLine = inp.readLine()) != null) { ←「何かサーバから来てたら(呪文)」
                //System.out.println("Server: " + responseLine+cnt);
                textview.setText("Server: " + responseLine); 来たデータを任意に処理
            }else{

                System.out.println("null in");
            }

            out.close();   
            inp.close();
        socket.close();  ←クライアント側はサーバと違って開けっ放しにしない。一通信ごとに閉じる。開けっ放しにしようとして半日つぶした(個人的には開けっ放しにしたいが・・・)。



         } catch (UnknownHostException e) { ↓勝手に生成される例外処理IOのほうは重要
            e.printStackTrace();
            System.out.println("UnknownHost err"+e);
             textview.setText("Server:none (unknownhost)" );
            return;
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("io err"+e);
            tv.setText("Server:none (IO) " );
            serverflag=1;     ←「サーバー無し」のフラグを立てる。後から別のボタンとかでこれを0にすると再接続できる(実装してないから予想)
            return;
        }

}else{
    tv.setText("サーバーが見つかりませんでした " ); ←サーバ無かったときの処理。適当に。

}

androidのソケット通信ここまで

おまけメモ : ~とりあえずボタン~ ちょっとボタンがいるときに

public class MainActivity extends Activity { Button bt;
protected void onCreate(Bundle savedInstanceState) {

bt = new Button(this);
bt.setText("button");

(レイアウト).addView(bt);
bt.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                

                (ボタンが押されたときの処理)
               
            }
       });


~androidでwebカメラの映像を見る~ ここも死ぬほど詰まった。どうにもならず結局外部ライブラリ(MjpegView Class)を使うことに(GNU GPL v3 注意)。
使い方抜粋(大きさ固定の仕方)
mjv=new MjpegView(this) {//表示の大きさを固定にする
             @Override
             protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

              super.onMeasure(widthMeasureSpec, heightMeasureSpec);

              setMeasuredDimension( 320 , 240 );
             }
            };
mjv.setSource(MjpegInputStream.read("http://(IPアドレス):(ポート)/?action=stream.mjpeg"));


以上。これでとりあえずandroidからpythonにデータを送れる(wifiでも)、しかもraspberry piにつないだwebカメラの映像もandroidで見れる(raspi側で mjpeg streamer などが必要)。
参考資料
http://linux.yebisu.jp/memo/800

これであとはpython-arduinoのプロトコルを作ればめでたくAndroidでコントロールできるカメラつきラジコンが出来上がる・・・予定。テストプロトコルでモーター動かすだけで満足してしまっている自分が怖い