2015年9月25日金曜日

angular.jsとonsenUI

node.js、express、mongoDBときているので次はangular.jsです。
UIはどうしようかと思ったのですが、angular.jsと相性が良さそうで日本語で情報が豊富なonsenUIにしてみました。
ちょっとまだソースコードがグチャグチャなのでもう少し整理してから後ほどGitにでもあげようと思います。
まだ、どの処理をサーバーのnode.js側で処理してどの処理をクライアントのangular.js側で処理するのがいいのかを悩み中です。
最初はほとんどの処理をangular.js側に持っていったのですが、mongoDBから読みだしたデータを大量に送った場合にかなり動作が重たくなることがわかったので、ある程度サーバーサイドで処理して扱いやすい形に変換してからクライアントサイドでUIに表示する形にしたほうがいいようです。
サーバーを動かしているさくらVPSのx86仮想サーバーとiPhone5sのARM系64bitCPUではやはりパフォーマンスが違いますね。


2015年9月18日金曜日

KiCad4.0 RC1(bzr6188) for OS-X

KiCad4.0 RC1が出ましたね。
早速試してみます。
build済みはNightlyBuildが
http://downloads.kicad-pcb.org/osx/
にありますが、TrackPad/MagicMouseの使い勝手がMacの標準的な使い方とあっていません。
具体的にはTrackPadならピンチで拡大・縮小、2本指の操作で指を動かす方向にスクロール、MagicMaouseなら1本指で指を動かした方向にスクロールというのが標準的な使い方ではないかと思います。(人によっては異論があるかもしれませんが、私はこれに慣れてしまっています)
NightlyBuildの方はTrackPadのピンチは効かず、2本指の操作で拡大/縮小、MagicMouseの1本指の操作も拡大/縮小になっています。
これはWindowsマウスのホイール動作に拡大/縮小が割り当てられているためと思われますが、クリック感があり上下にしか動かないホイールの動作とTrackPad/MagicMouseの360°操作では使い勝手が大きく違って、何をするにも画面が大きく拡大/縮小されてしまって使い物になりません。

仕方がないので自分でbuildします。
幸いなことに、この件に関するpatchがKiCadの中に既に含まれていて必要なpatchを当ててbuildしてくれるscriptが用意されています。
以前~gcorral/kicad/osx-trackpad-gesturesというbranchで進んでいたOSXのtrackpad対応が入っているようです。

これでbuildしてみて実行してみましたが、どうも以前のようにうまく行きません。
TrackPadのピンチで拡大/縮小が出来るようになっているのですが、2本指での操作でも拡大/縮小になってしまいます。MagicMouseの方も同様です。

osx-trackpad-gesturesと4.0のツリーを比較してみると関係ありそうな所が数箇所あったので修正してみました。
後々の為にpatchにしておきます。(あとでbuildの詳細でやり方を説明します)
これでbuildすると、当初の目論見どおり2本指の操作でスクロール、MagicMouseでもスクロールに出来ました。
さらに基板に日本語でシルクを入れられるように日本語フォント対応も追加します。
Windows環境が必要ですが、ここの手順どおりで日本語フォントを含んだnewstroke_font.cppを作成出来ます。
 http://wiki.kicad.jp/日本語フォントのマージ手順

ここまででひと通り準備が整ったので、以下buildの詳細手順です。
build方法は以下のページを参考にさせていただきました。
http://ochaochaocha3.hateblo.jp/entry/2015/01/05/installing-kicad-on-mac-os-x
ほぼ同じ手順ですが、patchに対応が入っていたりするので順番に記述します。

XcodeとCommandLineTools、brewが入っている前提で書きますので、ない場合は入れておいて下さい。

まずはbrewを最新の状態にして必要な物をinstallします。
# brew update
# brew install cmake bzr glew cairo swig

bzrに自分の名前とメールアドレスを設定します。
# bzr whoami '自分の名前 <name@example.net>'

適当なところにKiCad build用のdirectoryを掘ります。
# mkdir KiCad4.0
# cd KiCad4.0

