豆腐とコンソメ

豆腐とコンソメ

幸せと、主にラズパイとpythonについて書きます。

Udemyの無料のExpress講習受講メモ

Expressをすこしさわってみた。

Udemyの無料のExpress講習を受けたのでメモがてら記載します。
www.udemy.com

また他にも、以下の記事を参考にしました。
Express - Node.js web application framework

qiita.com

Expressをとりあえずインストー

npmでパッケージ取得をします。

(raspberry_3.5.1) masao-3:legocar_node.js konoe_mario$ npm install express

適当につくったプロジェクトのエントリーポイント、server.jsに以下のコードを書く。

var express = require("express");
var app = express();

app.get('/',function(req, res){
  res.send('Hello World');
});

app.listen(6677,function(){
  console.log("リクエストがあったよ");
});

ブラウザで「http://localhost:6677」にアクセスすると、「Hello World」が確認できる。

この時点で、Expressを導入するとURLディスパッチャの機能(ここではルーティングというべきかも)がシンプルになっていることがわかる。

Expressを使わない場合
server.on("request",function(req, res){
    var incomingUrl = url.parse(req.url);

   if(incomingUrl.pathname === "/controller"){
         if(req.method == "GET"){
                   res.end('Hello World');
         }
   }

});
Expressを使った場合
app.get('/controller',function(req, res){
  res.send('Hello World');
});

上記以外にも、javascirptやcssなどのstaticファイルをどうやって提供するかとか、エラーの場合のハンドリングとかいろいろあるみたい。
ただ、Expressを使わない場合の純粋なnode.jsだけでそこまでやってないので、あんまり有り難みがわからないかもしれません。

Expressのスケルトンを使う

とりあれず、触ってみようということで、Expressのスケルトンを作成してくれる機能を用いて使ってみます。

とりあえず起動してみる

Expressのスケルトンを作成するモジュールをインストールしておきます。 今回は、-gオプションを指定して、グローバル環境につっこんじゃいます。

(raspberry_3.5.1) masao-3:legocar_express konoe_mario$ npm install express-generator -g

インストールが完了したら、以下コマンドを実行します。

(raspberry_3.5.1) masao-3:legocar_express konoe_mario$ express

すると、いろいろなディレクトリやら、ファイルやらが作成されます。

(raspberry_3.5.1) masao-3:legocar_express konoe_mario$ tree -L 1
.
├── app.js
├── bin
├── node_modules
├── package.json
├── public
├── routes
└── views

package.jsonを見てみます。
これはExpressどうのこうのの話ではないのですが、dependenciesには必要なモジュールが記載されているみたいです。
結構いっぱいありますね。

  1 {
  2   "name": "legocar-express",
  3   "version": "0.0.0",
  4   "private": true,
  5   "scripts": {
  6     "start": "node ./bin/www"
  7   },
  8   "dependencies": {
  9     "body-parser": "~1.17.1",
 10     "cookie-parser": "~1.4.3",
 11     "debug": "~2.6.3",
 12     "express": "~4.15.2",
 13     "jade": "~1.11.0",
 14     "morgan": "~1.8.1",
 15     "serve-favicon": "~2.4.2"
 16   }

これを個別で「npm install body-parser」としていくと面倒なので、以下コマンドで、dependenciesに記載されているモジュールをインストールしてくれます。

(raspberry_3.5.1) masao-3:legocar_express konoe_mario$ npm install

さて、起動してみましょう、というところですが、肝心のエントリーポイントがわかりません。
何を「node xxx」で実行すればいいんでしょう。

package.jsonをよくみると、scirpts配下にstartなるものがあります。
どうもスクリプト経由で実行するのが一般的っぽいです。

スクリプトを実行するには、npmコマンドを使えばよいそうです。

(raspberry_3.5.1) masao-3:legocar_express konoe_mario$ npm start

もちろん、下記で実行しても同じです。

(raspberry_3.5.1) masao-3:legocar_express konoe_mario$ node ./bin/www

少し話がそれますが、node.jsのプログラムを直すたびに、Webサーバーをctrl + cで落として、再度起動させるのは面倒です。

なので、変更があった場合、それを検知して勝手に取込み直してくれるモジュール、nodemonをインストールしておきます。

(raspberry_3.5.1) masao-3:legocar_express konoe_mario$ npm install nodemon -g

nodemonの使い方は、単純に「node xxxx」としていたところを「nodemon xxxx」と置き換えるだけです。

Expressで使用するときには、先ほどのscriptsをnodemonで実行するように置き換えておきます。

 5   "scripts": { 
 6     "start": "nodemon ./bin/www"
 7   },

これで、「npm start」としたときにnodemonで実行されます。

それでは、繰り返しになりますが、以下のコマンドで起動します。

(raspberry_3.5.1) masao-3:legocar_express konoe_mario$ npm start

その後、「http:localhost:3000」にアクセスしてみると、以下が表示されるかと思います。

※デフォルトのポート番号は3000みたいです。

f:id:konoemario:20170721121713p:plain:w500

もうすこしみていく

無事、表示されたので、もう少し見ていきます。
といっても細かいところだだいぶわからない。

ルーティング

ケルトンで実装されているルーティング例として、「http://localhost:3000」と「http://localhost:3000/users」とがある。

これをどのように実現しているか見てみる。

冒頭のチュートリアルの例だと、/に対しての割り当ては以下のようにしていた。

app.get('/',function(req, res){
  res.send('Hello World');
});

ケルトンではもう少し実践的な?内容になっている。

app.jsファイルには、app.useだったり、app.setだったりいろいろあるのだけれども、いきなり何これ状態です。

ここに関しては素敵な記事がありました。

qiita.com

app.useを使って、指定のpathにきたリクエストに対して、処理を記述できます。

な、なるほどね!みたいな感じで次にいきます。

app.js

app.use('/', index);
app.use('/users', users);

app.useで設定しているindexとusersですが、以下のようにrequireでインスタンス化(この表現があってるのかわかりませんが)したものを設定しています。

app.js

var index = require('./routes/index');
var users = require('./routes/users');

一例として、users.jsを見てみます。

users.js

var express = require('express');
var router = express.Router();

/* GET users listing. */
router.get('/', function(req, res, next) {
  res.send('respond with a resource');
});

module.exports = router;

express.Router()と、router.getというものがでてきます。
router.getについては、冒頭のapp.getと似たようなものと考えればいいのですが、express.Router()はあんましっくりきてないです。

一点、おお、と思ったのが、router.get(‘/’,function)の/ですが、あくまで「http://localhost:3000/users」を/としています。

なので、以下のように「/hoge」を追加した場合、「http://localhost:3000/users/hoge」に対する処理となります。

express.Routerがこのあたりの仕組みを担っているのでしょうか。

users.js

var express = require('express');
var router = express.Router();

/* GET users listing. */
router.get('/', function(req, res, next) {
  res.send('respond with a resource');
});

//追加
router.get('/hoge', function(req, res, next) {
  res.send('fuga');
});

module.exports = router;

また、これはexpressの話というより、node.jsの話ですが「module.exports = router」の部分はrequrie()したときの返り値になるみたいです。

fernweh.jp

なので、さきほどのインスタンス化という表現をしていましたが、以下のapp.jsの処理ではexpress.Router()のから返されるオブジェクトが設定されているということがわかりました。

app.js

var index = require('./routes/index');
var users = require('./routes/users');

なんだかわかったようなわからないようなという感じですが、少しすっきりしました。

app.useの補足

さきほど、app.useの説明については、参考記事から引用させていただきました。

app.useを使って、指定のpathにきたリクエストに対して、処理を記述できます。

この部分について、もう少し掘り下げたいと思います。
ルーティングの箇所でrouter.getとかは置いておいて、基本的なやりかたとしては、以下の書き方になっていました。

app.get('/',function(req, res){
  res.send('Hello World');
});

これは、こう書いても同じことができるみたいです。

app.use('/',function(req, res){
  res.send('Hello World');
});

といっても、こうするとさきほどの「/users」にアクセスしたとしても、HelloWorldが表示されてしまいます。

そもそものapp.useの使い方は、とあるリクエストに対して、このミドルウェアを使うとか、そういった場合に使うみたいです。

使い方が非常に怪しいですが、一例をあげます。

以下のような関数、validateとrenderを作成します。
その上で、app.useで/testというURLに対して、関数を割り当てています。

app.js

app.use('/test',validate,render);

//認証っぽい関数
function validate(req, res,next){
    if(req.query.id === "100"){
        next();
        return;
    }
    res.send("validate Error");
}

//認証OKの場合
function render(req, res){
    res.send("validate OK");
}

http://localhost:3000/test?id=100」でアクセスすると、validate OKが表示されます。
ここでのポイントは、validate関数内のnext()になります。
next()はapp.useで渡した次の引数の関数が呼ばれる仕組みになっているみたいです。

もうすこしミドルウェアっぽい仕組みとして、リクエストがあったら、コンソールにログを出すということをしてみます。

以下のような関数を作成します。

app.js

//こんなかんじの関数を追加する。
function hoge(res,req,next){
   console.log("hoge");
   next();
}

おもむろにapp.useに追加します。

app.js

//hogeを追加
app.use(hoge);
app.use('/', index);
app.use('/users', users);
}

