httpを用いたデバイス制御 のバックアップの現在との差分(No.6)

更新


  • 追加された行はこの色です。
  • 削除された行はこの色です。
[[電気回路/zynq]]

* 概要 [#s41f0955]

ブラウザや Web API を使って http または https 経由でデバイスを制御したい。

- ブラウザから Web Form でレジスタをいじる
- Lab View などから Web API を叩いてレジスタをいじる

** 目次 [#ff916303]

#contents

* Web アプリのフレームワークに Sinatra を使う [#u717b5cd]

いろいろ考えられるけれど、ここでは ruby 製の Sinatra を使ってみる。

- http://sinatrarb.com/

理由は自分が ruby とか Rails とかに慣れていることと、Rails ほど重厚なフレームワークは必要ないこと。

ruby 製の簡易Webサーバーで動くため、apache や nginx を導入することなく使える。

* ruby のセットアップ [#kc961b46]

zynq の Ubuntu 18.04LTS に rvm を入れて、そこから ruby や各種 gem を準備する。

https://www.digitalocean.com/community/tutorials/how-to-install-ruby-on-rails-with-rvm-on-ubuntu-18-04

 LANG:console
 $ # install rvm
 $ sudo apt-get install -y gnupg2
 $ gpg2 --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
 $ cd /tmp
 $ curl -sSL https://get.rvm.io -o rvm.sh
 $ cat rvm.sh | bash -s stable
 $ source ~/.rvm/scripts/rvm 
 $ rvm list known
  # MRI Rubies
  [ruby-]1.8.6[-p420]
  [ruby-]1.8.7[-head] # security released on head
  [ruby-]1.9.1[-p431]
  [ruby-]1.9.2[-p330]
  [ruby-]1.9.3[-p551]
  [ruby-]2.0.0[-p648]
  [ruby-]2.1[.10]
  [ruby-]2.2[.10]
  [ruby-]2.3[.8]
  [ruby-]2.4[.5]
  [ruby-]2.5[.3]
  [ruby-]2.6[.0]
  ruby-head
  ...
 $ rvm install 2.6 # ruby 2.6 のインストール → かなり時間がかかる
 $ which ruby
  /home/takeuchi/.rvm/rubies/ruby-2.6.0/bin/ruby
 $ ruby --version
  ruby 2.6.0p0 (2018-12-25 revision 66547) [armv7l-linux-eabihf]

* Sinatra を使う [#b4acd73e]

 LANG:console
 $ mkdir ~/sinatra_app
 $ cd ~/sinatra_app
 $ git init
 
 $ # rvm の自動切り替え設定
 $ echo 2.6 > .ruby-version
 $ echo sinatra_app > .ruby-gemset
 $ cd .
  ruby-2.6.0 - #gemset created /home/takeuchi/.rvm/gems/ruby-2.6.0@sinatra_app
  ruby-2.6.0 - #generating sinatra_app wrappers.........
 
 $ # bundler を使って Gemfile で gem を管理する
 $ gem install bundler
 $ bundle init
 $ cat Gemfile
  # frozen_string_literal: true
  
  source "https://rubygems.org"
  
  git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
  
  # gem "rails"
 $ git add .
 $ git config --global user.email "takeuchi@bk.tsukuba.ac.jp"
 $ git config --global user.name "Osamu Takeuchi"
 $ git commit -m "start development"
 
 $ # Sinatra のインストール
 $ cat << EOT >> Gemfile
 
  gem "sinatra"
  gem "sinatra-contrib"
  EOT
 $ bundle install
 
 $ # Hello, world! アプリの作成
 $ cat << EOT > app.rb
  require "sinatra"
  require "sinatra/reloader"
 
  get "/" do
    "Hello, world!\n"
  end
  EOT
 $ bundle exec ruby app.rb &
  [2019-03-28 08:29:29] INFO  WEBrick 1.4.2
  [2019-03-28 08:29:29] INFO  ruby 2.6.0 (2018-12-25) [armv7l-linux-eabihf]
  == Sinatra (v2.0.5) has taken the stage on 4567 for development with backup from WEBrick
  [2019-03-28 08:29:29] INFO  WEBrick::HTTPServer#start: pid=14700 port=4567
 $ curl http://localhost:4567/
  Hello, world!
 $ fg
 ^C
 $ git add .
 $ git commit -m "hello world!"

* 外部へ公開する [#i822ce3c]

どうやら動いているみたいだけれど、
ホストPCのブラウザから zynq 機器の IP アドレスを指定して
http://xxx.xxx.xxx.xxx:4567/ へアクセスしても、
「正常に接続できませんでした」と言われた。

はじめ、ファイアーウォールのせいかと勘違いしたのだけれど、
実は今の手順でビルドした Kernel には iptable の機能が
備わっておらず、ファイアーウォールなんて影も形もない状態だった。

で気づいたのは、zynq のコンソールからでも 127.0.0.1 以外の IP アドレスを指定すると

 LANG:console
 $ curl http://192.168.2.2:4567/ # 自分のアドレス
  curl: (7) Failed to connect to 192.168.2.2 port 4567: Connection refused

のように弾かれるのだった。これは、127.0.0.1 しか listen していない感じ。

 LANG:console
 $ ruby app.rb --help
  Usage: app [options]
      -p port                          set the port (default is 4567)
      -o addr                          set the host (default is localhost)
      -e env                           set the environment (default is development)
      -s server                        specify rack server/handler (default is thin)
      -q                               turn on quiet mode (default is off)
      -x                               turn on the mutex lock (default is off)
 $ ruby app.rb -o 0.0.0.0 &
 $ curl http://192.168.2.2:4567/
  Hello, world!

これで外部からも繋がるようになった。

ただし、ポートを 80 番にしようとすると Permission Denied と言われる。

Well known port にサーバーを建てるには sudo が必要。
加えて環境変数の読み込みも必要。

 LANG:console
 $ ruby app.rb -p 80
  /home/takeuchi/.rvm/rubies/ruby-2.6.0/lib/ruby/2.6.0/socket.rb:201:in `bind': Permission denied - bind(2) for 127.0.0.1:80 (Errno::EACCES)
 $ sudo bash -c "source /home/takeuchi/.rvm/scripts/rvm;ruby app.rb -o 0.0.0.0 -p 80"
  (うまくいく)

開発が終わり実用段階へ到達したら -e production なども付けたいところ。

* erb テンプレートを使う [#p50fc0f4]

app.rb
 LANG:rb
 require "sinatra"
 require "sinatra/reloader"
 
 get "/" do
   erb :index
 end

views/index.erb
 LANG:erb
 Hello, world from erb! 

として、

 LANG:console
 $ curl http://192.168.2.2/
  Hello, world from erb!
 $ git add .
 $ git commit -m "hello from erb"

* layout を使う [#l643e96d]

- 個々に指定した出力が layout.erb の yield の部分にはめ込まれる。
- @title のような変数を view 側と共有できる

app.rb
 LANG:rb
 require "sinatra"
 require "sinatra/reloader"
 
 get "/" do
   @title = "Device Editor"
   erb :index
 end

views/layout.erb
 LANG:erb
 <!DOCTYPE html>
 <html lang="ja">
 <head>
   <meta charset="utf-8" />
   <title><%= @title %></title>
 </head>
 <body>
   <h1><%= @title %></h1>
   <%= yield %>
 </body>
 </html>

 LANG:console
 $ curl http://192.168.2.2/
  <!DOCTYPE html>
  <html lang="ja">
  <head>
    <meta charset="utf-8" />
    <title>Device Editor</title>
  </head>
  <body>
    <h1>Device Editor</h1>
    Hello, world from erb!
  </body>
  </html>
 $ git add .
 $ git commit -m "layout introduced"

* フォームを作成 [#m9611d1f]

erb から uio コマンドを呼び出し、得られたレジスタ値をフォームの input に表示する。

app.rb
 LANG:ruby
 require "sinatra"
 require "sinatra/reloader"
 
 get "/" do
   @title = "Device Editor"
   @regs = [
     # 表示名   name値   値の読取コマンド
     ["Reg 01", "reg1", "uio 0  0 "],
     ["Reg 02", "reg2", "uio 0  4 "],
     ["Reg 03", "reg3", "uio 0  8 "],
     ["Reg 04", "reg4", "uio 0 12 "],
   ]
   erb :index
 end

views/index.erb
 LANG:erb
 <form method="post">
   <% @regs.each do |reg| %>
   <div class="field">
     <label class="label"><%= reg[0] %>
       <div class="control">
         <input class="input" type="text" placeholder="<%= reg[0]%>" 
                name="<%= reg[1]%>" value="<%= `#{reg[2]}` %>">
       </div>
     </label>
   </div>
   <% end %>
 
   <div class="field">
     <div class="control">
       <button class="button is-primary">Submit</button>
     </div>
   </div>
 </form>

ブラウザから読みに行けば、フォームにレジスタの現在値が表示される。

&ref(default-form.png,,50%);

 LANG:console
 $ git add .
 $ git commit -m "device editor form created"

* スタイルシートを適用する [#n10145ff]

Bulma ( https://bulma.io/ ) という css フレームワークを使ってみる。

- [[知っておくと少し幸せになれるBulmaを使い方6選>https://tonyo.design/program/bulma/good_6_things_to_know_about_bluma/]]

layout に
- viewport 指定
- css への link
- font awsome の script 読み込み
- yield を section, container で囲む
- h1 に .title を付ける

の変更を行った。

views/layout.erb
 LANG:erb
 <!DOCTYPE html>
 <html lang="ja">
 <head>
   <meta charset="utf-8" />
   <meta name="viewport" content="width=device-width, initial-scale=1">
   <title><%= @title %></title>
   <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.4/css/bulma.min.css">
   <script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
 </head>
 <body>
   <section class="section">
     <div class="container">
       <h1 class="title">
         <%= @title %>
       </h1>
       <%= yield %>
     </div>
   </section>
 </body>
 </html>

フォームの html は何も変更していないが、ちゃんと今風の表示になった。

&ref(form-styled.png,,50%);

 LANG:console
 $ git add .
 $ git commit -m "style sheed applied"
 $ git commit -m "style sheet applied"

* フォームによる値の変更を反映する [#v1759dd6]

上記のフォームで Submit ボタンを押すと、

 Try this: 
 
 post '/' do
   "Hello World"
 end

が表示される。

フォームの書き換えでは "/" に対して POST が生じるのだ。
フォームの書き換えでは自身のアドレス "/" に対して POST が生じるのだ。

そこで app.rb を、

 LANG:rb
 require "sinatra"
 require "sinatra/reloader"
 
 REGS = [
   ["Reg 01", "reg1", "uio 0  0 "],
   ["Reg 02", "reg2", "uio 0  4 "],
   ["Reg 03", "reg3", "uio 0  8 "],
   ["Reg 04", "reg4", "uio 0 12 "],
 ]
 
 get "/" do
   @title = "Device Editor"
   @regs = REGS.clone
   erb :index
 end
 
 post "/" do
   @title = "Device Editor"
   @regs = REGS.clone
   @regs.each do |reg|
     if params.has_key? reg[1]
       begin
         value = Integer(params[reg[1]])
         system reg[2] + value.to_s # 値を書き込む
         system reg[2] + value.to_s # 値を書き込む(http からの値を system にそのまま渡すとか、超危険なので絶対真似しちゃダメ)
       rescue
         reg[3] = "error"
       end        
     end
   end
   erb :index
 end

とすることで、レジスタ値を変更することが可能になった。

 LANG:console
 $ git add app.rb
 $ git commit -m "post requiest is handled"


* SSL 化する? [#me8cda8f]

- https://qiita.com/k-ta-yamada/items/a8a50bd6e75e08c802dc
- https://stackoverflow.com/questions/3696558/how-to-make-sinatra-work-over-https-ssl

Lab View からのアクセスを考えると、むやみに SSL 化しない方が使いやすいかも。

どちらにしても LAN 内にしか公開しないし。

* 認証 [#fe47dab0]

こちらはすべきかなあ。

https://qiita.com/hiroki_y/items/06f5780543bec988d8b7#basic%E8%AA%8D%E8%A8%BC

まあ、しばらくはなしでも。

* 自動起動 [#z6271ba5]

とにかく起動するだけだけれど、以下のようにして行えた。

Webrick をデバッグモードで外向きに建てるとか
本来はあり得ないのでしょうけれど、
とりあえず開発用ということで、
しばらくはこのままいく。

https://blog.freedom-man.com/start-stop-daemon/

/boot/fpga_init.d/99_sinatra_app
 LANG:sh
 #!/bin/bash
 
 . /lib/lsb/init-functions
 
 PIDFILE=/home/takeuchi/sinatra_app/app.pid
 
 do_start () {
   cd /home/takeuchi/sinatra_app
 
   . /home/takeuchi/.rvm/scripts/rvm
   DAEMON=`which ruby`
   DAEMON_ARGS="/home/takeuchi/sinatra_app/app.rb -o 0.0.0.0 -p 80"
 
   echo -n "starting sinatra_app..."
   start-stop-daemon --start --background --exec $DAEMON --make-pidfile --pidfile $PIDFILE -- $DAEMON_ARGS
 
   result=$?
   if [ $result != "0" ]; then
     pid=`cat $PIDFILE`
     echo "daemon is already running. (pid=${pid})"
     exit 1
   else
     echo
   fi
 }
 
 do_stop () {
   echo -n "stopping sinatra_app..."
   start-stop-daemon --stop --pidfile $PIDFILE
   result=$?
   if [ $result != "0" ]; then
     pid=`cat $PIDFILE`
     echo "daemon is not running. (check $PIDFILE)"
     exit 1
   else
     echo
   fi
   rm -f $PIDFILE
 }
 
 case $1 in
   start)
     do_start
     ;;
   stop)
     do_stop
     ;;
   *)
     echo "Usage: $0 {start|stop}"
     exit 2
     ;;
 esac


作業履歴へ戻る → [[電気回路/zynq]]

* コメント・質問 [#n4ea9bc6]

#article_kcaptcha


Counter: 2504 (from 2010/06/03), today: 2, yesterday: 0