KiCad 4.0のソースコード入手
# bzr branch lp:kicad/4.0

wxPythonソースコード入手
http://www.wxpython.org/download.php#source
から下の方にあるwxPython-srcのlinkをたどってtar-ballを取得し先ほどのdirectoryに移動し展開します。
# tar xjvf wxPython-src-3.0.2.0.tar.bz2

展開されたdirectory名を変更しておきます。
# mv wxPython-src-3.0.2.0 wx-src

以下のファイルをダウンロードして先ほどのdirectoryに移動し展開します。
kicad4.0_osx_patch.tgz

kicad_macosx_scroll.patch
newstroke_font.cpp
の2つのファイルができます。

TrackPad、MagicMouseのスクロール対応とyosemite対応
# cd 4.0
# patch -p0 < ../kicad_maxosx_scroll.patch

このpatchで
4.0/common/draw_panel.cpp
4.0/common/view/wx_view_controls.cpp
4.0/scripts/osx_build_wx.sh
の3つのファイルを修正しています。

日本語フォント対応
# cp ../newstroke_font.cpp common/newstroke_font.cpp

boost対応
/usr/libに幾つかdylibが存在しないためエラーになる
# sudo ln -s /Applications/Xcode.app/Contents/Developer/usr/lib/*.dylib /usr/lib
でsymbolic linkを作成

El Capitanではrootでも/usr/libに書き込み出来ないのでrootless対応が必要になるようです。

wxPythonのbuild 結構時間がかかります。
# sh scripts/osx_build_wx.sh ../wx-src ../wx-bin ./ 10.10 -j4
# cd ..

kicad本体のbuild こちらもかなり時間がかかります。
# mkdir build
# cd buld
# cmake ../4.0 \
      -DCMAKE_C_COMPILER=clang \
      -DCMAKE_CXX_COMPILER=clang++ \
      -DCMAKE_OSX_DEPLOYMENT_TARGET=10.10 \
      -DPYTHON_EXECUTABLE=`which python` \
      -DKICAD_SCRIPTING=ON \
      -DKICAD_SCRIPTING_MODULES=ON \
      -DKICAD_SCRIPTING_WXPYTHON=ON \
      -DwxWidgets_CONFIG_EXECUTABLE=../wx-bin/bin/wx-config \
      -DPYTHON_SITE_PACKAGE_PATH=`pwd`/../wx-bin/lib/python2.7/site-packages \
      -DCMAKE_INSTALL_PREFIX=../bin \
      -DUSE_IMAGES_IN_MENUS=ON \
      -DCMAKE_BUILD_TYPE=Release
# make
# make install
# cd ..

ここまででKiCad4.0/bin/にkicadの本体が出来上がります。

次にライブラリなど/Library/Application\ Support/kicad/に配置するものを集めます。
まず集める為のdirectoryを準備します。

# mkdir -p release/kicad

NightlyBuildをみるとkicad以下には下記のdirectoryがあります。
kicad/demos
kicad/help
kicad/internat
kicad/library
kicad/packages3d
kicad/template
ただしkicad/internatはkicad/share/intarnatに置かないと効果がないようです。

kicadをbuildするとbinに出来ているkicad.appとdemosをコピーします。
# cp -pr bin/kicad.app release
# cp -pr bin/demos release/kicad

次にlibrary, packages3d, templateを取得します。
# git clone https://github.com/KiCad/kicad-library.git
# cp -pr kicad-library/library release/kicad
# cp -pr kicad-library/modules/packages3d release/kicad
# cp -pr kicad-library/template release/kicad

次はhelp,internatを取得します。
公式repoはbzr branch lp:~kicad-developers/kicad/docなのですがgitの方が速いので非公式repoですがこちらから取得。
# git clone https://github.com/blairbonnett-mirrors/kicad-doc.git
# cp -pr kicad-doc/doc/help release/kicad
# mkdir release/kicad/share
# cp -pr kicad-doc/internat release/kicad/share

