CSVの文字コードをUTF-8に移行しようとした話
この記事はAkerun Advent Calendar 5日目です。
はじめまして! AkerunのAPIや管理画面を主に開発しているエンジニアのhirataです。
CSVファイルをWindowsのMicrosoft Excelで読み込んだ時、日本語が文字化けする問題を解決しようとした時の話です。
よく見る解決方法は、読み込もうとしているCSVファイルの文字コードをSJISに変換してからExcelで開く事です。
しかし、SJISにも読めない文字が存在します。 例えば「土」に「口」の「𠮷」という文字です。使用している変換プログラムによっては「吉」ではなく「𠮷」を使う場合があるのでExcelで開くと文字化けします。
ここでもう一つの可能性がある方法を紹介したいと思います。 それは、UTF-8文字コードです。
UTF-8でCSVをエンコードすると「𠮷」という字も読めます。 だが、CSVをUTF-8にエンコードしてもExcelで開いたらまだ文字化けをしています。
ExcelでUTF-8を開く時はUTF-8のBOM付きというCSVではないと正しく読み込めない事がわかりました。 BOMとはByte Order Markの略で簡単に説明するとファイルの先頭に、このファイルはUTF-8ですよと数バイト付けてあげる事です。
これでExcelでも「𠮷」が文字化けしないで読めます。
文字化けしない事がわかったのでとりあえずCSVを「𠮷」で埋め尽くして保存しましょう。
少し寝かせて出来上がった物がこちらです。
え?
そうなんです、ExcelでUTF-8のBOM付きのファイルを編集して保存をしたらBOMをはずしてしまうのです。 読める事はできるが編集ができないCSVファイル、、、、
読み込む事はできても編集ができないのでこれからもSJISで頑張りたいと思います。
今回は、皆様が同じような過程で時間を無駄にしないように記事にしてみました。
組込みCテストフレームワークUnityを導入してテストコード生産性をUp
回路設計・ファーム開発担当のす〜(@ksksue)です。
弊社のBLEモジュールnRF52(Cortex-M4F)のファームウェア開発環境にテストフレームワークUnity導入したところ カンタンな導入でテストコード生産性が飛躍的に上がりました。今回はそのUnityの紹介&解説です。
IoT開発においてハードウェアとソフトウェアを組合せたテストは品質を担保するために大事な工程ですが、 テスト項目が多くなってくると非常に煩雑になりがちです。Unity導入によってその煩雑さが少なくなりました。
オレオレテストコードをいじりまわす日々
Unity導入前、最初はカンタンなオレオレテスト環境で進めていたのですが、テスト項目数が増えるに従ってその環境自体を場当たり的に改善していくといった作業が発生しました。 正直なことを言うと、改善したいという欲求にあがらえず、必要以上の作業をしていた部分があったと思います。 それが進むと今度は過去のテストコードの再利用性が悪くなり、それをカバーする作業発生といった悪循環におちいりました。 テストコードを書いているのか、環境改善しているのかよくわからないというひどい状況でした。
Unityを導入することによって、前述したようなテスト環境自体に手を加える必要性がなくなり、本質的な作業に集中できるようになりました。 テストコードスタイルが統一されることでコードの保守性がダントツによくなります。 色々な邪念がなくなりテストコード生産性が上がったことが最大の導入メリットだと思います。
機能は少ないが充分な組込みCテストフレームワーク
Unityと検索するといろいろと別のフレームワークの結果が出てくるのですが、本記事で扱うUnityはC言語テストフレームワークのUnityです。
Unityは主に以下の機能を提供します
- アサーション : 整数や配列などの期待値と実体値を比較しPASS/FAILを判定。FAILの場合そのアサートのファイル名と行数を出力
- レポート:何件PASS/FAILあったかをレポート
例えばアサーションは以下のように期待値と実行値を指定します。
TEST_ASSERT_EQUAL_HEX8(10, func(i));
func(i)の結果が10でなかった場合は以下のようにファイル名、行数、関数名、期待値と実行値を出力します。
..\test\unity_test.c:232:test_drv_spi_write__should__Write:FAIL: Expected 10 Was 0
レポート結果はシンプルにいくつテストし、FAILが何回発生したかが出力されます。
----------------------- 10 Tests 0 Failures 0 Ignored
他のテストフレームワークに比べると機能は少ないですが組込みCという環境を考えると充分と言えるでしょう。
さて、前置きが長くなりましたが以下導入方法の説明に入ります。
動作確認環境
※ Unityは非常に移植性が高いので一例と捉えてもらってOKです。基本的にはputやprintf などキャラクタ出力できる環境があればOKです。
Unity環境準備
ソースコードを下記ページから拝借します
https://github.com/ThrowTheSwitch/Unity
unity_config.h を編集します。環境依存の部分があるのでそこはよしなに設定してください。 ポイントは UNITY_OUTPUT_CHAR(a) の設定です。
#define UNITY_EXCLUDE_STDINT_H #define UNITY_INCLUDE_DOUBLE #define UNITY_OUTPUT_CHAR(a) NRF_LOG_OUTPUT_CHAR(a) #define UNITY_OUTPUT_START() NRF_LOG_INIT() #define UNITY_SUPPORT_WEAK weak __WEAK
編集前ファイル https://github.com/ThrowTheSwitch/Unity/blob/master/examples/unity_config.h
この NRF_LOG_OUTPUT_CHAR のput先に、今回はJLink RTTを指定します。JLink経由でキャラクタ出力できるので非常に便利です。 nRF52の場合は以下のようなファイルを用意しました。
unity_config.c
#include "nrf_log.h" void NRF_LOG_OUTPUT_CHAR(int c) { NRF_LOG_PRINTF("%c",c); }
これだけです。
あとはプロジェクトファイルに以下ファイルを追加
unity.c unity_config.c
以下ファイルをincludeパス上に追加
unity_config.h unity_internals.h
unity_config.hを有効にするためにコンパイルオプションのDefineに以下を追加
UNITY_INCLUDE_CONFIG_H
これでunity環境が整いました。
Unityテスト方法
あとは公式のドキュメントどおりにコーディングすればOKです。
引用: http://www.throwtheswitch.org/unity
以下のように、3つの引数の平均値を計算する関数をテストする場合を想定します。
テスト対象:DumpExample.c
#include "DumbExample.h" int8_t AverageThreeBytes(int8_t a, int8_t b, int8_t c) { return (int8_t)(((int16_t)a + (int16_t)b + (int16_t)c) / 3); }
テスト対象のヘッダファイル:DumpExample.h
#include <stdint.h> int8_t AverageThreeBytes(int8_t a, int8_t b, int8_t c);
以下のように UNITY_BEGIN(); と UNITY_END(); の間に RUN_TEST(テスト関数); を入れ込み、 テスト関数内でチェックのためのアサートを書きます。
テストコード:main.c
#include "unity.h" #include "DumbExample.h" void test_AverageThreeBytes_should_AverageMidRangeValues(void) { TEST_ASSERT_EQUAL_HEX8(40, AverageThreeBytes(30, 40, 50)); TEST_ASSERT_EQUAL_HEX8(40, AverageThreeBytes(10, 70, 40)); TEST_ASSERT_EQUAL_HEX8(33, AverageThreeBytes(33, 33, 33)); } void test_AverageThreeBytes_should_AverageHighValues(void) { TEST_ASSERT_EQUAL_HEX8(80, AverageThreeBytes(70, 80, 90)); TEST_ASSERT_EQUAL_HEX8(127, AverageThreeBytes(127, 127, 127)); TEST_ASSERT_EQUAL_HEX8(84, AverageThreeBytes(0, 126, 126)); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_AverageThreeBytes_should_AverageMidRangeValues); RUN_TEST(test_AverageThreeBytes_should_AverageHighValues); return UNITY_END(); }
このコードをコンパイルし、JLinkRTTでウォッチすると以下のような結果が返ってきます。
testUnit1.c:21:test_AverageThreeBytes_should_AverageMidRangeValues:PASS testUnit1.c:22:test_AverageThreeBytes_should_AverageHighValues:PASS ----------------------- 2 Tests 0 Failures 0 Ignored OK
レポートはUNITY_BEGIN()からUNITY_END()までのテストがレポートされます。 テストシナリオを区切りたい場合には別のBEGIN/ENDで区切るのがよいでしょう。
まとめ
組込みCテストフレームワークUnityの導入方法、カンタンな使い方の解説をしました。
弊社ではIoT製品が商材ということもあって、ハードウェア/ソフトウェア共に何千万回とテストをしています。 例えばAkerunの開け閉め、Flashの書換、LEDの点灯消灯などを実環境で動かしつつ、一つ一つエラー率などを確認しています。 弊社の開発室にはRPiやArduino、センサ、ソレノイド、モーターなどで作った治具が所狭しと並べられていて、 テストシーズンには常にガチャガチャと治具が動く音が響き渡っています。
Unityはそいういったテスト環境においてカンタンに導入でき、テストコードの保守性、生産性をアップさせる非常に強力なツールです。 同じような環境の現場の方々、導入を検討してみてはいかがでしょうか?
参考
UnityのHP : http://www.throwtheswitch.org/
Unityのgithubリポジトリ : GitHub - ThrowTheSwitch/Unity: Simple Unit Testing for C
告知
弊社ではエンジニアを募集しています!
パソコンをロックせずに放置する人をしつける12個のいたずら
Akerun Advent Calendar 3日目 〜セキュリティ意識の改善〜。
はじめまして! AkerunのAPIや管理画面を主に開発しているエンジニアのuchidaです。
みなさんはパソコンから離れるとき、ロックする習慣はついているでしょうか?
Photosynthでは「社内でもパソコンから離れる時はロックする」ということが社内規則で定められています。
こういったルールを規則として設けてる会社は多いのですが、守らない人というのは僕がこれまで努めたどの会社にもいました。
そういう人には離席した時にパソコンを操作して「実際に困る」という事を体験してもらえばいいのではないかと考え、今回は僕が考えた効果の高そうな方法を紹介します。
あくまでネタ記事として軽い気持ちで読んでいただけると幸いです。
今回の記事の対象OSはmacOSです(便宜上「パソコン」と表記します)。
1. ディスプレイの表示を回転させる
ディスプレイを回転させた状態で表示させます。 マウスカーソルの操作がとても大変になるので、もとに戻すのもイライラします。
作業時間
1分
手順
- 「システム環境設定」を開く
[⌘command]
キー +[⌥option]
キーを押しながら 「ディスプレイ」 を選択- 「回転」項目を「標準」から適当な角度のものに変更
- 表示されたダイアログの「確認」を選択
戻し方
- 「システム環境設定」を開く
[⌘command]
キー +[⌥option]
キーを押しながら 「ディスプレイ」 を選択- 「回転」項目を「標準」に変更
対策
パソコンから離れる時はロックしましょう。
2. ディスプレイの色を反転させる
表示する色を反転させます。 地味ですが、人によってはディスプレイが壊れたと勘違いするほどインパクトがあります。 あまり使うことがない機能なので戻し方がわからない人も多いと思います。
作業時間
1分
手順
[⌘command]
キー +[⌥オプション]
キー +[⌃control]
キー +[F5]
キー でアクセシビリティオプションを開く- 「ディスプレイの色を反転」 にチェックを入れる
- 「完了」を選択
一応ショートカットが用意されているので、こちらで動けば一発でいけます。
[⌘command]
キー + [⌥オプション]
キー + [⌃control]
キー + [8]
キー
戻し方
[⌘command]
キー +[⌥オプション]
キー +[⌃control]
キー +[F5]
キー でアクセシビリティオプションを開く- 「ディスプレイの色を反転」 のチェックをはずす
- 「完了」を選択
対策
パソコンから離れる時はロックしましょう。
3. デスクトップに紛らわしいファイルを沢山つくる
デスクトップに存在するファイルを沢山コピーして名前を似たものに変えます。 中身が同じなのでファイルを消す際にとても慎重になる悪質ないたずらです。 アイコンの並び順をシャッフルすると更に悪質ですね。
作業時間
1分
手順
- 既存のファイルを選択
[⌘command]
キー +[d]
キー でファイルを複製- ファイル名を変更
- 1 ~ 3を繰り返す
戻し方
手動で地道に不要なファイルを消すくらいでしょうか。
対策
パソコンから離れる時はロックしましょう。
4. デスクトップの壁紙をスクショに差し替える
デスクトップの壁紙を「デスクトップのスクリーンショット」に差し替えるいたずらです。 ファイルを消してもアイコンが残ったり、選択できない謎のファイルが存在するように見えます。 なかなか気づかないと思います。
作業時間
1分
手順
- Docを右クリック
- ["自動的に非表示"をオンにする]を選択
- Docが非表示になっている状態で
[⌘command]
キー +[⇧shift]
キー +[3]
キー でスクリーンショットを保存 - デスクトップを右クリック
- [デスクトップのバックグラウンドを変更]を選択
- 先程とったスクリーンショットに変更
戻し方
- デスクトップを右クリック
- [デスクトップのバックグラウンドを変更]を選択
- 元の壁紙ファイルを選択
対策
パソコンから離れる時はロックしましょう。
5. ログインシェルを変える
ターミナルのログインシェルはデフォルトではbashですが、普通のshに変えてしまういたずらです。 挙動はあまりかわりませんが、bash_profileやbashrcで追加しているパスやエイリアスなどが読み込まれないので、普段使ってるコマンドが使えないという事が起こります。
作業時間
1分
手順
- terminal.app を開く
echo '/bin/sh' >> .bash_profile
を実行
戻し方
.bash_profile
から該当のコードを削除
対策
パソコンから離れる時はロックしましょう。
6. stressコマンドをバックグラウンドで走らせる
常にパソコンが重たくなり、本体は熱くなり、ファンがうなり始めます。 ハード・ウェアに負担がかかり寿命が短くなったりするので長時間やるのは避けたほうがいいと思います。
作業時間
1分
手順
- terminal.app を開く
brew install stress && stress --cpu 4 --quiet &
を実行
戻し方
- terminal.app を開く
killall stress && brew uninstall stress
を実行
対策
パソコンから離れる時はロックしましょう。
7. デフォルトのエディタをnanoに置き換える
普段 vim や emacs などを使っている人をイラッとさせることができます。 コマンドを叩くとエディタが立ち上がってしまうので下手な操作ができなく、 普段 nano をつかっていない人は慎重に exit する操作をするはめになります。
作業時間
3分
手順
- terminal.app を開く
- 以下のコマンドを実行
# gitなどで立ち上がるエディターを変更 echo 'export EDITOR=nano' >> ~/.bash_profile # nano用の関数を定義 echo 'hoge(){ nano "${$1}"}' >> ~/.bash_profile # viをnanoに置き換え echo 'alias vi="hoge"' >> ~/.bash_profile # vimをnanoに置き換え echo 'alias vim="hoge"' >> ~/.bash_profile # emacsをnanoに置き換え echo 'alias emacs="hoge"' >> ~/.bash_profile # atomをnanoに置き換え echo 'alias atom="hoge"' >> ~/.bash_profile
戻し方
.bash_profile
から該当のコードを削除
対策
パソコンから離れる時はロックしましょう。
8. lsコマンドをslコマンドに置き換える
ファイル一覧を表示しようとすると蒸気機関車のSLが走ります。 個人的にこういうくだらないのが大好きです。
作業時間
3分
手順
- terminal.app を開く
- 以下のコマンドを実行
# slコマンドをインストール brew install sl # じゃまなエイリアスを貼っている場合があるので無効化 echo 'unalias sl' >> ~/.bash_profile echo 'unalias ll' >> ~/.bash_profile echo 'unalias tree' >> ~/.bash_profile # lsをslに置き換え echo 'alias ls="sl"' >> ~/.bash_profile # llをslに置き換え echo 'alias ll="sl"' >> ~/.bash_profile # treeをslに置き換え echo 'alias tree="sl"' >> ~/.bash_profile
戻し方
.bash_profile
から該当のコードを削除- terminal.app を開く
brew uninstall sl
を実行
対策
パソコンから離れる時はロックしましょう。
9. 環境変数の$PATHを空にする
ほとんどのコマンドが叩けなくなります。 一歩間違うと元に戻せなくなる可能性があり、非常に危険ないたずらです。
作業時間
1分
手順
- terminal.app を開く
- 以下のコマンドを実行
echo 'export PATH=""' >> ~/.bash_profile
戻し方
.bash_profile
から該当のコードを削除
対策
パソコンから離れる時はロックしましょう。
10. echoコマンドやcatコマンドを叩くと大音量で読み上げる
echoコマンドやcatコマンドを叩くと最大音量で出力を読み上げるいたずらです。 一瞬驚かせることができると思います。
作業時間
1分
手順
- terminal.app を開く
- 以下のコマンドを実行
# 引数を読み上げる関数を作成 echo 'hoge1(){ set Volume output volume 100; say "${$1}"}' >> ~/.bash_profile echo 'alias echo="hoge"' >> ~/.bash_profile echo 'alias print="hoge"' >> ~/.bash_profile # 引数のファイルを読み上げる関数を作成 echo 'hoge2(){ set Volume output volume 100; say -f "${$1}"}' >> ~/.bash_profile echo 'alias cat="hoge"' >> ~/.bash_profile echo 'alias less="hoge"' >> ~/.bash_profile echo 'alias tail="hoge"' >> ~/.bash_profile
戻し方
.bash_profile
から該当のコードを削除
対策
パソコンから離れる時はロックしましょう。
11. ブラウザを差し替える
Firefoxを起動したはずなのに、Safariが起動するというものです。 ChromeやOperaなども同じやり方で可能です。 人によってはデフォルトのブラウザが変わったと勘違いすると思います。
作業時間
1分
手順
- Finderで/Applicationsを開く
Firefox.app
の名前を_Firefox.app
に変更Safari.app
を選択して[⌘command]
キー +[d]
キー で複製してFirefox.app
に変更
戻し方
- Finderで/Applicationsを開く
Firefox.app
を削除_Firefox.app
の名前をFirefox.app
に変更
対策
パソコンから離れる時はロックしましょう。
12. シェルログイン時に「うっ…」となるメッセージを吐かせる
ターミナルを開く度に「進捗どうですか?」など出力するようにして何とも言えない気持ちにさせることができます。
作業時間
1分
手順
- terminal.app を開く
- 以下のコマンドを実行
echo 'echo "\n\n\n\n\t進 捗 ど う で す か ?\n\n\n"' >> /.bash_profile
戻し方
.bash_profile
から該当のコードを削除
対策
パソコンから離れる時はロックしましょう。
最後に
他にも、「パソコンを閉じた時にGetWildを大音量で流す」というのを仕込んだりするのも面白いですね。
毎回違ういたずらを仕掛けると効果的だと思います。
ただ、他人のパソコンを操作すること自体いいことではないですし、元に戻せなくなるとそれこそ仕事に支障をきたしてしまうので、実践される方は十分に注意してください。
CTOに話したら「alias emacs=viが鉄板やな」って言っていて、いたずらどころか宗教戦争になりかねないので、これもやめましょう。
それでは、以上のことは完全に自己責任でよろしくお願いします。
株式会社フォトシンスでは、セキュリティ意識の高いエンジニアを募集しています!
Slack Emojiを支える技術
この記事はAkerun Advent Calendar の2日目の記事です。 今回は弊社のSlack Emojiを量産する技術についてです。
Emojiは全従業員共通の概念
最初にEmoji(当時はEmoticonと呼んでいた)がSlackに追加されたときには、弊社のエンジニアやデザイナーがちょっと盛り上がった程度で終わっていました。
ですが、社員も増えて来てタイミングで大量にEmojiを追加してみたら、なんと皆さんいい感じに食いついてきて、今では一部の社員だけでなく、アルバイト含めた全従業員がカジュアルにEmojiを使っています。
またコミュニケーションが円滑に進んだり、業務の完了やステータスを伝えるために使われていたりと、多種多様案使い方のために、各自でカスタムEmojiを追加したりもするようになってきたので、その辺のEmoji追加を支える技術について書きたいと思います。
代表的な二つのChrome Extensionを導入する
これがあるとないとでは、今後のEmoji生活が変わってきます。 絶対に入れましょう。
複数のEmoji画像を一度にアップロードする
まず、ネットにある沢山のEmoji画像を大量にぶち込みたいので、このChromeExtensionを導入します。 これを導入することで、SlackのEmojiの管理画面に、以下の用にEmojiをドラッグアンドドロップできる、Viewが追加されます。
アップロードするEmojiは世界中の有志の方が親切にgithubに固めて上げてくれているので、それを使いましょう。ぐぐるとたくさんあります。 弊社ではポケモンが人気ですね。
GitHub - Templarian/slack-emoji-pokemon: Slack Pokemon Emojis
Web上の画像を右クリックからアップロードする
こちらもめちゃくちゃ便利です。
何か突発的にEmojiを追加しないといけないタイミングが皆さんあると思うのですが、これでググってすぐに話題に対応できるタイミングでEmojiを追加することができます。慣れれば最速5秒程度で追加できます。
また、なぜかこのサイトと親和性があり、クリックすると本来画像がダウンロードされるサイトなのですが、すぐにアップロードしてくれます(名前も勝手に付けてくれます)。
なので、まずは、このサイトでいいやつを追加してもいいかもですね。
Slackmojis - The Best Custom Slack Emojis
コマンドラインからSlackEmojiをアップロード
ネット上にある様々なEmojiをこのツールを使ってCLIから自動アップロードできます。
見ずにアップロードが不安なので、確認はした方がいいと思いますが。
便利。
絵文字系のEmojiを量産する
よく使います。先程のExtensionを入れていれば作成後に右クリックして上げるだけでいいので便利です。
社員の顔写真を自動でEmojiにする
これは自作です。MSのVison APIを使ってカジュアルにつくって見ました。 弊社では、全員の顔が見えるように、Slackのプロフィール画像を顔社員にしているので、今回はそれを自動でダウンロードして、加工しています。
Gem
# frozen_string_literal: true source "https://rubygems.org" gem "microsoft_computer_vision" gem "mini_magick" gem 'rest-client'
MSのVison APIのgemあるの便利。また、画像加工は今回はmini_magickを使って見ました。
SourceCode
手順としては以下です。
- SlackAPIを叩いて、プロフ画像のURL一覧を取得する
- そのURLをそのままVision APIに渡して顔座標を得る
- 画像URLと顔座標からMiniMagick自体のメソットで画像ダウンロード+画像加工を行う
#!/usr/bin/env ruby # coding : utf-8 require 'microsoft_computer_vision' require 'mini_magick' require 'json' require 'rest-client' class SlackProfileImage2Emoji class << self def run members = slack_members members.each do |member| sleep 5 puts member rect, err = detect_face member[:profile_image] unless err image = crop_face rect: rect, url: member[:profile_image] image.write "orgs/ps_#{member[:name]}.#{member[:profile_image].split('.').last}" # Slackにアップロードできるサイズに縮小する image.resize "128x128" image.write "icons/ps_#{member[:name]}.#{member[:profile_image].split('.').last}" end end end # 画像URLに対応する顔座標を返却する def detect_face image_url client = MicrosoftComputerVision::Client.new('<VISION_API_TOKEN>') options = { visual_features: 'Faces', details: 'Celebrities' } res = client.analyze(image_url, options) json = JSON.parse res.body puts json return { width: json["faces"].first["faceRectangle"]["width"], height: json["faces"].first["faceRectangle"]["height"], left: json["faces"].first["faceRectangle"]["left"], top: json["faces"].first["faceRectangle"]["top"], }, nil rescue puts "------- Error: Can not get face info" return nil, true end # Slackの従業員のプロフィール画像のURL一覧を取得する def slack_members res = RestClient.get 'https://slack.com/api/users.list', { params: { token: "<YOUR_SLACK_TOKEN>", } } (JSON.parse res.body)["members"].select{|member| # 適当にフィルタ member["deleted"] == false && member["is_restricted"] == true && member["is_bot"] == false && member["name"] != "slackbot" }.map{|member| # 名前と画像URLのハッシュにして返却 {name: member["real_name"].downcase.gsub(/ /, '_'), profile_image: member["profile"]["image_original"]} } end # 顔座標とURLを受け取って加工する def crop_face rect:, url: image = MiniMagick::Image.open(url) image.crop "#{rect[:width]}x#{rect[:height]}+#{rect[:left]}+#{rect[:top]}" image end end end if __FILE__ == $0 # 引数ありなら、単一のURLの画像についてだけ顔認識APIを叩く if ARGV[0] puts (SlackProfileImage2Emoji.detect_face ARGV[0]) else SlackProfileImage2Emoji.run end end
完成品
※わざと小さくしてます。
定期的に実行するだけでいいので、便利。
まとめ
皆様も素敵なEmojiライフを❗
株式会社フォトシンスでは、急成長するIoT製品「Akerun」を進化させるエンジニアを募集しております!
IoT開発を加速する!!IoTツール開発、その後
はじめまして!
この記事はAkerun Advent Calendar の記念すべき1日目の記事です。 今回はAkerunをつくっている会社フォトシンスのCTOのkazuphが担当します!(`・ω・´)ゞ
IoTツール開発
弊社のメインプロダクトはAkerunというスマートロックですが、その開発や工場での生産の過程で様々なIoTツールを開発しています。
つまり、ネットワークに接続するツールを開発することで、開発生産性を高めたり工場での作業フローを改善して、品質と原価削減を行っています。
去年のbuildersconやAdvent Calendarではその一部を発表させていただきました。
あれから1年
ちょこちょこ自分がつくったツールを晒してみます。詳しくは、別記事で掘るかも。
[mqtt-eater]MQTTの流れを保存し画面表示+Slackに通知する君
うちの開発だと登場人物が多くてテストをするときなどは、信号の発信役、中継役、受け取り役などパートごとに開発者が違うので、わざわざ動作を確認してもらうのが億劫なときがあります。そういうときに、Slackとかで見れると便利だろうということでつくりました。
今回は情報の発信元のログ(MQTTのPublish)をSlackに流し、中継役(ゲートウェイ)、受け取り役(スマートロック本体)の開発者がそれを見ます。
使用gemは以下(あ、rubyが前提になってる)。
ソースコードのイメージは以下のような感じです。
class Mqtt def self.run t = Thread.new do MQTT::Client.connect( remote_host: "<mqtt.broker.domain>", remote_port: <XXXX>, username: "<user_name>", password: "<password>", ssl: true, ) do |client| client.subscribe('<your_topic>/#') client.get do |topic,message| Slack.post "#dev-mqtt-log , "[MQTT] pub #{topic}: #{message}" ary = topic.split('/') DB.create_or_update ary.last, ary.first, message end end end end end
開発用にPOSTも付けるといいかなと思いましたが、社内向け管理画面に"Chamber"という名前のカッコいいツールがあるので、そっちと統合してもいいかなと思い一旦最低限までやってペンディング中。
Sinatraの部分は一旦僕がニヤニヤするためだけに、今後も開発を続けてもいいかなという感じです。
[auto-wifi-setting-changer]APの管理画面から自動でWi-Fi設定を更新する君
Wi-Fiの周波数・チャネルが切り替わるものが増えてきているので、それを連続で試験するためにつくりました。 APの管理画面はWebベースなことが多いので、スクレイピングすればほぼなんでもできます。
使用gem
- capybara
- selenium-webdriver
- chromedriver-helper
- pry
スクレイピングしたいと思う度にcapybaraは未だ顕在だし、調べれば調べるほどに簡単な書き方が出てきて本当に素晴らしいですね(無駄なsleepを徹底的に解除できる)。
最近はChromeDriverも安定していて、開発中はpryで止めてChromeのインスペクターでDOMを確認しながら、pryのconsoleにclick_on
とか打ち込みながらどんどんつくれるので、一瞬でつくれていいです。
ソースコードのイメージは以下みたいな感じ。
#!/usr/bin/env ruby # coding : utf-8 require 'capybara' require 'capybara/dsl' require 'selenium-webdriver' require 'pry' require 'logger' class WifiSetting include Capybara::DSL def initialize() Capybara.run_server = false Capybara.default_driver = Capybara.javascript_driver = :selenium_chrome # or :rack_test, :selenium, :selenium_chrome, :selenium_chrome_headless, :mobile, :webkit Capybara.app_host = "https://ap.ip" @log = Logger.new(STDOUT) @log.info "initialize" end def login visit("/") fill_in('username', with: ENV['ID']) fill_in('password', with: ENV['PASS']) click_button('Login') @log.info "login" end def change_channel(number) select "Channel #{number}", from: 'channel' click_button('Update') @log.info "Update Channel #{number}" end def change_state(enable: nil) click_on "Wireless9" if enable choose "Enabled", name: "wireless" else choose "Disabled", name: "wireless" end click_button('Update') @log.info "Update Enable #{enable}" end end if __FILE__ == $0 o = WifiSetting.new() o.login loop do o.change_state enable: true o.change_channel o.random_channel o.change_state enable: false end end
これの派生で、サクッとAkerunのユーザー向け管理画面からユーザー追加や鍵発行をして、ハード側にその情報がちゃんと伝搬するかなどのテストとかも行っております。
[akerun-api]Rubyを使ってバイナリ書き換えーる君
これは、ツールというか、Akerun API本体についに組み込みました。
もともと、工場で動くツール(shellでperlでワンライナー)でバイナリ置換を行っていたのですが、Rails(Ruby)側で置換して単にダウンロードすればいいんじゃね?IoTっぽいしということで、クラウド側に置換処理を持っていきました。置換のくだりは、冒頭に紹介したリンク先の記事をご覧ください。
置換部分のソースコードはこんだけ。BINARY形式で読み込むと普通にsubで置換できる。知らなかった。。。shellでperlでワンライナーしていたのはなんだったのか。
# バイナリの文字列置換 def binary_str_replace(path, org, new) binary = File.read(path).force_encoding('BINARY') binary.sub!(org, new) File.binwrite path, binary end # バイナリの数字置換 def binary_num_replace(path, org, new) binary = File.read(path).force_encoding('BINARY') binary.sub!([org].pack("H*"), [new].pack("H*")) File.binwrite path, binary end
まとめ
僕がこの1年でつくったツールの一部を紹介してみました。 気分が乗ったら深掘りや別のツールの紹介をしたいと思います。
明日は非エンジニアも含め多くの従業員が使いこなしているSlack絵文字を量産する技術について書きたいと思います! お楽しみに!
株式会社フォトシンスでは、急成長するIoT製品「Akerun」を進化させるエンジニアを募集しております!
BLE通信とコンカレントに利用可能な簡易ファイルシステム
Nordic nRF5ライブラリにFDS(Flash Data Storage)という簡易なファイルシステムライブラリがあるのでそれの紹介です。
サンプルコード
https://github.com/ksksue/fds-example
githubのコードでは以下の操作を行うサンプルを用意しています。
- 3ファイル書込
- 3ファイル検索
- 3ファイル削除
- 3ファイル検索(NOT FOUND期待)
実行結果
write file_id:0001 rec_key:0001, data:00001234 write file_id:0002 rec_key:0002, data:00005678 write file_id:0003 rec_key:0003, data:00009ABC found file_id:0001 rec_key:0001, data:00001234 length:1 found file_id:0002 rec_key:0002, data:00005678 length:1 found file_id:0003 rec_key:0003, data:00009ABC length:1 delete file_id:0001 rec_key:0001 delete file_id:0002 rec_key:0002 delete file_id:0003 rec_key:0003 ERROR drv_fds_find(171) : ErrNo. 10 // 削除した後、検索してもNotFoundエラーとなる ERROR drv_fds_find(171) : ErrNo. 10 ERROR drv_fds_find(171) : ErrNo. 10
FDSを使う背景
IoT製品においてThings側(マイコン)にデータを保存したくなるケースがあります。 ログ情報、ID情報をマイコン側でキャッシュすることでユーザー体感レイテンシを下げ、UXを上げるといったテクニックがありますが、 それを実現するためにはマイコンでしっかりとデータ保存する仕組みを構築しなければなりません。
そこで立ちはだかる問題としては、まずマイコンにはサーバーやPCのような使いやすいストレージがあるわけではないので256kBほどの容量の少ないFlashを利用することになります。 またBLE通信をベースとすると、BLE通信もFlashアクセスもCPU占有時間が長いため排他処理方法を考慮しなければなりません。 そして、やっかいなのはFlashの書込/イレース10,000回上限がある点です。仮に1日10回ほど同じ箇所に上書き更新するだけで1.5年ほどで寿命がきてしまうという点です。
FDSは、以下の3つの特長をそろえつつ、小さい容量で実装できる優れたファイルシステムライブラリです。
ほどよい検索:データに2種類の数値キー情報を付与することができます。そのキーで検索することができます。組込みにおいてはこれくらいのカンタンな検索性で十分です。
BLE通信とコンカレントに利用可能:nRF5シリーズはBLE通信ができるのが最大の特長ですが、そのBLE通信とFDSはコンカレントに利用可能です。 Flashの書込/イレースは時間のかかる処理のため同時に処理するとFlashアクセス/BLE通信のいずれかを止めることになりますが、 FDSではタスクをペリフェラルに委譲するといった処理を内部でしているため開発者はそこを意識する必要はありません。
ウェアレベリング:書込/イレース回数にかぎりのあるFlashにおいては重要な仕様です。 FDSでは削除しても実際にイレースするわけではなくフラグを立て、ガベージコレクトするときに実際にイレースするといった方法を取っています。
また、ハード的な制約としては2つありますので留意しておきましょう。
FDSの仕様
FDSの関数にはいわゆるCRUD(Create/Read/Update/Delete)が用意されていて FDSではwrite/find/update/deleteという名前が付いています。 以下、writeとfind, deleteについて見ていきます。
write
writeでは、データ情報の他に、後で検索するためのfile_idとrec_keyの数値を付与します。
writeのコード例
uint16_t file_id = 1; uint16_t rec_key = 1; uint32_t data = 0x1234; FDS_DPRINTF("write file_id:%04x rec_key:%04x, data:%08x\n", file_id, rec_key, data); ret = drv_fds_write(file_id, rec_key, &data, 1); CHECK_ERR(ret);
実行結果
write file_id:0001 rec_key:0001, data:00001234
※ drv_fds_writeはこちらで用意したFDSのラッパーですが、FDSを使うには十分な引数を備えています。
file_idとrec_keyは、範囲が - file_id 0x0000 - 0xBFFF - rec_key 0x0001 - 0xBFFF という以外は制約はありません。重複もOKです。 これら情報は検索のキーとして使われます
dataはワードアライメントのデータ、その長さをワード長で指定します。
find
findのコード例
file_id = 1; rec_key = 1; ret = drv_fds_find(file_id, rec_key, &data, &length); CHECK_ERR(ret); FDS_DPRINTF("found file_id:%04x rec_key:%04x, data:%08x length:%d\n", file_id, rec_key, data, length);
先程のwriteを実行した後、このコードを実行すると以下の結果になります。
実行結果
found file_id:0001 rec_key:0001, data:00001234 length:1
もし該当するfile_id&rec_keyのファイルが見つからなかった場合は、ret に FDS_ERR_NOT_FOUND ( = 10 )が返ってきます。
delete
file_idとrec_keyを指定して削除します。
file_id = 1; rec_key = 1; FDS_DPRINTF("delete file_id:%04x rec_key:%04x\n", file_id, rec_key); ret = drv_fds_delete(file_id, rec_key); CHECK_ERR(ret);
実行結果
delete file_id:0001 rec_key:0001
ガベージコレクト
ガベージコレクトを走らせることでFlashから物理的にデータをイレースすることができます。
FDSでは、deleteをしたときにファイルの削除フラグを立てることで論理的に削除しますが実際にFlashをイレースするわけではありません。 つまり、何度もwrite/deleteし続けるとFlash容量がオーバーすることになります。 そうならないよう、定期的にガベージコレクトを実行する必要があります。
サンプルコード内では最後に実行しています。
ret = drv_fds_gc(); CHECK_ERR(ret);
利用する際の注意点
自身のプロジェクトでFDSを使うには以下の点に気をつける必要があります。
softdeviceのsys_evt_dispatchでfs_sys_event_handlerを呼ぶ
fds内で使われているfstorageでsoftdeviceからのコールバックを受け取る処理があります。 fstorageではsoftdeviceに対してflashのwrite/eraseを要求し、その結果がコールバックとして返ってきます。 そのコールバックはsys_evt_dispatchに飛んでくるためfs_sys_event_handlerでそれをキャッチするという仕組みです。
コード上ではsoftdeviceに対してsys_evt_dispatchを登録します。
err_code = softdevice_sys_evt_handler_set((sys_evt_handler_t)sys_evt_dispatch); APP_ERROR_CHECK(err_code);
そのsys_evt_dispatchコールバック内でfs_sys_event_handlerを呼びます。
void sys_evt_dispatch(uint32_t sys_evt) { fs_sys_event_handler(sys_evt); }
SDK 12以上のFDSを使う
本記事では都合でSDK11のFDSを使っていますが、SDK12にてfds_gcにクリティカルな修正が入っています。 SDK12以上を使うか、SDK11にSDK12のfdsをポーティングすることをオススメします。
まとめ
FDSを使うことでnRF5 BLEマイコン上で簡易なファイルシステムを構築することができます。 FDSはNordicのnRF5シリーズ限定のライブラリではありますがIoT開発において非常に強力なツールになるでしょう。
告知
フォトシンスでは採用を強化しています。 IoT製品に興味のある方はぜひご検討ください。 www.wantedly.com
フォトシンスで社会人インターンが終わりました
前回に引き続いて
株式会社フォトシンスで社会人インターン?をしている(id:kazupyong)です。
本日でフォトシンスでの社会人インターンが終わります。
前回はインターンの経緯を書いたのですが、
今回は実際にインターンで何をやっているのかというのを書こうと思います。
Akerunのアーキテクチャー説明
まずはCTOの(id:kazuph1986)から
Akerunの歴史とAkerun Pro、Remote、ドアセンサー、NFCリーダーなどなど
Akerun本体のハードウェアの仕組みとWebやアプリで使ってるアーキテクチャーの説明と、
実際に鍵が空くまでのフローをレクチャーしてもらいました。
Redashのセットアップ
公式だとAMIから起動するのが主流のようですが、
AMIのベースとなっっているのがUbuntuでDebian系なので、
管理コスト的に社内のサーバーをAmazon Linux系に統一している関係で
Amazon LinuxにRedashを各種設定して設置することになりました。
設定方法などは公式の以下を参考にしつつ
ただメンテナンスされていないようなので、
試行錯誤しながらAmazon Linuxでの設定方法をフォトシンス社内Githubにドキュメント化しました。
Redashでデータ吸い出しSQL作成
DB構造をRailsのモデルやER図とか確認しながら1から把握して
社内のプロダクトの人と相談しながらいろいろな抽出したいデータをSQLゴリゴリ書きました。
今後エンジニアやデータアナリストがSQLをForkしてカスタマイズしやすいように
複数のサンプルSQLを書いておいておきました。
RedashとスプレッドシートとSlackの連携
Redashで作ったSQLデータをAPI経由でスプレッドシートのIMPORTDATA関数で読み取り、
それを定期的にSlackのチャンネルにPOSTするサンプル作りました。
今後KPIとかデータ抽出簡易化と情報共有をカスタマイズしやすいように書いておきました。
カスタマーサクセスチーム用の管理お問い合わせツール作成
カスタマーサクセスチームの電話サポートで
組織名検索→組織に設置されているAkerunの一覧+ログ閲覧など
Redashフォームを活用して最新のDBの値とかエラーログをダッシュボードで一覧表示できるようにしてみました。
まだ運用には乗ってないのですが、今後カスタマイズしつつ使いやすい形で作っておきました。
リリース前のQAお手伝い
今までWeb系やアプリしか経験なかったのですが、
IoT企業ということもあり実際のデバイスを含めた動作検証は新鮮でした。
Webの管理画面を操作して、アプリから実機を設定して、
実際のデバイスをアプリやNFCカードやボタンなどで操作して
実機動作とサーバーのログを確認するという机いっぱい使いながらの検証でした。
2週間のインターンを経て
IoTの企業での社会人インターンで、
ハードウェア系を初めてのことばかりの電光石火のような日々でした。
社内の色々な人とコミュニケーションを取ったり、
カフェスペースでランチを一緒に食べたり、自宅で使ってるラズベリーパイの雑談したりと
色々と無理を言った中で暖かく受け入れていただきましてフォトシンスの皆様には感謝です!
おまけ
フォトシンスにはPN制度という超素敵な制度があります。
正社員はもちろん、業務委託でもアルバイトでも社会人インターンでも
フォトシンスメンバー同士での飲み会や食事会の費用を1人あたり1回2000円
月5回まで会社が負担してくれる制度があります。
今週の水曜にその制度を使ってうしごろに焼肉食べに行ってきました!
通常5000円のコースがPN制度で一人3000円で美味しいお肉食べられました。
フォトシンスでは採用を強化しています!
興味ある人は以下のリンクからどうぞー