こうすると、ルートディレクトに限らず、どのディレクトリにアクセスしてもコンソールにhogeが表示されるかと思います。

このとき、next()をコメントアウトすると、hoge()だけが呼び出され、肝心のapp.use(‘/’, index)の処理が呼び出されません。

app.js

//こんなかんじの関数を追加する。
function hoge(res,req,next){
   console.log("hoge");
   //next();
}

app.useはさきほどのvalidateみたいに、app.user(function1,function2)みたいに書いたり、app.use(function1)、app.user(function2)みたいな書き方でもいいみたいですね。

冗長かもしれませんが、以下のようにapp.useの定義順によっては関数hogeが実行されませんでした。

app.js

app.use('/', index);
app.use('/users', users);
//ここに追加
app.use(hoge);
}

こういったことをするあれば、index.js側にもnext()をつけれあげればよさそうです。 あと、関数hogeのnext()は何もないのであれば、削除しときます。

index.js

var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
  next();
});

module.exports = router;

RaspBerryPiでラジコンをつくる(11):socket.ioをFlaskで実装する

日記

ラズパイにはまってから、IoTというキーワードによく反応するようになりました。
なかでも、トイレの使用状況をお知らせする、なんてのはよく聞く話かと思います。
先日、サービスエリアに寄った際も、トイレの混雑状況をお知らせする大きな看板が目につきました。
「空」だったり「混」という文字が出ているのを見て、お!っと思った次第です。
この仕組み自体は昔からあったものかもしれませんが、嬉しかった私は、奥さんに「見て!すごい目まぐるしく表示がかわってる!」とお伝えし、「満!混!満!混!」とはしゃいでいたら、奥さんにいろいろな意味で止めてくれる?、と窘められました。

さて、話はかわりますが、ここにきて、というか最初からかもしれませんが、ブログの目的が曖昧だったりします。
今はモチベーションを維持するためにも、こうやってやったことを発信できれば、と思っているのですが、成果物が雑だったり、成果物に対してブログを書くことに時間がかなり大きくって、どうなんだろうみたいなものもあって悩みどころです。

メモを取るだけだったらEvernoteにも書けばいいとも思いますし、きちんとした意見がほしいのであればQuitaに書いたほうがいいのかな、とか思ったりします。
モチベーションを保つためにもアクセス数はほしいし、切磋琢磨的なところで意見もほしいし、自分のメモとしても役に立ちたいし、みたいなところがいろいろあって、何が一番大事なのかを見失っています。

が、とりあえずは悩んで何もしないよりかは、まず動いている状態が大事だと思いますので、引き続きよろしくお願い致します。

前回の続き

前回はnode.jsを使ってsocket.ioを実装し、ブラウザからのキー入力をサーバーに送信するところまでやりました。
今回は、socket.ioのサーバー側の部分をpythonのWebアプリケーションフレームワークであるFlaskを使って、実装していきたいと思います。
理由は前回もありましたが、ラズパイのGPIOをコントロールする部分をpythonで実装しているためです。

tohutokonsome.hatenablog.com

Flaskとは

PythonのWebアプリケーションフレームワーク
以前扱った、PythonのWebアプリケーションフレームワークの「Django」よりさらに軽量(必要最低限のフレームワーク)な気がする。

実際の作業は公式?のチュートリアルを見て、試してみました。

1. Webアプリケーションを作る準備 — study flask 1 ドキュメント

ここに、socket.ioを実装していきます。

Flaskのsocket.ioについては、以下の記事を参考にさせていただきました。
Flask-SocketIOでWebSocketアプリケーション - Qiita

Welcome to Flask-SocketIO’s documentation! — Flask-SocketIO documentation


初期設定

ラズパイに、必要なpythonモジュールをインストールしていきます。

pythonのバージョンは以下を使用しています。

pi@raspberrypi:~ $ pyenv version
3.5.1 (set by /home/pi/.pyenv/version)

Flaskのインストー

pipで取得します。

pi@raspberrypi:~/myproduct $ sudo pip install Flask

地味にsudo権限だと環境変数変わるっていうのを忘れていて、混乱した。
sudoersを編集する必要があった。

