OmniAICreator/Qiita-1.07M huggingface dataset:
新卒1年目ITエンジニアのモチベーションはどこに

業務つかれたので息抜きにダラダラどうでもいいこと書こうと思います。

みなさんは仕事に対するやりがいみたいなものってどこに置いているでしょうか？

営業職であれば営業成績のようにわかりやすい数字に置くかもしれないですし、お客様の反応をじかに感じられるのが良かったりするかもしれませんね。

では数字があまり見えない職種、業務経験が1年目のエンジニアの人などはどうでしょうか？
上司の給料が高い場合はそれがモチベーションになるかもしれません。あと何年したらこれくらいもらえるかもなーみたいな。最近ではエンジニアは転職するのが当たり前みたいな雰囲気もでてきましたが。
積極的にほめてくれる上司がいたらその人の評価も一つのモチベーションになるかもしれません。自分が尊敬しているあの人に認めてもらえたらうれしいみたいな。
社内で表彰してもらえるみたいな制度があったらそれもいいかもしれません。うれしいっちゃうれしいですよね。

自分は今は、新しい技術を使ったときの面白さが一番のモチベーションになっています。こんな便利な機能あんのかーとか、こんな実装の仕方あんのかーとかいうのが楽しいですね。

でも、やっぱり仕事にしている以上まずはお金をモチベーションにするべきだと思うんですよね。稼ぎたいとかモテたいみたいなより本能にちかい欲求に従った方がパワー出ると思います。
ですから、しっかり技術力がついたらもっとお金も大事にしようと思っています。

という、お金にあまり興味なさげな知り合いのエンジニアを見て思ったことでした。
python  - Matplotlibによる2次元ヒストグラムを配列で書き出し

2次元のヒストグラムをpythonで描画する方法は、MatplotlibやOpenCVを用いたものが有名です。

本記事ではMatplotlibで2次元ヒストグラムを作成した際に、ヒストグラムを2次元配列としてテキストファイルに書き出す方法をメモしておきます。

[Python] Matplotlibで2次元のヒストグラム
--
Matplotlibを用いて2次元のヒストグラム画像を作成する方法については@supersaiakujinさんの記事がおすすめです。

