Slide 1

Slide 1 text

RAILSとSERVERLESS技術で 鉄道アプリを作った話 なぜ僕はRUBYでバイナリをパースしたのか 2020.03.19 銀座Rails#19 @free_world21

Slide 2

Slide 2 text

■ ⼩林 ノエル(36) ■ 職業︓ソフトウェアエンジニア – フリーランス – エメラダ株式会社 (emerada.co.jp) 執⾏役員 ■ 2008: フリーランスエンジニアとして独⽴ – flash/C#/rails/iOS/Androidなどなど ■ 2009: ⼤学院修了(情報理⼯学修⼠) ■ 2009: IPA未踏事業に採択される ■ 2016: エメラダ株式会社に参画 ■ 2018: 同社執⾏役員 ■ 〜現在: フリーランス&会社員として活動中 ■ 趣味︓世界のコワーキングスペースめぐり (ワーケーション︖) @free_world21 ANA B777-300 NH11 THE FARM@NY nomad works@NY CARR WORKPLACE@Chicago

Slide 3

Slide 3 text

エメラダ株式会社 ■ 2016年創業 ■ ⾦融分野で3業種の登録をし、1業種は廃業 クラウドファンディング形式で 個⼈投資家が未上場のベンチャーに 投資できるサービス 2017年11⽉リリース *2019年10⽉事業譲渡 *証券業を廃業 2018年5⽉リリース 成⻑中⼩企業を中⼼に 累計10億円を融資実⾏ 成⻑中⼩企業向け オンラインレンディング サービス 銀⾏と中⼩企業を結ぶ 資⾦繰り管理&モニタリング サービス 2019年5⽉リリース 地銀&信⾦を中⼼に数⼗⾏(庫)に 導⼊済み emerada-bank.com emerada-marketplace.com

Slide 4

Slide 4 text

エメラダ株式会社 ■ 2016年創業 ■ ⾦融分野で3業種の登録をし、1業種は廃業 クラウドファンディング形式で 個⼈投資家が未上場のベンチャーに 投資できるサービス 2017年11⽉リリース *2019年10⽉事業譲渡 *証券業を廃業 2018年5⽉リリース 成⻑中⼩企業を中⼼に 累計10億円を融資実⾏ 成⻑中⼩企業向け オンラインレンディング サービス 銀⾏と中⼩企業を結ぶ 資⾦繰り管理&モニタリング サービス 2019年5⽉リリース 地銀&信⾦を中⼼に数⼗⾏(庫)に 導⼊済み emerada-bank.com emerada-marketplace.com 2020年 AI財務分析サービス リリース予定 *エンジニア絶賛募集中︕︕ **お気軽にお声がけください

Slide 5

Slide 5 text

今回は鉄道アプリ開発の はなし

Slide 6

Slide 6 text

鉄道システムの雑な説明 TTC(Total Trafic Control) PTC(Programed Trafic Control)

Slide 7

Slide 7 text

TTC(Total Trafic Control) PTC(Programed Trafic Control) TID(Traffic Information Display) TID server TID client 鉄道システムの雑な説明

Slide 8

Slide 8 text

TTC(Total Trafic Control) PTC(Programed Trafic Control) TID(Traffic Information Display) TID server client 鉄道システムの雑な説明 ⽬的 スマホアプリとして 表⽰したい︕ *スクショは既存アプリの例

Slide 9

Slide 9 text

TTC(Total Trafic Control) PTC(Programed Trafic Control) TID(Traffic Information Display) TID server client 鉄道システムの雑な説明 バイナリ形式の データでやり取り client ⽬的 スマホアプリとして 表⽰したい︕ ここもやっぱり バイナリ *スクショは既存アプリの例

Slide 10

Slide 10 text

考案されたアーキテクチャ TID server TID clientもどき VPN Amazon VPC Amazon S3 Bucket JSONに変換 client ア プ リ に 配 信 *スクショは既存アプリの例

Slide 11

Slide 11 text

プロジェクト苦労話 ■ 導⼊されていたTIDシステムは1990年代後半に設計されたもの – 仕様書の created_at が1998年 ■ システム(仕様書)はIE4(当時の最新ブラウザ)を前提に設計されていた ■ めちゃくちゃステートフルな設計 – クライアント側はまず初期状態データをもらい、その後は差分データをもらう ような仕様 – 通信回線が貧弱だった時代。1ビットも無駄にできない。 ■ データはTCP/UDPソケット経由 – ︓「HTTP︖そんな軟派なプロトコルなんぞ信じられん」 ■ 何故かもらえないサンプルデータ – エンジニア︓「どうやって開発しろと・・・」 – どうやって⼿に⼊れたかは⼝頭でのみ説明

Slide 12

Slide 12 text

Let’s parse binary data with Ruby

Slide 13

Slide 13 text

Array#pack, String#unpack ■ String#unpack: バイナリデータを読み込んで⼈間が扱いやすい形にする ■ Array#pack: ⼈間が扱い安い形をバイナリデータにする content = File.read('./binary_data/input.data’) # 先頭1バイトを16進数として読み出し、10進数に変換 content[0].unpack("H*")[0].to_i(16) # 先頭2バイトを16進数として読み出し、10進数に変換 content[0..1].unpack("H*")[0].to_i(16) # 先頭4バイトを16進数として読み出し、10進数に変換 content[0..3].unpack("H*")[0].to_i(16)