WiringPiでエラーになった場合のメモ - 豆腐とコンソメ

インストールされてますね。

pi@raspberrypi:~/myproduct $ sudo pip freeze
click==6.7
Flask==0.12.2
itsdangerous==0.24
Jinja2==2.9.6
MarkupSafe==1.0
Werkzeug==0.12.2
wiringpi==2.32.1
wiringpi2==2.32.3

Flask-SocketIOのインストー

こちらもFlaskと同様にpipでもってくる。

pi@raspberrypi:~/myproduct $ sudo pip install flask-socketio

こちらも特に問題なくインストールされています。

pi@raspberrypi:~/myproduct $ sudo pip freeze
click==6.7
Flask==0.12.2
Flask-SocketIO==2.9.1
itsdangerous==0.24
Jinja2==2.9.6
MarkupSafe==1.0
python-engineio==1.7.0
python-socketio==1.7.6
six==1.10.0
Werkzeug==0.12.2
wiringpi==2.32.1
wiringpi2==2.32.3

唯一のモジュールの「app.py」の作成

Flaskを使ったチュートリアルでは、ブログを作成していたりしました。
しかし、今回必要なのは、以前作成したレゴカーのコントロール部分の橋渡しだけです。

f:id:konoemario:20170720223155j:plain:w500

なので、Flaskの基本的な機能については深く考えずに、進めます。

ということで、唯一作成するapp.pyの中身はほぼ以下の記事のままになります。

Welcome to Flask-SocketIO’s documentation! — Flask-SocketIO documentation

app.py

# -*- coding: utf-8 -*-
from flask import Flask
from flask_socketio import SocketIO,emit

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)

@socketio.on('connect',namespace='/legocar')
def init() :
    print('connected')

@socketio.on('sendMessage',namespace='/legocar')
def recieve_order(order) :

#ここにラズパイ操作用のモジュールを組み込む想定
    if order in {'forward','back'}:
       print("accell = " +order)
       testmethod(order)
    elif order in {'break'}:
        print("accell stop = " +order)
        testmethod(order)
    elif order in {'right','left'}:
       print("handle = " +order)
       testmethod(order)
    else:
        pass


@socketio.on('test',namespace='/legocar')
def testmethod(order):
    emit('test',order)


if __name__ == '__main__':
    socketio.run(app,host='0.0.0.0',port=5000)

検証がいろいろと足りていないのですが、自分のメモがてらポイントを記載しておきます。

  • Flaskは以下のように条件を、@構文で定義して、それに該当する処理である場合、直下の関数が呼ばれるみたい
#URLディスパッチャ、ホスト名のみでアクセスした場合の処理
@app.route('/')
def index():
    return 'Hello world!'
  • node.jsのsocket.onみたいなイベントも同じように@socketio.on(‘イベント名’,namespace)、直下に関数でいける

  • node.jsの場合、namespaceは使っていなかったけれども、どうもFlask-SocketIOでは必須?(未検証)

  • socketio.run(app)ではなくsocketio.run(app,host=‘0.0.0.0’,port=5000)とすること。 socketio.run(app)の場合、ローカルホスト内でしかFlaskにアクセスできない

そうしましたら、上記、app.pyをラズパイ上で実行します。

Flaskの起動

pi@raspberrypi:~/myproduct/legocar/legocar/server $ sudo python app.py 
WebSocket transport not available. Install eventlet or gevent and gevent-websocket for improved performance.
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

なにやらいわれていますが、無事起動しました。

これが終わったら、前回作成した「controller.html」を修正します。


controll.htmlの修正

前回、サーバー側のsocket.ioはnode.jsで実装していました。
これをさきほど作成したapp.pyに向きを変えます。

f:id:konoemario:20170720224254j:plain:w500

といっても、io.connectに引数として接続先を渡してあげるだけです。

controller.html(修正前)

    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
    <script src="/socket.io/socket.io.js"></script>
    <script>
        var socket = io.connect();

io.connectは引数なしの場合、デフォルトHTTPリクエストで要求したサーバー(ここではnode.jsのWebサーバー)に接続します。
引数を渡してあげることで、指定したサーバーに接続することができるみたいです。

また、接続先のアドレスですが、「ラズパイのIPアドレス:5000/legocar」としています。
ホスト名以降の/legocarってなんぞって感じなのですが、app.pyのnamespaceで指定したものになります。
namespaceそのものをあんまり理解してないので、ここではふーん、ぐらいで終えています。

controller.html(修正後)

    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
    <script src="/socket.io/socket.io.js"></script>
    <script>
        var socket = io.connect('http://ラズパイのIPアドレス:5000/legocar');

疎通確認

それでは、ラズパイにログインして、前回作成したnode.jsのプロジェクトとpythonのapp.pyを起動してみます。

node.jsのWebサーバー起動

pi@raspberrypi:~/myproduct/legoCarController $ node server.js

Flaskの起動(さっきと同じ)

pi@raspberrypi:~/myproduct/legocar/legocar/server $ sudo python app.py 
WebSocket transport not available. Install eventlet or gevent and gevent-websocket for improved performance.
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

これで、「http://ラズパイのIP:6677/controller」にアクセスすると、controller.htmlが表示され、ブラウザ上では、前回と同様の内容が表示されるます。
また、Flaskを起動しているコンソール画面には、ブラウザからのキー入力に応じて、app.pyのprintしている内容が表示されることが確認できます。

192.168.1.5 - - [19/Jul/2017 15:58:07] "POST /socket.io/?EIO=3&transport=polling&t=LrPexJY&sid=9364d0a57e82400da39bf9470d70d056 HTTP/1.1" 200 -
192.168.1.5 - - [19/Jul/2017 15:58:07] "GET /socket.io/?EIO=3&transport=polling&t=LrPexJc&sid=9364d0a57e82400da39bf9470d70d056 HTTP/1.1" 200 -
accell = forward
192.168.1.5 - - [19/Jul/2017 15:58:07] "POST /socket.io/?EIO=3&transport=polling&t=LrPexSK&sid=9364d0a57e82400da39bf9470d70d056 HTTP/1.1" 200 -
192.168.1.5 - - [19/Jul/2017 15:58:07] "GET /socket.io/?EIO=3&transport=polling&t=LrPexMK&sid=9364d0a57e82400da39bf9470d70d056 HTTP/1.1" 200 -
accell stop = break

なんだか、POST、GETがsocketと同じタイミングで吐き出されているのが気になりますが。一旦棚にあげています。

ラズパイコントロール用の処理を追加する

後は、以前以下の記事のときに作成したラズパイコントロール用の処理である「legocar_controller.py 」をapp.pyから呼ぶだけです。

