漆黒な技術メモ

管理人が必要に応じて自分のメモを好き勝手に投下するたまり場的ブログ

【ちょっと技術的なことAdventCalender】 hackU実装振り返り-3. nodejsでpostgresに接続-

この記事はちょっと技術的なことAdventCalenderの24日目です。
クリスマスに風邪をこじらせてしまい書くのが遅れました…

先週あたりからhackU開発後記としてつらつら書いていますが、今日は可視化システムで必要だったnodejsとDBの連携についてお話ししようと思います。

nodejs×postgreSQL

恐らく座布団が定期的に送ってくるデータをため込んで可視化する…という作業にはNoSQLであるmongoDBとかの方がパフォーマンス的には正しい選択肢なのですが、 今回は開発時間をあまりとれなかったので、早めにシステムを組み上げるため研究室内での運用実績が多いpostgresを利用しました。
nodejsからpostgresを利用するにはpgというパッケージを利用します。いつものように$npm install pgでインストールします。
パッケージがインストールできたらあとはコードを書くだけです。

記述コード

ここでは、引数で渡された任意のsql文を実行し、結果をコンソールに出力するというプログラムを紹介します。

var pg = require('pg');
var DBhost = "postgres://username:password3@www.example.jp:5432/dbname";

sql_custom = function (query,socket) {
    var result;
    var client = new pg.Client(DBhost);
    client.connect(function (err) {
        if (err) throw err;
        client.query(query, function (err, value) {
            if (err) throw err;
            console.log("get data");
            console.log(value);
            client_select.end();
        });
    });
});

pg.Client型の変数を使いまわして2回以上接続切断を行うと例外を吐いて止まってしまったので、毎回接続前に新しいインスタンスを生成して 局所変数として扱うのが一番よさそうです。
このコードでも何回も接続と切断を繰り返すと”too many clients”的なエラーが出てしまうので、少しこの部分は改良の余地がありそうですが…
今日はここまで!ありがとうございました。

【ちょっと技術的なことAdventCalender】 hackU実装振り返り-2. mbed2台を直結する-

この記事はちょっと技術的なことAdventCalenderの21日目です。
今日は、以前に引き続きhack Uで苦労したことにうちの1つをつらつら書いていこうと思います。

前回「mbed×websocketをやろうとしたらライブラリの都合でmbed2台を直結することになった」というお話をしたと思います。
今日は、そこで苦労した「意外とちゃんとわかってなかった電気回路のお話し」をしようと思います。

mbed2台を直結する

