OmniAICreator/Qiita-1.07M huggingface dataset:
VS codeの便利な使い方 [mac編]

現在RubyとRuby on Railsの学習中です。コードを書く時に知っておくと、ストレスなく作業できるものをまとめています（随時追加予定）。簡単なものですが参考になればと思います。

## 文字選択
同じ文字を複数指定してまとめて編集する
Command + D
(押すたびに複数選択する事ができる）

## 1行削除
shift + Command + K

## カーソル移動
カーソルを一気に行の末尾に移動する
Command + 右カーソル
カーソルを一気に行の先頭に移動する
Command + 左カーソル
カーソルを一気に最初の行の先頭に移動する
Command + 上カーソル
カーソルを一気に最終行の末尾に移動する
Command + 下カーソル

## tableタグ関連
tableタグの中身を一気に作成する
例えば、tableタグの中にこのようなHTMLを作りたい場合、

```
<thead>
    <tr>
      <th scope="col"></th>
      <th scope="col"></th>
      <th scope="col"></th>
    </tr>
</thead>
```
thead>tr>th*3[scope=col]　と記入してTabキー
*[数字]で複数個を作成できます。


## コメントアウト
コメントアウトの仕方
Command + /(スラッシュ）
複数行まとめて範囲してもOK

## 統合ターミナルを開く
Control + Shift + ^

## インデント関係
インデントを追加
tab

インデントを削除
shift + tab

## 文字拡大・縮小
(JISキーボード）
拡大
shift + command + (+)

縮小
command + (_)
AWS VPNの削除

# 事象

AWS Site-to-Siteの設定が残っており、毎日課金が発生していたので削除しようと思ったが、
<font color="red">The VPN gateway is in use.</font>と出力されて削除ができなかった。

# 事前確認

VPCからデタッチされていることを確認

# 試したこと

1. コンソールから削除
2. Cloud Shellから削除

Cloud Shellの場合以下のようなコマンドとなる

```
aws ec2 delete-customer-gateway --customer-gateway-id CGWのID

aws ec2 delete-vpn-gateway --vpn-gateway-id VGWのID
```

しかしこれでも削除はできなかった。

# 原因

サイト間のVPN接続の設定が残っていた。
この設定で仮想プライベートゲートウェイ（VGW）とカスタマーゲートウェイ（CGW)を紐づけていたため、The VPN gateway is in use.と出力されていた模様。

# 感想

作業をしているうちに、VGWをどのサービス(VPCやサイト間のVPC接続）で利用したかが曖昧になっていた。

こういった作業をするときは、まずは全体の俯瞰図を用意しておかないと、後で全体の構成がわからなくなり、削除や設定変更の時にも困ると感じた。
AWS Route53 でサブドメインを切るのがとても簡単でした。

| | |
|:-:|:-:|
| ![スクリーンショット 2020-12-29 7.07.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954797/3b53226c-1642-00f5-5fb9-71a2a8b43b40.png) | Route53 メニューの **ホストゾーン** を選択 |
|![スクリーンショット 2020-12-29 7.09.08.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954797/36d6c9ff-dc6c-3724-4c16-2fdfe0e941f6.jpeg)|**ドメイン名** を選択|
|![スクリーンショット 2020-12-29 7.14.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954797/03d7a3de-7f56-392c-662b-71951316741e.png)|**レコード** を選択 <br>からの<br>**レコードを作成** を選択|
|![スクリーンショット 2020-12-29 7.21.43のコピー.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954797/880cac8d-d62d-21d0-3133-e2164e7d400f.jpeg)|**レコード名** を入力<br>からの<br>**エイリアス** を ON<br>からの<br>**トラフィックのルーティング** をドメイン割り当てたい ELB とかに指定|

これだけ。簡単。

おしまい。
firebaseの認証がうまくいかなかったときの対処法（The current domain is not authorized for OAuth operations）

# 事象
事象としては、こちらとほぼ同じ。

[firebaseのauth認証が通らない（not authorized to run this operation）](https://qiita.com/hanlio/items/357095a184dad32e93f2)

consoleに以下のエラーが出ていた。

``Info: The current domain is not authorized for OAuth operations. This will prevent signInWithPopup, signInWithRedirect, linkWithPopup and linkWithRedirect from working. Add your domain (xxxxxxxx.web.app) to the OAuth redirect domains list in the Firebase console -> Auth section -> Sign in method tab.
``

# 確認

## Firebase設定の確認

Firebase -> Authentication -> Sign-in method の下の方にある「承認済みドメイン」には、対象のアプリのドメインが登録されている。
Firebase Hostingを使って開発しているのでデフォルトで登録済みなのです・・・

![Authentication - Firebase コンソール - Google Chrome 2020-12-29 07.09.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/409199/3c015d83-0caa-03aa-5c4e-fd4bf3f4e13c.png)


## 上記のサイトを参考に、config回りを確認。
main.jsに以下のように記載済み。（Firebaseからコピペしてきたやつ）
```js:main.js
// Initialize Firebase
  var firebaseConfig = {
    apiKey: "xxxxxxxxxxxxxxxxxxxxxxx",
    authDomain: "xxxxxxxx.firebaseapp.com",
    databaseURL: "https://xxxxxxxx.firebaseio.com",
    projectId: "xxxxxxxx",
    storageBucket: "xxxxxxxx.appspot.com",
    messagingSenderId: "xxxxxxxx",
    appId: "xxxxxxxx",
    measurementId: "xxxxxxxx"
  };
  // Initialize Firebase
  firebase.initializeApp(firebaseConfig);
```

# 結論
四苦八苦して、たどり着いたのは、、、
configを以下のように修正。理由は分からないが、firebaseapp.comではなくて、web.appを登録すれば行けた。

```js:main.js
// Initialize Firebase
  var firebaseConfig = {
    apiKey: "xxxxxxxxxxxxxxxxxxxxxxx",
    authDomain: "xxxxxxxx.web.app",★
    databaseURL: "https://xxxxxxxx.firebaseio.com",
    projectId: "xxxxxxxx",
    storageBucket: "xxxxxxxx.appspot.com",
    messagingSenderId: "xxxxxxxx",
    appId: "xxxxxxxx",
    measurementId: "xxxxxxxx"
  };
  // Initialize Firebase
  firebase.initializeApp(firebaseConfig);
```
MySQLが起動できない場合の解決法

「/etc/my.cnf」を、変にいじりませんでしたか？
こんな感じのしませんでした？

```
sudo vim /etc/my.cnf
```

これのせい（というか自分のせい）でしばらくMySQLにログインできませんでした。
ファイル内に書いた一部が使えなかったみたいです。
Vue Composition API で書くと何が嬉しいのか

# はじめまして

都内で WEB 開発をしています。今は主に Vue を書いてます。

現在、私の所属するフロントエンドチームでは、Vue 2 系 + TypeScript をつかって開発しています。
当然、これまでは (いわゆる) Options API によってコンポーネントのコードを記述していたのですが、数ヶ月前くらいから「これからは基本的に Composition API をつかっていこうぜ！」という方針になりました。

「Composition API をつかっていこうぜ！」というのはイイけど、Composition API をつかうメリットが何なのか、モチベーションはどこにあるのか・・・。そういった本質的な価値がイマイチよく分からないまま、実装者が Reactivity を強く意識する、新しいコーディングスタイルの世界に足を踏み入れました。

Composition API をつかい始めると、世界が一気に変わります。
以降でも触れますが、TypeScript が活躍できるようになったおかげで、Vue の Composable な機能や、サードパーティライブラリ、とくに Vue Apollo (GraphQL クライアント) まわりの型の複雑さにヒーヒー言ってました。
それ以外にも、Reactivity が意図せずに途切れたり、vue の composable な機能を `setup()` の call の外でつかおうとすると怒られたり。
ハマりポイントな Composition API のクセはたくさんあります。

ですが、最近はちょっと慣れてきて、「あー、なるほどね」と思うことも増えてきました。
そんな最近の私の「あー、なるほどね」ポイントをご紹介します。
(具体的なコードとかは載せてません。文章だけ)

# あー、なるほどね

## 1. template で利用している変数が追いやすくなる

Compostion API を利用すると、「あれ、この変数 (option のプロパティ) って、修正したら描画に影響あるかな？」というのが格段に減ります。(以降は option のプロパティのことも変数と呼びます)
なぜなら、Compostion API では、template で用いられる変数は `setup()` の戻り値でないといけないからです。

Options API は、data、method、computed などのプロパティが、全て template から参照可能なため、注意深く template を読み直さなければ描画に直接的に関わる変数を特定できません。
小さなコンポーネントではとくに問題はありませんが、大規模なコンポーネントでは "改修" が新たなバグを産む潜在的リスクを抱えていました。

これに比べ、本項の冒頭でも触れたように、Compostion API では「template で用いられる変数は `setup()` の戻り値でなければならない」という制約があるため、裏を返せば `setup()` の `return` を見ればどの変数が描画に使われているかが一目で分かるということになります。
また、処理を追う際も、`return` から辿っていけばいいので、コードを読解する時間も短縮できると思います。

## 2. 処理の分割・カプセル化・共通化・外部化がしやすくなる

Options API では、「data は data、method は method、computed は computed」というように、それぞれのセクションに定義しなければいけませんでした。
そのため、処理の流れを追う際に、「この data は この method でつかわれてて、しかもそれは computed で変換されてて・・・。」とコードの中をあっちゃこっちゃ駆け回っていました。

Composition API では、そうした制約はないので、関連した文脈を持った処理をひとまとまりにすることができます。
「処理をひとまとまりにすることができる」ということは、それらをクロージャに閉じ込めて、"状態" を隠蔽することもできますし、そうなれば当然カプセル化もしやすくなります。

さらに、Composition API では「全てが `this` に所属する」といった世界観から解放されるため、最小限の処理は Pure な関数にするモチベーションも高まります。
「Pure な関数にするモチベーションが高まる」ということは、頻出処理を共通化したり外部化することへの心理的なコストも下がり、再利用性の高いコードが産まれやすくなる　ということにも繋がるかと思います。

## 3. TypeScript のパワーが発揮される

Composition API をつかう最大のメリットの一つです。
個人的には ***2. 処理の分割・カプセル化・共通化・外部化がしやすくなる*** もけっこー重要だと感じていますが、やはりココを「最大のメリット」として捉える方は多いと思います。

Composition API をつかうと、とにかく TypeScript が活躍してくれます。

Composition API をつかうと、「変数名の変更漏れてて動かなくなってたわ！バグです！」ということがなくなります。
なぜなら TypeScript が仕事をしてくれるからです。

Composition API をつかうと、「API レスポンスを格納してた変数の構造体が変わってたわ！バグです！」ということがなくなります。
なぜなら TypeScript が仕事をしてくれるからです。


Composition API をつかうと、「この変数の中身なんだったか分かんねえしコード追うのも面倒くさいし変数名的にも 
Boolean やろ。と思ってたら String でした！バグです！」ということがなくなります。
なぜなら TypeScript が仕事をしてくれるからです。

TypeScript を適切につかっていれば、TypeScript がめちゃくちゃ仕事してくれます。
TypeScript を適切につかうために学習コストもランニングコストもかかりますが、同じようにコードの信頼性を高めるために単体テスト書くことを考えたらカワイイものではないかと思います。
あと、TypeScript 慣れておくと型に厳格な言語を新しく学ぶ際の障壁も下がります。

# そんな感じです

これを読んで下さった "Composition API コワイ" 勢の皆さんも、Composition API に畏れず立ち向かっていけば、いつか「あー、なるほどね」となる日が来るんじゃないかなと思います。保証はしないけど。

おしまい。
MacBook ProでPyAutoGUIの座標が正しく取れない？

# 動かない
Python + PyAutoGUIでMacBook Pro(15-inch, 2018)のDockにあるアイコンをクリックさせようと、こんなコードを書いたら動かなかった。

```python:click.py
import pyautogui as pag

position = pag.locateCenterOnScreen('target_app.png', confidence=0.9)
pag.click(position)
```
※target_app.pngはDockのアイコンをスクリーンショットした画像

Dockが一瞬だけピクッと動いて、ターゲットのアプリケーションが起動しない。

Dockのアイコン位置の座標を調べたところ、locateCenterOnScreenで取得した値と、アイコンにマウスを当ててposition()で取得した値がぜんぜん違うことに気づいた。

**locateCenterOnScreenで取得した値**
```　x=3046, y=2335　```

**pyautogui.position()で取得したマウスカーソルの値**
```　x=1517, y=1169　```

# Retinaディスプレイの解像度が関係している？
xもyも半分くらいの値なのでRetinaディスプレイの解像度が関係しているのかと思い、それぞれを2分の1にした値をclick()に渡してみた。

```python:click.py
import pyautogui as pag

x,y = pag.locateCenterOnScreen('target_app.png', confidence=0.9)
pag.click(x/2, y/2)
```

ビンゴ。
これでDock上のアイコンをクリックしてアプリケーションを起動することができた。
Gatsby.jsにMaterial-UIを導入する手順


## 手順

### 1. `gatsby-plugin-material-ui`と`@material-ui/core`をインストール

```
yarn add gatsby-plugin-material-ui @material-ui/core
```

その他のライブラリとしては以下のようなものがあります。必要に応じてインストールしてください。

- `@material-ui/icons`
- `@material-ui/lab`
- `@material-ui/pickers`

### 2. `gatsby-config.js`に以下を記述

```gatsby-config.js
module.exports = {
  plugins: [`gatsby-plugin-material-ui`],
};
```

## 詳細

### Material-UI
GoogleのMaterialデザインをベースに開発された、UIコンポーネントライブラリ

## 参考資料
- [Material-UI](https://material-ui.com/)
- [gatsby-plugin-material-ui](https://www.gatsbyjs.com/plugins/gatsby-plugin-material-ui/)
Amazon Location Service (Preview)で地図を表示する

re:Invent 2020で発表され、現在プレビューのAmazon Location Serviceを試してみました。
プレビュー段階ながら東京リージョンで利用できるのは有り難いですね。

# マップリソースを作成する
コンソールへログイン後、Amazon Locationのページへ遷移し、左メニューの`Manage Resources`の`Maps`をクリック
続いて`Create map`をクリック
![location0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44921/75f7d2cf-432b-4dcc-1092-95bdfe007074.png)

Nameに任意の名前を入力し、Mapsセクションにてマップタイプというかスタイルを選択、`Create map`ボタンをクリック
![location1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44921/678421b9-d182-3a5c-47f4-2ad2ff74ac8d.png)
![location1-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44921/f80c48b5-8a90-41d8-a9ff-0a443e566001.png)

作成が完了すると`ARN`が表示される。この後使うのでメモしておく。
![location2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44921/4b4debdc-e6e5-a137-6a7b-b90f6111c67b.png)

# IDプールの作成
IDプールの管理から新しいIDプールの作成を行う。
![idpool0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44921/c31ec4ad-120a-ae24-01c5-a93b5bdf61dc.png)
ここで`認証されていないIDに対してアクセスを有効にする`にチェックを入れて`プールの作成`ボタンをクリック。
![idpool1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44921/5b85a58a-ba92-41cc-5620-d509d161e34e.png)
詳細を表示すると2つの`ロールの概要`があるが、下の方が`unauthenticated identities`のものなので、こちらのポリシードキュメントに以下を追記する。

※Resourceの`<region>`と`<account ID>`は任意の値に置き換えてください。

```
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "MapsReadOnly",
      "Effect": "Allow",
      "Action": [
        "geo:GetMapStyleDescriptor",
        "geo:GetMapGlyphs",
        "geo:GetMapSprites",
        "geo:GetMapTile"
      ],
      "Resource": "arn:aws:geo:<region>:<account ID>:map/ExampleMap"
    }
  ]
}
```

![idpool2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44921/0e9e6d9b-0953-a311-9948-eec9befe6c11.png)

# 地図の表示
ドキュメントによると
* AWS SDK
* Mapbox GL JS
* Mapbox Maps SDK
* Tangram
* Tangram ES
による使用方法が案内されている。

iOSやAndroidのネイティブアプリに組み込む場合にはAWS SDKまたはMapbox Maps SDKを使うことになりそう。
今回はブラウザで見れれば良いのでMapbox GL JS(とReact)を使って表示してみました。

![サンプルイメージ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44921/02a0b7fa-c7e5-0bd6-e737-ac5f9b731a2a.png)

# 感想
* MapboxやGoogleMapsと比較すると地図を表示させるまでの準備に若干手間がかかる印象
* 地図の注記にアルファベット表記が散見されるので国内向けとして使うにはちょっと気が引ける
* 建物などはよく整備されているデータを使っている（OpenStreetMapのようにスカスカではない）

# 期待したいこと
* 注記を日本向けにしてほしい
    * 注記の密度が低い
    * 日本語で表示してほしい

# 今後試したいこと
* ジオコーディング
* ルーティング

# デモ
https://fervent-lewin-cb4528.netlify.app/

# サンプルソース
サンプルのソースコードはこちら
https://github.com/chaki1019/aws-location-service-app

# 参考
https://docs.aws.amazon.com/ja_jp/location/latest/developerguide/using-amazon-location.html
機械学習モデルを用いた顔画像データセット仕分け(#3)

#概要
UTKFaceデータセットを下記特徴ごとに仕分けする。
・問題なし
・グレースケール
・複数人が写っている
・加工処理されている

###仕分けの流れ
[前回作成した学習用モデル](https://qiita.com/dream88sound/items/12cfe5217ca705334a05)を使用し、UTKFaceデータセットのpart1フォルダの画像データを一枚ずつ判別
↓
Googleマイドライブに下記フォルダを作っておき、判別された画像を保存していく
・normal
・grayscale
・several
・processing

###環境
Google Colaboratory（GPU）

```csharp
# UTKFace part1 ZIP読み込み
z = zipfile.ZipFile('/content/drive/My Drive/part1.zip')
imgfiles = [ x for x in z.namelist() if re.search(r"^part1.*jpg$", x)]

for imgfile in imgfiles:
    image = Image.open(io.BytesIO(z.read(imgfile)))
    image = image.convert('RGB')
    image_p1 = image.resize((im_size, im_size))
    data = np.asarray(image_p1)
    # shapeを(299,299,3)→(1,299,299,3)に変更する
    data = data[None, ...]
    # ZIPフォルダ内に保存されていたファイル名を取得
    file = str(os.path.basename(imgfile))
    # 学習済モデルに画像一枚分のデータを入力して予測する
    pred=model.predict(data)
    # 判別された値によって画像データを保存する場所を変更する
    if np.argmax(pred) == 0:
      image.save('/content/drive/My Drive/normal/' + file)
    elif np.argmax(pred) == 1:
      image.save('/content/drive/My Drive/grayscale/' + file)
    elif np.argmax(pred) == 2:
      image.save('/content/drive/My Drive/several/' + file)
    else :
      image.save('/content/drive/My Drive/processing/' + file)

z.close()
del z, imgfiles
```
model.predictは複数枚の画像読み込みを想定（モデルの引数が４次元）しているため、１枚ずつ予測していこうとするとエラーが出ました。
data = data[None, ...]で３次元から４次元に変換すると予測してくれました。
画像のファイル名は変更せずに各フォルダに仕分けをして保存しています。

###仕分けされたフォルダの画像を確認
（スクショを貼ってたんですが、違法なのかもしれないと思い削除させて頂きました）

####grayscaleフォルダ
色がついている画像もちらほら見受けられますがほぼ判別できているようです。


####severalフォルダ
人が複数人写っている画像で学習していましたが学習用データが少なかったためか、ぬいぐるみや動物が写っている、もしくは背景がシンプルではない画像もこちらのフォルダに入ってきているように感じました。

####processingフォルダ
顔に文字が被っていたり、渦巻きのような模様が入っているデータをほぼ判別できています。

###結果
判別精度はぼちぼちですが、normalフォルダにすっきりした画像を集める事ができました。
このnormalフォルダを利用して年齢や性別が特定できるモデルを作っていこうと思います。
Makefile で環境ごとに違う docker-compose.yml を利用する



```make

ENV=local

pre:
ifdef e
ENV=${e}
endif

set-env :=\
	export ENV=$(ENV) ;\
	export COMPOSE_PATH_SEPARATOR=: ;\
	export COMPOSE_FILE=docker-compose.$(ENV).yml ;

up: pre
	$(set-env)\
	docker-compose up -d
```

コマンド実行の時は

```bash
$ make up e=production
```

とかを叩く。

おしまい。
Docker のリソースを全停止したり全削除したりするコマンド


環境構築中とか検証中とかは、まっさらにしてしまいたいことが多いので備忘録として。

# Docker コンテナを全停止する
```bash
$ docker stop $(docker ps -q)
```

# Docker コンテナを全削除する
```bash
$ docker rm $(docker ps -q -a)
```

# Docker イメージを全削除する
```bash
$ docker rmi $(docker images -q)
```

# Docker ボリュームを全削除する
```bash
$ docker volume rm $(docker volume ls -qf dangling=true)
```

おしまい。
EnOcean BLE スイッチ Easyfit(EWSDB)を買ってみた①　動作を確認する

#目次
・はじめに
・実物を見てみる
・データは飛んでいるのか

#はじめに
　EnOceanがBLE（BluetoothLowEnergy）に対応したと聞き、購入してみた。
　動作の確認を行う。

　スイッチと環境センサがあるが、スイッチのほうを購入する。
　このデバイスは、電源は不要となっており、スイッチを押した力で発電し、その電気のみでBLEのパケットを飛ばすらしい。
　BLEのアドバタイジング・パケットにデータをのっけて飛ばす、ブロードキャスト通信とかいうやつだ。
<img width=25% src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184089/baa5d4f0-3da0-e583-36d2-c49fdac78da6.png>
　スイッチサイエンスさんで購入できる。
https://www.switch-science.com/catalog/6526/

#実物を見てみる
　実物の裏面である。QRコードに情報が入っているらしくQRコードリーダーで読み取ってみる。
<img width=40% src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184089/33fcdda3-9362-1d91-382c-a712ec03e640.png>
　QRコード【30SE215000183BE+ZB2FEFBE22A5393040BD27E9452AC7599+30PE8221-A280+2PDC03+S07015370】であった。
　ユーザーマニュアルによると以下であるらしい。
<img width=70% src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184089/1eca94e4-20be-123c-72fd-a61ab17c378b.png>
　Source Address：E215000183BE
　Security Key：B2FEFBE22A5393040BD27E9452AC7599
　Ordering Code：8221-A280
　Step Code and Revision：DC03
　Manufacturer：07
　Serial number：015370
【Security Key】以外は裏面に記載があった。

#データは飛んでいるのか
　実際にデータは出るのかスマホで情報をとってみる。
　スマホに【BLE Scanner】アプリを入れ、右上（下図赤丸）のスイッチを押してみる。
　スイッチには4か所あり、それぞれにA0、A1、B0、B1のID（橙字）が指定されている。
<img width=25% src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184089/35d55f3c-ffb1-53c2-2881-0ebbde72a84e.png>
　押すとアプリ画面に以下（赤枠）の表示が増えた。アドレスが【E215000183BE】なので間違いない。
<img width=25% src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184089/5b42e509-a1a0-69b9-ee1a-df1131fa44cb.png>
 RAWデータを表示する。【DA030602000009B66BC982】というデータが飛んできたらしい。
<img width=25% src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184089/d41cf8ca-3eb2-1c42-6263-c9eb568f9767.png>
 ユーザーマニュアルによるとデータは以下のようになっているらしい。
<img width=70% src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184089/e03a6a6d-f12b-3cbe-4f77-0eec6419149c.png>
 よってこのデータの中身は以下のようになっており、どのボタンが押されたかのデータ（SwitchStatus）は【0x09】となる。

|Manufacturer ID|Sequence Counter|Switch Status|Optional Data|Security Signature|
|:--:|:--:|:-:|:-:|:-:|
| DA03|06020000|09|-|B66BC982|

【SwitchStatus】は以下のようになっている。
<img width=70% src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184089/c4fed5c9-2874-cf9a-d6a1-b7d4b42f740b.png>

　ボタンは【B0】のため、Bit3が1、またボタンを押しているため、Bit0が1となる。
　このため、今回のスイッチステータスは0b00001001（0b00001001 = 0x09）となる。
　正しい値が飛んでいることを確認できた。

　まずは以上である。BLEの勉強がてらもう少し遊んでみるつもりである。
Jetbot RCカー＆M5stack core2 でモード切替

![IMG_3082a.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617683/b2cc3d13-26a5-1d63-c61b-61420cd105ce.jpeg)

Jetbot RCカーでM5stack core2 コントローラーのZ軸加速度データを使い
ラインフォローのモード入り切りも実装しました。
JetbotはCore2を傾けた方向に進み、裏返しで自動運転です。
<追伸＞Core2が落下しないように輪ゴム追加しました（落ちるとパニックになります）。
動画はこちら。
https://www.youtube.com/watch?v=m27CFoWe25k

```ruby:live_demo.ipynb
import ipywidgets
from socket import *

automode = 0
manualR = 0
manualL = 0

## UDP受信クラス
class udprecv():
    def __init__(self):

        SrcIP = "192.168.***.***"                         # 受信元IP
        SrcPort = 9000                                  # 受信元ポート番号
        self.SrcAddr = (SrcIP, SrcPort)                 # アドレスをtupleに格納

        self.BUFSIZE = 1024                             # バッファサイズ指定
        self.udpServSock = socket(AF_INET, SOCK_DGRAM)  # ソケット作成
        self.udpServSock.bind(self.SrcAddr)             # 受信元アドレスでバインド

    def recv(self):
        global manualR, manualL, automode, a0_slider, a1_slider, a2_slider
        
        while True:                                     # 常に受信待ち

            data, addr = self.udpServSock.recvfrom(self.BUFSIZE)
                                                        # 受信
            #print(data.decode(), addr)                 # 受信データと送信アドレス表示
            a = data.decode().split(',')
            #print(float(a[0]), float(a[1]), float(a[2]))
            a0_slider.value = float(a[0])
            a1_slider.value = float(a[1])
            a2_slider.value = float(a[2])

            manualR = (float(a[0]) - float(a[1]))*0.4
            manualL = (-float(a[0]) - float(a[1]))*0.4

            if float(a[2]) < -0.2:
                automode = 1                   # 裏返しでラインフォローON
            else:
                automode = 0                   # 表向きでラインフォローOFF

a0_slider = ipywidgets.FloatSlider(min=-2.0, max=2.0, step=0.01,  value=0.0, description='a[0]')
a1_slider = ipywidgets.FloatSlider(min=-2.0, max=2.0, step=0.01, value=0.0, description='a[1]')
a2_slider = ipywidgets.FloatSlider(min=-2.0, max=2.0, step=0.01, value=0.0, description='a[2]')
display(a0_slider, a1_slider, a2_slider)
```

```ruby:live_demo.ipynb
def line_follow():
    execute({'new': camera.value})
    camera.observe(execute, names='value')

import threading

# UDP run
th1 = threading.Thread(target=udp.recv)
th2 = threading.Thread(target=line_follow)

th1.start()
th2.start()

display(a0_slider, a1_slider, a2_slider)
```
#参考
kashi takuさん、つっきーさんありがとうございました。
[Python3] UDP でデータを送受信する方法
https://qiita.com/kashitaku/items/8db929ad6bf3a13e5e68
ESP32でUDP通信やってみた（ESP32インストール手順解説あり）
https://qiita.com/Tsukkey/items/247285c703fbbc6c6cd2
AWS EC2でMySQL総まとめ【単純かつ明快に】

## まず、アクセスするぜ🚶‍♀️

■1. rootのパスワードを知る。(2通り) 

```
cat /var/log/mysqld.log | grep password
A temporary password is generated for root@localhost: ??????????
```

```
sudo cat /var/log/mysqld.log | grep "temporary password"
A temporary password is generated for root@localhost: ??????????
```

■2.mysqに入る (2通り)

```
mysql -u root -p
```

```
mysql-ctl start
```

■3.rootのパスワード設定。
「-------」はパスワードを設定しよう。
（大文字・小文字・数字・特殊記号はそれぞれ最低１つは入れる。）

```
set password for root@localhost=password('-----------------');
Query OK, 0 rows affected, 1 warning (0.00 sec)
```
## 表示👁

■データベースの情報確認コマンド

```
show databases;
```

■テーブルの情報確認コマンド

```
show tables from データベース名;
```

■カラム（項目）の情報確認コマンド

```
show columns from テーブル名 from データベース名;
```

```
show create table テーブル名;
```

```
desc テーブル名;
```

```
select * from テーブル名;
```

## 追加➕

■データベース追加

```
create database データベース名;
```


文字コード指定（utf8とか）

```
create database データベース名 default character set 文字コード;
```

■テーブル追加 
1.

```
use データベース名;
```
2.(オプションはなくてもいい。)

```
create table テーブル名
(
カラム名 データ型 オプション,
カラム名 データ型 オプション,
カラム名 データ型 オプション,
カラム名 データ型 オプション
); 
```
2.(example)

```
create table test
(
id integer,
name char(16),
username char(20) not null,
sub_name text
);
```

→ [よく使うデータ型 一覧](https://proengineer.internous.co.jp/content/columnfeature/6728)、[オプション](https://uxmilk.jp/12725)

2.(文字コードをutf8に指定)

```
create table テーブル名
(
カラム名 データ型 オプション,
カラム名 データ型 オプション,
カラム名 データ型 オプション,
カラム名 データ型 オプション
); DEFAULT CHARSET=utf8;
```

## 削除🗑
■データベースを削除

```
drop database データベース名;
```

■テーブルを削除

```
drop table テーブル名;
```

## ユーザー権限🙋‍♂️
■MySQLのユーザーを見る

```
SELECT User, Host FROM mysql.user;
```

■ユーザーを追加する

```
CREATE USER 'ユーザ名@'ホスト名' IDENTIFIED BY 'パスワード';
```

■特定ユーザーの権限確認

```
show grants for ユーザー名@ホスト名;
```

■あるユーザーのselect、update、insertを許可する。
selectと、updateのみ

```
grant select, update on データベース名 to ユーザー名@ホスト名 identified by 'パスワード';
```
selectと、insertのみ

```
grant select, insert on データベース名 to ユーザー名@ホスト名 identified by 'パスワード';
```

■権限変更を反映する。

```
FLUSH PRIVILEGES;
```

## システム🤖

■MySQLの細かい状態（バージョン、ログインユーザー、文字コードなど色々）

```
status
```

■MySQL再起動

```
sudo service mysqld start
```

■細かい設定の書き換え

```
sudo vim /etc/my.cnf
```
VSCodeのターミナル(PowerShell)文字化け対策

#はじめに
VisualStudioCodeでターミナルをPowerShellにすると、日本語が文字化け…。
chcp　すると　よくわからない文字コードが…。

#設定
以下を設定しました。

```json:settings.json
{
    "terminal.integrated.shellArgs.windows": "-NoExit -Command chcp 932"
}
``` 

#参考 
https://qiita.com/s4i/items/75c19c9feb10b54c1ce9
Raspberry OS 64bitのWi-Fi接続

## 目的

Raspberry Pi 4を買ってきたので、自宅のWi-Fiに参加させようと思ったら、なぜか接続できなかったのでメモ。

## 前提条件

* Raspberry Pi 4にはモニターもキーボードもつながっていない
* 2020-08-20-raspios-buster-arm64.imgを使用
* 有線LANは確保できる

## インストールが終わってからの手順

* /bootディレクトリに"ssh"という名前のファイルを作る
* とりあえず有線LANで接続して起動
* aptコマンドを使ってとりあえずすべてを細心にアップデートしてリブート
* ルーターの設定に従って、ローカルエリア内にあるはずのRaspberry Piを探してpiユーザーでログイン
* rfkillコマンドを使って現在のBluetooth, Wi-Fiの状態をチェック
* ブロックされてたら解除
* raspi-configコマンドを使ってWi-Fiを設定


## やったコマンド

適宜読み替えてほしい。

### SDの作成

```
$ sudo dd if=2020-08-20-raspios-buster-arm64.img of=/dev/rdisk2 bs=1m
$ touch /Volumes/boot/ssh
$ diskutil umount /Volumes/boot
```

ここでできたディスクをRaspberry Piに挿入してブート。

### Raspberry Piでのコマンド

```
$ sudo rfkill
$ sudo rfkill unblock all
$ sudo apt update
$ sudo apt upgrade
$ sudo apt dist-upgrade
$ sudo shutdown -r now
```

再起動してきたらraspi-configでWi-Fi設定。

## 最後に

32bitカーネル版はWi-Fiがブロックされてないので、正式版が出るときには問題なくなってるのではないかとは思うが。
データベースを使用して処理を書いてみる備忘録(同じ事前も書いたけど)

データベースの処理を前の記事でも書いたが再度復習として備忘録します。まとめます。

<h1>マイグレーションファイルを作成する。</h1>
今回は投稿記事としてarticleテーブルを作成します。

```terminal:ターミナル
php artisan make:migration create_articles_table --create=articles
```


パスは

database/migrations/2020_12_23_191814_create_article_table
で作られています。


中身は少し書き換えたんでデフォルトより書き換わってますが、


```php:2020_12_23_191814_create_article_table
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateArticlesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('articles', function (Blueprint $table) {
            $table->id();
            $table->string('article_title');←追加
            $table->string('article');←追加
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('articles');
    }
}

```

<h1>マイグレーションを実行する</h1>
作成したマイグレーションファイルを元にマイグレーションを実行する。

```terminal:ターミナル
php artisan migrate
```

因みに自分のsqliteのバージョン

```terminal:ターミナル
sqlite3 --version
3.34.0 2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089fed5b
```

数字の羅列があるが大丈夫なのか??
とりあえずプロジェクトディレクトリまで移動する。

<h1>データベースに追加してみる。</h1>
```terminal:ターミナル
sqlite3 database/database.sqlite 
```


実行結果

```terminal:ターミナル
SQLite version 3.34.0 2020-12-01 16:14:00
```

<h1>テーブルを確認する</h1>
データベースへ接続ができたら、以下コマンドで現存するテーブルを確認します。


```terminal:ターミナル
sqlite> .table

```

実行結果、以前作成したテーブルもあるので合計六つのテーブルが確認できます。

```terminal:ターミナル
articles         migrations       users
failed_jobs      password_resets  votings
````
<ul>
<li>articlesテーブル</li>
<li>migrationsテーブル</li>
<li>usersテーブル</li>
<li>failed_jobsテーブル</li>
<li>password_resetsテーブル</li>
<li>votingsテーブル</li>
</ul>

以上がテーブル一覧です。

<h1>テーブル定義を確認する</h1>

```terminal:ターミナル
.schema articles
```
実行結果

```terminal:ターミナル

CREATE TABLE IF NOT EXISTS "articles" (
      "id" integer not null primary key autoincrement, 
      "article_title" varchar not null, 
      "article" varchar not null, 
      "created_at" datetime null, 
      "updated_at" datetime null
);
```


とりあえずこれで、、
間違いはあると思うのでなにかあればご指摘頂ければ幸いです。
Gatsby.jsにmicroCMSを導入する手順

## 手順

### 1. `gatsby-source-microcms`をインストール

```
yarn add gatsby-source-microcms
```

### 2. `.env.development`ファイルを作成して以下を記述

```.env.development
MICROCMS_API_KEY = ×××××××××××××××××××××××××
```

### 3. `gatsby-config.js`に以下を記述

```gatsby-config.js
module.exports = {
  plugins: [
    {
      resolve: "gatsby-source-microcms",
      options: {
        apiKey: process.env.MICROCMS_API_KEY,
        serviceId: "myblog",
        apis: [
          {
            endpoint: "posts",
          },
        ],
      },
    },
  ],
}
```

## 用語解説
### microCMS
> microCMSは日本製のHeadless CMSでCaaS型のCMSです。
Vue.js プロフェッショナルプログラミング p.334

### CaaS型
> CaaS型（CMS as a Service）は、PaaS（Platform as a Service）型のCMSを指します。
CMSをサービスとして展開している形態で、データなどはそのサービスのクラウド上で保存される形になります。
Vue.js プロフェッショナルプログラミング p.332

### HeadlessCMS
> HeadlessCMSは、表示部分を持たないCMSで、「ヘッドレス」のヘッドレスはCMSの「表示部分」を指します。
Vue.js プロフェッショナルプログラミング p.239

### CMS

> CMSとは、Content Manegement Systemの略で、Webサイトのコンテンツを構成する情報（テキスト・画像・レイアウト情報：HTML/CSS/JavaScript）や作成したコンテンツを公開するためのワークフロー（承認フロー）を管理するシステム。代表的なCMSとしては、WordPressやMovable Typeなどが挙げられます。
Vue.js プロフェッショナルプログラミング p.330

## 参考資料

### gatsby-source-microcms

- [gatsby-source-microcms](https://www.gatsbyjs.com/plugins/gatsby-source-microcms/)

### microCMS
- [microCMS](https://microcms.io/)

### Headless CMS

- [ヘッドレスCMSとは何か？従来CMSとの違いやメリデメを解説！](https://microcms.io/blog/what-is-headlesscms)
- [Headless CMSを使うとSEOに弱くなる？](https://microcms.io/blog/headlesscms-and-seo)
- [なぜヘッドレスCMSを選ぶのか？ヘッドレスCMSが選定される7つの理由](https://microcms.io/blog/7reasons-to-choose-headlesscms)
Raspberry pi zeroのcronでgcloudコマンドが動かない時

まあ、恒例のトラブル発生ですよ。

せっかくRaspberryPi zeroにgcloud sdk導入したのに
cronで動かそうとしたら動かなくて。

一つは時間設定が日本時間になってなかった。
これは簡単に解決。

[Raspberry Piの時刻を合わせる - Qiita](https://qiita.com/msrks/items/d6f7fa721399db4fe8d9)

```
Dec 28 06:00:01 raspberrypi CRON[2959]: (pi) CMD ([2960] /usr/bin/gcloud compute instances start <instance>)
Dec 28 06:00:17 raspberrypi CRON[2959]: (CRON) error (grandchild #2960 failed with exit status 1)
Dec 28 06:00:17 raspberrypi CRON[2959]: (pi) END ([2960] /usr/bin/gcloud compute instances start <instance>)
```

この状態だと何のエラーかわからないので色々検索してみた。

[Raspberry Pi Zero WHで作った定点カメラを改良したい②画像に日付を付ける – dalomo](https://dalomo.net/blog/2019/07/17/575/)

この記事を参考にcrontabのコマンドを変更

```cron
30 * * * * /usr/bin/gcloud compute instances start <instance> >> /tmp/test.log 2>&1
```

実行後覗いてみたところ

> ERROR: (gcloud.compute.instances.start) Underspecified resource [instance]. Specify the [--zone] flag.

というログが出てきた。
要はzone設定されてないからインスタンスが見つからんぞってことらしい。
[command line interface - ERROR: (gcloud.compute.instances.stop) Could not fetch resource - Stack Overflow](https://stackoverflow.com/questions/46882340/error-gcloud-compute-instances-stop-could-not-fetch-resource)
みたいなことがこの記事に書いてあった。

> --zone を指定してやればいいみたいでもある

なので
cronを

```
30 * * * * /usr/bin/gcloud compute instances start <instance> --zone asia-east1-a >> /tmp/test.log 2>&1)
```

に変更

```
Starting instance(s) <instance>...
.......done.
Updated [https://compute.googleapis.com/compute/v1/projects/<project_name>/zones/asia-east1-a/instances/instance].
Instance internal IP is 11.111.1.1.1
Instance external IP is 11.1.1.111.1
```

動いたー！

全然情報なくて一歩一歩解決に向けてやっていきました。
もし似たようなお困りごとの方のために備忘録として残します。
JavaScriptの配列操作でハマった話

つまらないことでハマったので備忘録を残します。

## やりたいこと
長さの決まった2次元配列（`arr`）に`arr[index]`でアクセスして値を更新したい。

## 問題が起きるコード

```javascript
(function(size){
    const arr = new Array(size).fill([]);

    // arr[index]にアクセスする何らかの処理
    for(let i=0;i<arr.length;i++){
        arr[i].push(i);
    }
    for(let i=0;i<arr.length;i++){
        console.log(arr[i]);
    }
})(10)
```

想像していた出力は以下の通りです。

```javascript
// debugger eval code
Array [ 0 ]
Array [ 1 ]
Array [ 2 ]
Array [ 3 ]
Array [ 4 ]
Array [ 5 ]
Array [ 6 ]
Array [ 7 ]
Array [ 8 ]
Array [ 9 ]
```

しかし、得られた出力はこちらです。

```javascript
// debugger eval code
Array(10) [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Array(10) [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Array(10) [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Array(10) [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Array(10) [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Array(10) [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Array(10) [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Array(10) [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Array(10) [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Array(10) [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
```

## 原因
完全に私の理解度の問題で`Array.prototype.fill`の挙動を勘違いしていました。
よく考えれば当たり前ですが、`fill`メソッドでは再帰的に新しいインスタンスを作成するのではなく、単純にオブジェクトの代入を行います。
上記のコードでは初期化部分に問題があり、初期化部分は以下のコードと等価になります。

```javascript
// 問題の起きるコード
const arr = new Array(size).fill([]);

// 問題の起きるコードと等価のコード
// size = 10
const ref = [];
const arr = [ ref, ref, ref, ref, ref, ref, ref, ref, ref, ref ];
```

基本仕様として、JavaScriptは値（`number | string | boolean` など）は値の代入、オブジェクトは参照を代入します。
上記のコードで何が起きるかというと、`arr[index]`で参照される値はすべて`ref`となってしまうため`arr[index].push`した場合は要素全てに再帰的に`push`したような挙動になります。

## 解決策
インスタンスを新しく作成すれば問題を回避できるので`Array.prototype.map`や`Array.prototype.filter`を使用すれば回避できます。(もっと簡潔な解決策があれば教えて下さい...)

```javascript
// 抜粋
const arr = new Array(size).fill(0).map(n => []);
```

```javascript
(function(size){
    const arr = new Array(size).fill(0).map(n => []);

    // arr[index]にアクセスする何らかの処理
    for(let i=0;i<arr.length;i++){
        arr[i].push(i);
    }
    for(let i=0;i<arr.length;i++){
        console.log(arr[i]);
    }
})(10)
```

## 参考
[1] MDN, `Array`, https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array
[2] MDN, `Array.prototype.fill`, https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/fill
AVI Utilでフレーム補間（2020年12月版）

Avi Utilでフレーム補間ができますね。

【フレーム補間前】
https://youtu.be/vZFyy5wTsy0

【フレーム補間後】
https://youtu.be/DO45Brl7LYs

とてもヌルヌルになるのですが、ネットでやり方を調べてもうまく行かない罠が結構ありました。
私がいま時点で成功した方法を備忘録的に記載します。

1.AVI UtilのDL
まぁ基本として。DLして下さい。
http://spring-fragrance.mints.ne.jp/aviutl/

2.AvisynthのDL
こいつも落としてインスコして下さい。
https://sourceforge.net/projects/avisynth2/

3.MVToolsのDL
こいつを落として、AviSynthの以下の場所に格納して下さい
https://www.avisynth.info/?%E3%82%A2%E3%83%BC%E3%82%AB%E3%82%A4%E3%83%96
```C:\Program Files (x86)\AviSynth+\plugins\mvtools2.dll```

4.warpsharpのDL
こいつを落として解凍し、中にあるavsinp.auiというファイルを、aviutl.exeと同じ階層に置いて下さい。
https://www.avisynth.info/?%E3%82%A2%E3%83%BC%E3%82%AB%E3%82%A4%E3%83%96

5.x264guiExをDL
この辺を参考に導入して下さい
https://aviutl.info/x264guiex-intro/

6.L-SMASH WorksをDL
この辺を参考にして入れてください
https://aviutl.info/l-smash-works/

7.avsファイルの作成
拡張子avsというファイルをメモ帳などで自作します。
なんか世の中には色々サンプルがありますが、私はこれでできました（("G:\1.mp4"のところは変換したいファイルのパスなので適宜書き換えて下さい）。

```directshowsource("G:\1.mp4") #変換する動画のパスに書き換える
ConvertToYUY2() 
super=MSuper(pel=4, hpad=0, vpad=0)
backward_1 = MAnalyse(super, chroma=false, isb=true, blksize=8, blksizev=8, searchparam=3, plevel=0, search=3, badrange=(-24))
forward_1 = MAnalyse(super, chroma=false, isb=false, blksize=8, blksizev=8, searchparam=3, plevel=0, search=3, badrange=(-24))
backward_2 = MRecalculate(super, chroma=false, backward_1, blksize=8, blksizev=8, searchparam=2, search=3)
forward_2 = MRecalculate(super, chroma=false, forward_1, blksize=8, blksizev=8, searchparam=2, search=3)
MBlockFps(super, backward_2, forward_2, num=60, den=1, mode=0)```

8.変換動画ソースを用意する
はい、インストールは終わりましたので変換したい動画ソースです。
これは「1920x1080」より大きいと失敗するようです。
必ずそこは守って下さい。

9.変換準備
8で用意した動画を7のファイルパスに設定して書き換え、AVI Utilを起動します。
そしてavsファイルをAVI Utilにドラッグドロップします。
AVI Utilに映像が映れば成功です（映らなければどこかで失敗している）

10.書き出す
AVI Utilの「ファイル」→「プラグイン出力」→「拡張x264出力」
これで時間がかかりますが書き出されます。

11.ヌルヌル！
ヌルヌルを楽しんで下さい！！
Laravel　sqlite手始め備忘録（前も書いたけど）

前にもララベルでのsqlite3の実行方法は書きましたが、改めてもう一度、まとめてといことで書きます。

<h1>sqliteのデータベースファイルの作成</h1>

まず、sqliteのデータベースのファイルの作成をします。Laravelのプロジェクトディレクトリに移動して、以下のコマンドでデータベースファイルを作成します。

```terminal:ターミナル
$type database/database.sqlite
```
sqlite3ではデータベースを作成する際は、ファイルを作成するだけでいいようです。


<h1>Laravelの設定ファイル.envの編集</h1>


今度は、Laravelからsqlite3に接続する際の情報が記載されている.envファイルを編集します。
DB_CONNECTIONを書き換え以下はコメントアウトしておきます。

```terminal:ターミナル
DB_CONNECTION=mysql　→　DB_CONNECTION=sqlite
# DB_HOST=127.0.0.1
# DB_PORT=3306
# DB_DATABASE=homestead
# DB_USERNAME=homestead
# DB_PASSWORD=secret
```


<h1>config/databaseの編集</h1>
今回自分はsqliteを利用するのでdafaultをmysqlからsqliteに変更します。

```terminal:ターミナル
  'default' => env('DB_CONNECTION', 'mysql'),　⇒  'default' => env('DB_CONNECTION', 'sqlite'),

```

<h1>マイグレーションの実行</h1>
```terminal:ターミナル
$php artisan migrate
```

<h1>実行結果</h1>

```terminal:ターミナル
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table
```

エラーが出なければOK。


間違っていることが大いにあり得るので賢い方ご指摘していただければと思います。よろしくお願いいたします。
アドベントカレンダー26日目：SREを読んでみた「時系列データからの実践的なアラート」編①

今回から待ちに待った実践編に入ります。

マズローの欲求階層にならい、プロダクション環境にも欲求階層があります。より高い階層の欲求を満たすためには下位の階層を満たす必要があります。
上位から、プロダクト、開発、キャパシティプランニング、テスト及びリリース、ポストモーテム/根本原因分析、インシデント対応、モニタリングがあります。

今回は、時系列データからアラートを通知する実践的な方法を学んでいきます。

モニタリングにより、インシデント対応へ正しいアプローチをすることができ、またサービスの達成度の計測を行うことができます。
大規模なシステムにおいて、モニタリングが難しいことにはいくつか理由があります。
例えば、以下のような場合があります。

> * **分析の対象となるコンポーネント数そのもの。**
* **メンテナンスの負荷を適度に低く保たなければならない**

大規模なシステムにおけるモニタリングシステムは、１台のマシンの障害でアラートをだすことはできません。なぜなら、多すぎるアラートは対応することができないためです。その代わりにシステム自体に障害に対する耐性をもたせることが重要です。
モニタリングシステムにおいては、サービスに関する高レベルのアラートを出しながらも、個々のコンポーネントの調査もできることが求められます。
Googleにおいては、10年にわたり、モニタリングシステムが進化してきました。

## Borgmonの誕生
Borgmanは、Borgを補完するために作られたモニタリングシステムです。

Borgmanでは、共通のデータ公開フォーマットを利用します。これによりデータ変換などのオーバーヘッドをなくすことができます。データは、グラフの描画やアラートの生成に使われ、通常はクラスタごとにBorgmanを起動します。

Borgmanでは、単一のターゲットのすべてのメトリクスの収集を１回のHTTPのフェッチで行えるようになっています。以下は、取得時の例です。

```shell
curl http://webserver:80/varz
http_requests 37
errors_total 12
```

## アプリケーションのインスツルメンテーション
/varzのHTTPハンドラは単純に、１行につき１つの変数をキーと値を空白で区切り、すべてのエクスポートされた変数をプレーンテキストで返します。後にマップ方変数の機能が追加されたので、エクスポートする側がいくつかのラベルを変数名に対して定義し、値のテーブルやヒストグラムをエクスポートできるようになる。
以下に示すのは、25個のHTTP200のレスポンス、12個のHTTPのレスポンスを含むマップ型です。

```
http_responses map:code 200:25 4040:0 500:12
```

プログラムにメトリクスを追加したい場合は、メトリクスが必要なところで1つの宣言をコードに追加すればできます。
変数の定義とBorgmonにおける変数の利用が分離されているので、変更管理に注意が必要になります。
しかし、これは後に、ルール生成と検証を行うツールが作成され、解消されました。

## まとめ
* プロダクション環境の欲求階層は重要。
* Borgmanというモニタリングシステムが作られた。

#### 参考文献
1. Betsy Seyerほか　SRE サイトリライアビリティエンジニアリング　オライリー　113-116
Postmanを使ってみて便利やなっと設定した事


## はじめに
[Postman](https://www.postman.com/downloads/)を使っていて毎回 **History** から呼び出して使っていて面倒。
何かイイ方法ないんかなっと調べて現状落ち着いた使い方を共有
皆さんはどんな感じで便利設定っとかしてはるんかなと。
より良い楽出来る設定など教えて頂けると幸いです。

※ 添付画像：ダーク😎でやってます。

### できること
- 試しでデータの挙動を確かめる時に便利
- ターミナルで打たんでイイんで自分は結構好き

## やったこと
 - **Collection**でフォルダ作成：各APIを格納しておく
 - 軽く説明入れておく
 - Headers にtoken 2パターン入れておく

![Postman 2020-12-28 .png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/898763/6dc127a0-36dc-2cd2-bd99-7d8b78ed7e37.png)

#### Collectionでフォルダ作成：各URLを格納しておく

- `+ New Collection`をクリック
- Name, Description(なくても良さそう)を記入し右下の`Create`で新規作成

![Collection -create.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/898763/e91bc61f-8736-9649-77c8-a45fd87ed2f2.png)

![ Postman-create-folder.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/898763/3b8d7c6b-a7c8-0e70-1dc2-509db2cd9f84.png)

##### 保存するまでの流れ

1.保存しようとすると、**SAVE REQUEST** として入力項目が出てくるので入力
![save-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/898763/3309bf76-7628-bbe1-6541-65eec8b1f8ef.png)

2.項目埋めたらSave出来るようになるのでクリック
![save-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/898763/51ba395c-9667-7d7c-352e-b2ad5c5a24e0.png)

3.指定した **Collection**に格納されていることを確認
![save-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/898763/60087041-2c68-5cab-de76-b7458c144e91.png)

4.対象ファイルをクリックすればお手軽に呼び出し可能
![save-4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/898763/0c5d621e-9f89-d27b-3f66-688ae47741dd.png)

5.後は試したAPIをSubフォルダ作成して, 管理しておくなり後でも見やすいかと

```bash
Collection名　　　　　　　　  # 開発しているプロジェクト名に合わせると管理しやすいかと
   ├── auth                # 認証操作
   ├── article　           # 記事操作
   ├── draft　　            # 下書き操作
   ├── current/article     # ログインしての記事操作
   └:...
```

6.常時上書きも可能なので、ちょっと変更した場合も良しなに対応可能


#### 軽く説明入れておく
* MarkDown で書けるので結構好き
* どんなAPIか忘れても、説明入れておけば、アーっそんな感じで使ってたなーと思い出せる。

#### Headers にtoken 2パターン入れておく
* ログインユーザを複数で試す時に結構楽
* 使いたいKEYだけにチェックボックス入れておけば、同じKEYが重複していても問題ない


## まとめ
* 何回も同じAPIを試すなら、格納しておくのが楽。
* **History** から探して試すより**イイ**!!
* 欲をいうと**VALUE**のコピペが面倒なんでドラッグで他のAPIに渡せるのが理想。自分がやり方見つけられていないだけかも
 - ログインしたユーザの**Headers**情報を使い回す時とか
 - 記事作成/削除/上書きなど結構な**イライラ度**


## 参照
* ダウンロードページ：https://www.postman.com/downloads/
ムービーをランダムに一定時間ずつChromeCastで再生する

ChromeCastはほっとけばランダムにGoogle Photosに溜めた写真を流してくれるので、家族で思い出話ができて嬉しい。ただ、写真だけしか流してくれないので、何とかムービーのほうを流すようにならないかなと思っていたけど、一向に機能追加されないorz。仮にスマホのアプリでムービーをキャストするのがあっても、Youtubeのプレイリストからランダムに全部再生する機能だけで、これらのアプリでは長時間のムービー（お遊戯会のムービーとか）が流れ始めたら最後、全部流すまで終わらないので、これじゃない感がある。pychromecastを使えば簡単にmp4を再生できそうだったので、サクッと作ってみた。

## 事前準備
- Linux環境 （自分はWSLを使ってる）
- HTTPサーバ（自分はapacheを使ってる）
- python3

## 動画の準備
WSLで見える適当なフォルダにムービーを入れておく。1,000個くらい入れたかな。これらのムービーはChromeCastで直接再生できるフォーマットである必要がある。対応していないムービーはffmpegで変換しておいて、タイムスタンプを揃える。
(下記は拡張子.MOVのムービーを.m4vの拡張子を使って変換して保存している。)

```
for i in *.MOV; do ffmpeg -y -i $i -s 1920x1080 -vcodec libx264 -r 30 -b 1024k -acodec aac -ac 2 -ar 44100 -ab 128k -f mp4 `basename $i .MOV`.m4v; done

for i in *.MOV; do day=`date '+%Y%m%d%H%M' -r $i`; touch -t $day `basename $i .MOV`.m4v; done
```

それらのムービーファイルをhttpサーバで表示できるように設定する。apacheなら、/var/www/html以下にシンボリックリンクを作っておけばOK。

## ムービーをランダムに選んで、２０秒間ランダムな時間から再生を開始するプログラム

```
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from hachoir.metadata import extractMetadata
from hachoir.parser import createParser
import time
import pychromecast
import random
import os
import datetime
from datetime import datetime as dt

playtime=20
waittime=3 #ChromeCastの動画切り替えにかかる時間分だけこのプログラムが待ってあげる時間
moviefolder="/mnt/e/movie/" #サーバ内でムービーを保存している場所
httpurl="http://192.168.1.10/movie/" #ChromeCastから見えるサーバ上のムービーの場所

# List chromecasts on the network, but don't connect
services, browser = pychromecast.discovery.discover_chromecasts()
# Shut down discovery
pychromecast.discovery.stop_discovery(browser)

# Discover and connect to chromecasts named Living Room
chromecasts, browser = pychromecast.get_listed_chromecasts(friendly_names=["Living"])
[cc.device.friendly_name for cc in chromecasts]

cast = chromecasts[0]
# Start worker thread and wait for cast device to be ready
cast.wait()
print(cast.device)
print(cast.status)

mc = cast.media_controller

stopflag=0
while stopflag == 0:
  tempfile=random.choice(os.listdir(moviefolder))
  print(tempfile)
  filepath = moviefolder+tempfile
  dt = datetime.datetime.fromtimestamp(os.stat(filepath).st_mtime)
  key = dt.strftime('%Y/%m/%d  %H:%M:%S')
  print(key)
  parser = createParser(filepath)
  meta = extractMetadata(parser)
  duration = meta.get("duration")
  print(duration.seconds)
  starttime=int(random.uniform(0,duration.seconds-playtime))
  if starttime < 0:
    starttime = 0
  mc.play_media(httpurl+tempfile, 'video/mp4', current_time=starttime)
  if duration.seconds < playtime:
    time.sleep(duration.seconds+waittime)
  else:
    time.sleep(playtime+waittime)
```

あとは、ターミナルを開いて、このプログラムを動かせば自動で２０秒間ランダムに選んだムービーを再生してくれる。子供たちが懐かしそうに見入ってた。たまに結婚式のムービーとか出てきてこういうアプリがないとなかなか見ないよなぁと思う。
pychromecastのお作法は結構変わるみたいなので、[本家HP](https://github.com/home-assistant-libs/pychromecast)を見たほうがよさそう。
実際には、PHPからキックして起動するようにして、ページを２０秒で自動リロードするようにしておいて、スマホでブラウザを開いている間だけ再生するようにして使っている。

## 参考にしたサイト

https://blog.sky-net.pw/article/26
【Ruby On Rails】特定のユーザーだけが編集ページに遷移できるようにする記述

備忘録です。

# 前提
MacOS Catalina
ruby 2.6.5p114
Rails 6.0.3.4
deviseを導入済（authenticate_user!というヘルパーメソッドを使用）

# 特定ユーザーだけを編集ページへ遷移させる記述

```ruby:hoges_controller.rb
class HogesController < ApplicationController
  before_action :authenticate_user!, only: [:edit]
  before_action :specified_hoge, only: [:edit, :update]
  before_action :specified_user, only: [:edit]

（中略）

  def edit
  end

  def update
    if @hoge.update(hoge_params)
      redirect_to root_path
    else
      render :edit
    end
  end

  private

  def hoge_params
    params.require(:hoge).permit(:text).merge(fuga_id: params[:fuga_id], user_id: current_user.id)
  end

  def specified_hoge
    @hoge = Hoge.find(params[:id])
  end

  def specified_user
    redirect_to root_path unless @hoge.user.id == current_user.id 
  end
end


```

上記のうち、ポイントはココ↓

```ruby:hoges_controller.rb
  before_action :specified_user, only: [:edit] 
```

```ruby:hoges_controller.rb
  def specified_user
    redirect_to root_path unless @hoge.user.id == current_user.id 
  end
```

before_actionを用いて
1. before_actionの後ろにメソッド名を任意の名前で名付ける。
2. そして、対応するアクション（今回はonlyオプションでeditアクション）を指定する。
3. メソッドに処理を書く。

このようにすることで、例え他のユーザーがURLを手動で書き換え編集ページへ遷移しようとしても、ホーム画面へリダイレクトさせることができる。

今回の場合、hogesテーブルに保存されているユーザーidと現在ログインしているユーザーidが一致しない場合はroot_pathへ遷移させることができます。
AWS EC2で、MemoryError

Pythonのpymagnitudeを使っていたところ..
こんなのがターミナルに出てきて、計算ができなくなりました。

```
MemoryError: Unable to allocate 1.51 GiB for an array with shape (1015474, 200) and data type float64
```

「インスタンスタイプの変更」で、今選んでるインスタンスのメモリより多い物を選びましょう。
そしたら解決しました。

以下のサイトにわかりやすく解説が載ってます。どうぞ。
https://www.atmarkit.co.jp/ait/articles/1407/15/news005.html
Unity ShaderGraph レシピ #11 薄膜シェーダ（テクスチャマップ版）

#MembraneShader（Texture）

![gif_animation_011.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198311/3d5155f0-7368-54f2-9e90-d30993b23e36.gif)

[薄膜シェーダ（アルファ値版）](https://qiita.com/Teppy/items/f307004241cf97539fac)よりも、グラデーションを任意に設定できるので表現力豊かに作ることができます。

#作り方
トゥーンシェーダと薄膜シェーダの作り方のアプローチの合わせ技です。
任意のグラデーションのテクスチャ（MembraneMap）を用意し、エッジ抽出の結果をUVとして利用します。

### 1.エッジの抽出
視線ベクトルとメッシュの法線の内積の結果から輪郭に近い頂点かどうかを算出します。
詳しくはリムライトや薄膜シェーダ（アルファ値版）で説明しています。
![スクリーンショット 2020-12-28 16.33.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198311/7a6b1bf0-372a-eab1-53da-08dc805f017c.png)

### 2.薄膜用テクスチャでマッピング

![スクリーンショット 2020-12-28 17.59.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198311/36d90b2c-7906-d8c3-3edc-3fc3b0435291.png)


### 3. 加算半透明
BlendをAdditiveにしましょう。
![スクリーンショット 2020-12-28 17.58.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198311/ea2f0c7a-d12a-0996-7351-37a96555af14.png)



###完成！
MembraneMapを変えることで様々な表現になります。

![membrane01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198311/797bb024-1c54-6dc9-f8d7-7190ee13d2e9.png)
![gif_animation_011.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198311/3d5155f0-7368-54f2-9e90-d30993b23e36.gif)

![membrane02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198311/1f5d4fa1-3324-f811-b529-9c76f6f554d6.png)
![gif_animation_012.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198311/4044858d-ba70-99c1-bfbf-f949fbfb6274.gif)

![toon3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198311/fad9a3f8-adb5-540f-4fd8-8ac866d87b58.png)
![gif_animation_013.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198311/74d36faa-4100-4388-d576-7247bb4fb6aa.gif)

---
その他のレシピは[ShaderGraphレシピ一覧](https://qiita.com/Teppy/items/73fc6141751a62f5265d)にまとまっています
ubuntu 20.04 + Geth環境のether送金

ubuntu 20.04 + Geth1.9.25(Go-Ethereum)環境でのether送金手順をまとめる。

# EOAアカウントの所有etherの確認
`> eth.getBalance(eth.accounts[NUmber])`コマンドで、送金元・先のEOAアカウントのether持ち高を確認しておく。

```
> eth.accounts
["0x911e18204582a6de08dd2b1f7540b9167ce93b67", "0x634c921bfbad7756c7988ea74e4bdf5e5387620d"]
>
> eth.getBalance(eth.accounts[0])
2.105e+21
> eth.getBalance(eth.accounts[1])
100000000000000000000
```

# アカウントロック解除
ether送金時にはアカウントロックを解除する必要がある。`personal.unlockAccount(EOAアカウントのアドレス)`コマンドでロックを解除する。アンロックの時間はデフォルト300秒でタイムアウトし、ロックされる。

```
> personal.unlockAccount(eth.accounts[0])
Unlock account 0x911e18204582a6de08dd2b1f7540b9167ce93b67
Passphrase:
true
>
```

コマンドは、以下のようにオプションを拡張することが可能。アンロック時間に0を指定すると、ロックされない。`personal.unlockAccount(EOAアカウントのアドレス, "パスワード", "アンロック時間(秒)")`

# etherの送金
送金時は採掘処理が実行されている必要がある。プライベートネットワークであり、自身以外に採掘者がいないため、採掘を実行する。

```
> eth.hashrate
0
> eth.mining
false
> miner.start()
null
> eth.mining
true
> eth.hashrate
2619
>
```

ロック解除した`account[0]`から`account[1]`へ送金する。送金単位はweiの単位指定となるので、単位変換関数であるweb.toWeiを使う。実行結果としてトランザクションIDが返される。

```
> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei(5, "ether")})
0xf03e4882a766d3aa4d3b188bbe191721be63ed2233792e7fcb1d5b7e1dd6b2eb
```

トランザクションの内容は`eth.getTransaction('トランザクションのアドレス')`コマンドで確認することが出来る。

```
> eth.getTransaction('0xf03e4882a766d3aa4d3b188bbe191721be63ed2233792e7fcb1d5b7e1dd6b2eb')
{
  blockHash: null,
  blockNumber: null,
  from: "0x911e18204582a6de08dd2b1f7540b9167ce93b67",
  gas: 21000,
  gasPrice: 1000000000,
  hash: "0xf03e4882a766d3aa4d3b188bbe191721be63ed2233792e7fcb1d5b7e1dd6b2eb",
  input: "0x",
  nonce: 0,
  r: "0xda879400e1472aaed815371ab259874a0303f17d732e83959345f4322b5c8195",
  s: "0xc89b9f49f14fa35778b84164eb2a39e34f004ede08044ddc257d332ba7f8fca",
  to: "0x634c921bfbad7756c7988ea74e4bdf5e5387620d",
  transactionIndex: null,
  v: "0x42",
  value: 5000000000000000000
}
>
```

しばらく時間がかかるようで、送金確認後に更新したいと思う。
PL/pgSQLとSQLのメモ

## 説明
これを見ればすべてが思い出せる。
そんな風に使いたい自分用メモです。

## 環境
| モノ     | バージョン                        |
|:-----|:-----------------------|
| OS   |Windows10 Home（64bit） | 
| PostgreSQL   |9.4.12 | 
※localはCで、それ以外は全部でデフォルト

## 前提
https://www.postgresql.jp/document/9.4/html/plpgsql.html

## ログ関係
とりあえずパラメータ確認

```SQL
SHOW client_min_messages;
SHOW log_min_messages;
```

ログには出したくない。画面(client)で見れればいい。
そんな時は、NOTICEにしておけば、logがWARNINGなら出ないぞ。

| 対象 | 優先順位 |
|:-----|:-----------------------|
| client|DEBUG5、 DEBUG4、DEBUG3、DEBUG2、 DEBUG1、LOG、NOTICE、 WARNING、ERROR、FATAL、PANIC | 
| log|DEBUG5、DEBUG4、 DEBUG3、DEBUG2、DEBUG1、 INFO、NOTICE、WARNING、 ERROR、LOG、FATAL、PANIC| 

※LOGの順序が違うので注意ね
※ログが文字化けしたら別問題
※ログはpowershellでtailしとく

```:powershell

Get-Content -Path "C:\Program Files\PostgreSQL\9.4\data\pg_log\aaa.log" -Tail 0 -Wait -Encoding UTF8

```



## 無名ハロワ

```SQL
DO
$$
BEGIN
 RAISE NOTICE 'HELLO WORLD';
END;
$$
```

$$で囲まれたのが本体

## ハロワ関数

```SQL
CREATE OR REPLACE FUNCTION helloWorld()
RETURNS void AS $$
BEGIN
 RAISE NOTICE 'HELLO WORLD';
END;
$$ LANGUAGE plpgsql
```
※PROCEDURE オブジェクトは11から。

## 実行手順
* pgAdmin→SQLエディタ
* Terminal
    * ログインしつつ `psql -U postgres -f hoge.sql`
    * ログインの後に ` \i hoge.sql`

## その他

### １０万回サイコロ
```SQL
SELECT num
    ,  COUNT(*)
  FROM (
        SELECT format('Dicing eyes %s' ,(floor(random() * 6) + 1 )::int ) AS num
          FROM generate_series(1,100000) 
   ) as foo
 GROUP BY 1
 ORDER BY 1
```
|num|count|
|:-----|:-----|
|Dicing eyes 1|16593|
|Dicing eyes 2|16657|
|Dicing eyes 3|16573|
|Dicing eyes 4|16567|
|Dicing eyes 5|16665|


### 集約関数
```SQL
WITH t(no,word) AS (
    SELECT '1','リンゴ' UNION ALL
    SELECT '2','ゴリラ' UNION ALL
    SELECT '3','ラッパ' 
) 
SELECT string_agg(word,'→' ORDER BY no) 
    ,  array_agg(word)
  FROM t 
```

| 結果|
|:-----|
|リンゴ→ゴリラ→ラッパ   |
|{リンゴ,ゴリラ,ラッパ} |

### 配列関数
```SQL
WITH t(word) AS (
    SELECT ARRAY['リンゴ','ゴリラ','ラッパ']
) 
SELECT UNNEST(word)
  FROM t 
```

| 結果|
|:-----|
|リンゴ|
|ゴリラ|
|ラッパ|
Unity ShaderGraph レシピ #10 薄膜シェーダ(MembraneShader) Alpha値バージョン

#MembraneShader(Alpha)
![gif_animation_010.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198311/75d957ab-91af-5192-8a70-ecabcab1bbb6.gif)

輪郭部分から中心に向かうにしたがって透明度が上がっていく表現です。
「おばけ」とか「レントゲン」とかそういう雰囲気を簡単に出すことができます。
今回説明するのはとにかく簡単につくれるレベル１バージョンです。

#作り方

### 1.エッジを抽出してアルファ値とする
エッジの抽出方法はリムライトの回で説明した方式ままです。
視線ベクトルとメッシュの法線で内積を取って判定します。
その結果をアルファに入れるだけです。

![スクリーンショット 2020-12-28 16.44.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198311/faafee4d-4988-36f1-cd27-c63fbc5960b6.png)

### 2.EdgePowerの中身
[リムライトの記事](https://qiita.com/Teppy/items/1c463acba460ee3c5856)と同じですが一応貼っておきます。
![スクリーンショット 2020-12-28 16.33.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198311/6d28978f-b8cf-005b-ca7d-605c5ff947ce.png)


###完成！
![gif_animation_010.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198311/75d957ab-91af-5192-8a70-ecabcab1bbb6.gif)

---
その他のレシピは[ShaderGraphレシピ一覧](https://qiita.com/Teppy/items/73fc6141751a62f5265d)にまとまっています