tohutokonsome.hatenablog.com

ひどく汚いコードですが、とりあえず自分の環境では稼働が確認できました。

 SocketIO,emit
import legocar_controller  as LegoCar

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)


myLegoCar = LegoCar.LegoCarController()


@socketio.on('connect',namespace='/legocar')
def init() :
    print('connected')

@socketio.on('sendMessage',namespace='/legocar')
def recieve_order(order) :
#TODO ひどいコードなので直したい
    if order in {'forward'}:
       print("accell forward " +order)
       #レゴカーのアクセル(前)
       myLegoCar.accelerator(b"w")
       testmethod(order)
    elif order in {'back'}:
        print("accell back " +order)
        #レゴカーのアクセル(後)
        myLegoCar.accelerator(b"s")
        testmethod(order)
    elif order in {'break'}:
        print("accell stop " +order)
        myLegoCar.accelerator(b"f")
        testmethod(order)
    elif order in {'right'}:
       print("handle = " +order)
       #レゴカーのハンドル(右)
       myLegoCar.handle(b"d")
       testmethod(order)
    elif order in {'left'}:
        print("handle = " +order)
        #レゴカーのハンドル(右と左)
        myLegoCar.handle(b"a")
        testmethod(order)
    else:
        pass

@socketio.on('test',namespace='/legocar')
def testmethod(order):
    emit('test',order)

if __name__ == '__main__':
    socketio.run(app,host='0.0.0.0',port=5000)

次回は、操作するUIだったり、カメラ部分を改善していきたいと思います。

「無職です」というのにまだ慣れない

会社を辞めて、そろそろ3週間が経とうとしています。

無職のせいか1日が本当にあっという間に終わってびっくります。

しかし、仕事をしていたときは、仕事さえなければあれもこれもやりたいのに!なんで思っていたのですが、いざ仕事を辞めてみると、むしろやりたいことに対しての生産性が落ちている気がします。

現にこのブログの更新頻度も、落ちてしまいました。

変に時間がある分、いろいろ手をだして目的を見失っている感が半端ないです。
そして、久しくやってなかったCivlization4もやってみよっかな、とか某FPSゲームのエイム練習もしてみたいな、とかよくわかんないとこでも盛り上がっちゃいます。

さて、本題なのですが未だに(といってもまだ3週間なのかもしれませんが)無職です、っていうことに対してどこか気恥ずかしさがあります。
昼間のスーパーのお菓子コーナーで、「ドンタコス」と「キャベツ太郎」どっちにするかを3分ほど悩んで 、「やっぱキャベツ太郎だな!」と声に出して購入した際など、今、すごい無職ぽかった!と感動していたのですが、いかんせん周りに喧伝するのはちょっと気がひけます。

市役所の手続きだったり、携帯の手続きだったりで、職業を書くのが恥ずかしいです。
紙に書くのだけれあればまだいいのですが、電話や対面で言うのが結構恥ずかしいです。

先日のことでした。

転職活動の面接が終わった後に、免許の更新し忘れで失効してしまった免許を取りに行こうと、駅のベンチに座って免許合宿の申し込みを行いました。

オペレーターのお姉さんに職業を聞かれた際に、「無職です」というと、食い気味で「あ、かしこまりました!」と言われるといろいろ勘ぐってしまいます。
免許失効してるわ、無職だわでどう思われているんだろうと。
考えすぎかとは思うのですが、免許合宿に参加するであろう若い方たちと混ざるのだけでもドキドキしているせいか、神経質になっています。

悲しかったのが、電話を終えて一息ついたところで、スーツを来た若い青年に声を掛けられました。

どうも、研修の一貫で、外部の人間と名刺交換をしろというものをやっているらしく、名刺を交換してほしい、と言われました。

自分は面接のためにスーツを来ていたので、外回りをしている営業か何かに見られたのかと思います。
今思えば、「転職活動中なので名刺ないんですよ」と言えばよかったのですが、免許合宿の申し込みで若干凹んでいたせいか、「今名刺持ってないんだよ、ハハッ」といって切り抜けようとしました。

そしたらあろうことか青年は、一筆だけでもほしいと、紙を渡してきました。

名前だけ書いて返したら、会社名もお願いします、と言ってくるじゃないですか。

心の中で、適当に会社名を書くか、いや、前職の会社名を、、、といろいろ悩んだのですが、正直に「今無職なんで、、、」と答えたところ、青年も、あ、、っていう感じになり名前だけ書いて終わりました。

こんなふざけた研修はいますぐ廃止してほしい。

RaspBerryPiでラジコンをつくる(10):ブラウザから操作する

以前、Terminal経由でCUI操作によるラジコンのコントローラーを作りました。

tohutokonsome.hatenablog.com

tohutokonsome.hatenablog.com

動いているところ
www.youtube.com

今回は、ブラウザごしに操作できるようにしていきたいと思います。

構成ですが、以下を予定しています。
f:id:konoemario:20170719083402j:plain:w500

ブラウザからのキー入力情報をもとに、ラジコンを操作するのですが、socket.ioを使って実現します。
socket.ioを調べると、node.jsの例がいっぱいでてくるので、それをベースでいろいろと試していました。

ただ、肝心のラズパイを操作する部分を今までPythonで実装してきたので、それはそのまま使いたい気持ちがありsocket.ioの部分はPythonで実装することにしました。
Pythonでのsocket.ioはPythonの簡易WebフレームワークであるFlaskで使えるみたいなので、こちらを使用することにします。

ブラウザ側のsocket.ioですが、これもPython用のものがあるのですが、せっかくnode.jsを使ってきたので、こっちはnode.jsベースで実装します。

なのでラズパイにはnode.jsとFlaskと二つWebサーバを立てることになります。
最終的には外部のネットワークから操作したいのですが、この決断がどんな影響がでるのか、なんともいえません。

PythonのWebフレームワークとしてDjangoをやってきたのですが、ここにきてFlaskを使用することになりちょっと悲しいです。
とはいえ、Djangoでもsocket.ioが使用できないわけでもなさそうなので、どこかでできればなぁと思ってます。

この記事は何回かに分けて書きます。

今回は、とりあえずnode.jsのみでキー入力部分の確認を行います。



node.jsをラズパイにインストールする

まずは、ラズパイにnode.jsをインストールします。
Rasbianにはデフォルトでnode.jsが入っているみたいですが、バージョンが古いので新しくします。

こちらの記事を参考に、インストールを行いました。

第三回 Raspberry Pi 3に最新のNode.jsをインストールする - Qiita

pi@raspberrypi:~ $ sudo apt-get install -y nodejs npm