mbedをはじめとしたマイコン関係のモノを繋ぐときには、p9,p10などのピンにジャンパ線を差し込み、UARTを利用したシリアル通信を行うのが一般的かと思います。 f:id:igbt3116redtrain:20161223050117p:plain
(https://www.switch-science.com/catalog/250/より引用)
しかし我々、シリアルポートを繋ぎ、サンプルプログラムを動かしても、通信できない…

なんかよくわからないけどシリアル通信はうまくいかないと判断し、仕方なく複数の空きピンを利用して並列送信をすることにしました。
しかしそれでもうまくいかない…

原因はソフトウェアではなく電気回路

単純な1,0のDigitalOutの通信がうまくいかないのはいくらなんでもおかしい、と思い、DigitalInをいろんなピンにつないで動きを見てみました。
すると、DigitalInと同じmbedのVCCでは入力を1とみなすのに、もう片方のmbedのVCCでは入力を0と判定しました。
ここでもしかしてと思い、一方のmbedのVCCからもう片方のmbedのGNDの電圧を測定すると…0Vでした。

つまり、ただVCCとGNDを用意すればいいだけではなく、きちんと「回路」を形成してあげる必要があるようです。
電気をきちんとわかってる人からしたら「何当たり前のことを言ってんだこいつは」と思われるかもしれませんが、ソフトウェアだけをいじってる人間はこの辺が意外とわかってなかったりするようです(byわかってなかった人)

回路を形成する

形成するといってもそんな難しいことではありません。通信用のピンの他に、互いのmbedのGNDをジャンパ線で直結するだけです。
f:id:igbt3116redtrain:20161223051742j:plain
これを行うだけで、2台のmbedの信号を無事やり取りすることができました!

今日はここまで

【ちょっと技術的なことAdventCalender】 hackU実装振り返り-1. mbed×websocket-

この記事はちょっと技術的なことAdventCalenderの21日目です。 いよいよ最後も見えてきました。自分の担当分はこれを含めてあと4回です。

今日は、前回お話ししたhack Uで苦労したことにうちの1つをつらつら書いていこうと思います。

mbed × Webscoketは難しい

今回の残業防止IoT座布団では、座布団側のマイコンにmbedというARMマイコンを、サーバ側にnode.jsを利用しました。 この業界だと今流行りのマイコンは圧倒的にArduinoで、普通ならそっちを使うんですが、今回はチームメンバーが研究室でmbedを使っていること、 僕も昔に少し触れたことがあることから、mbedを採用しました。

で、mbedにもいくらかwebsoket関係のライブラリはあり、その中にはSocket.IOのラッパも存在したので、「これで問題なくSocket.IOを使ったwebSocketが使えるだろう」… って考えてた僕が馬鹿でした。 そのSokcet.IOのライブラリはどうもうまく動きませんでした。 パケットとライブラリ実装を確認すると、ライブラリのLast Commit Date が2012年になっており、そのころからSocket.IOの仕様が大きく変わってしまったため通信ができませんでした。 恐らくやるとしたらSocket.IOの仕様とライブラリの実装を読みくだきながらアレンジする必要があると思ったので、断念しました… 他にもWebsocketという名前そのままのライブラリがありました。しかしこれも最終コミットが2011年とまあまあ古い状態でした。ただRFC準拠実装みたいなので、何とかなるかなあと思い、サーバ(node.js)側でsocket.IO以外のライブラリを検討しました。

実装

結果node.js側で用意したのはwsというパッケージです。 インストールは $npm install ws で普通にインストールできます。 mbed側は先ほどお話ししたWebscoketライブラリを利用します。 フルソースは後日gitにアップするんで、一番要になる部分の実装だけ抜き出して…

mbed側

#include "mbed.h"
#include "Websocket.h"
#include "EthernetNetIf.h"

void init(char * hostAndPort){
    eth = new EthernetNetIf();
    EthernetErr ethErr = eth->setup();
    if (ethErr) {
        printf("\r\nERROR %d in setup.\r\n", ethErr);
    }
    printf("hostandport %s\n", hostAndPort);
    
    ws = new Websocket(hostAndPort, eth);
    printf("websock init\n");
}
    
bool connect(){
    printf("connecting...");
    int failcount=0;
    
    while(! ws->connect()) {
        failcount++;
        if(failcount>10){
            printf("cannot connect failed.\n");
            return false;
        }
        printf("cannot connect websocket, retrying...\n");
        wait(2);
    }
     return true;
}

int mess_send(char * msg){
    ws->send(msg);
    printf("data send\n");
    return 1;
} 

int mess_recv(char * msg){
    if (ws->read(msg)) {
        return 1;
    }else{
        return -1;
    }
}

int main() {
    init("ws://www.example.jp");
    printf("connect start\n");
    connect();
    printf("connect");

    char msg[64];
    while(1) {
        Net::poll();
        mess_send("hello");

        int ret = mess_recv(msg);
        if(ret)
            printf("recv: %s\n",msg);
        
        Net::poll();
    }
}

サーバ側

var wss = require('ws').Server;
var mport = 12020;
var ws_Mbed = new wss({ port: 12020 });

ws_Mbed.on('connection', function (ws) {
    wsm_groval = ws;
    countor = 0;
    console.log("connect from mbed");

    ws.on('message', function (message) {
        console.log('received:'+ message);
        var type;
    });
});

こんな感じで実装すればmbedでもwebsocketで通信できます!!

欠点

ただ、このmbedライブラリは大きな欠点を抱えています。イーサネットのライブラリに古いライブラリであるErthernetIFライブラリを利用しています。 現在はErthenetInterfaceというライブラリを利用するのが普通です。 古いErtherNetIFライブラリには大きな欠点があります。それはmbedのRTOS(非同期処理)ライブラリとすこぶる相性が悪いということです。 我々は新しいイーサネットライブラリへの付け替えにチームメンバが挑んでくれましたが、残念ながらうまくいきませんでした。
というわけで、たまたまmbedが2台あった我々は、mbed2台で通信側、センサ側と分け、2台のmbedをジャンパ線で直結してなんとか乗り切ろうという発想に至りました。
次回はそこでの苦労をお話ししたいと思います。

追記

mbed側のライブラリにWebSocketClientというライブラリがありました。こちらはErthenetInterfaceをライブラリに使っていたので、こちらを使えば次回に書くような苦労はしなくてもよかったのかもしれません。。。

【ちょっと技術的なことAdventCalender】Hack U(ハッカソンイベント)に参加してきました

この記事はちょっと技術的なことAdventCalenderの19日目です。
今日は日曜日に参加したHack U 2016 大阪会場 Student Hackathon - Yahoo! JAPANについてお話ししようと思います。 hacku.yahoo.co.jp

Hack Uとは

Hack U(ハック・ユー)は、限られた期間の中で、学生がプロダクトを自ら企画・開発・発表するイベントです。ものづくりの楽しさを体現できるよい機会となるよう、ヤフーの現役社員が学生のみなさんを全力でサポートいたします。

(公式サイトより)
まあすなわち、学生限定の相当大規模なハッカソンイベントと言ったところです。
今回のテーマは「自動○○」となっています。とはいうものの、何かプログラムを作ると大抵は何かを自動化しているので、結構何でもありの色彩が強い大会ではあります。
勿論優秀賞など、賞もありますが、残念ながら今回自分たちはじゅしょいうすることができませんでした。

自分たちの作ったもの

今回自分たちは、ネタに走りに行く気満々で「ブラックな人たちを助けよう」というお題目(当然このお題目の決め方もノリ)で、「残業防止座布団:‡漆黒からの解放‡」という作品をつくりました。
座布団の中に圧力センサ、加速度センサを埋め込み、長時間座っていることや貧乏ゆすりを検出して離席を促す作品です。 f:id:igbt3116redtrain:20161220052620j:plain この座布団の中にセンサとスピーカを仕込ませてあります。一見するとただの座布団(というかクッション)ですが
f:id:igbt3116redtrain:20161220052844j:plain 座面下にマイコン(mbed)、背もたれにバッテリーと通信モジュールを付加してあります(椅子は会場のモノです)
これらを利用して、

  • 離席を促すべき時に音楽を鳴らす機能
  • イライラ度可視化
  • 着席などのイベント発生に合わせたツイッターへの投稿機能
  • 可視化画面からの座布団の動作制御

を行っています。
f:id:igbt3116redtrain:20161220053343p:plain
この作品の主目的からは外れるためあまり注目はされませんでしたが、「webブラウザからの座布団動作」ができるようになっています。
自分はここに一番こだわりを持っていました。両方向のIoTを何とか形にしてみたかったからです。

利用した技術と自分の担当範囲

アーキテクチャ図にするとだいたいこんな感じです。 f:id:igbt3116redtrain:20161220055101p:plainバイス、マイコン、データベース、比較的新し目の通信規格&サーバ実行環境、可視化、他システムとの連携などIoTエコシステムと言われそうな部分を一通り実装しました。
(本当はAndroid連携をやりたかったのだけど実装に使える時間の確保ができませんでした…)
自分はデバイスを少しと、マイコンの通信部分、サーバサイド処理を担当しました。

次以降の記事の話

今回HackUの作品について書くのはここまでにしておいて、次回以降はこの実装で苦労した部分のことを自分のメモを兼ねて何回かに分け書いていきたいと思います。
またアドベントカレンダー期間が終わるころまでに今回のソースをgithub上に公開したいと思っています。
それでは!

【ちょっと技術的なことAdventCalender】改行区切りのログ出力をExcelとかで使えるようにCSVに変形するPythonスクリプト

この記事はちょっと技術的なことAdventCalenderの15日目です。
気づいたらもう折り返しを過ぎていますね…

プログラムを実行したときのログ出力

って、大体こんな感じで出力するようにプログラム組んでいる方が大半だと思います。

time : 10:00
power : on
value : 1

time : 10:10
power : off
value : 0

time : 10:20
power : on
value : 2

...

まあ実装上この形式で書くのが一番楽ですし、ログとして書く分には何も問題ないのですが… ある時、このログをExcelなどで集計しなくてはならない…!!なんてことになったら、この形式でやるのは超面倒ですね…

time,power,value
10:00,on,1
10:10,off,0
10:20,on,2

とかにできるのが一番理想です。
と、そんなことを実際にしなくてはならない場面に遭遇してしまったので、スクリプトを書きました

実際に作ったpythonスクリプト

引数でファイル名を指定します。空白行をCSVの行の分け目と認識するようにしています。
また、カラムはブロックごとに異なっても(途中から"Battery"カラムが増えたり、消えたり) 大丈夫なようになっています

#coding: utf-8
import sys

if len(sys.argv) < 2:
    print "usage filename"
    sys.exit(1)
filename = sys.argv[1]

f=open(filename)
lines=f.readlines()
valueText=""
columnTitles=[]
textBuf={}
for line in lines:
    if '\n' in line[0]:
        for col in columnTitles:
            if col in textBuf:
                valueText += textBuf[col]+","
            valueText+=","
        valueText += "\n"
        textBuf.clear()
    else:
        sptline=line.replace("\n","").split(":",1)
        if sptline[0] not in columnTitles:
            columnTitles.append(sptline[0])
        textBuf[sptline[0]] = sptline[1]

for col in columnTitles:
    print col+"," ,
print ""
print valueText ,

これを使って

$ python parser.py log.log > log.csv

とかやれば、変形できます。

実行結果

こんな感じです

$ cat status.log
time:2016/12/11 23:32:32
SSID:gp30
Mode:MultiShot
SubMode:TimeLapse
Recoding:True
Num. of taken:268
Num. of remaining:11794
SDcard:True
Battery:3

time:2016/12/11 23:40:25
SSID:gp29
Mode:MultiShot
SubMode:TimeLapse
Recoding:True
Recode:test
Num. of taken:278
Num. of remaining:11794
SDcard:True
Battery:3

$ python genlogparser.py status.log
time, SSID, Mode, SubMode, Recoding, Num. of taken, Num. of remaining, SDcard, Battery, Recode,
2016/12/11 23:32:32,gp30,MultiShot,TimeLapse,True,268,11794,True,3,
2016/12/11 23:40:25,gp29,MultiShot,TimeLapse,True,278,11794,True,3,test,

今日はここまで、それでは!!

【ちょっと技術的なことAdventCalender】自分がよく忘れがちなシェルスクリプトの文法&定型句まとめ

この記事はちょっと技術的なことAdventCalenderの13日目です。

このブログでもシェル関係のお話を取り上げることがそこそこ多いことからもわかるに、管理人はサーバ管理に研究にと、比較的多くの機械でシェルスクリプトを書いています。
が、シェルスクリプト歴は正直1年にも満たず、まだまだいろいろ覚えきれていないことが多い駆け出しシェルエンジニアです。
そこで今回は、「自分がよく使うにも関わらずよく忘れがちなシェルスクリプトの文法&定型句」をまとめてここに書いておきたいと思います。

現在時刻を任意のフォーマットで取得

どちらかというとdateコマンドの使い方ですね…

#!/bin/sh
echo `date '+%Y/%m/%d %H:%M:%S'` #2016/12/13 12:00:54

任意のコマンドの引数に変数を入れて実行、結果を変数へ

#!/bin/sh
var="value"
ret=`command ${var}

ファイルの絶対パスを分割し、ファイル名、ディレクトリ名、最下部のディレクトリ名だけ抽出する

fullPath="/home/user/document/file.txt"

fileName=`basename ${fullPath}` # => file.txt
dirName=`dirname ${fullPath}` # => /home/user/document
lastDirName=`${dirName%/*}` # ==> document

10回ループ

#!/bin/sh
for i in `seq 1 10`
do
    echo "$i times"
done

コマンドライン引数からファイル名を参照し、1行ずつ処理

#!/bin/sh
filename=$1
cat $filename | while read line
do
    ehco $line
done

今日はここまで

【ちょっと技術的なことAdventCalender】サーバのHDDをSSDに変えるのに苦労した話

この記事はちょっと技術的なことAdventCalenderの11日目です。

つい先日、研究室内のとあるサーバのHDDをSSDにして高速化しよう!というお話が上がり、サバ管を兼ねている自分が、その交換を行うことになりました。
研究室にはディスクの中身をハードウェア的にコピーするための機械があったので、適当にHDDとSSDをガチョンガチョン挿して動かせば終るだろうと思っていたのですが…大間違いでした。
ちなみに対象のサーバはRAIDが組んであるため、コピーするディスクは2枚になります。 そして購入の都合上、TrancendとSAMSUNGSSDを1枚ずつ購入したのですが、何故か同じ方法でできない…
ということで、自分の格闘記録を今後のために残しておきます。

共通

始めにGpartedをインストール

$ sudo apt-get install gparted

Trancend SSD

UbuntuSSDを接続し、Gpartedで1つパーティションを作成
File systemはcleardにする

その後、コピー用スタンドにHDDとSSDを接続し、コピーボタンを押す。

SAMSUNG SSD

UbuntuSSDとHDDを接続する。
SSDに何かパーティションができている場合は、Gpartedでパーティションを削除 その後、ddコマンドを用いて全てコピー

# /dev/sdbがコピー元、/dev/sdcがコピー先とする
$ sudo dd if=/dev/sdb of=/dev/sdc bs=512

進捗状況を見る場合、別のターミナルより以下を打ち込む

# nは進捗を表示する間隔(秒)
sudo watch -n 60 "pkill -USR1 dd"

ここまでこれば、SSDを接続し起動して、fdiscやcat /proc/mdstatなどでRAIDの状態を確認すれば完了です。
今日はここまで