helpが古いのでkicad.jpの方々の最新版の翻訳(感謝!)に差し替えます。
# curl http://kicad.jp/translate/CvPcb.pdf -o release/kicad/help/ja/CvPcb.pdf
# curl http://kicad.jp/translate/Eeschema.pdf -o release/kicad/help/ja/Eeschema.pdf
# curl http://kicad.jp/translate/GerbView.pdf -o release/kicad/help/ja/GerbView.pdf
# curl http://kicad.jp/translate/Getting_Started_in_KiCad.pdf -o release/kicad/help/ja/Getting_Started_in_KiCad.pdf
# curl http://kicad.jp/translate/IDF_Exporter.pdf -o release/kicad/help/ja/IDF_Exporter.pdf
# curl http://kicad.jp/translate/KiCad.pdf -o release/kicad/help/ja/KiCad.pdf
# curl http://kicad.jp/translate/Pcbnew.pdf -o release/kicad/help/ja/Pcbnew.pdf
# curl http://kicad.jp/translate/Pl_Editor.pdf -o release/kicad/help/ja/Pl_Editor.pdf

これでrelease以下にkicad.appとkicadの2つのdirectoryが出来上がりました。
ここまでのreleaseをKiCad4.0RC1にrenameしたものを下記に置いておきます。

KiCad4.0RC1.zip

上のファイルを展開して
kicad.appは/Applications
kicadは/Library/Application\ Support
にそれぞれ移動します。

上記からダウンロードした場合、最初の1回めは開発元が未確認のため開けませんと言われてしまうので /Applicationフォルダを開いてKiCadを右クリックし「開く」を選択してダイアログボックスの「開く」を選択すると起動します。
起動するとタイトルバーにKiCad (2015-09-12 BR6188) .....と出ていれば正しく開けていると思います。
/Library/Application\ Support/kicad/demoに幾つかサンプルが入っているのでkicadから開いてみてください。

まだRC1なのでお試し用であることを踏まえて使ってください。

2015年9月11日金曜日

UIの設定ファイル


node.jsのサーバーとmongoDBでサーバーサイドまで繋がったので、その先のクライアントサイドの実現方法を考えていきます。
UIをHTMLでベタに書いていくのはありえないのでUI定義ファイルを設計します。
まず最初にどんなUIにするのか仮決めします。一度パスが通るところまで作ってしまってから気に入らなければ修正する方針で行きます。
初めてなので色々と想定できていない事があると思うので、とりあえず一度作ってみるのが手っ取り早いですよね。

ざっと要件を考えます。
・全体のステータスを表示するメインページ
 ・部屋ごとにまとめて表示
 ・制御できるものに関しては選択すると制御用のボタンが出てくる
  ・TVはon/off/volume+/volume-/選局のボタン
  ・エアコンはモード切り替え/温度選択/offのボタン
・温湿度のグラフページ
 ・3日分くらい表示
・ログの確認ページ
 ・状態の変化、コマンドの記録
・デバイスの動作状況の確認ページ
 ・各コントロールモジュールの電源電圧と動作状況
といったところを表示します。

 最終的に作ったUIのメインページはこのような感じです。

  


UI定義ファイルは最終的にjavascriptで扱うのでJSONで記述します。

最初は部屋を定義します。
この後のItemの定義で使用するroomと表示用のlabelを定義しています。
Itemとは上のUIの1行ごとの部分を指しています。
この定義は上のUIのグレーになっている部分のタイトルなどに使用されます。
  "RoomList": [
    { "room": "all", "label": "全体"},
    { "room": "living", "label": "リビング"},
    { "room": "kitchen", "label": "キッチン"},
    { "room": "bathroom", "label": "浴室"},
    { "room": "bedroom", "label": "寝室"},
    { "room": "studyroom", "label": "書斎"}

  ],