バージョンを確認。おお、かなり古いっぽい。 ※apt-getするまえにやればよかった。もともとラズパイに入ってるバージョンとのこと。

pi@raspberrypi:~ $ node -v
v0.10.29
pi@raspberrypi:~ $ npm -v
1.4.21
pi@raspberrypi:~ $ sudo npm cache clean
pi@raspberrypi:~ $ sudo npm install n -g
pi@raspberrypi:~ $ sudo n stable

バージョンを確認したところ、まだ古い。

pi@raspberrypi:~ $ node -v
v0.10.29

とりあえす再起動したら新しくなりました。
Macの検証環境よりバージョン上がってるけれども、これで進めることにした。
何気に、npmのバージョンもあがってた。

pi@raspberrypi:~ $ node -v
v8.0.0
pi@raspberrypi:~ $ npm -v
5.0.0


コントロールする画面を作成する

とりあえず、node.jsだけを使ってブラウザでキー入力した情報をリアルタイムでサーバーに連携する機能を作成します。

f:id:konoemario:20170719085247j:plain:w500

node.jsの基本的な部分は、以下の記事で試しました。
tohutokonsome.hatenablog.com

なにはともあれ、node.jsのプロジェクトの作成していきます。

(raspberry_3.5.1) masao-3:legoCarController konoe_mario$ npm init
name: (legoCarController) nodeapp
version: (1.0.0) 
description: legoCarController
entry point: (index.js) server.js
test command: 
git repository: 
keywords: 
author: tohu
license: (ISC) 
About to write to /Users/konoe_mario/WebstormProjects/legoCarController/package.json:

{
  "name": "nodeapp",
  "version": "1.0.0",
  "description": "legoCarController",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "tohu",
  "license": "ISC"
}

続いて、エントリーポイントとなるserver.jsを作成します。
「http://ラズパイのIP:6677/controller」にアクセスすると、controllr.htmlを返すだけのものになります。

server.js


var http = require("http");
var server = http.createServer();

//urlディスパッチャに使う
var url = require("url");

//htmlファイルの読み込み
var fs = require("fs");

//socket.ioで使う
var io = require("socket.io")(server);


server.on("request",function(req, res){
  console.log("request");

  var incomingUrl = url.parse(req.url);

  if(incomingUrl.pathname === "/controller"){

        fs.readFile("./client/controller.html","utf-8",(err,data)=> {

            if (err) {
                res.writeHead(404, {'Content-Type': 'text/html'});
                res.write('');
                res.end("<h1> Not Found </h1>");
            }else{
                res.writeHead(200, {'Content-Type': 'text/html'});
                res.write(data);
                res.end();
            }
        });


  }else{
      res.writeHead(404,{'Content-Type':'text/html'});
      res.write('');
      res.end("<h1> Not Found </h1>");
  }

});


server.listen(6677);

続いて、「controller.html」はほぼ空っぽですが、こんな感じにしました。

controller.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>レゴカーコントローラー</title>
</head>
<body>
    <h1>おはんきー</h1>
</body>
</html>

そうしたら、実際に「http://ラズパイのIP:6677/controller」にブラウザでアクセスして表示されることを確認します。

f:id:konoemario:20170718110003p:plain:w500

controller以外のところにアクセスするとNot Foundとなりますね。

f:id:konoemario:20170718110007p:plain:w500

socketを実装する。

次にserver.jsのsocket.ioを実装します。

その前に、socket.ioのモジュールをインストールしておきます。

‘’‘sh (raspberry_3.5.1) masao-3:legoCarController konoe_mario$ npm install socket.io ’‘’

インストールしたら、server.jsに以下のコードを追加します。
追加する場所に注意が必要です。自分は嵌まりました。

Node.js のsocket.ioでCan't set headers after they are sent - Qiita

server.js(抜粋)



//socket.ioで使う、server.on("request",function)より後がよい
var io = require("socket.io")(server);

//socket確認用のコード
io.sockets.on("connection",function (socket) {
    console.log("socket connection");

    socket.on("sendMessage",function (data) {
        socket.emit('test',data);
    });

});


次にcontroller.htmlにも、キー入力情報を受け取る機能と、socket.ioを実装します。

controller.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
    <script src="/socket.io/socket.io.js"></script>
    <script>
          <!--javascript-の処理を書く->
    </sciprt>
    <meta charset="UTF-8">
    <title>レゴカーコントローラー</title>
</head>
<body>
    <h1>おはんきー</h1>
    <div id="controller"></div>
</body>
</html>

jqueryとsocket.ioの読み込みを追加しました。 ついでに、キー入力結果を表示するようの「

」も追加しています。

その後に、肝心のjavascirptの処理を追加しています。

controller.html(javascirpt部分を抜粋)



        var socket = io.connect();

        $(function() {
            socket.on('connect', function () {
            });

            socket.on('test',function(data){
                render(data)
            });

       });

        function render(data) {
            $("#controller").text(data);
        }

        //キーが押された場合のイベント
        document.onkeydown = function(e){
            isSendMessage(e.keyCode,"keydown");
        };

        //キーが離された場合のイベント
        document.onkeyup = function(e){
            isSendMessage(e.keyCode,"keyup");
        };

        //Windowが非アクティブになったら、エンジンが止まるように決め打ちで送信する。
        window.onblur = function () {
            isSendMessage(87,"keyup");
        };

        //socket通信をするかどうか制御する関数
        function isSendMessage(key_code,keykind){

            switch(key_code){
                //W
                case 87:
                    if(keykind === "keydown"){
                        sendMessage("forward");
                    }else{
                        sendMessage("break");
                    };
                    break;
                //S
                case 83:
                    if(keykind === "keydown"){
                        sendMessage("back");
                    }else{
                        sendMessage("break");
                    };
                    break;
                //D
                case 68:
                    sendMessage("right");
                    break;
                //A
                case 65:
                    sendMessage("left");
                    break;
                default:
                    break;
            }
        }

        //socket通信
        function  sendMessage(message)  {
            socket.emit("sendMessage",message);
        }

再度、「http://ラズパイのIP:6677/controller」にアクセスすると、以下のようになるかと思います。

f:id:konoemario:20170719090704p:plain:w500

W、A、S、Dを押下すると、それに紐付いた動作名がブラウザに表示されます。

ちなみに、javascrptのキー入力のイベントに関しては、以下のサイトを参考にさせていただきました。
JavaScriptプログラミング講座【キーボード操作について】

処理の順番としては、以下のようなイメージです。
f:id:konoemario:20170719080945j:plain:w500

次回は、socket.ioのサーバ側をFlaskに置き換えていきます。

ダンスとエンジニアの夢

先月、愛知の友人の結婚式に出席し、その帰りに実家の静岡の友人へもとへ遊びにいった。