[[Python]Matplotlibで2次元ヒストグラムを作成する方法](https://qiita.com/supersaiakujin/items/ca47200393180a693bdf)

概要は、「Matplotlib」ライブラリの 「hist2d」関数 に元データとなる2つのnumpy配列を渡すことで2次元ヒストグラムを作成しています。

元データとなる２つのnumpy配列はそれぞれ、「ｘ座標のみの配列」、「ｙ座標のみの配列」を作成する必要があることは注意が必要です。

![Figure_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/229955/b3375eb2-7220-3f47-47e1-27cd4f1c361b.png)
このような2次元ヒストグラムは以下のサンプルプログラムで書けます。

```Python:hist2d
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm

#元データ
x = (1.1,1.1,3.2,3.1,1.1,2.1,1.1)
y = (-1.1,-2.1,-2.1,-2.1,-3.1,-2.1,-2.1)

#グラフ描画のための前準備
fig = plt.figure()
ax = fig.add_subplot(111)

#ヒストグラム作成 hist2d(配列,配列, 描画範囲指定(下限、上限、分割数), カラーバーの色合い指定)
H = ax.hist2d(x,y, bins=[np.linspace(-5,5,10),np.linspace(-5,5,10)], cmap=cm.jet)
ax.set_title('1st graph')
ax.set_xlabel('x')
ax.set_ylabel('y')
fig.colorbar(H[3],ax=ax)
plt.show()
```


2次元ヒストグラムの値を配列で書き出す
--
2次元ヒストグラムの値を配列としてテキストファイルやcsvに書き出すにはnumpyのsave関数が便利です。

```Python:savetxt
#ヒストグラム作成 hist2d(配列,配列, 描画範囲指定(下限、上限、分割数), カラーバーの色合い指定)
H = ax.hist2d(x,y, bins=[np.linspace(-5,5,10),np.linspace(-5,5,10)], cmap=cm.jet)
np.savetxt("2Dhist-array.txt", H[0], fmt = "%s")
```

hist2dの返り値をMatplotlibの公式マニュアルで確認すると1つ目の返り値がヒストグラムの値であることが分かります。
そのためH[0]を取り出します。

fmt = "%s"は文字列でフォーマット指定しています。

|hist2d|                      |
|-----------|-----------------|
|H[0]  |ヒストグラムの値(2次元配列)|
|H[1]  |x軸の分割値（1次元配列）|
|H[2]  |y軸の分割値（1次元配列）|
|H[3]|カラーバー情報(QuadMesh)|
[Matplotlib.pyplot.hist2d](https://matplotlib.org/3.3.1/api/_as_gen/matplotlib.pyplot.hist2d.html)

```Python
#ヒストグラム作成 hist2d(配列,配列, 描画範囲指定(下限、上限、分割数), カラーバーの色合い指定)
H = ax.hist2d(x,y, bins=[np.linspace(-5,5,10),np.linspace(-5,5,10)], cmap=cm.jet)
print(H[0])
>>>[[0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 1. 2. 1. 0. 0. 0. 0. 0.]
 [0. 0. 1. 0. 0. 0. 0. 0. 0.]
 [0. 0. 2. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0.]]
```

出力される配列を見る限り、y軸に沿って1次元配列が生成されています。

今回のコードをまとめました。

```Python
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm

#元データ
x = (1.1,1.1,3.2,3.1,1.1,2.1,1.1)
y = (-1.1,-2.1,-2.1,-2.1,-3.1,-2.1,-2.1)

#グラフ描画のための前準備
fig = plt.figure()
ax = fig.add_subplot(111)

#ヒストグラム作成 hist2d(配列,配列, 描画範囲指定, カラーバーの色合い指定)
H = ax.hist2d(x,y, bins=[np.linspace(-5,5,10),np.linspace(-5,5,10)], cmap=cm.jet)
ax.set_title('1st graph')
ax.set_xlabel('x')
ax.set_ylabel('y')
fig.colorbar(H[3],ax=ax)
plt.show()

#2Dヒストグラムのテキストファイルへの書き出し
np.savetxt("2Dhist-array.txt", H[0], fmt = "%s")
```
AWS IoT Greengrass CoreをRaspberry Piにサイレントインストールする手順

こちらにある `gg-device-setup-latest.sh` スクリプトを利用します。サイレントインストールの元情報もここを参考にしています。

https://docs.aws.amazon.com/greengrass/latest/developerguide/quick-start.html

以下の項目を組み合わせてサイレントインストールします。


# 事前準備
SSHを有効化して、作業フォルダを作成します。

```sh
mkdir ~/work
cd ~/work
```
作業PC側で、[以下のワンライナーでAWS認証情報を取得](https://qiita.com/yomon8/items/937855d2aa2f14ff47db)します。

```sh 
aws sts get-session-token --query 'Credentials | {A:AccessKeyId,B:SecretAccessKey,C:SessionToken}' --output yaml | awk  'NR==1{print "export AWS_ACCESS_KEY_ID="$2} NR==2{print "export AWS_SECRET_ACCESS_KEY="$2} NR==3{print "export AWS_SESSION_TOKEN="$2}'
```

# 変数の設定

```sh
# ワンライナーで取得したAWS認証情報
export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
export AWS_SESSION_TOKEN=AQoDYXdzEJr1K...o5OytwEXAMPLE=

# 以下は適当に調整します
MAC_ADDR=$(ethtool -P eth0 | cut -d" " -f3 | tr -d :)
export GG_GROUP_NAME=otomo-pi-grp-${MAC_ADDR}
export GG_CORE_NAME=otomo-pi-core--${MAC_ADDR}
export AWS_REGION=ap-northeast-1
```

# OS設定とConfigの生成

```sh
wget -q -O ./gg-device-setup-latest.sh https://d1onfpft10uf5o.cloudfront.net/greengrass-device-setup/downloads/gg-device-setup-latest.sh && chmod +x ./gg-device-setup-latest.sh && sudo -E ./gg-device-setup-latest.sh bootstrap-greengrass \
--region ${AWS_REGION} \
--group-name ${GG_GROUP_NAME} \
--core-name ${GG_CORE_NAME} \
--ggc-root-path / --log-path ./ --deployment-timeout 300  \
--verbose
```

スクリプトが完了した時点で、以下のように `GreengrassDeviceSetup.config.info` が出力されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116068/9587dc6e-1afb-0ae0-66de-59db8fde4597.png)


cgroup等のOS設定を反映させるため再起動します。

```sh
sudo reboot
```

# Greengrass Coreのインストール

Raspbean再度SSHにてログオンします。

```sh
# AWS認証情報の設定
export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
export AWS_SESSION_TOKEN=AQoDYXdzEJr1K...o5OytwEXAMPLE=

# ワークディレクトリに移動
cd ~/work

# プロンプトの答えのyesを流し込みスクリプトを実行
echo "yes" | sudo -E ./gg-device-setup-latest.sh bootstrap-greengrass
```

以下のように表示されればインストール完了です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116068/f7104ffe-81d8-69eb-be30-5e13e9eddde3.png)

# [Option] Python3.7のgreengrasssdkインストール
greengrasssdkは初期では導入されてないので、必要に応じてインストールします。

```sh
curl -kL https://bootstrap.pypa.io/get-pip.py | sudo python3.7
sudo pip3.7 install greengrasssdk
```

# [Option] systemdの設定

以下のコマンドでGreengrass Coreをサービス化します。

```sh
# systemdの設定を投入
cat <<EOF | sudo tee /etc/systemd/system/greengrassd.service
[Unit]
Description=Greengrass Daemon

[Service]
Type=forking
PIDFile=/var/run/greengrassd.pid
Restart=on-failure
ExecStart=/greengrass/ggc/core/greengrassd start
ExecReload=/greengrass/ggc/core/greengrassd restart
ExecStop=/greengrass/ggc/core/greengrassd stop

[Install]
WantedBy=multi-user.target
EOF
```


```sh
# サービスを有効化
sudo systemctl daemon-reload
sudo systemctl enable greengrassd.service
sudo systemctl start greengrassd.service
```


# Ansible化

[こちらの自分のブログ記事](https://yomon.hatenablog.com/entry/2020/09/greengrass_ansible)でAnsible化もしてみました。
SQL Server: クエリストアが使えない環境下で物理読み取りサイズが大きなクエリを探す方法

クエリストアが使えれば済む話ですが、バージョン等の関係でクエリストアが使えないこともあると思います。
そのような環境下において、物理読み取りサイズが大きなクエリを探す方法を考えてみました。

[こちら](https://techblog.zozo.com/entry/sqlserver-query-timeout)のブログで、dm_exec_requestsのダンプを定期的にとることで、物理読み取りサイズが大きなクエリを探す方法について紹介しました。

ただし、こちらのクエリだと、「並列実行で物理読み取りし続けて、読み取りがおわった瞬間に処理が終わるようなクエリ」については検知しにくいです。

↓ブログで紹介されている方法を使って5秒間隔でsys.dm_exec_requestsをダンプした結果です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148310/a240efcf-2a71-dd03-a19f-5b88a2bd9cdd.png)
CXPACKET待ちとなっている状態ではreadsがカウントアップされないため、①から④のクエリはすべてreads=0のままとなっています。

ただし、同様のクエリをsys.dm_exec_query_statsから取得すると、かなり大量の物理読み取りを行っていたことが分かりました。

```sql
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
SELECT TOP 100
  DB_NAME(qt.dbid) as database_name
  --query text
  ,qt.TEXT as parent_query
  ,SUBSTRING(qt.TEXT, qs.statement_start_offset / 2, (
      CASE 
        WHEN qs.statement_end_offset = - 1
          THEN LEN(CONVERT(NVARCHAR(MAX), qt.TEXT)) * 2
        ELSE qs.statement_end_offset
      END - qs.statement_start_offset
   ) / 2) as statement
  -- total
  ,total_worker_time / 1000 as total_CPU_time_ms
  ,total_elapsed_time / 1000 as total_duration_ms
  ,total_physical_reads as total_physical_reads
  ,total_physical_reads * 8 / 1024 / 1024 as total_physical_reads_gb
  ,execution_count
  ,last_execution_time
FROM sys.dm_exec_query_stats qs
OUTER APPLY sys.dm_exec_sql_text(qs.sql_handle) AS qt -- クエリテキスト用
ORDER BY total_physical_reads / execution_count desc
```

↓数十GBの物理読み取りが発生しているものも。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148310/e081d671-6e97-e05d-1a22-c956a6dc22b3.png)

では、dm_exec_query_statsだけ取得すればいいのでは、という話になりますが、
・キャッシュアウトされるとNG（環境によっては数10秒でキャッシュアウトする可能性も）
・完了したものしか格納されない
という条件があります。

したがって、
dm_exec_requestsとdm_exec_query_statsのダンプを組み合わせて総合的に判断するのがよさそうです。
Raspberry Pi 4をOpenJTalkで日本語発話デバイスにする ～とりあえず時報を実装～

# モチベーション
GoogleHome等のスマートスピーカでは、「今何時？」「今日の天気は？」と聞かないと時間や天気を教えてくれない。自宅環境にて、定期的に聞きたいことは定期的に自動で発話してくれると便利なのでRaspberryPi4を日本語発話デバイスにする。

# 実装方法（ハード）
RaspberryPi4本体の3.5inchイヤホンジャックをスピーカに接続しておく。

# 実装方法（ソフト）
[Open JTalk](http://open-jtalk.sourceforge.net/)というLinuxで動作する日本語の音声合成ソフトを使う。（※[ここ](http://open-jtalk.sp.nitech.ac.jp/)でデモができる。）RaspberryPi4へのインストールは以下のコマンドを使う。

* OpenJTalk本体

```
$ sudo apt-get install open-jtalk
```

* 日本語辞書

```
$ sudo apt-get install open-jtalk-mecab-naist-jdic
```

* 標準の男性音声モデル

```
$ sudo apt-get install hts-voice-nitech-jp-atr503-m001
```

上記を完了するとターミナルから`open_jtalk`コマンドが実行でき、日本語文字列を受け取って音声合成ができるようになる。使い方は、以下の記事が参考になる。
[OpenJtalkを使って日本語をRaspberry Piに喋らせる - Qiita](https://qiita.com/coffiego/items/4fc3b0be78fcded3eef0)

このコマンドを、扱いやすくするため以下のようにシェルスクリプト化する(ファイルに実行権限を付与することを忘れずに)。
保存場所はユーザホームディレクトリ(~)直下に作成したscriptsディレクトリとした(`~/scripts/jtalk.sh`で実行できる)。

```shell:jtalk.sh
#!/bin/bash
if [ -p /dev/stdin ]; then
    cat -
else
    echo $1
fi | open_jtalk -x /var/lib/mecab/dic/open-jtalk/naist-jdic -m /usr/share/hts-voice/nitech-jp-atr503-m001/nitech_jp_atr503_m001.htsvoice -ow /dev/stdout | aplay --quiet
```

`jtalk.sh`の動作は、標準入力（パイプ）で渡された文字列、またはコマンドライン引数の１つ目の文字列を発話する（スピーカから音が出る）というもの。
実行例を以下に示す。

```shell:
$ echo "あいうえお" | ~/scripts/jtalk.sh #「あいうえお」と発話
$ ~/scripts/jtalk.sh "かきくけこ" #「かきくけこ」と発話
```

これで、使いやすい日本語発話モジュールが完成した。

# 応用例
発話モジュールを使った応用例として、時報を実装する。
読み上げたい時刻文字列を作って、パイプなりコマンドライン引数なりで先程の`jtalk.sh`に渡してやればよい。時刻文字列の作成には、dateコマンドを使う。dateコマンドは、実行時に時刻フォーマットを表す文字列を渡してやることで、カスタムされた文字列出力を得ることができる。
dateコマンドの詳細な使い方は、以下のWebページが参考になる。
[date コマンド | コマンドの使い方(Linux) | hydroculのメモ](https://hydrocul.github.io/wiki/commands/date.html)

```shell:
$ date
2020年  9月  9日 水曜日 18:12:45 JST #標準の出力

$ date "+現在の時刻は、%-H時%-M分です"
現在の時刻は、18時12分です #渡された内容でフォーマットされた出力
```

上記を踏まえ、実行したときの時刻を発話するシェルスクリプトを次のように実装した（dateコマンドの結果文字列をパイプで渡してるだけ）。
保存場所はjtalk.sh同様ユーザホームディレクトリ(~)直下に作成したscriptsディレクトリとした。

```shell:time_signal.sh
#!/bin/bash
date "+現在の時刻は、%-H時%-M分です" | ~/scripts/jtalk.sh
```

この`time_signal.sh`をcrontabで定期実行するように登録すれば、時報の完成。
以下の例では、毎時00分と30分に「現在の時刻は、●●時▲▲分です」と発話される。

```shell:
$ crontab -e
#以下の記載を追加して保存
*/30 * * * * ~/scripts/time_signal.sh > /dev/null 2>&1
```

ちなみに`time_signal.sh`について、なぜこの短い分量をわざわざシェルスクリプトファイル化したのかというと、cronに登録するときにはパイプ（│）の表記は使えず、単体のコマンドである必要があったため。

# 所感・今後の予定
部屋で過ごしてるときに、定期的に時間が読み上げられることで、なんとなくダラダラ過ごしてしまうのを防止できている感じがする。
他にも、Webスクレイピングで株価やニュースの文字列をとってきて、それを指定した時刻に発話させるのもいいかも、と考えているところ。
また、名古屋工業大学発の「mei & takumi」のモデルを使って話者を女性に変えることを予定している（音声データ`.htsvoice`ファイルをダウンロードしてきて `open_jtalk`コマンドの`-m`オプションで渡せばよい）。
dockerでWebアプリ構築した(1)

自分はdockerを基盤にWebアプリの開発を行っています。
その開発基盤の構築をちょろっとやったので記事に纏めました。
初歩的なものですが、自分の備忘録？的な感じとして記事を書いていきます。


# Docker images nginx
まず、nginxのdocker imagesを取ってくる。

```
$ docker pull docker.io/nginx
Using default tag: latest
latest: Pulling from library/nginx
bf5952930446: Already exists 
cb9a6de05e5a: Pull complete 
9513ea0afb93: Pull complete 
b49ea07d2e93: Pull complete 
a5e4a503d449: Pull complete 
Digest: sha256:b0ad43f7ee5edbc0effbc14645ae7055e21bc1973aee5150745632a24a752661
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest
```

で取得！
docker imagesで確認します。

```
$ docker images
REPOSITORY                             TAG                 IMAGE ID            CREATED             SIZE
nginx                                  alpine              ecd67fe340f9        2 months ago        21.6MB

```

# docker runしていくぜ！
<font color="Red">注意: 以下の操作で一部間違ってやっているものがあります。（docker runしているコマンドにミスがあります。）</font>
images⇨container
をしていきます。

container作成には、docker runが必須です。
では、行っていきます。

```
$ docker run -d -p 8000:80 --name cesium_nginx -v /マウントするフォルダ:/home ecd67fe340f9 
a115c1e7a815a4856b2b51755e217f2cc1aa856c81489d9ae3020f4fefd0883f
```

今回は、ポートを立てるだけでなくマウントもしていきます。
使用オプション
` -p `外部からアクセスされるポート番号:コンテナ側のポート番号を指定。
` -d `バックグラウンドで実行。
` -v マウントするフォルダ:マウント先`フォルダのマウントを実行。-v local:cotainer
` --name `コンテナ名を決める。

```
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
a115c1e7a815        ecd67fe340f9        "/docker-entrypoint.…"   5 seconds ago       Up 4 seconds        0.0.0.0:8000->80/tcp   cesium_nginx
```

# localhost:8000をみにいく

http://localhost:8000/
を開くと...

![スクリーンショット 2020-09-09 18.17.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/699389/f2953d22-0ecb-321f-a0ca-3a659079d91f.png)

こんなものが...なんだこれ...

これは、nginxが正常に動いた証！！！
つまり、成功？
かと、思ったが、、自分のマウントしたフォルダには既にhtmlファイルがあります。
だから、それが表示されないと成功ではない。

# 修正

何が間違っていたか...端的に言えば、マウント先が間違っている。
自分は` /home`にマウントしました。
理由は、適当...どこでも良いやって思ってました。
### そんなあまくなかった...
docker rm や docker stopしました。（そこらへんの処理は省きます）

```
$ docker run --name cesium_nginx -v /マウントするフォルダ:/usr/share/nginx/html:ro -d -p 8081:80 ecd67fe340f9 
f85c35793c4bb7368af7798db788f707bcbd4278bbcfdf87a360f727e6433c00
```

` /usr/share/nginx/html:ro`これが必要でした。

さて、これでlocalhost:8081を開きましょう！

![スクリーンショット 2020-09-09 18.25.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/699389/b5b957a4-381d-f7ca-2b70-52b4d0f07ee2.png)
### きたーーーー
やっと目的の画面が...（この画面はCesium.jsというWebアプリケーションの画面です。ちょっとアレンジ加えていますが...）

# まとめ
### しっかり仕様書？やしっかり調べてからやっていこう

こっからスタートや！色々いじっていきます。楽しい楽しい開発の始まり~(見ての通り既に開発していましたが...)
Electron(+Vue)でnode-oracledbを利用する

# はじめに
Windows環境でのVue+Electronアプリ開発においてOracleデータベースへのアクセスに一苦労あったので実現方法を備忘録として残す。

# 環境
- Windows 10
- npm 6.14.8
- Vue CLI 4.5.4

# プロジェクト作成

### Vueプロジェクト作成
Vue CLIを利用してVueプロジェクトを作成。Vue 3.x+Typescriptを選択。その他オプションは下記の通り。

```
>vue create electron-oracle

Vue CLI v4.5.4
? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, TS, Linter
? Choose a version of Vue.js that you want to start the project with 3.x (Preview)
? Use class-style component syntax? No
? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? No
? Pick a linter / formatter config: Standard
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? (y/N)  No
```

### Electron化
VueプラグインからElectron化する。途中でバージョンを聞かれるので`^9.0.0`を選択する。

```
vue add electron-builder
```

### 動作確認
ここまでで自動生成されたコードの動作確認をする。
Electronアプリケーション上にVueのいつものページが表示されていればOK。

```
npm run electron:serve
```

# node-oracledbインストール
**ポイント**
Electronで利用する場合はElectron独自のパラメータを利用して`oracledb`ビルドする必要があるが、npm経由でインストールした場合はそれがされておらず、またリビルドの対象にもならないため使えないらしい。（[参考](https://github.com/oracle/node-oracledb/issues/918)）

そのため、ソースからインストールする必要がある。

### 事前準備

ビルドに必要なライブラリを事前にインストールしておく。

#### windows-build-tools
Windowsの場合はコンパイラセットとして`windows-build-tools`をインストールする。コンパイラインストール時に管理者権限が必要なので「管理者として実行」したコマンドプロンプトで実行すること。

```
npm install --g --production windows-build-tools
```

（Visual C++ build toolsがうまく入らなかったのでVisual Studio Build Toolsから手動インストールした。）

#### node-gyp
`node-gyp`コマンドが必要になるのでグローバルインストールしておく。

```
npm install -g node-gyp
```

インストール後はコマンドとして実行できることが確認できる。

```
>node-gyp --version
v7.1.0
```

### インストール
GitHubのソースアーカイブを指定してインストールする。URLは[リリースノート](https://github.com/oracle/node-oracledb/releases)を参照して必要なバージョンに適宜変更すること。

```
npm install --save https://github.com/oracle/node-oracledb/releases/download/v5.0.0/oracledb-src-5.0.0.tgz
```

# データベースアクセス実装
基本的な実装方法は[公式のサンプル](https://github.com/oracle/node-oracledb/tree/master/examples)を参照のこと。

**ポイント**
`node-oracledb`はNode.jsのライブラリなので、ElectronではMainプロセスで実行する必要がある。
すなわち画面操作からキックする場合はIPC通信によりRenderプロセスからMainプロセスに処理をリクエストする必要がある。
(※Renderプロセスで`ipcRenderer`を利用するのに一工夫が必要。今回はローカルで完結するアプリケーションであったため簡単に`nodeIntegration: true`とした。)

# 参考
- [Installing node-oracledb Version 5.0 | node-oracledb](https://oracle.github.io/node-oracledb/INSTALL.html)
- [Node-oracledb Examples](https://github.com/oracle/node-oracledb/tree/master/examples)
- [How to use oracledb in an electron app #918](https://github.com/oracle/node-oracledb/issues/918)
- [Windows10にてNode環境構築によくあるエラー - Qiita](https://qiita.com/chenglin/items/b3ca0bc1dca0ed682b05)
M5StickVで音声認識してみる

# はじめに

aNo研さん @nnn112358 が記事で説明して頂いているとおりM5StickVでマイクが使えるようになったので、M5StickVで音声認識してみました。[MaixCubeで音声認識を試してみる](https://qiita.com/fukuebiz/items/ba03b4a455bf2dbce311)のM5StickV版。K210に搭載されたハードウェアサポートのFFTを利用します。

# Maixduino(PlatformIO + Arduino開発環境)

Sipeed社から提供されているMaixduinoの開発環境にSpeech Recognitionのサンプルのコードとモデルが含まれています。

サンプルのコードでは、音声入力をMFCC（メル周波数ケプストラム係数）分析した後、DTW（動的時間伸縮法）で類似度を求めているようです。MFCCでK210のFFTが使われています。

場所

.platformio/packages/framework-maixduino/libraries/Maix_Speech_Recognition/examples

# サンプル

サンプルのモデルをそのまま使ってM5StickVで試したソースは[こちら](https://github.com/fukuen/m5stickv-speech-recognition-demo)

起動すると、まずモデルを読み込みます（シリアルコンソールに下記のメッセージが表示されます）

init model...
init model ok!

次に、周囲の雑音が小さくなるのを待ちます

get noise again...

音声入力待ちになります

speaking...

ここで、「レッド」あるいは「グリーン」あるいは「ブルー」と話しかけます。

うまくいけば、

res : red

と、表示されて赤のLEDが表示されます。

うまく認識されない時は、

recognize failed.

と表示されます。


# 結果

現状ではあまり認識率がいいとは言えません。マイク入力のコードは見直しが必要かもしれません。Sipeedのサンプルに自分のモデルを作るコードもあるので、自分の声でモデルを作ると少し認識率が上がりそうです。
M5StickVは、簡単に音声認識（AI）も試すことができました。
JavaScriptを学ぶ：スラスラ読めるJavaScript Chapter1

# はじめに
[@kuromailserver](https://twitter.com/kuromailserver?ref_src=twsrc%5Egoogle%7Ctwcamp%5Eserp%7Ctwgr%5Eauthor)さんの『会津わろ法則』に参加。
その中で紹介された学習書の中でとりかかりやすく読破できそうな『スラスラ読めるJavaScript』をチョイス。
この投稿は本書を読んで自分なりのノートとしてまとめていく。

注：本書では表なども使って丁寧に解説されているがそれを持ってこれないので他サイトのリンクを載せている。

# 文字の表示

プログラミングの第一歩、画面に文字を表示。
コンソールへの表示は**console.logメソッド**（メソッド＝コンピューターに対する命令文)を使用。
`console.log('表示させたい文字');`で表示させたい文字がコンソールに表示される。

```JavaScript:コンソール
console.log('Hello');
```

>Hello

コンソールにHelloの文字が表示された。

# 演算子を使って計算

人が四則計算をする場合「+」「-」「×」「÷」の記号を使うが、JavaScriptでする場合は「+」「-」「*」「/」の**演算子**を使う。

左辺を50、右辺を2として順に計算してみる。

```JavaScript:コンソール
console.log(50 + 2);
console.log(50 - 2);
console.log(50 * 2);
console.log(50 / 2);
```

>52
>
>48
>
>100
>
>25

ちゃんと足し算、引き算、掛け算、割り算の計算結果が出た。
この他に「%」(割った余りを表示)「**」(べき乗計算を表示)がある。
上記と同じ値で計算してみる。

```JavaScript:コンソール
console.log(50 % 2);
console.log(50 ** 2);
```
>0
>
>2500


参考:[式と演算子](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators)

演算子には優先順位がある。
参考:[演算子の優先順位](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Operator_Precedence)

表を見ると「+」「-」より「*」「/」「%」のほうが高く、最優先されるのが()を使ったグループ化。
試しにやってみるとこんな感じ。

```JavaScript:コンソール
console.log(10 + 4 * 2);
console.log(5 - 8 / 4);
console.log(5 * (4 - 3));
```

>18
>
>3
>
>5

（「式と演算子」や「演算子の優先順位」みたいなのはPDF化してすぐに参照できるようにしておくのが良さげ。）
※負の数をあらわすとき
「-」の演算子は左側が数値以外だと「負の値」となる。
※整数と実数
JavaScriptでは整数と実数は同じNumber型のデータ。
整数と実数同士で計算もできるが、整数同士の計算のほうが圧倒的に早いとのこと。
先に進んだ時に実際に動作させて確認したい。

# 変数を使って計算
値や文字列などをいれておける箱みたいなもの＝「変数」。
`let 変数名 = 値や文字列;`で新しく変数が作れる。
さっそくやってみましょう。

```JavaScript:コンソール
let num = 777;
let word = 'リンゴ'
```

これだけだとキチンとできたかどうかわからないので、`console.log()`で表示してみましょう。

```JavaScript:コンソール
let num = 777;
let word = 'リンゴ'
console.log(num);
console.log(word);
```

>777
>
>リンゴ

キチンとできていた。
`let`は新しく作る際には必要だが、1度作った変数の中身を変えるときはいらない。

変数の命名にはルールがある。
1:半角のアルファベット、アンダースコア、数字を組み合わせて作る
2:数字のみ、先頭が数字の命名はNG
3:予約語と同じ名前はNG(予約語と予約語、予約語と他の文字を合わせた場合はOK)
参考:[予約語](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Reserved_Words)

また、JavaScriptは変数、演算子、メソッドなどを識別する。
なので、変数名に演算子が入っていると変数として機能しない。

`promptメソッド`を使うとユーザーから入力したものを扱える。
`変数 = prompt('ダイアログボックスに表示したい文字列');`
実際にやってみると

```JavaScript:コンソール
let answer = prompt('文字を入力');
console.log(answer);
```

ダイアログボックスが表示されるので「カエル」と入力。

![prompt.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/705885/b3ff7cf2-3aa1-cebb-15d4-1db6a60df465.jpeg)

すると

>カエル

と入力したものが表示された。

次は2つの数値を入力してその合計が表示されるプログラムを実行する。
1つ目を「5」、2つ目を「3」と入力。
結果は「8」が表示されるはずだが……

```JavaScript:コンソール
let num = prompt('1つ目の数値');
let num2 = prompt('2つ目の数値');
console.log(num + num2);
```

>53

表示されたのは「53」。
「+」の演算子は左右どちらかが文字列だと「連結」という命令に変化。
`promptメソッド`で入力すると入力したものはすべて文字列として扱われる。
なので「53」という結果が表示された。

「連結」ではなく「足し算」として実行するには文字列を数値に変換する処理が必要。
それには`parseInt`という関数がある（関数については後のChapterで説明がある）。
`parseInt`は指定したものを「整数」として返す。
先ほど入力したプログラムに`parseInt`をいれれて、1つ目を「5」、2つ目を「3」と入力すると……

```JavaScript:コンソール
let num = prompt('1つ目の数値');
let num2 = prompt('2つ目の数値');
console.log(parseInt(num) + parseInt(num2));
```

>8

「5」と「3」を合計した「8」が表示された。
group byは、内部的にはソートをするため、メモリとＣＰＵの負荷がかかる

▼メモ
ORDER BYやGROUP BYは、どちらもデータのソートが必要な処理です。
ソートをする際には、データをメモリ上に展開し大小比較をして並べ替えるのですが、この処理にはメモリもCPUもたくさん必要になります。
そして、処理に必要なメモリとCPUの量は、データが多くなればなるほど指数関数的に大きくなっていきます。

https://employment.en-japan.com/engineerhub/entry/2017/06/26/110000
Zabbix5でアラートをメールで発報する方法

![CentOS-8.2.2004](https://img.shields.io/badge/CentOS-8.2.2004-brightgreen) ![Zabbix-5.0.3](https://img.shields.io/badge/Zabbix-5.0.3-brightgreen)

Zabbixで様々なアラートをトリガーしていると、いちいちZabbix管理画面でチェックするのが面倒です。ある程度の深刻度のアラートが起きたらメールを送信するようにします。

## SMTPサーバ設定

Zabbix Webコンソールにログインし、左メニューの`管理`→`メディアタイプ`→`Email`を選択します。

<img width="512" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97774/eb7b174c-d0bf-7316-c17f-e43cb4a9496f.png"/>

使用しているSMTPサーバに合った設定を行い更新します。

<img width="512" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97774/f8847427-1922-c803-edea-7be2f29040d9.png"/>

設定を保存したあと、メディアタイプ画面の`Email`行にある`テスト`をクリックします。

<img width="512" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97774/ceaecc93-1dc5-7c5f-6431-caae9d22a6f8.png"/>

`送信先`に受信可能なメールアドレスを入力し、テストを実行します。

<img width="512" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97774/03a18356-0248-01d8-8ab4-7e6de2596d24.png"/>

問題なく送信先で受信が出来たらSMTPサーバの設定は完了です。


## アクション有効化

左メニューの`設定`→`アクション`→`Report problems to Zabbix administrators`を選択します。

<img width="512" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97774/0f7dc199-3cba-301c-c60c-f6f1e3b121f3.png"/>

アクションを有効にします。

<img width="512" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97774/46105363-623b-e880-974f-fa264ef7554c.png"/>

## アラート送信先アドレス設定

左メニューの`管理`→`ユーザー`→`Admin`を選択します。

<img width="512" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97774/7166adc5-fcd5-7a77-ee65-0ae13ee8bb90.png"/>

`メディア`設定にて、メディアタイプが`Email`の設定を追加して更新します。

<img width="512" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97774/960ec6d1-1cd4-7011-946e-5781305dbe16.png"/>

設定内容の例です。

<img width="512" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97774/8797fb4b-277c-6a6c-919d-bd6344632282.png"/>


以上で設定したアラートの深刻度に応じてメールが飛ぶようになりました。
語尾を”だなも”にするChrome拡張機能を作ってみたんだなも


ですます調の文章を読むのに飽きたなあ、、
ということで、webページ上にある文の語尾を全て「だなも」にするChrome拡張機能を作ってみました。
![スクリーンショット 2020-09-09 1.47.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581663/68ac2505-fca8-0c09-bdc9-aca6106dff13.png)


##ファイルの準備
今回作成するファイルはたったの３つだけです。
1.manifest.json(設定ファイル)
2.JQueryファイル
3.JavaScriptのファイル（メイン処理を記述）

まずは適当な場所に上記３つのファイルを格納するフォルダを作成します。

**①マニフェストファイルの作成**
先ほど作ったフォルダの中に「**manifest.json**」という名前のファイルを作成します。

```json:manifest.json
{
  "name": "Danamo",
  "version": "0.0.1",
  "manifest_version": 2,
  "description": "語尾をだなも変換します",
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["jquery-3.5.1.min.js", "danamo.js"],
      "run_at": "document_idle"
    }
  ]
}
```

ー解説ー
　"name"： 拡張機能の名前
　"version"： 拡張機能のバージョン
　"manifest_version"： マニフェストファイルのバージョン
　"description"： 拡張機能の説明文
　"matches"： どのURLパターンで動作するか（今回はALLとしています）
　"js"： 読み込ませるJSファイルの指定
　"run_at"： ファイルを読み込むタイミング（今回はDOM構築完了後の実行）


**②JQueryのダウンロード**
[Jquery](https://jquery.com/download/)
上記のリンク先からJqueryのファイルをダウンロードし、フォルダの下に配置します。
ダウンロードする際は、ダウンロードリンクを[右クリック]→[リンク先を別名で保存]から任意の場所に保存します。
![スクリーンショット 2020-09-09 0.31.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581663/de9272a9-6864-53a2-f720-53eb8676b02d.png)

**③JavaScriptファイルの作成**

「**danamo.js**」という名前のファイルを作成します。

```javascript:danamo.js
$("body").contents().each(function(){
    if (this.nodeType == 1){ 
		$(this).html($(this).html().replace(/。/g, "だなも。"))
	}
})
```

ー解説ー
全ての子要素における要素ノード（ノードタイプが１）に対して、「。」を「だなも」に置換します。
※ノードタイプでの条件分岐をしないとエラーになる

以上でファイルの準備は完了です。

##拡張機能を追加する
chromeブラウザを起動し、右上のメニューから設定を開きます。（macの場合、⌘+,で開ける）
設定画面に移動したら、左のリストにある**拡張機能**をクリックします。
拡張機能の画面で、右上にある**デベロッパーモード**をオンにします。
![スクリーンショット 2020-09-08 21.42.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581663/c1db5c53-354f-e5fc-1751-e20872613e33.png)
オンにしたら、左に表示されている**パッケージ化されていない拡張機能を読み込む**をクリックし、今回作成したフォルダを選択します。
![スクリーンショット 2020-09-09 0.41.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581663/37d3e916-7eed-79ee-bf25-2ded2f9d6f12.png)
選択が完了すると、作成した拡張機能が無事に追加されたことが確認できます。


##動作確認

拡張機能を有効にして、適当なサイトに向かうと文章の語尾が「だなも」になっていることが確認できるかと思います。
※画像は弊ブログ
![スクリーンショット 2020-09-09 1.54.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581663/c6f2e733-d1ce-670c-ce66-fee422127952.png)
文末のパターンを細かく分けてもっと自然な感じに置換できればなあと。
文量の多いページだと読み込みに時間がかかるので、使わない時は無効化しておきましょう。

##参考URL
[Chrome 拡張機能のマニフェストファイルの書き方](https://qiita.com/mdstoy/items/9866544e37987337dc79)
[[JS]jQueryで replace() を使って特定の文字列を置換する方法](http://www.webantena.net/javascriptjquery/jquery-replace/)
[PHP]郵便番号を正規表現でチェックする

今回は下記のコードを使って見ていきます。

```php
$zip = '１２３−４５６７';  //←全角

$zip = mb_convert_kana($zip, 'a', 'UTF-8');
if (preg_match("/\A\d{3}[-]\d{4}\z/", $zip)) {
  print('郵便番号：〒' . $zip);
} else {
  print('※郵便番号を 123-4567の形式でご記入ください');
}
```

↓のように表示されます。

```
郵便番号：〒123-4567  //←半角になっている
```

※**mb_convert_kana**については → [こちら](https://qiita.com/maca12vel/items/9378ec4ae64242ac487e)

#####パラメーターで「**'a'**」を指定する理由
郵便番号は数字なので「**'n'**」では？と思うかもしれませんが、
**-(ハイフン)**が含まれているので英数字扱いとなります。
「**'a'**」と指定することで、英数字を半角に直すことができます。

本題の正規表現ですが、まず``preg_match``というファンクションを使います。
続く``/\A\d{3}[-]\d{4}\z/``について見ていきます。

---

####`\d{3}`
**d**というのは数字「DECIMAL（デシマル）」のこと
**{3}**は3つ
つまり、**「数字を3つ並べること」**という指定

####`[-]`
前後を**-(ハイフン)**で結んでくださいという指定

####`\d{4}`
**「数字を４つ並べること」**

ここまでで、``\d{3}[-]\d{4}``で郵便番号の書式をチェックしています。


それでは、前後の「**\A**」「**\z**」は何か？

####`\A`
**「文章の先頭であること」**を指定


####`\z`
**「文章の最後であること」**を指定

まとめると、``/\A\d{3}[-]\d{4}\z/``というのは
「数字3つ並んでいるのが先頭で、間に-(ハイフン)を挟んで、終わりは数字4つ並んでいます」
というルールの記述になります。


**-(ハイフン)**有無のチェックもされているので、
``$zip = '1234567';``だと

```
※郵便番号を 123-4567の形式でご記入ください
```

と返されます。

---

以上です。お疲れ様でした。
WWW(World Wide Web)、電子メール、ネットワークに潜む脅威

#WWWのサービス
　WebサーバとWebブラウザが欠かせないものだが、そのやり取りはものすごく単純。
　![図114.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/686519/f66f9f10-854b-b632-ac59-3d8c2b130f6e.png)




#WebページはHTMLで記簿する
　Webページは**HTML(HyperText Markup Language)**と言う言語で記簿されている
　HTMLの書式は、タグと呼ばれる予約語をテキストファイルの中に埋め込むことで、文書の見栄えや論理構造を指定するようになっている
　![図115.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/686519/7364dfdb-bd20-8c79-6fc7-75b33ae30f64.png)


#URLはファイルの場所を示すパス
　Web上で取得したいファイルの場所を指し示すには**URL(Uniform Resource Locator)**という表記方法を用いる。
　URLによって規模されたアドレスは、次のような形式になっている。


#電子メール
　手紙のコンピュータネットワーク版。
　メールアドレスを使ってメッセージをやり取りする。
　昔はテキスト情報しかやり取りできなかったが、**MIME(Multipurpose Internet Mail Extension)**という規格の登場で
　様々なファイル形式を扱うことができるようになった

#メールアドレスは、名前@住所
　電子メールのやり取りにも**メールアドレス**という、住所*名前に相当するものを使用する。

　**・ドメイン名**
　　　メールアドレスの@より右側の部分は「ドメイン名」をあらわします。
　　　インターネット上における私書箱の位置
　**・ユーザ名**
　　メールアドレスの@より左側の部分は「ユーザ名」を表す。

#電子メールを送信するプロトコル
　電子送信には**SMTP**受信には**POP**を使用する。

　**・SMTP**
　![図116.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/686519/c2a3e4b1-8be5-7e16-6c8b-bfedc5f39c6b.png)


　**・POP**
　![図117.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/686519/a3928844-259f-55d3-d4aa-ecdb6f6dc002.png)


#ネットワークに潜む脅威
　悪意を持った人間が自社のネットワークに侵入できてしまうと情報の漏洩や重要なデータやファイルの破壊される恐れが出てきてしまう
　また、侵入を許さなくても大量の電子メールを送り付けてきたり、企業Webサイトを繰り返しリロードして負荷を増大させたりすることで
　サーバの処理能力をパンクさせたりする妨害行為なども起こり得る
　これに対して、企業の持つ情報をいかに守るかが**情報セキュリティ**と呼ぶ

#セキュリティマネジメントの3要素
　![図118.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/686519/e06a93a5-aed2-1702-aa9e-7c5cb0684211.png)



#セキュリティポリシ
　企業としてどのように取り組むかを明文化して、社内に周知・徹底をする
　セキュリティポリ氏は基本方針と対策基準と実施手順の３階層で構成されている

#以上
ZoomをFacebook連携しているときに、Facebookのメールアドレスを変更すると詰む

# 何が起きたか

**ZoomにFacebook連携でログインできなくなりました。**
厳密には、Facebook連携でログインすると、別の新規ユーザーとしてサインアップされます。
元々Proプランに契約などをしている人は、いきなりBasicプランに落とされて絶望の渦に叩き込まれます。

# 経緯

Facebookには8-9年前に登録していたのですが、その際にはフリーメールの捨てアドレスで登録していました。
当時は学生で、フリーメールをいくつか利用して、各種SNS系のツールを登録して試していた時期でした。

今回、アカウントの整理ついでにそのアドレスを変更しよう、と現在常用しているアドレスに切り替えました。
そして、Facebookのアカウントからは旧アドレスを削除。

朝、Zoomに入ろうとして「Facebook連携」でログインしようとすると…
**新規ユーザーとしてサインアップされました**。

Zoomのメールアドレス／パスワードで旧アドレスを使ってログインを試みます**が、失敗。**
Facebook連携と、メールアドレス・パスワード認証は別物でした。

慌ててFacebook側に旧アドレスを復帰させようとしました。
旧アドレスを登録すると、そのアドレスにアクティベート用のメールが送られ、メール上でアクティベートをして初めて有効化されます。

その捨てアドレスは、もはやパスワードも喪失されており、パスワード再設定のための情報もどこか彼方。
パスワード再設定のためには電話番号が必要ですが、その電話番号も10年前くらいの古い電話番号になっており、認証コードを受領すらできません。

**Zoomにログインするには旧アドレスが必要 -> 旧アドレスにログインしてメールを確認しないといけない -> そもそも旧アドレスにログインできない -> 死**

# 結局どうしたか

**必死で旧アドレスを復旧しました。**
パスワードは分からないので、そういえばメールを転送する設定にしていたな…と10年前の記憶を頼りに、また別のフリーメールにログイン。
こちらは奇跡的にログインできました（ログインも10年ぶりくらい）。
そのアドレスに届いていたメールを使って、なんとか旧メールアドレスをFacebookに設定しなおし、事なきを得たのでした。

# 最悪のケース

アカウントが永遠に使えなくなる、のはまだいいとして、
Proプランなどの有料プランに登録していた場合、そのプランを解除する手段がなくなります。
**自分のアカウントがZoom社にとっておいしいATMと化します。**
（さすがに問い合わせればなんとかなると信じています）

# 教訓

- **ZoomのFacebook連携をしたら、Facebookのメールアドレスを変えてはいけない**
    - Zoom側で先にメールアドレスを変更したらできるのかもしれませんが未検証
    - そもそもFacebook連携しないほうがいいのかもしれない
- 昔作った捨てアドレスを使い続けているとそのうちひどい目に合う
- 軽い気持ちでアカウント情報を整理しようとすると痛い目に合う
【Rust】rusoto_dynamodbが使いやすくなるdynomiteの紹介

# はじめに

RustでDynamoDBを扱うときは[rusoto_dynamodb](https://rusoto.github.io/rusoto/rusoto_dynamodb/index.html)を使いますが、
[dynomite](https://lessis.me/dynomite/dynomite/index.html)というCrateを組みあわせると非常に使いやすくなります。
カッコよく言えば、ハイレベルなインターフェースを提供してくれます。

DynamoDBの`Books`というテーブルに項目を作成するコードを、
「rusoto_dynamodbのみで書いた場合」と「dynomiteを使った場合」で比較してみます。

## rusoto_dynamodbのみで書いた場合

rusoto_dynamodbでテーブルに項目を作成するときは、
`PutItemInput`構造体のインスタンスを生成する必要があります。
作成したい項目の情報は`HashMap<String, AttributeValue>`でitemにセットします。

```rust
pub struct PutItemInput {
    pub item: HashMap<String, AttributeValue>,
    pub table_name: String,
    ...
}
```

`AttributeValue`はDynamoDBのデータ型[^1]に対応したフィールドを持ちます。

```rust
pub struct AttributeValue {
    pub b: Option<Bytes>, // Binary
    pub bool: Option<bool>, // Boolean
    pub bs: Option<Vec<Bytes>>, // Binary Set
    pub l: Option<Vec<AttributeValue>>, // List
    pub m: Option<HashMap<String, AttributeValue>>, // Map
    pub n: Option<String>, // Number
    pub ns: Option<Vec<String>>, // Number Set
    pub null: Option<bool>, // Null
    pub s: Option<String>, // String
    pub ss: Option<Vec<String>>, // String Set
}
```

例えば、下の表のような項目を作成するとします。

| 名前 | 値 | データ型 |
|:--|:--|:-- | 
| id | 101 | Number |
| title | Book 101 Title | String |
| price | 2 | Number |

このときのコードはこんな感じです。

```rust:rusoto_dynamo.rs
let mut new_item = HashMap::new();
new_item.insert(
    "id".to_string(),
    AttributeValue {
        n: Some("101".to_string()),
        ..AttributeValue::default()
    },
);

new_item.insert(
    "title".to_string(),
    AttributeValue {
        s: Some("Book 101 Title".to_string()),
        ..AttributeValue::default()
    },
);

new_item.insert(
    "price".to_string(),
    AttributeValue {
        n: Some("2".to_string()),
        ..AttributeValue::default()
    },
);

let input = PutItemInput {
    table_name: "Books".to_string(),
    item: new_item,
    ..PutItemInput::default()
};
```

## dynomiteを使った場合

まずは項目を表す構造体を定義します。

```rust
use dynomite::Item;

#[derive(Item)]
struct Book {
    #[dynomite(partition_key)]
    id: i32,
    title: String,
    price: i32,
}
```

すると先ほどのコードはこのように簡潔に書けます。
`new_item.into()`で`Book`構造体を`HashMap<String, AttributeValue>`に変換してくれます。

```rust:dynomite.rs
let new_item = Book {
    id: 101,
    title: "Book 101 Title".to_string(),
    price: 2,
};

let input = PutItemInput {
    table_name: "Books".to_string(),
    item: new_item.into(),
    ..PutItemInput::default()
};
```

このとき、`Book::from_attrs(hm)`で`HashMap<String, AttributeValue>`->`Book`構造体の変換もサポートしています。
項目を取得したときに大活躍します。


# 参考

[rusoto_dynamodb - Rust](https://rusoto.github.io/rusoto/rusoto_dynamodb/index.html)
[softprops/dynomite: ⚡🦀 🧨 make your rust types fit DynamoDB and visa versa](https://github.com/softprops/dynomite)


[^1]: [Naming Rules and Data Types - Amazon DynamoDB](https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypes)
@babel/parser を console.log デバッグする

気軽に `console.log` デバッグがしたかったときのメモ。

Prettier と違って Flow で書かれてるのでそのまま Node.js では動かないのでビルドする必要がある。

```sh
$ make watch
```

で変更を監視してビルドして、`/packages/babel-parser/lib/index.js` を見れば良い。

なんか適当なファイルを作って

```js
// tmp.js
const { parse } = require("./lib/index.js");

const code = `const foo = "foo";`;

const result = parse(code, { plugins: ["typescript"], errorRecovery: false });

console.log(JSON.stringify(result));
```

とか好きな内容にして

```sh
$ node ./tmp.js
```

すればうごく。最初はビルドするのがめんどくさいから flow-node で実行しようと思ったけど、flow-node は ESM 対応がされていなくて @babel/parser のソースをそのまま実行できなかったので諦めてビルドすることにした。(ちなみに一瞬 flow-node の ESM 対応をしようとしたけど手元でいじって動かすには Flow をソースからビルドする必要があるっぽく(?)、OCaml 環境のセッティングが面倒くさかったのでやめてしまった。)
CdSセンサモジュールを自作してみた

　中華製でよく売られているセンサモジュールを自作してみた。
　↓こういうやつ
![DSC_0083.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405502/e2156a6b-b45c-a9c8-e508-140d1ad5729a.jpeg)

![DSC_0082.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405502/b8cae048-bad4-c187-8efe-a24bc8ef9e4b.jpeg)

　正面についているのは、CdSセルで、明るいと抵抗値が小さくなり、暗いと抵抗値が大きくなる代物です。その抵抗値の変化を利用して、分圧抵抗でCdSの抵抗値の変化を電圧の変化として出力します。それをコンパレータをつかって基準電圧と比較して高低によってHIGH/LOW出力をするものです。基準電圧は可変抵抗で調整可能です。

　モジュールのピンは左から、AO(Analog OUT)、DO(Digital OUT)、GND(0V)、VCC(3.3V)になります。実際に動作確認したところ、AOは、明⇒暗になるにしたがって数値が高くなっていきました。DOは、基準電圧より低いときはLOW／高い時はHIGHとなりました（回路図のLED2:LOW時⇒点灯、HIGH時⇒消灯）。

　では自作してみましょう。ネットから拾った回路図をもとに作成した回路図は以下になります。
![CdS_sensor_回路図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405502/ef2de3a3-2e86-24f2-07db-20b746c27b50.png)
回路図をもとにブレッドボードで自作してみました。
　↓こんな感じ
　![DSC_0077.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405502/0c4570c6-71fb-0025-2a0e-a859e46b4e0e.jpeg)

![CdS_sensor_ブレッドボード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405502/79315084-4d25-1235-f223-45357daf794a.png)

　動作確認したところ、AOは、明⇒暗になるにしたがって数値が高くなっていきました。中華モジュールの結果と同じでした。ちなみに、AOの測定は、CdSセルの分圧抵抗のところで測定しました。DOは、基準電圧より低いときはHIGH／高い時はLOWとなりました。こちらは、中華モジュールと逆の結果となりました。

　中華モジュールの基盤が間違ってコンパレータの＋側（以後V+）と－側（以後V-）を逆に接続してしまっているようです。

　コンパレータの機能として、V+＞V-の場合、出力はハイインピーダンス状態になり、V+＜V-の場合はスイッチが入って電流が出力側からコンパレータ側に流れます。ちなみにLM393コンパレータはオープンコレクタ出力になっております。
　回路図を見てみますと、V+には基準電圧が接続され、V-にCdSの分圧抵抗が接続されています。明の状態ではCdSセルの抵抗値が小さいのでV+＞V-ですが、明⇒暗になると、CdSセルの抵抗値が高くなり、V-の電圧が高くなっていくので、どこかのタイミングでV+＜V-となります。するとコレクタのスイッチが入って（出力側の電圧がHIGH⇒LOW）電流が流れることになります。

 この中華モジュールを明るい時に、ライト点灯させるときは以下のように接続します。
![CdS sensor connect method 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405502/c876f8e8-dcf6-f9fa-e2bb-7dc323237daa.png)

逆に、暗い時に、ライト点灯させるときは以下のように接続します。
![CdS sensor connect method 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405502/5ba77515-cbb5-07a2-4eaa-1a1c5c9d35c7.png)

　データシートもなく現物だけ届きましたので、理解するまで色々と時間がかかりましたが、これで目的通りに自作できる準備が整いました。
　次回は自作したら報告します。
[PHP]半角数字に直して、数字かどうかをチェックする

今回使うファンクションは``mb_convert_kana``

例えば、以下のように記述されていたとする

```php
$age = 20;    //←半角数字

if (is_numeric($age)) {
  print($age . '歳');
} else {
  print('※年齢が数字ではありません');
}
```

表示は下記の通り

```
20歳
```

``is_numeric``というファンクションで、
指定されたパラメータが数値かどうかをチェックすることができる。
上記の例では（$age）をチェックしている。

もし、``$age = 'あいうえお'``とすると

```
※年齢が数字ではありません
```
と表示される事になる。

また、``$age = '20'``ではなく``$age = '２０'``（全角数字）にした場合も、
同じように「※年齢が数字ではありません」と表示されてしまう。

ユーザーが上記のように、半角で入力しなければいけない場合でも、
誤って全角で入力してしまうことが多々ある。

なので、仮に全角数字を入力してしまっても、半角数字に直してくれるよう記述すれば良い。

そこで使うのが冒頭でも述べた通り``mb_convert_kana``というファンクション
先にコードを書いてしまうと以下の通り。

```php
$age = ２０;    //←全角数字

$age = mb_convert_kana($age, 'n', 'UTF-8');
if (is_numeric($age)) {
  print($age . '歳');
} else {
  print('※年齢が数字ではありません');
}
```

**２０**と全角数字で入力されているにも関わらず、以下のように表示されます。

```
20歳
```
``mb_convert_kana``について説明
**mb** → マルチバイトの略で、日本語など全角を扱う時に使われる言葉
**convert_kana** → 様々なカナを変換するファンクション

パラメータによって色々な変換ができるが、
ここでは``'n'``とすることで半角数字に直すという指定ができる。

``'UTF-8'``は、使っている文字コードを指定している。

上記コードでは**２０**が全角数字だが、「**'n'**」のおかげで半角数字に直される。
よって、数値として認識され**20歳**と表示されるようになる。

---

以上です。お疲れ様でした。
【練習】ArrayList

```java

package Array_pra;

import java.util.ArrayList;
import java.util.Iterator;


public class Animals {



	public static void main(String[] args) {
		//まずはインスタンスをつくる
		ArrayList<String> Animal=new ArrayList<String>();
        //値をaddメソッドで追加
		Animal.add("ひつじ");
		Animal.add("ねこ");
		Animal.add("いぬ");
		Animal.add("きつね");
		Animal.add("りす");

		//Animalの要素数を出力
		System.out.println(Animal.size());


		//Animalの配列内が空っぽか否か
		System.out.println(Animal.isEmpty());

        //Animalのどこに"ひつじ"が入ってるか
		System.out.println(Animal.indexOf("ひつじ"));

        //Animalの中に入っている要素をひとつづつ取り出す
		Iterator <String> itAnimal=Animal.iterator();
		while(itAnimal.hasNext()){

			String aaa=itAnimal.next();

			System.out.println(aaa);
		}
	}


}


```

実行結果
5
false
0
ひつじ
ねこ
いぬ
きつね
りす
Pythonに指を代行してもらう、その後は目と脳も…

##背景
タブレットとかで作業してて、しばらくの間タップ続ける必要があるときってありますよね。
でもちょっと別の作業してて手が離せない、とか両手骨折でギブスしてて、とか。
そんなときもタップを代行してくれる「指」になるものをPythonとArduinoとサーボモーターで作ってみました。
これが動けば、次はタブレットの画面をキャプチャして、AIが判断して指に指示を出すやつを作ります。

##ArduinoUNOスターターキット購入
まずはマイコンボードを。今回の目的ならArduinoで十分よねーって感じでセンサーとかついてるキット探してたら、Amazonで3000円ちょっとでこんなんが買える！安いですねー。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324631/c39e2c89-e40e-5b19-5b78-3c0d689b136d.png)
早速Arduino専用の開発言語SketchのIDEをインストールしてLED点滅の入門編、いわゆる「Lチカ」を試してみました。うんうん、簡単。
Sketchって、PC上でコンパイルして、そのモジュールをArduinoにUSB転送して動かす仕組みなんですね。ふむふむ。
で、指の代行をしてもらうサーボモーターにも登場していただくことに。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324631/9ee08e31-a038-8eb5-d0f0-015c78ddc36f.png)
こんな感じで3cm四方くらいの大きさで、角度指定するとその角度まで移動してくれるやつ。クルクル回るモーターではないです。
このサーボをコントロールするSketchは下記のような感じでめちゃ簡単。サーボがつながるArduinoのピンを指定して、そこに角度を入力するだけで動きます。適当にディレイ入れて、良い感じに動くことを確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324631/adcb3fff-6f4a-1020-850a-cc2ae8f6f8e7.png)
##指を作る
サーボが動作することが確認できたので、タブレットをタップするための指を製作します。
サーボモーターの先に針金を括り付けて、その先に静電部分をつけます。電気を通して柔らかい感じが指に似てるので、イヤホンのイヤーパッドをアルミホイルで包んでます。そこにジャンパーワイヤーを差し込んで、GNDに接地してます。
サーボモーターは軽いので、大型の鉄のクリップで固定してます。
![9C9F0974-9B54-4904-838D-343930155B93.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324631/2ef32e8e-8f9a-0bf8-25f5-b9efffc68b12.jpeg)
##Pythonでサーボをコントロールするための準備（Firmata）
さて、このままでは動かすプログラムはPC上でSketchIDEを使ってコンパイルしたものをロードするため、いろいろ状況に応じて指示を出すことができません。
そこで、PCとリアルタイムに連携できる仕組みに変えていきます。
そのためのライブラリが「Firmata」になります。これは、PCとやり取りするためのプロトコルのようなもので、Sketchに入っているサンプルのFirmataコードをコンパイルしてArduinoにロードするだけで、PCからの指示待ち状態になります。
今回は「ServoFirmata」というサーボ用のプログラムをロードしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324631/07c99013-96ea-6779-fb29-ea43c717ee62.png)
##Pythonでサーボを動かそう（Pyfirmata）
あとはPythonでサーボを動かすプログラムを書くだけです。
そのためには「Pyfirmata」ライブラリをインポートする必要があります。pipでサクッとインストール。

```
pip install pyfirmata
```
インポートさえすれば、あとはArduinoがつながってるポートに接続して、サーボのピンを指定して、そこに角度を書き込むだけです。下記サンプルは適当に動いて、休んで、朝4：50には終了する、みたいなコードになってます。ランダムにスリープしてるのは、人間らしい挙動にするためです。（あくまでも動作確認のためのコードなので、何に使ったかは聞かないでくださいｗ）

```python:
import pyfirmata
import time
import sys
import random
import datetime

# >python servo.py [wait] [loop]
args = sys.argv
wt = 40.0
lp = 15
if len(args) > 2:
    wt = float(args[1])
if len(args) == 3:
    lp = int(args[2])

# setup servo moter for arduino UNO
board = pyfirmata.Arduino('COM3')
it = pyfirmata.util.Iterator(board)
it.start()
pin9 = board.get_pin('d:9:s')

while True:
    loop_n = random.randint(lp, lp + 5)
    for i in range(loop_n):
        pin9.write(110)
        time.sleep(0.25 + random.random() * 0.1)
        pin9.write(100)
        time.sleep(1.2 + random.random() * 1.0)

    time.sleep(wt + random.random() * 5.0 - float(loop_n - lp))

    # terminate at 04:50
    dt_now = datetime.datetime.now()
    if dt_now.hour == 4 and dt_now.minute > 50:
        break
```
##終わりに
ここまでで、Pythonを使って指の代わりのサーボを動かすことに成功しました。
あとは、タブレットの状況に応じて指示を自動で変化させるところに着手します。具体的にはタブレット画面をキャプチャしてPCに取り込み、予め学習しておいたAIの分類モデルを通して現在の状況を見極め、的確な指示を出すプログラムを作る予定です。
ここまでできると、目と脳と指を人間から代行することができそうです。意味があるかどうかは別問題ですがｗ
Eccubeで○○円以上送料無料にする処理


`app/Customize/Service/PurchaseFlow/Processor/CustomDeliveryFeePreprocessor.php` というファイルを下記のように作成します。

```php:CustomDeliveryFeePreprocessor.php
<?php

namespace Customize\Service\PurchaseFlow\Processor;

use Eccube\Service\PurchaseFlow\ItemHolderPreprocessor;
use Eccube\Entity\ItemHolderInterface;
use Eccube\Service\PurchaseFlow\PurchaseContext;
use Eccube\Repository\BaseInfoRepository;
use Eccube\Annotation\ShoppingFlow;

/**
 * Description of CustomDeliveryFeePreprocessor
 *
 * @ShoppingFlow
 */
class CustomDeliveryFeePreprocessor implements ItemHolderPreprocessor {

    /** @var BaseInfo */
    protected $BaseInfo;

    /**
     * DeliveryFeePreprocessor constructor.
     *
     * @param BaseInfoRepository $baseInfoRepository
     */
    public function __construct(
            BaseInfoRepository $baseInfoRepository,
    )
    {
        $this->BaseInfo = $baseInfoRepository->get();
    }

    public function process(ItemHolderInterface $itemHolder, PurchaseContext $context)
    {
        $this->updateDeliveryFeeItem($itemHolder);
    }

    /**
     * @param ItemHolderInterface $itemHolder
     */
    private function updateDeliveryFeeItem(ItemHolderInterface $itemHolder)
    {
        // 10,000円を超える場合は送料無料
        $totalPrice = 0.0;
        foreach ($Shipping->getOrderItems() as $item) {
            if (!$item->isDeliveryFee()) {
                $totalPrice = $totalPrice + $item->getTotalPrice();
            }
        }
        if ( $totalPrice >= 10000 ) {
            foreach ($Shipping->getOrderItems() as $item) {
                if ($item->isDeliveryFee()) {
                    $item->setPrice(0);
                }
            }
        }
    }

}

```

かんたんですね。
Dockerコマンド備忘録

現在立ち上がっているdockerをまとめて落とす

```bash

docker ps -a | awk -F '  +' 'NR > 1 {print $7}' | xargs _docker_ rm -f
```

dockerコンテナ内からmacのlocalhostにアクセスしたいとき

```bash

host.docker.interna
```

特定のメソッドのみテスト

```bash

gradle test --tests "*HogeTest.testNewInstance" -i
```

特定のクラスのみテスト

```bash

gradle test --tests "*HogeTest" -i
```

コンテナを一括で消す

```bash

docker container list -qf name=zubatoku.*_<name> | xargs docker container rm -f
```

現在起動中のコンテナに関わるものはそのまま残したい場合

```bash

docker system prune -af --volumes
```

コンテナを全て消す

```bash

docker rm -f $(docker ps -aq)
```

コンテナイメージを全て消す

```bash

docker rmi -f $(docker images -q)
```

docker volumeを全て消す

```bash

docker volume rm -f $(docker volume ls -q)
```
javascriptで関数をwrapする


javascriptで関数をwrapしたいときー
例えば、非同期処理の完了を待つまでなんかしたいときとか


```javascript
const wrapper = function(func) {
    /*** なんか処理 ***/
    let result = func();
    /*** なんか処理 ***/
};
```

async/awaitバージョン

```javascript
const wrapper = async function(func) {
    /*** なんか処理 ***/
    let result = await func();
    /*** なんか処理 ***/
};
```

普通ですね

つかうときー

```javascript
// 引数を取らない関数の場合
wrapper(funcA)

// 引数を取る関数の場合 ここが特殊！
wrapper(funcB.bind(null, "unko"))
```
[PHP]連想配列とforeach構文

##連想配列について

例えば以下の配列があるとする

```php
$fruits = ['リンゴ', 'レモン', 'バナナ'];
```

この変数``$fruits``に格納されている配列において、
``リンゴ``を取り出したい時は以下のように記述する

```php
print($fruits[0]);
```

これで取り出せたが、それぞれの値に対応する添字を覚えておかないと意味がない。
そんな時に使うのが``連想配列``
順番が関係ないような配列の場合によく使う。

上記の配列を連想配列のカタチにすると以下のようになる。

```php
$fruits = [
  'apple' => 'リンゴ',
  'lemon' => 'レモン',
  'banana' => 'バナナ'
];
```

この時、=>の左側の部分(``'apple'``)を``キー``、右側(``'リンゴ'``)を``値(バリュー)``と呼ぶ。
※キーはなんでも良いが、見た時に誰でも理解できるようなものが良い

連想配列において、先ほどと同じように``リンゴ``を取り出したい時は以下のように記述する

```php
print($fruits['apple']);
```

---

##foreachについて

``foreach``は配列専用の構文
配列の中身を繰り返し取り出すことができる。

先ほどの``$fruits``の中身(値のみ)を``foreach``で取り出すには以下のように記述する。

```php
foreach ($fruits as $fruit) {
  print($fruit . "\n");
}
```

(見やすくするために``"\n"``として改行している)
このように記述すると、表示は下記の通りになる。

```
リンゴ
レモン
バナナ
```

上記例では値のみ取り出しているが、連想配列ではキーも一緒に取り出したい場合もある。
そのような場合は以下のように記述すれば良い。

```php
foreach ($fruits as $fruit => $katakana) {
  print($fruit . "：" . $katakana . "\n");
}
```

表示は下記の通り

```
apple：リンゴ
lemon：レモン
banana：バナナ
```

``$fruit => $katakana``の部分は
``'apple' => 'リンゴ'``
``'lemon' => 'レモン'``
``'banana' => 'バナナ'``
に対応しているわけですね〜

---

以上です。お疲れ様でした。
Essential Phone (PH-1) with twrp.2020【Evolution X】

Essential PhoneにEvolution Xを焼くためにTWRPについて色々と模索する必要があったためここに記しておく。現時点/現環境でTWRPを起動させることができない場合にこの記事は参考になるのではないかと思う。

この記事によって発生した損害について私は一切の責任を持たないので自己責任でよろしくお願いしたい。素人の備忘録なので手順が何のための手順なのかを理解していない節もある。あくまでここに示すのは自分が実際に行って成功した例である。

Essentialは倒産してWebページも削除されたため、この記事に書いてある必要なデーターは各自で探してくる必要がある。

# 環境構築
OSはWindows
- Essential-PH1-WindowsDrivers.exeの導入をしておく。
- ADB/FASTBOOTが使えるか確認しておく。
- ブートローダーアンロック済み。

# 準備
- PH1-Images-QQ1A.200105.007.zip
- twrp-3.2.3-0-mata.img
- EvolitionXのOTAのROM

# 手順
Essential Phoneは倒産してしまい、最終アップデートは2020 Febの`QQ1A.200105.032`となった。このバージョンはどうやらTWRPをFlashできない問題を抱えているらしい。2020 Janの`QQ1A.200105.007`に戻す必要がある。

Androidが起動している状態で

```
$ adb reboot bootloader
```

からブートローダーを起動。

`PH1-Images-QQ1A.200105.007.zip`を解凍し、中身のファイル群の中の`flashall.bat`をダブルクリックして走らせる。しばらく待つと再起動されて`QQ1A.200105.007`のROMに戻すことができる。この過程でユーザーデータは全消去される。

再びAndroidが起動し、初期設定は省いてADBが使える状態にする。

これ以降は`a`と`b`が出てくるが、初めに`current-slot: _a`と出れば逆に読み替える。

```
$ adb reboot bootloader

$ fastboot getvar current-slot
current-slot: _b

$ fastboot flash boot_a twrp-3.2.3-0-mata.img

$ fastboot --set-active=a
``` 

音量ボタンと電源ボタンでリカバリーを選択し起動。TWRPが起動する。タッチは効かないのは既存の問題で気にする必要はない。

```A.bat
$ adb shell twrp sideload

$ adb sideload EvolutionX_4.6_mata-10.0-20200809-1528-OFFICIAL.zip
```

しばらく待つと実行が完了する。PCのログにエラーが出ていなければ成功だし、失敗しても再度上記の`A.bat`の内容を実行すればよい。自分は進捗が47%で止まり実行が完了してしまうが、ROM焼きは成功しているようである。

# あとがき
過去記事でLineageOSを焼いた。そのころのLineageOSの感触と今回焼いてみたEvolution Xとを比較すると後者は設定項目に無骨さがなく親しみやすいように感じた。また、Evolution XはGMSがあらかじめROMに内包されているので別途GAppsを導入する必要がないのはいい点だと思う。逆にGoogleの縛りから開放されてAOSP味を追求したいという人には向いていないと思う。
他のカスタムROMは知らないが、EssentialのStocckのROMの延長線でセキュリティーアップデートなどを適用していきたい人にはおすすめできるかと思う。
[JavaScript] 文字列のアルファベット大小文字考慮した辞書順ソート

# アルファベット順、大文字小文字無視したソート

次のようなコードがよく紹介されていました。

```js
var source = ['a', 'B', 'A', 'b', 'aa', 'Aa', 'AA', 'aA', 'aB', 'ab', 'Ab', 'AB']
source.sort((a, b) => {
  a = a.toLowerCase();
  b = b.toLowerCase();
  if (a < b) {
    return -1;
  } else if (a > b) {
    return 1;
  }
  return 0;
})
console.log(source);
// [ 'a', 'A', 'AA', 'aA', 'aa', 'Aa', 'aB', 'ab', 'Ab', 'AB', 'B', 'b' ]
```

このコードだとある程度は文字列を並び替えられるのですが、大文字と小文字は同一視されているので、どちらかが優先されることがないです。

いわゆる辞書順を考えると、大文字を優先するか、小文字を優先するか、を判断したいところ。

# アルファベット順、大文字、小文字、優先順位をつける。

次のようにします。

```js
var source = ['a', 'B', 'A', 'b', 'aa', 'Aa', 'AA', 'aA', 'aB', 'ab', 'Ab', 'AB']
source.sort((a, b) => {
  const la = a.toLowerCase();
  const lb = b.toLowerCase();
  if (la < lb) {
    return -1;
  } else if (la > lb) {
    return 1;
  } else {
    for(let i = 0, l = a.length; i < l; i += 1) {
      if (a[i] < b[i]) {
        return -1;
      }
      if (b[i] < a[i]) {
        return 1
      }
    }
  }
})
console.log(source);
// [ 'A', 'a', 'AA', 'Aa', 'aA', 'aa', 'AB', 'Ab', 'aB', 'ab', 'B', 'b' ]
```

アルファベット順で、大小文字区別なしで同一文字の場合には、1文字ごとに並び順を判定するとよいです。

小文字優先にしたい場合は、for の内部の、-1 と 1 を入れ替えるとよいです。

```js
// [ 'a', 'A', 'aa', 'aA', 'Aa', 'AA', 'ab', 'aB', 'Ab', 'AB', 'b', 'B' ]
```
# 追記：アルファベット順、大文字、小文字、優先順位をつける。

より効率よいコードを、コメント欄で @sugoroku_y さんに教えていただきました。ありがとうございます！

このように書いても同じ結果が出力されます。

```js
var source = ['a', 'B', 'A', 'b', 'aa', 'Aa', 'AA', 'aA', 'aB', 'ab', 'Ab', 'AB']
source.sort((a, b) => {
  const la = a.toLowerCase();
  const lb = b.toLowerCase();
  if (la < lb) {
    return -1;
  } 
  if (la > lb) {
    return 1;
  } 
  if (a < b) {
    return -1;
  }
  if (b < a) {
    return 1
  }
})
console.log(source);
// ["A", "a", "AA", "Aa", "aA", "aa", "AB", "Ab", "aB", "ab", "B", "b"]

var source = ['a', 'B', 'A', 'b', 'aa', 'Aa', 'AA', 'aA', 'aB', 'ab', 'Ab', 'AB']
const sorted = source.map(
  s => ({o: s, l: s.toLowerCase()})
).sort((a, b) => {
  if (a.l < b.l) return -1;
  if (a.l > b.l) return 1;
  if (a.o < b.o) return -1;
  if (a.o > b.o) return 1;
  return 0;
}).map(e => e.o)
console.log(sorted)
// ["A", "a", "AA", "Aa", "aA", "aa", "AB", "Ab", "aB", "ab", "B", "b"]
```

JS Bin で動かせるコードを載せました。
https://jsbin.com/busucecotu/2/edit?html,js,console