次に各制御対象毎にUI定義をします。全部出しても同じような定義だらけなので代表的なものだけに絞っています。

 "ItemList": [
   { 
     "room": "all",
     "label": "玄関",
     "status": [
       {"label": "玄関錠", "sensor": "entrance_key_stat"}
     ],
     "buttons": [
       {"label": "open", "command": "entrance_key_open"},
       {"label": "close", "command": "entrance_key_close"}
     ]
   },

このItemは上のUIの一番上の方の玄関のラインを定義しています。
最初にroom:でallを指定しているのでUI上は全体の項目の中に配置されています。
次のlabelはこのItemの項目名として表示されているものです。
status arrayの中はCloseと表示されている状態表示の定義です。
この中のlabelはsensorのための表示用ラベルですがここのUIでは使用していません。
後にGraph等の中で必要になるかもしれないので定義しています。未定義の場合はroom.label+item.labelとみなされます。
button arrayの中はこのItemを選択した時に出てくるボタンを定義しています。下が玄関Itemを押すと出てくる制御用のボタンです。

  
labelでボタンに表示する名称、commandに送信するコマンドを定義しています。

次のItemは制御部分が無くstatusが2つあるものの定義です。
  
この例のように同じような種類の情報表示のみの場合に使っています。

  { 
    "room": "all",
    "label": "室内1F/2F",
    "status": [
      {"label": "室内1F温度", "type": "temp", "sensor": "internal_1f_temp"},
      {"label": "室内2F温度", "type": "temp", "sensor": "internal_2f_temp"}
    ]
  },

statusにtypeを設定すると単位をつけたりいくつかのところで特別な処理をしたりします。
今のところtemp,humidity,pir,rainと設定なしの5種類があります。

次はエアコン用のitemです。
  {
    "room": "study room",
    "type": "aircon",
    "label": "エアコン",
    "status": [
     {"type": "temp", "sensor": "studyroom_aircon_temp"},
     {"sensor": "studyroom_aircon_stat"}
    ],
    "commandPrefix": "studyroom_aircon_", 
    "buttons": [
     {"label": "off", "command": "off"}
    ]
  },
typeでairconであることを指定しています。これはモード切り替え、温度切り替えのために特殊処理があるためairconであることを指定します。 statusは温度とエアコンの状態を表示しています。commandPrefixは送信コマンドの前に追加する文字列を指定していて、この後出てくるAirconListで指定しているコマンドと合わせることで送信するコマンドを決定します。これはエアコン自体のコマンドは同じものだけどコントロールモジュールが違うのでエアコン毎にprefixを付けたい場合に付加します。
buttonsでモード切り替え、温度切り替え、送信ボタン以外のボタンを定義します。ここではOffボタンだけです。
エアコンを選択すると下の状態になります。

  

ここでcoolerの所を上下にスライドするとモードが切り替わります。


  
さらに温度の所を上下にスライドすることで温度設定を変更できます。
このカルーセルの動作の度にリモコンコードが出てしまうと煩わしいので決めた後にsendボタンで送信するようにしています。

カルーセル自体のUI設定はItemListの中ではなく別途AirconListの中で定義しています。

  "AirconList": [
    { "label": "auto", "color": "#404040",
      "command": [
        { "label": "auto", "commandSuffix": "auto" }
      ]},
    { "label": "cooler", "color": "#2586d9",
      "command": [
        { "label": "18", "commandSuffix": "cooler_18" },
       : 途中省略
        { "label": "29", "commandSuffix": "cooler_29" },
        { "label": "30", "commandSuffix": "cooler_30" }
      ]},
    { "label": "heater", "color": "#d93625",
      "command": [
        { "label": "18", "commandSuffix": "heater_18" },
       : 途中省略
        { "label": "28", "commandSuffix": "heater_28" },
        { "label": "29", "commandSuffix": "heater_29" }
      ]}
  ],

 最後にTVのitemです。

  {
    "room": "living",
    "type": "tv",
    "label": "TV",
    "commandPrefix": "",
    "buttons": [
      {"label": "on", "command": "tv_on"},
      {"label": "off", "command": "tv_off"},
      {"label": "vol+", "command": "tv_vup"},
      {"label": "vol-", "command": "tv_vdn"}
    ]
  },

typeをtvにしてitemがTV用であることを指定します。
commandPrefixはエアコンの時と同じです。
buttonsにon, off, vol+, vol-を指定しています。
TVの場合はこれらのボタンと選局のカルーセルが現れます。

  

TV局名の所を上下にスライドすることで選局できます。

  
選局後に局名ボタンを押すことでコマンドが送信されます。
TVの選局リストは下記のように定義しています。

 "TVChCommandList": [
    { "label": "NHK", "commandSuffix": "tv_1" },
    { "label": "NHK", "commandSuffix": "tv_2" },
    { "label": "日テレ", "commandSuffix": "tv_4" },
    { "label": "テレビ朝日", "commandSuffix": "tv_5" },
    { "label": "TBS", "commandSuffix": "tv_6" },
    { "label": "テレビ東京", "commandSuffix": "tv_7" },
    { "label": "フジテレビ", "commandSuffix": "tv_8" },
    { "label": "TOKYO MX", "commandSuffix": "tv_9" },
    { "label": "放送大学", "commandSuffix": "tv_12" }
  ]

今回はここまで。




2015年9月4日金曜日

mongoDB/mongoose

自宅のNATの内側から、さくらVPSのnode.js上のサーバーまでsecure websocket serverが開通したので気温や湿度、コマンドのやりとりや各種センサーの情報を記録するデータベースを設定していきます。
MEANスタックというのが流行りのようなので、mongoDBを使ってみることにしました。
node.js上のアクセスはmongooseを使用しています。
素人が書くよりも色々ネット上に書いてくれている情報があるのでインストール方法とかは省略します。
mongooseはSchemaを設定してアクセスするようなので、最初にどういうデータで格納していくかを考えていきます。
まず、定期的に測定する気温や湿度などのセンサー情報と何かスイッチ等をon/offした時などのタイミングで上がってくるイベントの最新情報のみを記録するためのStatusSchemaを考えます。これはLogとは別に最新データを素早くアクセスするために記録します。
情報としては接続先のclientID、センサー名称、センサーの値、更新時のTimeStampです。

var StatusSchema = new mongoose.Schema({
  client: {type:String, required: true},
  sensor: {type:String, required: true},
  value: {type:String, required: true},
  update: {type:Date, default:Date.now}
});
var Status = db.model('status', StatusSchema);

次にデバイスの最新情報を取得して保持しておくためのDeviceInfoSchemaです。
記録する情報は接続先のclientID、デバイス名称、デバイスタイプ、ZigBeeアドレス下位32bit、デバイスの機能情報、バージョン、電源電圧、デバイス状態、更新時のTimeStampです。
var DeviceInfoSchema = new mongoose.Schema({
  client: {type:String, required: true},
  device: {type:String, required: true},
  type: {type:String, required: true},
  addrL: {type:String, required: true},
  func: {type:String, required: true},
  version: {type:String, required: true},
  voltage: {type:String, required: true},
  state: {type:String, required: true},
  update: {type:Date, default:Date.now}
});
var DeviceInfo = db.model('deviceInfo', DeviceInfoSchema);

次は全ての変化の記録を記録していくLogSchemaです。
ここには接続先のclientID、情報のタイプ、データベースを検索する時に情報粒度を落とすためにつける分解能フラグ、更新時のTimeStamp、データ本体です。
データ本体はコマンド、レスポンス、ステータスなどのメッセージobjectです。
var LogSchema = new mongoose.Schema({
  client: {type:String, required: true},
  type: {type:String, required: true},
  fine: {type:Boolean, required: false},
  timeStamp: {type:Date, default:Date.now},
  data: mongoose.Schema.Types.Mixed
});
var Log = db.model('log', LogSchema);

最後にclientごとのUIを定義するためのClientUISchemaです。
これは制御する機器のコマンドとUIの表記を記述したテーブルを記録しています。
var ClientUiSchema = new mongoose.Schema({
  client: {type:String, required: true},
  clientUi: mongoose.Schema.Types.Mixed
});
var ClientUi = db.model('client_ui', ClientUiSchema);

長くなってきたのでUIのテーブルについては次回にします。