静岡の友人とは大学のダンス部がきっかけで仲良くなり、大学の4年間は同じダンスチームのメンバーとして切磋琢磨してきた。

大学は留年さえしなかったけれども、通年を通して単位がろくにとれなかったりするほど講義にはでなかったけれども、ダンスの練習は信じられないくらいやっていたと思う。

チームとしてはいろいろなコンテストに積極的に参加して賞をとったり、クラブイベントにも呼ばれたりして、思い返せば夢のような時間だった。

何をしに大学に入ったんだという批判はもっともだけれども、僕にとっては非常に貴重な時間だったし、いろいろ考えさせられたりもした。

何よりもダンス部に入っていなかったら、きっと友人はできなかっただろうし、卒業もできなかっただろうし、今も結婚することもできなかったはずだ。(それにきっと今でも童貞だったはずだ。)


そんな楽しい時間だったのだけれども、後悔していることがある。


チームとしては、いろいろと挑戦できたし、成果も出すことができた。
だけれども個としては、もっとうまく、楽しめるようになりたかった。


ストリートダンスでは、即興で踊るダンスバトルという形式がある。
僕はそれがかなり苦手だった。

www.youtube.com

メンタルが弱いせいか、音もよく聞けなくなるし、頭で考えすぎてしまって同じ動きばかりになってしまったりと、あんまりいい経験がない。

だから、ダンスバトルで輝いている人を見ると本当に羨ましくもあったし、その人の頭の中はどうなっているんだろうと思うこともあった。
なによりダンスを楽しんでるってこういうことだろ!みたいな意識もあった。

練習のときに何気なく、今の感じはよかった、と思うことはあっても、イベントを通して自分がよかったと思えるようなムーブができたことは最後までなかった。


一方の友人は、ダンスを始めた当初から、自分がうまいと勘違いをしていたせいもあって(後日、あのころの自分はひどかったと言っていたから間違いない)、そういったダンスバトルも楽しんでいた。

実力的にはそこまで違いはなかったように思えるのだけれども、ダンスに対する姿勢だったり、メンタル的な部分で負けていたように思える。


とにかくそんな後悔を抱えたまま、自分はなんとなくSEを始めて、今に至る。ダンスはもうやっていない。
さらに言えば今は無職だけれども。


話を元に戻す。


静岡の友人は今もダンスを続けていて、主にダンススクールの講師として生計を立てている。

僕らの大学のダンス部時代では、スクールの先生といえば雲の上はいいすぎだけれども、それはもうすごい!みたいな存在だった。

今、友人はその立場にいて、クラブイベントも主催していたりして、生活は大変だと言っていたけれども、あのころの僕たちの憧れの存在になっている。

遊びにいったその日の夜は友人主催のクラブイベントに行った。

イベントに来ている人たちは、友人の教え子だったり、ダンサー友達だったり、大抵知り合いみたいで(地方のイベントは大概そんなもんだと思うけれども)みんな友人に声をかけていた。

一方の僕は、従来のメンタルの弱さと人見知りのせいもあって、気後れしながら後ろのほうで音楽を聞いていた。

やっぱりダンスを辞めてしまった自分が、そういった文化のある場所に行くのはなんとなく気後れしていた。

クラブも海外に行く時は観光ついでに行くのだけれども、日本では大学以来遊びにいっていない。

それでも、その日のイベントの選曲は90年代のHIPHOPがたくさん流れていて、とてもいい雰囲気だった。

アルコールの力を借りて、時間帯のせいもあってあまり人のいない最前列のスピーカー前に行くことにした。

でっかい音で聞く音楽は、それはそれで良いものがあって、全然動けないなりに一生懸命動いた。

周りから見れば、変なのがいる、と思われたのかもしれないけれども、その時の僕はあの箱の中で一番楽しんでいた自信がある。

時間にすれば1時間もなかったかもしれないけれども、どんなに踊れるダンサーよりも、僕の方が楽しんでいたように思える。

ものすごく疲れたころに、ちょうどピークの時間帯を迎えていて、その頃にはぐったりしていたのだけれども、とても楽しい時間だった。

ダンスを辞めてしまったからとか、もう動けないだとか、最近の音楽を聞いていないだとか、知らない人ばっかだとか、周りの目が恥ずかしいだとか、普段の自分なら楽しめない要素は満載だったように思える。

それでも、それを乗り越えた先は本当に楽しくって、踊れる踊れないは関係なしに音楽を聞けた。

大学のころに足りなかった部分は、周囲との比較や、他人の目を気にし過ぎていて、純粋に楽しむことが足りなかったんだと思う。


さて、タイトルのエンジニア部分に話を移す。


以前、こんな記事を書いた。

tohutokonsome.hatenablog.com

tohutokonsome.hatenablog.com

だらだらといろいろ書いているけれども、エンジニアになりたいんだけれども、そこまでプログラムを書くことが好きな自信がない、みたいな内容である。

これも、結局のところプログラムが好きな他者と比較してしまうと、どうしても自分なんか、みたいな気持ちがでてきてしまうというものである。

実際のところ、僕の今のスキルはうんこみたいなものだし、他者と比較してしまうとそれはもうひどいものだと思う。

それも、やっぱりプログラムを書くのって楽しいなと思える気持ちが自分にはあるのだから、他者どうこうではなく、挑戦したいと思った。

エンジニアになって楽しんでやるという気持ちを持っていきたい。

とりあえず、すべては次の会社見つけてからなんですが。


最後に、自分の一番好きなダンス動画貼っときます。
バトル形式ではなく、ショーケースです。

www.youtube.com

ぐっとくるポイントを書きたくなっちゃったので書きます。
とはいえだいぶ昔の知識なので、間違っていることや曖昧なところが多いです。

動画はbattle of the yearというブレイクダンスの世界大会です。
年1回開催されていて、各国の予選で勝ち上がったチームが本戦(ドイツ)に出場します。

大会の内容は、チームのショーケース(チームで事前に演目を考えておいて踊るもの)で評価された上位2チームがダンスバトルに進み優勝チームを決めるみたいなやつです。
※ショーケースの上位4チームか、2チームかはうろ覚え。

動画の内容は、2005年のbattle of the yearのドイツの本戦で、日本の「一撃」というチームのショーケースになります。

何がいいかっていう部分なんですが、2005年以前のbattle of the yearでは、ブレイクダンスのムーブ(ヘッドスピンとか)がかなり進化していて、なんかダンスっていうよりかは、体操選手というかびっくり人間みたいな部分が突出していました。

ブレイクダンスというからには、派手な技の受けはやっぱり重要なのですが、どうしてもダンスの音楽みたいな部分がおろそかになる印象が(自分は見ていて)ありました。

