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"
* フォームによる値の変更を反映する [#v1759dd6]
上記のフォームで Submit ボタンを押すと、
Try this:
post '/' do
"Hello World"
end
が表示される。
フォームの書き換えでは "/" に対して 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 # 値を書き込む
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
* コメント・質問 [#n4ea9bc6]
#article_kcaptcha
Counter: 2528 (from 2010/06/03),
today: 2,
yesterday: 2