uTrackMe おもしろい!

uTrackMe おもしろいです.かなり DIY なアプリなのですが,GPSログを任意のサーバにPOSTで投げつけられます.ドキュメントもちゃんと読んでませんが,アプリを起動してからのトラックログをまとめて任意の秒間隔*1でサーバに通知できるようです.(最大で何点分のトラックを投げられるかは調べてない).

ともあれ,必要なのは最新の情報だろ!ってことで,受信用の ruby サーバCGIスクリプトを書いてみました!!

#!/usr/bin/ruby
print "Content-type: text/html\n\n"

require "cgi"
cgi = CGI.new

if cgi["label"] != "" then
  i = 0
  while i < 86400
    if cgi["time[#{i}]"] == ""
      n = i
      break
    end
    i += 1
  end

  t = Time.at(cgi["time[#{n-1}]"].to_i)

  str = cgi["label"] + ', ' + t.strftime("%Y/%m/%d-%H:%M:%S").to_s + ", " + cgi\
["time[#{n-1}]"] + ', ' + cgi["lat[#{n-1}]"] + ', ' + cgi["lon[#{n-1}]"] +  ', \
' + cgi["alt[#{n-1}]"]

  File.open("./data/" + t.strftime("%Y%m%d").to_s, 'a'){|f|
    f.write str + "\n"
  }

else
  str = 'Null'
end

print <<EOM
[SUCC] #{str}
EOM

現状,最新のポイントをサーバに送って,サーバではCGIの動いている直下の data フォルダにログを書き出すだけです.つまり,WiFiや3G接続できないときはそのときのGPS情報がサーバに届かずロストします.
しかしながら,このアプリではアプリを起動してからの過去のログもキッチリサーバに送っているので,これらの情報をスクリプト側で再度拾ってやることで,ロストしていたトラックまで補完できますね! いや,これはすごい! 早くこのスクリプトを完成させよう.

アップデートしたよー

#!/usr/bin/ruby
print "Content-type: text/html\n\n"

require "cgi"
require "csv"

cgi = CGI.new

if cgi["label"] != "" then
  i = 0
  while i < 86400
    if cgi["time[#{i}]"] == ""
      n = i
      break
    end
    i += 1
  end

  t = cgi["time[#{n-1}]"].to_i
  time = Time.at(t)

  str = cgi["label"] + ',' + time.strftime("%Y/%m/%d-%H:%M:%S").to_s + "," + cgi["time[#{n-1}]"] + ',' + cgi["lat[#{n-1}]"] + ',' + cgi["lon[#{n-1}]"] +  ',' + cgi["alt[#{n-1}]"]

  if File.exist?("./data/" + Time.at(t).strftime("%Y%m%d").to_s) == true
    fn = "./data/" + Time.at(t).strftime("%Y%m%d").to_s
  else
    if File.exist?("./data/" + Time.at(t-1).strftime("%Y%m%d").to_s) == true
      fn = "./data/" + Time.at(t-1).strftime("%Y%m%d").to_s
    end
    fn = nil
  end
  
  if fn != nil
    f = open(fn, "r")
    f.seek(-str.size-1, IO::SEEK_END)
    last = f.gets
    last_t = CSV.parse(last)[0][2]
    f.close

    i = 0
    forgotten_num = 0
    last_put_time = 0
    while i < 86400 && cgi["time[#{i}]"].to_i < t
      if cgi["time[#{i}]"].to_i > last_t.to_i + cgi["delta"].to_i
        forgotten_str = cgi["label"] + ',' + Time.at(cgi["time[#{i}]"].to_i).strftime("%Y/%m/%d-%H:%M:%S").to_s + "," + cgi["time[#{i}]"] + ',' + cgi["lat[#{i}]"] + ',' + cgi["lon[#{i}]"] +  ',' + cgi["alt[#{i}]"]
         
        if cgi["time[#{i}]"].to_i - last_put_time > cgi["delta"].to_i
          File.open("./data/" + Time.at(cgi["time[#{i}]"].to_i).strftime("%Y%m%d").to_s, "a"){|f|
            f.write forgotten_str + "\n"
          }
          last_put_time = cgi["time[#{i}]"].to_i
          forgotten_num += 1
        end
      end
      i += 1
    end
  end
    
  File.open("./data/" + time.strftime("%Y%m%d").to_s, "a"){|f|
    f.write str + "\n"
  }
  
else
  str = 'Null'
end

if forgotten_num == 0
print "[Succ] " + str
else
  print forgotten_num.to_s + " point(s) not completed just have been up-loaded!"
  File.open("./data/error", "a"){|f|
    f.write Time.now.to_s + " " + forgotten_num.to_s + " point(s) not completed just have been up-loaded!" + "\n"
  }
end

というわけで,準リアルタイムアップロード中に WiFi や 3G がちょん切れても,再接続時に未アップロードのポイントをまとめてアップするバージョンに対応しました.
ただし,日付越えをまだ試験しておらんので,注意.

設置方法は簡単.ruby-CGI の動く Linux PC (たぶんどのプラットフォームでもOK)で,CGIとして動くようにこのプログラムを配置し,配置したディレクトリ直下に chmod 777 な data ディレクトリを設ける.設置した cgi へ向けて uTrackMe を設定すれば完了.
data/以下に,その日の日付のテキストファイルが生成され,下記のようなファイルが生成されます.1カラム目の文字列は, uTrackMe で設定した文字列が入ります.

iPodTouch3G,2010/05/24-02:47:06,1274636826,35.XXXXXX,139.XXXXXX,29
iPodTouch3G,2010/05/24-02:47:12,1274636832,35.XXXXXX,139.XXXXXX,27
iPodTouch3G,2010/05/24-02:47:16,1274636836,35.XXXXXX,139.XXXXXX,27
iPodTouch3G,2010/05/24-02:47:22,1274636842,35.XXXXXX,139.XXXXXX,28
iPodTouch3G,2010/05/24-02:47:26,1274636846,35.XXXXXX,139.XXXXXX,30

フォーマットは,

端末識別文字列, 日時, UNIX時刻,緯度,経度,高度

です.
さて,あとはこれを Google Maps に投影するだけなんだけどね….

なお,本プログラムですが,ヘタクソでも目をつぶってください.はじめて ruby さわりましたのです.

  • 未実装&バグ
    • 複数台対応.現状端末名1つにしか対応してない.
    • たまに,uploadミスしてないのに,数十個〜数百個のポイントがアップロードされてないと判断して,二重にアップロードしちゃう.なんでだろう. 標準出力の桁数が変化したときにf.seekが上手く動いてないのが原因ですね.明日直しておきます.

*1:ミニマム5秒ですが