一方、「一撃」というチームは、昔からbattle of the yearには出場している老舗のチームです。

ただ、2005年当時の印象だと、突出した大技をもっているようなメンバーはおらず、「一撃」が日本代表になったと聞いて、なんでだろうと思った記憶があります。

しかし、この動画を見て納得しました。
当時のブレイクダンスのムーブのスキルからいっても突出したものはないのですが、ショーケースの構成や音の取り方が本当にかっこよくって、今でもたまに見ると鳥肌がたちます。

奥さんの扶養に入れない:退職時のメモ

2017年6月30日を持って、会社を退職しました!

退職しました!なんて、えくすとらなんちゃらやら、エクトプラズムやら正式名称を思い出せない!←これをつけていますが、不安でいっぱいです。

今後再び退職するときのメモになるかと思うので、やったことを書いておきます。

退職時したときの状況

退職したときの状況は以下の通り。

  • 退職した日は2017年6月30日
  • 自己都合で退職
  • 転職活動中、9月ぐらいから働きたい
  • 扶養家族はいない。働いているパートナーがいる。
  • 年収は500万くらい

お金の話

退職しても税金やら年金やらの支払いは当然発生しちゃいます。
予定として2ヶ月程度、無職ライフを楽しむつもりなのですが、その間の税金を節約できないかといろいろ調べていました。

住民税は無理そうなのですが、健康保険、年金はパートナーの方が働いているのであれば、扶養に入る、というのが一番お得な選択肢になるかと思います。

私も奥さんが働いているので、扶養に入るのを狙っていました。
手続きは奥さんの会社で行う必要があるので、お願いします!と言っていたのですが、奥さんが会社の上司に確認したところ、そんな旦那とは別れなさい、と一蹴されてしまったみたいです。

世間は厳しいなぁと感じている今日このごろです。  

また、扶養に入るのにはいろいろな手続きが必要で、時間が結構かかるみたいなので、それを狙う方は在職中に、パートナーの方にいろいろと確認してもらったほうがいいみたいです。

今後の教訓としたいです。

以降は、扶養に入れなかったわたしが行った各種手続きになります。

住民税

住民税は毎年6月から翌年5月までが一年分に相当する。
自分の場合、6月末に辞めたので、7月から、翌年5月分までは、会社の給与から引き落とされるのではなく、個別に支払う必要がある。

退職時に、個別で支払う場合どうするか、ということを聞かれる。
選択肢としては、

  1. 一括徴収:年度分をまるっと会社に渡して、会社が市町村に支払い
  2. 普通徴収:市町村から納付手続きの依頼がくるので、それに従って支払い

とがある。

ちなみに給与から引き落とされる場合は、特別徴収というらしい。

住民税は、前年の給与をもとに計算されるので、一括徴収にした場合、どれくらい必要になるのか、ということを調べることができる。
自分の場合だと、人事の方が教えてくれて、24,6400円ということがわかった。

先行きが怖かったので、普通徴収でお願いします、とした。
普通徴収の場合、年4回、市町村から住民税の支払いの依頼がくるみたい。

なので、市役所にいって手続き等はとくに発生せずに、納付書が来るのを待つのみです。

健康保険

これがないと、歯医者にも満足にいけない。

とりえた選択肢としては以下の通り

  1. 前職の健康保険を任意継続 会社が負担してた保険料分を払うことになるみたいなので、毎月健康保険として給与から引かれていた額の倍の金額を払わねばいけないみたい。 なのでおすすめしない、というとそうでもなく、国民保険もかなり高いみたいなので、選択肢としてはありみたい。参考サイトのモデルケースでは、年収が430万超えるとか、扶養がたくさんいると、任意継続のほうが国民保険よりもお得っていう試算がでている。

  2. パートナーの扶養に入る 加入条件を満たせば、一番いいかも。 収入の条件に130万とあるが、これは前年ではなく、申請した以降の想定の金額が130万という意味みたい。 今は無職になったので収入の見込みがないということで扶養申請はできる。 が、タイトルの出落ち通り、この選択肢はなくなってしまった。

  3. 国民保険に加入する 結局、市役所に行って国民保険に加入することにした。
    市役所に確認したところ、次々の保険料は14000円ぐらいだった。

市町村によって、国民保険の金額は恐ろしいほど異なるみたいなので、確認はしたほうがよさそう。

扶養に入るせよ、国民保険に加入するにせよ、組合の健康保険を脱退しましたという脱退証明書の提出が求められる可能性があるので、あらかじめ用意しておくといいかもしれない。
自分の場合は、加入している組合のWebサイトから申請ができた。

年金

これも選択肢としては、扶養に入るか、国民年金に入るかのどちらかしかない。
奥さんの扶養に入れないので、市役所にいって国民年金加入の手続きを行った。月々16000円ぐらい。

ハローワークへ行く

これがまだ確認できてないです。
ハローワークいって雇用保険申請の手続きをしておくと、再就職時にお祝い金がもらえるそうです。
ただ、失業保険ベースのお金みたいです。
ということは、自己都合で会社を辞めた場合、3ヶ月経過しないと失業手当はでないので、自分みたいに2ヶ月程度辞めるといった場合は、受給要件を満たしていないかもしれません。

確認が出来しだい、更新したいと思います。

その他

他にも年度末の所得税に気をつけろ!とかあるかと思いますが、2ヶ月には働いているつもりなので、今回は対象外にしました。
2ヶ月後、働いていますよね、、、

Node.jsを使って、簡単なWebアプリケーションをつくる(2)

前回の続き

今回は、リクエストしたURLの内容に応じて、ブラウザ側の表示を変えます。

tohutokonsome.hatenablog.com

URLディスパッチャを実装する

まずは、アクセスするURLに応じて、ブラウザに返す内容を変更してみます。

server.js(前回の状態)


var http = require("http");

var server = http.createServer();

server.on("request",function(req, res){
   console.log("リクエストがあったよ");
   
   //HTTPヘッダを編集する。
   res.writeHead(200,{'Content-Type':'text/html','charset':'UTF-8'});
   //サーバーにレスポンスを返す。
   res.end("<h1>おはんきー!</h1>");
});

上記の状態を、以下のように修正しました。

server.js(修正後)



var http = require("http");

var server = http.createServer();

//urlディスパッチャに使う
var url = require("url");