Slide 14

Slide 14 text

Array#pack, String#unpack ■ String#unpack: バイナリデータを読み込んで⼈間が扱いやすい形にする ■ Array#pack: ⼈間が扱い安い形をバイナリデータにする content = File.read('./binary_data/input.data’) # 先頭1バイトを16進数として読み出し、10進数に変換 content[0].unpack("H*")[0].to_i(16) # 先頭2バイトを16進数として読み出し、10進数に変換 content[0..1].unpack("H*")[0].to_i(16) # 先頭4バイトを16進数として読み出し、10進数に変換 content[0..3].unpack("H*")[0].to_i(16) "\xF0\xE1\u000F\u001E" ["f0e10f1e"] 4041281310

Slide 15

Slide 15 text

Array#pack, String#unpack ■ String#unpack: バイナリデータを読み込んで⼈間が扱いやすい形にする ■ Array#pack: ⼈間が扱い安い形をバイナリデータにする File.open("./binary_data/output.data", 'w+') do |f| # 10進数を16進数に変換し、バイナリとして書き出す f.write [123.to_s(16)].pack("H*") # "E2 BB B0 F1"をバイナリとして書き出す f.write ["e2bbb0f1"].pack("H*") # "10110110" = \xB6 という2進数をバイナリとして書き出す decimal_number = "10110110".to_i(2) # いちど10進数にする f.write [decimal_number.to_s(16)].pack("H*") end

Slide 16

Slide 16 text

Maskをしたい時 "11101010" ■ 例︓1バイトの中で真ん中4ビットが⾞両数(6両編成、8両編成、10両編成) 0xEA

Slide 17

Slide 17 text

Maskをしたい時 "11101010" ■ 例︓1バイトの中で真ん中4ビットが⾞両数(6両編成、8両編成、10両編成) 0xEA ”00111100" & ”00101000" || 0x3c 0x28

Slide 18

Slide 18 text

Maskをしたい時 "11101010" ■ 例︓1バイトの中で真ん中4ビットが⾞両数(6両編成、8両編成、10両編成) 0xEA ”00111100" & ”00101000" || 0x3c 0x28 mask_number = 0x3c # 0xea は実際はどこかから読み出される値 result = 0xea & mask_number # 2ビット右シフト num_of_car = result >> 2 => 10

Slide 19

Slide 19 text

Maskをしたい時 "11101010" ■ 例︓1バイトの中で真ん中4ビットが⾞両数(6両編成、8両編成、10両編成) 0xEA ”00111100" & ”00101000" || 0x3c 0x28 0xea.to_s(2)[2..5].to_i(2) => 10 ⽂字列操作のほうが楽だったりする

Slide 20

Slide 20 text

余談︓rubyでソケット通信 ■ TIDサーバは社内からしかアクセスできない ■ しかし開発者は⼿元で開発したい – 常駐ぜったいしたくないマン ■ Wi○○○○arkで通信の内容を解析 ■ 開発者が⼿元で動く疑似TIDサーバを実装 ■ 疑似TIDサーバを相⼿に、列⾞情報のやりとりをするプログラムを実装 ■ 本物のTIDサーバとつながったのはリリース3⽇前 assign_port_server = TCPServer.new('0.0.0.0', 21000) client = assign_port_server.accept req = client.recv(1) client.send ['00'].pack('H*'), 0 socket = TCPClient.new('0.0.0.0', 21000) socket.puts “hello, tcp server” Server Client

Slide 21

Slide 21 text

アプリからのアクセスはS3のみ ■ AmazonS3は超優秀なWebサーバ – S3内のファイルはHTTP(S)でアクセス可能 ■ iOS/Android両⽅対応しなければなかかったので、アプリ画⾯の多くはWebView ■ Railsで列⾞在線位置画⾯の haml/JS/CSS をコーディング ■ hamlはActionView::Baseを使ってHTMLにレンダリングしてS3にデプロイ ■ assets:precompileしたjs/cssをS3にデプロイ ■ TIDサーバとやりとりするTIDクライアントもどきもrails内に実装 renderer = ActionView::Base.new("#{Rails.root}/app/views/") html = renderer.render ( template: "#{line_name}/index.html.haml", layout: ‘layouts/application.html.haml’)

Slide 22

Slide 22 text

実際のアーキテクチャ TID server TID clientもどき VPN Amazon VPC Amazon S3 Bucket JSONに変換 CI/CDでデプロイ アプリに配信 HTML内 のJSが 取 得 *スクショは既存アプリの例

Slide 23

Slide 23 text

まとめ ■ 鉄道システムについて雑に説明しました ■ Rubyでバイナリをパースする⽅法を紹介しました ■ ServerlessにHTML/JS/CSS, JSONを配信する仕組みをご紹介しました ■ 次回 – Rails with Serverlessについてもっとくわしく︖ – Railsとkubernetesについて︖ https://github.com/f-world21/binary_sample