server.on("request",function(req, res){
   console.log("リクエストがあったよ");
   
   var incomingUrl = url.parse(req.url);


   //サーバーにレスポンスを返す。
    if (incomingUrl.path === "/hello"){
       res.writeHead(200,{'Content-Type':'text/html'});
       res.write('<head><meta charset="UTF-8"></head>');
       res.end("<h1>おはんきー!</h1>");
   }else if(incomingUrl.path === "/bye") {
       res.writeHead(200,{'Content-Type':'text/html'});
       res.write('<head><meta charset="UTF-8"></head>');
       res.end("<h1>さよならんきー!</h1>");
   }else{
       res.writeHead(404,{'Content-Type':'text/html'});
       res.write('<head><meta charset="UTF-8"></head>');
       res.end("<h1> Not Found </h1>");
    }
 
});

引数reqにはurlの情報をもっていて、url.parse関数を用いることで解析することができます。
その情報をもとに、表示の出しわけをしているだけになります。
すごくあっさりしてますね。

htmlファイルを返す

次に、「http://localhost:6677/hello」にアクセスしたら、htmlファイルを返却するようにします。

プロジェクトのディレクトリ配下に、clientディレクトリを新規で作成し、その中に「index.html」をおきました。

masao-3:SampleProject konoe_mario$ tree
.
├── client
│   └── index.html
├── package.json
└── server.js

index.htmlは、適当に作成します。

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>おはんきーブログ</title>
</head>
<body>
    <h1>てきとうにつくった</h1>
    <form>
        <input id="nickname" type="text"   name="nickname" placeholder="nickname"/>
        <textarea id="text" placeholder="message" name="text"></textarea>
        <br/>
        <br/>
        <button type="submit">送信</button>
    </form>

</body>
</html>

server.js側には、fsモジュールを読み込み、htmlファイルをfs.readFile関数で読み込んでいます。
読み込んだ結果は、第2引数のcallback関数のdataにあたるんですが、このへんの書き方がまだ慣れないです。

server.js(抜粋)



//htmlファイルの読み込み
var fs = require("fs");

server.on("request",function(req, res){
   console.log("リクエストがあったよ");

   var incomingUrl = url.parse(req.url); 

  //サーバーにレスポンスを返す。
    if (incomingUrl.path === "/hello"){
        //htmlファイルの読み込み
        fs.readFile("./client/index.html",(err,data)=>{
            if(err) {
                res.writeHead(404,{'Content-Type':'text/html'});
                res.write('<head><meta charset="UTF-8"></head>');
                res.end("<h1> Not Found </h1>");
            }

            res.writeHead(200,{'Content-Type':'text/html'});
            res.write(data);
            res.end();

        });


この状態で、「http://localhost:6677/hello」にアクセスすると、index.htmlの内容がブラウザに表示されるかと思います。

動的コンテンツを表示する

phpとかDjangopythonでもあったように、htmlの中にサーバー側で処理するスクリプト言語を埋め込んで動的に内容をかえたい、とういうことをやっていきます。

まわりくどいですが、最初は自分でなんとかしてみようという発想です。

さきほどの「index.html」にResultみたいなエリアを追加しています。

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>おはんきーブログ</title>
</head>
<body>
    <h1>てきとうにつくった</h1>
    <form>
        <input id="nickname" type="text"   name="nickname" placeholder="nickname"/>
        <textarea id="text" placeholder="message" name="text"></textarea>
        <br/>
        <br/>
        <button type="submit">送信</button>
    </form>
    
 <!--なにかを表示させるエリア-->
   <h1>Result</h1>
    <div id="result">
       <h2><%=hoge %></h2>
    </div>

</body>
</html>

「h2」の間に「hoge」を%で囲っています。 これは、ただの置き換えする際の目印なのでなんでもいいです。
ここの部分をserver.jsで書き換えます。

server.js(抜粋)



//htmlファイルの読み込み
var fs = require("fs");

server.on("request",function(req, res){
   console.log("リクエストがあったよ");

   var incomingUrl = url.parse(req.url); 

  //サーバーにレスポンスを返す。
    if (incomingUrl.path === "/hello"){
        //htmlファイルの読み込み
        fs.readFile("./client/index.html","utf-8",(err,data)=>{
            if(err) {
                res.writeHead(404,{'Content-Type':'text/html'});
                res.write('<head><meta charset="UTF-8"></head>');
                res.end("<h1> Not Found </h1>");
            }

           //htmlファイルの一部を置き換え 
            var rendarHtml =  data.replace("<%=hoge %>","fuga");
            
            res.writeHead(200,{'Content-Type':'text/html'});
            res.write(rendarHtml);
            res.end();

        });


上記のように、fs.readFileで読み込んだhtmlファイルをdata.replaceで置き換えています。
こうすると、以下のように、置き換えた文字列「fuga」が表示されるかと思います。

ちなみに、こっそりfs.readFileの引数にencodingであるutf-8を追加しています。
これがないと、引数dataは、文字列として扱われないため、replace関数を使用することができません。

TypeError: data.replace is not a function

みたいなエラーがでるかと思います。



さて、動的コンテンツを表示させるのに、毎回上記のようなreplaceを使ったり、htmlファイルをserver.jsで組み立てするのは面倒です。
なので、こういうときはejsという外部モジュールを使うみたいです。

qiita.com

EJSでぐぐると、それはもう新たな世界が広がりそうで本当に果てがないなぁなんて感じます。

ここでは、深く考えずに使ってみます。

ejsはNode.jsに標準で含まれていない機能になるので、別途インストールする必要があります。
とはいえ、パッケージマネジャーであるnpmを使うことで簡単に終わります。

masao-3:SampleProject2 konoe_mario$ npm install ejs

上記コマンドをたたくことで、プロジェクト配下に、node_modulesディレクトが作成され、その中身をみると、ejsディレクトリが作成されていることが確認できます。

├── node_modules
│   ├── ejs

あとは、server.js側でrequire(“ejs”)とするだけで使用ができます。 ejsでは置き換えたい文字列を%で囲む等のルールになっているので、index.htmlの内容は変更していません。

server.js(抜粋)



//htmlファイルの読み込み
var fs = require("fs");

//動的コンテンツを提供するライブラリ
var ejs = require("ejs");

server.on("request",function(req, res){
   console.log("リクエストがあったよ");

   var incomingUrl = url.parse(req.url); 

  //サーバーにレスポンスを返す。
    if (incomingUrl.path === "/hello"){
        //htmlファイルの読み込み
        fs.readFile("./client/index.html","utf-8",(err,data)=>{
            if(err) {
                res.writeHead(404,{'Content-Type':'text/html'});
                res.write('<head><meta charset="UTF-8"></head>');
                res.end("<h1> Not Found </h1>");
            }

           //htmlファイルの一部を置き換え 
            var rendarHtml =  ejs.render(data,{hoge:'fuga'});
            
            res.writeHead(200,{'Content-Type':'text/html'});
            res.write(rendarHtml);
            res.end();

        });


今回は、ここまでにしたいと思います。