OmniAICreator/Qiita-1.07M huggingface dataset:
【iOS】アーキテクチャ MVVM 解説してみた！

# 目的
iOS開発における MVVM について知ること

(キーワード: `データバインディング`, `リアクティブプログラミング`, `RxSwift`, `Combine`)


<img width = 800 src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/764557/89aed054-28a1-a7f1-abc7-2674fbdfe8bd.png">

# MVVM とは？
`データバインディング`という仕組みを使用したアーキテクチャです。(データバインディング👉超重要)

構造は `M: Model` `V: View` `VM: ViewModel` となる。
**画面の描画 = View** と **プレゼンテーションロジック = ViewModel** を分離するためのアーキテクチャ。GUIアーキテクチャの1種。

ViewとViewModelを`データバインディング`という仕組みで関連づける。
ViewModelの状態変更に同期してViewの状態も更新され、画面に反映される。


# データバインディングとは？
2つのデータの状態を監視し同期する仕組みのこと。
ViewModel のデータ変更を View が検知してデータを自動的に更新する。(逆もあり得る)
View, ViewModel の関係性が1方向の場合もあれば、双方向の場合もある。


# なぜ データバインディング が使われるのか？
手続的な描画指示を書く必要がなくなり、宣言的なバインディングによって ViewModel自信の状態を更新するだけで Viewの描画処理が発火するため。


# iOS開発で MVVM が使われる理由
関数型リアクティブプログラミング(Functional Reactive Programming: FRP)(※1)との相性が良いから。

FRP に特化した外部ライブラリとして `RxSwift` がある。
`RxSwift` を使用することで宣言的な記述をしながら複雑なバインディングを表現できる。

(※1): FRP は Modelの変更 や Viewの変更 が相互接続されてリアクティブに反応しあえる点と、手続き的ではなく宣言的にロジックを表現できる点 が評価されている


# MVVM の構造

<img width = 800 src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/764557/89aed054-28a1-a7f1-abc7-2674fbdfe8bd.png">

### M: Model
UIに関連しない純粋なドメインロジックやそのデータを持つ。
MVC, MVP といった MVVM以外のGUIアーキテクチャの Model と同じ役割。
View や ViewModel がなくてもビルド可能である必要がある。

### V: View
ユーザー操作の受付と、画面表示を担当する。
ViewModel が保持する状態とデータバインディングして、ユーザー入力に応じて ViewModel が自身が保持するデータを加工・更新することで、バインディングした画面表示を更新する。

### VM: ViewModel (最重要)
3つの役割がある。
- View　に表示するためのデータを保持すること
- View　からイベントを受け取り、Modelの処理を呼び出すこと
- View からイベントを受け取り、加工して値を更新すること

画面表示のために必要な 状態 と ロジック を担うのが ViewModel である。


## 補足
次は RxSwift を使用して MVVM のプログラムを作成してみたいと思います！
LGTM, ストック してもらえると、とても嬉しいです 🙌

## 参考資料
[1], iOSアプリ設計パターン入門 (電子版: 税込3240円)

https://peaks.cc/books/iOS_architecture
SQLの句のまとめ

```
SQL
	1. 列指定はSELECT、行指定はWHERE句の後
	2. SELECTとかは縦に書いて行った方がわかりやすい
```

```
WHERE句
	1. WHERE句で>とか<を使って指定することができる（例：WHERE ID < 10 IDが10よりも小さい行を取得する）
```
```
ORDER BY句
	1. 並び替えはORDER BY ASC　昇順（昇っていくから1から）　デフォルトだから省略可能
	2. ORDER BY DESC　降順
	3. ORDER BY  ID はIDの昇順
```
```
GROUP BY句
	1. カラムをグループにしてなにかできる？
	2. SUM()と使うことが多いのか？
	3. 同じカラムをまとめて合計を出すことに使われることが多い。商品とか。タオルがいくつとか。
	4. カラムを指定せずにcount(*)を使うとテーブルの全件数を知ることができる。よく使うから覚えておいて損はない。
```
```
HAVING
		1. グループ化された中からさらに条件指定して検索する
		2. SELECTとかHAVINGと呼ばれる優先順位とかあるから要注意
```
```
JOIN
		1. 内部結合と外部結合がある
		2. INNER JOIN は内部結合
		3. LEFT JOIN、OUTER JOINは外部結合
```
```
リストア
		1. データベースのバックアップを取ること
		2. ファイルとして出力することをデータベースをdumpするという言い方をする
		3. ターミナルでファイル名からデータベースを復元するのをリストアと言う
```
```
LIKE演算子
		1. LIKEのあとは''が必要。その中にパターンマッチング条件を入れていく。%とかも。
		2. WHERE name LIKE '%田%'　なぜWHEREのあとなのかがわかった。WHEREのあとは条件を入れるところだから、nameが田のような行っていう意味の条件になる。
```
```
	IN句
		1. IN()の中にある値のいずれかに当たる行を取得
```
'bits/stdc++.h' file not foundの対処法



```c++
#include <bits/stdc++.h>
using namespace std;

int main()
{
	cout << "Hello, bits!" << endl;
	return 0;
}
```

上記コードをコンパイルすると以下のエラーが発生しました。

```bash
$ clang++ test.cpp
test.cpp:1:10: fatal error: 'bits/stdc++.h' file not found
#include <bits/stdc++.h>
         ^~~~~~~~~~~~~~~
1 error generated.
```

bits/stdc++.hファイルが存在していないというエラーが出ています。

以下解決策です。
## 1. `clang -v` でデフォルトで参照するパスを確認する

```bash
$ clang++ -v test.cpp 
Homebrew clang version 13.0.1
Target: x86_64-apple-darwin21.2.0
Thread model: posix
InstalledDir: /usr/local/opt/llvm/bin
... 省略 ...
#include "..." search starts here:
#include <...> search starts here:
 /usr/local/opt/llvm/bin/../include/c++/v1
 /usr/local/Cellar/llvm/13.0.1/lib/clang/13.0.1/include
 /Library/Developer/CommandLineTools/SDKs/MacOSX12.sdk/usr/include
 /Library/Developer/CommandLineTools/SDKs/MacOSX12.sdk/System/Library/Frameworks (framework directory)
... 省略 ...
```

`#include <...> search starts here:` 以下に参照しているディレクトリが表示されています。

## 2. パスにbits/stdc++.hを作成する

1.で確認したパス内にbits/stdc++.hを作成します。

[https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/precompiled/stdc%2B%2B.h](https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/precompiled/stdc%2B%2B.h)
こちらからソースをコピーして貼り付けます。

```bash
$ cd /usr/local/opt/llvm/bin/../include/c++/v1
$ mkdir bits
$ touch bits/stdc++.h # ここにソースを貼る
```

めでたくこれで動きました。
```bash
$ clang++ test.cpp
$ ./a.out
$ Hello, bits!
```

## 参考

[gcc コンパイルオプション備忘録](https://qiita.com/seriru13/items/c2f5192615162c4c3f47)
dockerでapt-get update -qqをしたら失敗した話

## 起こった事象
- docker-compose run *** でコマンドを実行した際に次のエラーが発生した。

```
GPG error: http://security.debian.org/debian-security bullseye-security InRelease: At least one invalid signature was encountered.
```

- Dockerfileの２行目でエラーになっていた。
```
1. FROM ruby:2.7.5
2. RUN apt-get update -qq
...
```

## 試したこと
- docker image prune
- docker container prune
下記を参考
https://qiita.com/yukia3e/items/6e2536dd90d34a8b01cc

- --no-cacheでbuild
下記を参考
https://nice-mikan.hatenablog.jp/entry/2020/07/30/013517

## 解決方法
- 最終的に下記の方法で解決しました。
docker pull ruby:2.7.5

下記のサイトの一番最初に書いてあったのですが、見落として
色々回り道してしまいましたが、元イメージが最新でなかったのが
原因でdockerから最新のイメージをpullすれば良いという
単純な結論でした。

下記を参考
https://nice-mikan.hatenablog.jp/entry/2020/07/30/013517


## 参考
https://qiita.com/yukia3e/items/6e2536dd90d34a8b01cc

https://nice-mikan.hatenablog.jp/entry/2020/07/30/013517
ピアリングでRDSへの接続が上手くいかないときのチェック項目

EC2から別のVPCにあるRDSのMySQLへログインしようとして、VPCのピアリング設定しましたが、RDSへログインできなくて悪戦苦闘しました。
たいていの場合は、単純な見落としなので、整理すれば解決すると思いチェック項目を表に残します。
僕の場合は、ルーティングテーブルの設定漏れでした。

||サービス|項目|内容|
|:--:|:--:|:--|:--|
|1|VPC|ピアリング|リクエスタ→EC2のあるVPCに設定、アクセプタ→RDSのあるVPCに設定|
|2||ピアリング|作成後に承認していない|
|3||ルートテーブル|EC2側→RDSのあるVPCへのルート記載漏れ、RDS側→EC2のあるVPCへのルート記載漏れ|
|4||ルートテーブル|EC2・RDSのあるサブネットの関連付け漏れ|
|5||セキュリティグループ|RDS側→インバウンド:TCP:3306の記載漏れ、EC2側→アウトバウンド:すべてのトラフィックの記載漏れ|
|6||VPC|EC2のあるVPCのDNS解決→有効化|
|7||ピアリング|アクセプタのDNS解決→有効化|
|8|EC2|EC2|VPCとサブネットグループの取り間違え|
|9||EC2|セキュリティグループの取り間違え|
|10|RDS|サブネットグループ|VPCとサブネットグループの取り間違え|
|11||RDS|セキュリティグループの取り間違え|
|12||RDS|マスターユーザーとマスターパスワードの間違え|

```sh:ec2内
$ nslookup database-1.c66gddgkm8ok.ap-northeast-1.rds.amazonaws.com
# Error
# => 名前解決ができていない → 6、7

$ mysql -u admin -p -h xxxx.xxxx.ap-northeast-1.rds.amazonaws.com
# ERROR 2003 (HY000): Can't connect to MySQL server on 'xxx.xxx.xxx.xxx' (110)
# => ルーティングorファイアーウォールの設定ができてない → 3、4、5 など

$ mysql -u admin -p -h xxxx.xxxx.ap-northeast-1.rds.amazonaws.com
# ERROR 1045 (28000): Access denied for user 'admin'@'xxx.xxx.xxx.xxx' (using password: YES)
# => ユーザー名orパスワードが間違っている → 12
```
ターミナルからcodeコマンドを使ってVSCodeを起動する方法

今まで`code`とすればVSCodeを開くことができていたのに、急にできなくなったので
VSCodeのドキュメントを見ながらターミナルから`code`コマンドを使ってVSCodeを呼び出せるように設定していく。

## インストール方法
これは普通にインストールするだけなので割愛

## コマンドラインからの起動
VSCodeをパスに追加した後、`code`と入力することでターミナルからVSCodeを起動することができるようになる。

1. VSCodeを起動する
1. コマンドパレット（com + Shift + P）を開き、`shell command`を探す
1. `code`コマンドをPATHコマンドにインストールする
1. ターミナルを開き、開きたいディレクトリまで移動し、`code .`

## なぜcodeコマンドが使えなくなっていたのか
![スクリーンショット 2022-02-26 17.20.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601125/0c5dfb0d-2a4e-3a9c-297f-4907ee82d5ff.png)
何度か同じような手順でcodeコマンドを登録していたつもりだったのですが、上記画像を見るとインストールとアンインストールがあり、PATHから自分でcodeコマンドを削除していたことに気がつきました。
conda環境で作成したSpleeterをJavaから無理やり呼びだして処理した

## 事の発端
Javaのプロジェクトで作業をしているときに音と声を分けたい処理が出てきた。
調べてみるとSpleeterがいいらしく、言語はPythonだった。
今更JavaからPythonに書き換えるのは辛いため、どうにかならないか？
そうだ、無理やり呼びだしてみよう。

## 環境
Amazon Corretto 17.0.2
conda 4.11.0 (Python 3.7)
Windows 10 Home 64bit

## Anaconda + Spleeterの構築ファイル
こちらの記事を参考に作成。
[Qiita: 一番簡単なWindowsによるSpleeter使用環境構築](https://qiita.com/east_1106/items/7281c4cb83c9c2be0d86)

```yaml:spleeter-cpu.yaml
name: spleeter-cpu

channels:
  - conda-forge
  - anaconda

dependencies:
  - python=3.7
  - tensorflow=1.14.0
  - ffmpeg
  - pandas==0.25.1
  - requests
  - pip
  - pip:
    - museval==0.3.0
    - musdb==0.3.1
    - norbert==0.2.1
    - spleeter==2.1.1
```

## Javaから呼び出し
プロジェクトが割とハードコードでも問題無い場所だったため無理やり実装をする。
手順としては`ProcessBuilder`で`Process`を作成した後、condaのバッチファイルを読み込み`Process`を無理やりconda環境で動かす。
```java
ProcessBuilder builder = null;
Process process = null;
// 以下パスは各自置き換えてください
File path = new File(System.getProperty("user.dir") + File.separator + "spleeter");
File audioFile = new File(path, "audio.wav");
// ここのconda.batも
builder = new ProcessBuilder("D:\\conda\\conda.bat", "activate", "spleeter-cpu", "&&", "spleeter", "separate", audioFile, "-o", "./", "-p", "spleeter:2stems");
builder.directory(path);
builder.redirectErrorStream(true);
try {
    process = builder.start();
    InputStreamReader isr = new InputStreamReader(process.getInputStream(), "UTF-8");
    BufferedReader r = new BufferedReader(isr);
    while (true) {
        Thread.sleep(100);
        String line = r.readLine();
        if (line != null) {
            // Spleeterログ出力
            System.out.println(line);
        }
    }
    if (!process.isAlive()) {
        break;
    }
    process.waitFor();
    process.destroy();
} catch (Exception e) {
    e.printStackTrace();
}
```

## まとめ
無理やり動かすことはできる。
現状プロジェクトでは特に問題は発生していないが`Process`を使ってる以上別の問題が発生してくると思う。
そこが怖い為、別の方法を模索してみようと思う。
自宅にKubernetes環境を構築し開発中のアプリケーションをデプロイ、helmでパッケージ導入

kubernetesを触りたい時、AWSやGCPは無料枠が少なく月額料金も案外高いため、自宅に余っているWindowsPCにkubernetesを導入した。そこに開発中のアプリケーションをデプロイしたりhelmパッケージでファイル共有サーバーなどを導入できるようにした。

## はじめに
自宅の余っているデスクトップやノートパソコンにkubernetes環境を導入し、イントラ内で開発中のアプリケーションをデプロイしたりワンコマンドでファイル共有やバッチ処理、性能監視などのサービスを立ち上げる。

## TL;DR
Windows上にWSL2でUbuntuをインストール。次にkubernetesを構築するためにminikubeとパッケージ管理のためにhelmを導入。Ingress-ControllerでL7レベルでリバースプロキシにより各ポッドのサービスへルーティングしてサービスを外部へ公開。

1. WSL2でUbuntuを導入、Dockerのインストール
2. minikubeの導入
2. kubectlの導入
2. kubectlでminikubeへアクセスできるか確認
3. Helmを導入
4. クラスタ内のサービスへルーティング

## 実現できたこと
自宅で余っているWindows PCにkubernetesを構築し、開発中のアプリのデプロイやファイル共有サーバーなどのHelmパッケージを導入。


#### helmで導入したパッケージ例
- minio: ファイル共有
S3と互換性のあるminioを導入。ネットワーク内のPC間のファイル共有サーバーに。ブラウザやコマンドラインからアクセス可能。
- airflow: バッチ処理
定期実行したい処理（朝スケジュールを確認してslackへ送るなど）の実行、機械学習などの重たい処理の実行
- harbor: イメージリポジトリ
自宅でdocker imageをbuildして他のサーバーからも参照したい時に、Docker Hubへインターネット経由でアップロードすると容量も大きく時間もかかるため、自宅のイントラにイメージリポジトリを設置。
（その他）
Keycloak: 共通のアカウント認証、gitlab: イントラのgitリポジトリ、Prometheus+grafana: サーバーの性能監視　など

## Windows PCにkubernetes環境を構築
### 1. WSL2でUbuntu20.04を導入、Dockerのインストール

- Windows docsを参考にWSL2でUbuntuを導入

https://docs.microsoft.com/ja-jp/windows/wsl/install

- Dockerをインストール(以下を実行)
```bash
sudo apt-get update -y && sudo apt-get upgrade -y
sudo apt install apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable"
sudo apt update && sudo apt install docker-ce
```

### 2. minikubeの導入
```bash
curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 && chmod +x minikube
sudo mkdir -p /usr/local/bin/
sudo install minikube /usr/local/bin/
```

### 3. kubectlの導入
```bash
curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x ./kubectl
sudo mv ./kubectl /usr/local/bin/kubectl
```

### 4. kubectlでminikubeへアクセスできるか確認
```bash
sudo service docker start
minikube start --vm-driver=docker
kubectl get node
```

### 5. Helmを導入
Helmはkubernetes上へサービスを展開することができるコマンドで、１コマンドでサービスが立ち上がる。
```bash
curl -O https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3
bash ./get-helm-3
```

### 6. クラスタ内のサービスへルーティング
Kubernetesはマイクロサービスアーキテクチャと相性が良く、Kubernetes上でさまざまなサービスを疎結合で作成し、それらをAPIなどを経由して連携させることが可能。
```bash
minikube addons enable ingress
```
Bashで改行区切りと空白区切りの相互変換

# はじめに
Bashを使用して行から列への展開、あるいは列から行への展開を行う方法を複数示します。
ここで、行とは改行で区切られた文字列、列とは半角空白で区切られた文字列を指します。
各コマンドの解説は致しません。

## 行から列へ
例として`seq 100`の結果を使用する。
```bash:入力データ
$ seq 100
1
2
3
4
5
...
(略)
...
98
99
100
```

```bash:期待結果①
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
```

列数を限定して列展開する方法も示します。（例では3列です。）

```bash:期待結果②
1 2 3
4 5 6
7 8 9
10 11 12
...
(略)
...
94 95 96
97 98 99
100
```

### awk
```
seq 100 | awk '{printf $0" "}END{print ""}'
seq 100 | awk '{printf $0" "}NR%3==0{print ""}'
```

### xargs
```
seq 100 | xargs
seq 100 | xargs -n3
```

### sed
```
seq 100 | sed -z 's/\n/ /g'
seq 100 | sed -zE 's/([^\n]+)\n([^\n]+)\n([^\n]+)\n/\1 \2 \3\n/g'
```

### paste
```
seq 100 | paste -sd " "
seq 100 | paste -d " " - - -
```

## 列から行へ
例として`seq 100 | xargs`の結果を使用する。
```bash:入力データ
$ seq 100 | xargs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
```

```bash:期待結果
1
2
3
4
5
...
(略)
...
98
99
100
```

### awk
```
seq 100 | xargs | awk '{for(i=1;i<=NF;i++){print $i}}'
```

### xargs
```
seq 100 | xargs | xargs -n1
```

### sed
```
seq 100 | xargs | sed 's/ /\n/g'
```

### tr
```
seq 100 | xargs | tr " " "\n"
```

# おわりに
他の方法あります、あるいは上の方法もっと改善できます、など教えていただけると幸いです。
[Dart] const vs final 


# 導入

const final どちらも変数を値を書き換えられないものということは理解できる。

``` dart
void main() {
  
  const int myConst = 1;
  final int myFinal = 2;
  
  myConst = 3; // error
  myFinal = 4; // error
}
```

どのような違いがあるか。

# const

定義時のみ定数を代入できる。

```dart:NG
void main() {
  
   const int myConst; //定義 //error
  
   myConst = 1; //代入
}

```


```dart:NG
void main() { 
  
  const DateTime myConst = DateTime.now(); // error
}
```

```dart:OK
void main() {
  
  final int myFinal = 2; //OK
}
```

# final

定義した後でも代入可能。
定数以外のインスタンス等も確保可能。

``` dart
void main() {
  
  final DateTime myFinal;  //定義
  myFinal = DateTime.now();  //代入
  
  print('myFinal: $myFinal'); //2022-02-26 16:23:54.138
}
```

# まとめ

| const | final |
|:-:|:-:|
|  定数**のみ**定義可能 | 定数に**限らず**定義可能  |
|  **定義時のみ**代入可能 | **定義後も**代入可能  |
GitHub CLIを使ってみる２


開発環境にローカルのリポジトリを作ります。

```
git init
```

次に

```
git add .
```
`git add`コマンドでコミットしたいファイルを「インデックス」あるいは「ステージングエリア」と呼ばれる領域に追加します。`.`は全てのファイルです。

```
git status
```
で確認してみましょう。

コミットしていきます。

```
git commit -m "First Coomit"
```
コミットできたら、`gh`コマンドでリモートのリポジトリを作っていきます。


```
gh repo create
```
```
? Repository name {リポジトリ名}
? Repository description {自由に書いてみて}
? Visibility  [Use arrows to move, type to filter]
> Public
  Private
  Internal
```
`? Repository name`リポジトリの名前を聞かれます。そのままでいいならEnter。
`? Repository description`リポジトリの説明。自由に書いてみてください。
`? Visibility`公開するかしないか聞いています。`Public`にします。後で変更可能です。

```
? This will add an "origin" git remote to your local repository. Continue?
```
`origin`という名前のローカルリポジトリに追加されます。`y`を推します。

まだ完成ではありません。ここで反映していきます。

```
git checkout -b develop
```
その後にプッシュしていきます。

```
git push -u origin develop
```

ここでちゃんと反映されているか確認してみましょう。

```
gh repo view --web
```

ブラウザが立ち上がり、作成したリポジトリが出ます。

これにて完成です！
GitHub CLIを使ってみる

# GitHub CLIを使ってみよう

`GitHub CLI`を使うとブラウザ側で操作していた事がターミナルで操作できるようになりました。

https://github.com/cli/cli

Homebrewを最初にインストールしておきましょう。
```
brew install gh
```

インストールが完了すると`gh`のコマンドが打てるようになります。

次にGitHubのログインをターミナルで行います。
```
gh auth login
```
```
> GitHub.com
> GitHub Enterprise Server
```

`GitHub.com`か`GitHub Enterprise Server`どちらか聞かれたらGitHub.comを選んでください。
すでにログインしている場合は
```
? You're already logged into github.com. Do you want to re-authenticate? 
```
と出てくるので`y`を選び

```
> HTTPS
> SSH
```

Git操作に適したプロトコルは何ですか？と聞かれ`HTTPS`か`SSH`が出るので`HTTPS`を選びます。

GitHubのクレデンシャルでGitを認証しますか？`y`

```
> Login with a web browser
> Paste an authentication token
```

GitHub CLIをどのように認証しますか？`Login with a web browser`を選びます。
認証トークンを持っているなら` Paste an authentication token`を選びます。

```
! First copy your one-time code: コード
- Press Enter to open github.com in your browser... 
```

`コード`には英数字がありこちらをコピーしてEnterをしたらブラウザでGitHubが開きます。さきほコピーしたやつを張り付け`Continue`=>`Authorize Github`=>自分のGitHubのパスワード入れて完了です!
あとは実行しているターミナルにEnterを押したら。
```
- gh config set -h github.com git_protocol https
✓ Configured git protocol
✓ Logged in as 自分のGithubの名前
```
これが出できたら最初の設定は完了です。

続き↓

https://qiita.com/kubota20/items/7b42a0c2bc3f91b87d06
p37 迷路最短路 (C#)

### 自分(ほぼ正解▲)
流れは正解しているが、一部※queueに二次元配列を代入する方法がわからなかったので、「ほぼ正解」とした。
```
using System;
using System.Reflection;

public class Program
{

    public static void Main()
    {
        int n = 10;
        int m = 10;
        int[,] map = new int[n][m];  //new int[10][10] = { { #, S, #, #, #, #, #, #, ., # }, が10個続く }
        int[,] answer_map = new int[n][m]; //最短距離を記述していくマップ
        int start_x, start_y;
        int goal_x, goal_y;
        var que = new Queue<int>();//行くことのできる可能性のある場所を積んでおくため

        //answer_mapの初期化 //最初は大きな値にしておく
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; i < m; j++)
            {
                answer_map[i][j] = 9999;
            }
        }

        //スタート地点のanswer_mapを0にする
        answer_map[start_x][start_y] = 0;
        //再帰関数に代入 //返り値が答え
        answer = exec(start_x, start_y); 
        //答え出力
        Console.Write(answer);

        int exec(int x, int y)
        {
            //渡ってきた座標の上下左右を探し、"."ならキューに積む
            if (map[x][y + 1] == '.') que.Enqueue();//上
            if (map[x][y - 1] == '.') que.Enqueue();//下
            if (map[x - 1][y] == '.') que.Enqueue();//左
            if (map[x + 1][y] == '.') que.Enqueue();//右

            //キューからx座標,y座標を取り出す //取り出せなければ探索終了
            if (queue.Dequeue())
            {
                //キューから取り出す //############################################queueに二次元配列を代入する方法がわからない############################################
                now_x, now_y = queue.Dequeue();
                //今入ってる値と、直前から+1した値がどちらが大きいかを比較する
                if (answer_map[now_x][now_y] < answer_map[x][y] + 1)
                {
                    answer_map[now_x][now_y] = answer_map[x][y] + 1;//更新
                }
                exec(now_x, now_y);
            }
            else
            {
                return answer_map[goal_x][goal_y];//キューから取り出せる座標がなくなったら,Gに記載されてる値をreturnする
            }
        }
    }
}
```


### 模範解答
 > ※queueに二次元配列を代入する方法がわからなかった

の部分は、以下のように実装していた。(C++の場合)
```
//型の定義
typedef pair<int, int> P;

//queueの定義
queue<P> que;

//queueにpushする
que.push(P(start_x, start_y));

//queueから取り出す
p = que.front();

//x座標
p.first;

//y座標
p.second;
```

#### c#で実装するとこうなる
C++で実装した際に使用した`typedef`だが、C#ではこれに該当するクラスはないらしいので、別の方法でやるしかない(以下参照)。
https://www.delftstack.com/ja/howto/csharp/typedef-equivalent-in-csharp/

実装パターン
①そもそもキュークラスを使わず、配列でなんとかする
②キュークラス使っていい感じに整える

②でやってみた。動くかどうかはhttps://dotnetfiddle.net/srx9kM で試した。
```
using System;
using System.Collections.Generic;
					
public class Program
{
	public static void Main()
	{
        //二次元配列のint[,]が入るキューを定義
		var que = new Queue<int[,]>();
        //座標を代入
		que.Enqueue(new int[,] {{100,200}});
		que.Enqueue(new int[,] {{300,400}});

		//キューから座標を取り出す
		int[,] p = que.Dequeue();
		Console.WriteLine("Count：" + p.Length);
		Console.WriteLine("0番目の最初：" + p[0,0]);
		Console.WriteLine("0番目の２つ目" + p[0,1]);
		
　　　　　　　　　　　　　　　　//キューから座標を取り出す
		int[,] r = que.Dequeue();
		Console.WriteLine("1番目の最初：" + r[0,0]);
		Console.WriteLine("1番目の２つ目" + r[0,1]);
	}
}

```



#### option
上下左右の座標指定の仕方も賢かった。
【Unity】Androidの実機でVideoPlayerが正常に動かない問題

現在バグフィックス中。対応はバージョン2022からで修正次第2021,2020,2019辺りでも対応予定とのこと。（2022年2月現在）
https://forum.unity.com/threads/playing-video-on-android-lag-stuttering.524329/ 参照

ーーーー
※23年1月現在対応済み
VideoPlayerのSkip On Dropにチェックを入れる
動画のCodecやクオリティー、Videoの場所は問わず



ーーーー

症状
Androidの実機で動画を再生すると1秒間に300ms秒分ずつぐらいしか進まない
音も飛び飛びで見るに堪えない

環境
unity 2020.3.22 (一部3.28と3.14含む）
端末Rakuten hand

試したこと（全てRender texture上に表示）
マルチスレッドレンダリング機能のオフ
オーディオ再生direct、Audio Source
再生中のGC停止

インクリメンタルGC
・Addresseble（アセットバンドル）
H264（インポートしたデータ、動画ファイル自体の解像度や品質の変更もしたが変化なし）
H265（Unity内の変換機能）
Vp8（Unity内の変換機能）
圧縮、非圧縮
品質Low、Mid、High（Unity内の機能）
音声のない動画と音声に分けて、VideoPlayerと同時に別のオーディオソースで音を再生

・StreamingAseets
pathでmp4を取得


まとめ
秋口から対処方法を探していてネット上で見かけた方法含めてビルドのたびにまめに試していたが、どれも変化がなかった
巻き戻すと2秒ぐらいスムーズに再生されるのでやりようはあるかもしれないが、ミドルウェアなり有料アセットなりを使った方がよさそう
【AWS】fargateの停止方法

# この記事のきっかけ
プログラミング初学者の[@kat_log](https://twitter.com/kat__log "Twitter")と申します。

YouTubeにて[クラインさんという方の動画](https://www.youtube.com/watch?v=2_FxLp9xgmo "【AWS 入門】ECS(Fargate)とECRで楽々コンテナからHelloWorldしよう！")を元に、fargateを使ってHello worldをやってみました。
Hello world完了後にECSのfargateの停止方法が分からず調べたのですが「自動停止」「自動起動」の方法という記事が多く手動停止に至るまで苦労したため情報共有です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1455073/4a11bfd3-9b8c-7001-1c76-c292cd3fbe91.png)


:::note warn
プログラミング学習中の初学者が調べながらたどり着いた方法になります。
より良い方法（正しい方法？）をご存知でしたらご教示いただければと思います。
:::

# 停止前

ページが表示できています↓

![Screen Shot 2022-02-26 at 13.50.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1455073/d0c7cd2e-5ecb-f5c3-b80f-a456dad67a22.png)


# 停止方法

AWSコンソール、ECSの「クラスター」ページより対象クラスターをクリック

![Screen Shot 2022-02-26 at 13.51.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1455073/cdc92c69-7950-b7b6-b23b-e8ad7b76b786.png)

「サービス」タブで対象サービス名をクリック

![Screen Shot 2022-02-26 at 13.57.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1455073/973007fe-67e7-935f-59e2-dc6e703c3a27.png)

右上の「更新」をクリック

![Screen Shot 2022-02-26 at 14.06.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1455073/600277a3-ee38-5eca-a92f-da80f3b45fc7.png)

「サービスの設定」ページに行くので、下の方にスクロールし **「タスクの数」を「0」** にして「次のステップ」をクリック

![Screen Shot 2022-02-26 at 14.10.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1455073/b7cd6b79-bc5f-a48c-571d-fb5bc9cfdbca.png)

「次のステップ」をクリックしていき最後は「サービスの更新」をクリック

![Screen Shot 2022-02-26 at 14.13.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1455073/85a31cd4-cc94-e690-c70c-5f37e82b44d4.png)

完了です！

![Screen Shot 2022-02-26 at 14.15.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1455073/9cf4e46c-a1a9-6c0a-74a1-ca715fd75199.png)

# 停止の確認

再度ページにアクセスしても表示できなくなっています。

![Screen Shot 2022-02-26 at 14.26.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1455073/d0ffcb93-8afb-6d34-6dcf-956f2f090544.png)

また、サービスのタスクで見てみると`Running`のものがなくなっています。

![Screen Shot 2022-02-26 at 14.27.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1455073/598f073f-7a8c-22de-24bb-9e583a30009b.png)

逆に`Stopped`に先程止めたタスクが入っています。

![Screen Shot 2022-02-26 at 14.30.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1455073/d0bed1ae-915b-b6b5-9c50-3685482910f2.png)

以上です！

# おわりに
お読みいただきありがとうございました。
どなたかの参考になれば嬉しいです！

# 参考

https://chat.stackoverflow.com/transcript/214788/2020/11/3/9-21
MackerelでRaspberry PiのCPUクロックの遷移をグラフ表示する

# Raspberry Piの現在のCPUクロック設定の取得方法

以下のファイルに、現在のCPUクロック設定が格納されています。単位はKHz。

```
$ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq 
600000
```
## MackerelでRaspberry Piのクロックを記録する

Mackerelとは、はてなが提供しているサーバー監視サービスで、無料プランでも5台まで監視できるのでRaspberry Piの監視にちょうどいいです。問題発生時にメールだけでなくLINEに通知するようにもできます。
監視だけでなく、温度などを記録してグラフ表示することができるので、CPUのクロックも表示してみます。

基本的な設定はこちらを参考：[Raspberry Pi を Mackerel で監視する](https://qiita.com/ww24/items/5409aefd87f14beff504)

※64bit OSの場合は、mackerel-agent_linux_arm.tar.gzではなくmackerel-agent_linux_arm64.tar.gzを利用します。

## CPUクロック設定の表示スクリプトを作る

```sh:/home/pi/bin/cpufreq.sh
#!/bin/bash
SECONDS=`date '+%s'`

cpufreq () {
    NAME='freq.'`basename $1`
    FREQ=`cat $1/cpufreq/scaling_cur_freq`
    VALUE=`echo $(($FREQ/1000))`
    echo -e "${NAME}\t${VALUE}\t${SECONDS}"
}


for cpu in `echo /sys/devices/system/cpu/cpu0`; do
    cpufreq $cpu
done
```

これfor文になってますが、最初は4つのCPUの設定クロックを個別に取得していました。しかし、Raspberry Piの場合、全CPU同一クロック設定だということがわかり、cpu0のみチェックするようにしています。

## Macherelのチェック項目として追加

```/etc/mackerel-agent/mackerel-agent.conf
[plugin.metrics.cpufreq]
command                 = "/home/pi/bin/cpufreq.sh"
```

スクリプトの実行によりCPUクロックが上昇する可能性があるので、追加する場所はなるべく先頭のほうがおすすめです。

## Mackerel agent再起動

```
sudo systemctl start mackerel-agent
```

## Mackerelでの表示例

NASとしても使われているRaspberry Piなので、それなりに忙しそうです。

![スクリーンショット 2022-02-26 15.39.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12960/53fd750a-999d-86c9-a6a5-1392d6a0440b.png)

## 関連記事

https://qiita.com/tinoue@github/items/868b848999c3571ccf9f

https://qiita.com/tinoue@github/items/303cd3e3d030f4e89c59
Webアプリケーション開発のお勉強 #7

# 1.この記事の内容

[Webアプリケーション開発のお勉強 #6](https://qiita.com/ryoma-jp/items/1b4f1cc90531b408b98d)まででブラウザ操作でCNNの学習を実行し，Tensorboardと接続するところまでを実装しました．

本記事では，学習実行前に学習パラメータを変更するビューの追加とその仕組みを紹介します．  
ソースコード全体は[GitHub](https://github.com/ryoma-jp/AI_Dashboard)で公開しています．
本記事に対応するコミットは[a7b5902fa17855dd143d5340969b0738d056e7e5](https://github.com/ryoma-jp/AI_Dashboard/tree/a7b5902fa17855dd143d5340969b0738d056e7e5)です．

AI_Dashboard自体は引き続き更新を続けますが，これまでの記事でWebアプリ開発における基礎的な要素は紹介できたと思いますので，お勉強の連載記事としては今回で最後とします．

# 2.実装概要

DataAugmentationや学習時のハイパーパラメータをひとつのjsonファイルでまとめ，これをモデル毎に管理し，Webビューと学習アプリで共有する構成としました．

各パラメータをデータベースとして管理すると，ユーザが設定するパラメータが増加すると，モジュール間の引数も増加し内部仕様の複雑化につながります．
jsonファイルによる管理とすることでモジュール間インターフェースの共通化と簡略化を図ります．

# 3.実装内容詳細

学習パラメータの初期値は```media/config/```以下にプリセットのjsonファイルを配置しておき，新規モデルの作成時にプリセットのjsonファイルをモデル管理ディレクトリにコピーします．
プロジェクトディレクトリ及びモデルディレクトリはそれぞれ，プロジェクト名・モデル名からハッシュ値を算出し，データベースに保存します．

モデル毎にパラメータを変更する際は，ブラウザ上で変更した値でjsonファイルを更新します．

学習を実行する際は，モデル管理ディレクトリ以下のjsonファイルを学習アプリに渡します．

下図はブラウザ上の操作と内部処理の関係図です．

![processing_flow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/983907/a61603d3-0c3f-fab4-29fc-f92be14806db.png)


# 4.さいごに

機械学習モデルを扱うWebアプリを題材に勉強した内容を連載記事として続けてきました．
フロントエンドとバックエンドの接続の仕組みとプログラムの書き方の，基礎的な内容は学べたと思います．

手探りで試しながらではありましたが，これらの内容が少しでも参考になれば幸いです．

# 5.[参考]jsonファイルの仕様

パラメータをjsonファイルに記述する際，パラメータ毎に下記のキー構成で登録します．

|キー名|用途|
|:--|:--|
|name|パラメータ名<br>パラメータ設定ページの項目名で使用する|
|description|パラメータの概要<br>パラメータ設定ページのテキストボックスのplaceholderで使用する|
|dtype|パラメータの型<br>int, float, str, boolから選択する<br>jsonからロードすると文字列型となる為，dtypeに従ってキャストする|
|value|パラメータの値|
|configurable|パラメータの設定可否<br>True：設定可能，False：設定不可<br>設定可能なパラメータはブラウザから変更可能|
|selectable|パラメータ設定が選択式か否か<br>True：選択，False：直接入力<br>Trueはドロップダウンで変更，Falseはテキストボックスに入力して変更する|
|items|ドロップダウンの選択肢|

# 6.関連リンク

* [Webアプリケーション開発のお勉強　目次](https://qiita.com/ryoma-jp/items/1a0a438313dcb30ef6fc)
Kotlinのyield関数についてまとめてみる

今まで、KotlinのCoroutineを書いている中での時々登場する、``yield``がなにをやっているのかいまい理解せずになんとなくで、書いていたので``yield``について調べてみました。

:::note info
Sequenceで使う``yield()``もありますが、今回はCoroutineの``yield``についてです
:::

# yield関数とは？
``yield``関数は、KotlinのCoroutineのsuspend関数です。

# yieldを使うとどうなるのか？
``yield``を使うと、例えば複数のCoroutineを同時に実行しているときに、どれか一つが時間のかかる重い処理だった場合に、他のCoroutineに対して実行を譲ることができる関数です。

::: note warn
（コメントで指摘いただきありがとうございます。）
複数のスレッドを持っている場合、スレッドを譲った前と後でスレッドが異なっている場合があるので、そこを注意する必要があります。
:::

コードで書くとこんな感じです。

```Kotlin
fun main() {
    runBlocking {
        launch {
            while (i < threshold) {
                if (i % 2 == 0) {
                    println("$i is even")
                }
                i++
                yield()
            }
        }

        launch {
            while (i < threshold) {
                if (i % 2 != 0) {
                    println("$i is odd")
                }
                i++
                yield()
            }
        }
    }
}
```
出力

```
0 is even
1 is odd
2 is even
3 is odd
4 is even
5 is odd
6 is even
7 is odd
8 is even
9 is odd
```

この例では、まず最初に0を出力した後に、``yield``を使用することで、最初の処理から二つ目の処理に実行が譲ることができています。もし、一つ目の処理中に``yield``がなければ実行結果はこうなります。

```
0 is even
2 is even
4 is even
6 is even
8 is even
```

# 最後に
Coroutineについて、まだまだ自分自身知らないことだらけなので、これから勉強する必要があると強く感じました。
参考文献の本はすごくわかりやすくCoroutineについて書いてあるので、Coroutineいまいち分からないって方はぜひ見てみてください！

# 参考
https://zenn.dev/at_sushi_at/books/edf63219adfc31
Vue.jsにおけるマウントについて

## 目的
当記事は筆者の学習アウトプットを目的としております。
もしご覧になった方で当記事の内容に誤りがあった際は、ご指摘頂けると幸いでございます。

今回はVue.jsにおける「マウント」について触れます。

## マウントとは
広義的な意味としての、IT分野における「マウント」という言葉の意味は以下の通りとなっております。

【マウントとは、コンピュータに接続した周辺機器や外部記憶装置等をオペレーティングシステム（OS）に認識させ、利用可能な状態にすることを指します。 例えばクラウドサービスにおいて、イメージディスクとよばれるISOファイルを仮想マシン上で認識させることを「ISOファイルのマウント」と呼びます。】　※クラウド・データセンター用語集参照
https://www.idcf.jp/words/mount.html#:~:text=%E3%83%9E%E3%82%A6%E3%83%B3%E3%83%88%E3%81%A8%E3%81%AF%E3%80%81%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%82%BF%E3%81%AB,%E3%81%AE%E3%83%9E%E3%82%A6%E3%83%B3%E3%83%88%E3%80%8D%E3%81%A8%E5%91%BC%E3%81%B3%E3%81%BE%E3%81%99%E3%80%82

筆者の解釈だと、
【AにBをぶち込み、Aの中でBを使えるようにする】
みたいなイメージを持っています。かなり雑な考え方だとは思いますが…。

## じゃあ、Vue.jsの中ではどうなの？
Vueのインスタンスをマウントすると、指定したHTML要素はVueが生成するHTML要素に置き換わります。Vueインスタンスで定義した機能などは、マウントしたHTML要素とその子孫の要素でのみ使用可能です。

例えば、「<div id="app></div>」にVueインスタンスをマウントした場合、「<div id="app></div>」の内部でのみVueインスタンスで定義した機能を使用できます。

```sample.html
<div id="app>
<!--この中でのみVueインスタンスで定義した機能を使用できる-->
</div>
```

## マウントしてみよう
Vueインスタンスはmountというメソッドを持っています。
その引数にセレクターの文字列を渡すことでマウントができます。

```sample.js
Vue.createApp({
//ここにアプリケーションの機能を定義する
}).mount("#app") //Vue.createApp({})がインスタンス！
```

以上のコードは「<div id="app"></div>という一つのHTML要素にマウントしている状態」ということとなります。


## 最後に

以下、ポイントをあげていきます。
・Vue.jsではVueインスタンスを作成してマウントすることで、Vueアプリケーションを動作させる
・Vueインスタンスの作成にはVue.createAppメソッドを使用する
・Vue.createAppの引数に機能を定義していく
・Vueインスタンスのマウントにはmountメソッドを使用
・マウントしたHTML要素はVueが生成するDOMに置き換わり、その内部でのみVueの機能が利用できる
主要なクラウドストレージサービス（＋α）を調べてみた

# 1. はじめに
諸事情でクラウドストレージサービスを調べる必要があったので、ついでに記事にしてみました。

# 2. 法人向け
Boxのようにクラウドストレージサービスを専門に提供している企業と、MicrosoftのようにOfficeアプリケーション等も提供している企業に大別できそうです。法人は個人と比べると、ドキュメントを保存することが多いと思います。
ついでに、ランサムウェアに感染してファイルが暗号化されてしまった場合にファイルを復元する方法についても調べてみました。

* Box・・・Box DriveまたはBox Sync（Box Driveの旧版）を使用している場合のみ影響あり。ファイルを復元するためには、サポートへ問い合わせる or APIを使用する。
* Dropbox・・・ファイルのバージョン履歴からは1ファイルずつしか復元できない。巻き戻し機能であれば多数のファイルを一括で復元できる。
* OneDrive (Microsoft)・・・バージョン管理機能を有効にしておき、1ファイルずつ復元する。（既定は無効）
* Google Drive・・・1ファイルずつ、古いバージョンのファイルを復元する。

# 3. 個人向け
上記サービスの個人向けプランに加え、Appleなどもサービスを提供しています。個人は法人と比べると、写真や動画を保存することが多いと思います。

* iCloud (Apple)
* Amazon Drive

# 4. おわりに
サービス単体で見ると、ファイルのリストア機能はBoxとDropboxが高機能ですね。さすが専業ベンダ。

# 4. 出典
* [ランサムウェア (box)](https://support.box.com/hc/ja/articles/360043694054-%E3%83%A9%E3%83%B3%E3%82%B5%E3%83%A0%E3%82%A6%E3%82%A7%E3%82%A2)
* [ファイルがランサムウェアによって破損または名前が変更された場合 (Dropbox)](https://help.dropbox.com/ja-jp/accounts-billing/security/ransomware-recovery)
* [OneDrive for Business はランサムウェア対策となり得ますでしょうか。](https://answers.microsoft.com/ja-jp/msoffice/forum/all/onedrive-for-business/2cce7fab-4aff-414a-8e0a-c5ece352010f)
* [組織をランサムウェアの脅威から守るためのベスト プラクティス (Google Drive)](https://cloud.google.com/blog/ja/products/identity-security/5-pillars-of-protection-to-prevent-ransomware-attacks)
ネットワーク調査用のLinuxコマンド覚書

[拠点間VPNを構築](https://qiita.com/tinoue@github/items/ebcdf5423d87df0ad7cc)して、そのパフォーマンスを調べるために使ったツールの覚書です。

## iftop

定番。ホスト・ポート毎のトラフィックを表示してくれる。

実行例

```
iftop -i eth0 -l -P -m 30M
```

`-l` : リンクローカルのIPv6アドレスを表示
`-P` : ポートを表示
`-m 30M` : トラフィックのグラフのスケールを最大30Mbpsに設定

## trafshow

iftopにちょっと似ているが、Layer2レベルのトラフィックも表示される。

`apt-get install netdiag`でインストール。

TODO: netdiagパッケージにはほかにもtcpblast, netload, netwatch, statnet, tcpsprayなど、興味深い名前のコマンドが含まれるので調査。

## iptraf-ng

メニューベースで各インタフェースの状態をいろんな切り口で見ることができる。

`Detailed interface statistics`をよく使う。指定したインタフェースのパケット数をサイズ別に表示してくれるので、MSS clampが効いているかの確認に便利。

## iperf

実行例（ホスト側）

```
iperf -s
```

実行例（クライアント側）

```
iperf -c 192.168.0.1 -M 1300
```

`-c` : サーバ側のIPアドレス
`-M 1300`: MSSを1300に指定。IPフラグメンテーションの有無によるパフォーマンスの違いを調べるときに使う。
`-V`: IPv6でのテスト

## glances

ネットワークだけでなく、システム全体のステータス表示を行う。
なお、インストールの際は`glance`ではなく`glances`であることに注意。間違えると全然関係ない重量級サービスがインストールされてえらい目にあいます（あいました）。

## 参考リンク

https://orebibou.com/ja/home/201409/20140902_001/
Blenderで移動できる窓をまとめて作る

## Blenderで移動できる窓をまとめて作る

前記事「[Blenderで移動できる窓を作る](https://qiita.com/SaitoTsutomu/items/78469e2c226cae442f30)」では、1つの移動できる窓の設定をPythonで行いました。

本記事では、複数の窓の設定をまとめて行う方法を紹介します。

## ポイント

Pythonで作成したパネルのボタンは、別のプログラムからも実行できます。
たとえば、前記事のボタン「Set to frame」を実行するコードは、下記のようになります。

```py
bpy.ops.object.set_frame_operator(
    frame="frame",
    glass="glass",
    wall="wall",
    hole="hole",
    doset=True
)
```

参考：[BlenderでPythonを実行する方法](https://qiita.com/SaitoTsutomu/items/cec67381a8789b40e377)

複数の窓の設定は、CSVファイルに書いてあるとしましょう。
行ごとに各窓のパラメータを読み込み、上記を実行すればOKです。

## やってみよう

- Blenderを起動し、scriptingワークスペースにし、新規を押す。
- [前記事](https://qiita.com/SaitoTsutomu/items/78469e2c226cae442f30)のコードをコピペしスクリプト実行する。
- Layoutワークスペースに戻り、「Make sample」ボタンを押す。
    - frame, glass, wall, holeの立方体（Cube）が作成される（glassは内部にあり見えない）
- `1`（視点の前）を押し、正面にする。
- wallをx軸方向に広げる。
- アウトライナーでframe, glass, holeを選択し、3Dビューポートで2回複製し、下記のように窓を3つにする（サイズは適当に変えても良い）。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13955/347a6947-1d44-5706-5873-f5b853f5203f.jpeg" width="500">

- scriptingワークスペースにし、もう一度、テキストメニューの新規を選ぶ。
- 下記をコピペし、スクリプト実行する。

```py
import bpy

data = """\
frame,glass,wall,hole
frame,glass,wall,hole
frame.001,glass.001,wall,hole.001
frame.002,glass.002,wall,hole.002
"""

lines = data.splitlines()
header = lines[0].split(",")
for line in lines[1:]:
    opt = dict(zip(header, line.split(",")))
    bpy.ops.object.set_frame_operator(doset=True, **opt)
```

※ CSVを用意するのは手間なので、CSV内のテキストの代わりに`data`を使っています。

- Layoutワークスペースに戻り、3Dビューのシェーディングをマテリアルプレビューにして確認しましょう。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13955/576f59ca-430f-d1cc-4b40-b195f75961c9.jpeg" width="300">


以上
RDKitでパスやファイル名に日本語を含むSDFファイルを強引に読み込む方法

# はじめに
RDKitでパスやファイル名に日本語を含むSDFファイルを読み込めないイライラにサヨナラするために調べてみたメモ。

# 現象

例えば以下のように、パスやファイル名に日本語を含むSDFファイルをSDMolSupplierで読み込もうとする。

```python
sdf_filepath = "テスト\αサンプル.sdf"
sdf_sup = Chem.SDMolSupplier(sdf_filepath)
```

するとこんなエラーが発生する。
```python
OSError: File error: Bad input file テスト\αサンプル.sdf
```

どうやらSDMolSupplierがマルチバイト文字に対応してないようである。
このためにわざわざ綿密に計画したフォルダ名を変更するのもイラっと来る。
また、ファイル名を化合物名とした場合などに、ファイル名にギリシャ文字などのマルチバイト文字が含まれるのは実務上よくある話だ。

# 対処方法
対処方法として以下の方針を立てた。

-  **ファイルの存在する場所にカレントディレクトリを移動**して読み込むようにする。こうすることでフォルダ名を気にする必要がなくなる。
- ファイル名についてはどうしようもないので、**一旦ファイル名を変更し、読み込みが終わったらファイル名を元に戻す**。読み込み途中で処理が異常終了した場合のロールバック等の考慮が面倒だが他に思いつかない。

# やってみよう

対処方針にしたがって先ほどのコードを改良したものを示す。

```python
sdf_filepath = "テスト\αサンプル.sdf"
import os

# カレントディレクトリを保持
current_path = os.getcwd()
# カレントディレクトリをファイルの存在するフォルダに変更
os.chdir(os.path.dirname(sdf_filepath))
# ファイル名を取得
filename = os.path.basename(sdf_filepath)
# ファイル名を一時変更
tmpname = "tmp.sdf"
os.rename(filename, tmpname)

# 一時変更したファイル名でSDFの読み込み
sdf_sup = Chem.SDMolSupplier(tmpname)
for mol in sdf_sup:
    if mol is not None:
        <処理>

sdf_sup = None

# ファイル名を元に戻す
os.rename(tmpname, filename)

# カレントディレクトリを元に戻す
os.chdir(current_path)
```

これで無事、読み込みができるようになった。

# おわりに
ロールバック等を考慮すると本番システムに組み込むのは怖いが、ちょっとした解析であれば十分実用に使えると思う。
pg_ctlコマンド使い方

pg_ctlを使って、便利だなと思ったのでまとめて見ました。

1. サーバを再起動せずに設定ファイル（postgres.conf）を反映
$PG_DATA/postgres.confを修正後に、以下のコマンドでサーバを再起動せずに設定を反映できる！
マジで便利(^o^)！
※全ての設定項目を反映できるわけではないよう
＜設定できた内容＞
・ログ出力関係の設定
・AUTOVACUUMの無効化（off）

```sql:pg_ctl
pg_ctl reload
```
なぜIPアドレスもMACアドレスも必要なのか？ IPアドレスとMACアドレスの違いのポイントを簡潔にまとめました。

## IPアドレスとMACアドレスの違いは？
IPアドレスとMACアドレスは、どちらも通信相手を識別するための情報です。「なぜIPアドレスもMACアドレスも両方必要なのか？」「IPアドレスとMACアドレスは何が違うのか？」ということを疑問に思ったことがあると思います。簡潔にIPアドレスとMACアドレスの違いのポイントとまとめます。

## IPアドレスとMACアドレスの違いのポイント
IPアドレスとMACアドレスの違いのポイントをまとめると以下です。
+ IPアドレスは「論理的なデータ」の宛先と送信元を示す
+ MACアドレスは「物理的な信号」の宛先と送信元のネットワークインタフェースを示す

「データ」は「0」と「1」で構成されている論理的なものです。見たり触れたりする物理的な実体はありません。なので、「0」と「1」がそのままネットワーク上に流れていくわけではありません。電気信号や光信号、電波といった物理的な信号として伝えていきます。

**IPアドレスで「0」と「1」の「論理的なデータ」の宛先と送信元を示します。** そして、「論理的なデータ」は物理的な信号としてネットワークインタフェースから送り出されて、適切な宛先のネットワークインタフェースまで伝えられていきます。**MACアドレスは、「物理的な信号」の送信元と宛先のネットワークインタフェースを示す。**

:::note info
MACアドレスはイーサネットインタフェース(LANポート/NIC)や無線LAN(Wi-Fi)インタフェースのアドレスです。「ネットワークインタフェース」とは、イーサネットインタフェースまたはWi-Fiインタフェースです。話しを簡単にするためにイーサネットインタフェースのみを想定します。
:::

## IPアドレスはネットワークインタフェースに対して設定する
IPで「論理的なデータ」を送信するためには、「物理的な信号」に変換してインタフェースから送り出します。そして、IPで「論理的なデータ」を受信するには、その「物理的な信号」がインタフェースに伝わってこなければいけません。このことから、**IPアドレスはインタフェースに対して設定する**ことになります。

MACアドレスを持つイーサネットやWi-Fiのインタフェースに対してIPアドレスを設定することで、IPアドレスとMACアドレスを関連付けます。つまり、IPによって「論理的なデータ」を送り届けるには、どのインタフェースに「物理的な信号」が伝わっていけばよいかがわかるようにしています。以下の図は、IPアドレス設定の簡単な例です。

![ip-address-and-mac-address03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/541748/2474e2f2-49e8-23db-b619-84fcb0c6c043.png)

この図で、MACアドレス「PC1」というイーサネットインタフェースにIPアドレス「192.168.1.1」と設定しています。IPアドレス「192.168.1.1」へ「論理的なデータ」を送り届けるためには、MACアドレス「PC1」のイーサネットインタフェースにイーサネットの「物理的な信号」が伝わればいいということを示しています。

**IPヘッダには宛先IPアドレス「192.168.1.1」を指定して、宛先MACアドレスには「PC1」を指定するというように、IPアドレスもMACアドレスも両方とも指定します。そうすることで、「論理的なデータ」を送り届けたい宛先にきちんと「物理的な信号」が伝えられていくようになります。なので、IPアドレスもMACアドレスも必要です。**

:::note info
ただ、イーサネットやWi-FiなどMACアドレスを持つインタフェースを利用しているときだけです。MACアドレスがないネットワークインタフェースの種類もあります。
:::

## IPアドレスとMACアドレスを人間同士の会話にたとえると

+ IPアドレスは「論理的なデータ」の宛先と送信元を示す
+ MACアドレスは「物理的な信号」の宛先と送信元のネットワークインタフェースを示す

このことについての理解を助けるために、人間同士の会話にたててみましょう。

私たちが会話するときに、相手に伝えたい話題は物理的な実体はありません。人が考えていることは、目で見たり手で触れたりすることができませんよね。会話で伝えたい「話題」は、「論理的なデータ」に例えることができます。そして、会話相手の「意識」が「IPアドレス」に例えられます。伝えたい話題は、思っているだけでは伝わりません。空気の振動、つまり、「声」という「物理的な信号」にしないといけません。そして、その声は相手の耳に伝わらないといけません。会話相手の耳がイーサネットなどのネットワークインタフェースに例えられます。声を発する口もネットワークインタフェースです。つまり、会話の相手の「耳と口」が「MACアドレス」と考えてください。

![ip-address-and-mac-address09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/541748/734f0eeb-8cb2-8788-9ba6-47f88e04ed8c.png)

![ip-address-and-mac-address10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/541748/68645dd8-498f-6fe2-60ed-35ebba88671b.png)

人間が会話するときにいちいち会話相手を明示的に特定して、その人の耳に声が届くようになんて考えたりしません。でも、コンピュータ同士の通信のときにはIPヘッダに宛先IPアドレス、イーサネットヘッダに宛先MACアドレスを指定しなければいけません。

また、図のPC1とSRV1は、同じネットワークにつながっています。人間同士の会話の例えだと遠く離れたデータの転送「ルーティング」は、うまく当てはまりません。たとえ話というものは、そんなものですね。説明したいことにピッタリと当てはまるたとえ話なんてないものです。

## さらに詳細は
あらためて、IPアドレスとMACアドレスの違いのポイントです。

+ IPアドレスは「論理的なデータ」の宛先と送信元を示す
+ MACアドレスは「物理的な信号」の宛先と送信元のネットワークインタフェースを示す

「0」「1」の「論理的なデータ」は電気信号など「物理的な信号」として伝えていきます。なので、「論理的なデータ」の宛先と送信元を示す「IPアドレス」も「物理的な信号」の宛先と送信元を示すMACアドレスも必要です。

この記事は、私のWebサイトのダイジェストです。より詳細な解説は、以下をご覧ください。

https://www.n-study.com/ip-addressing/ip-address-and-mac-address/
[備忘録] wslからusbカメラにアクセス

## 環境構築
### wslカーネルのビルド
- 下記の通り
  - [GitHub, PINTO0309, wsl2_linux_kernel_usbcam_enable_conf, https://github.com/PINTO0309/wsl2_linux_kernel_usbcam_enable_conf#2-usage](https://github.com/PINTO0309/wsl2_linux_kernel_usbcam_enable_conf#2-usage)

## 実行（Powershell ⇒ wsl）

### PowerShell(管理者)

- 下記のコマンドで使用したいカメラの番号を取得（例：1-4）
```bash
$ usbipd wsl list
```
- 有効化
```bash
$ usbipd wsl attach --busid 1-4
```
- 無効化
```bash
$ usbipd wsl detach --busid 1-4
```
### wsl

- usbカメラのデバイスファイルのパーミッションを変更
```bash
$ sudo chmod 777 /dev/video* 
```
- `fswebcam`による動作確認
```bash
$ fswebcam -d /dev/video0 -r 640×480 test.jpg
```

![test.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2525418/2d75ce4a-21a2-818b-0133-c31eb44542f4.jpeg)

- `v4l2-ctl`を使ったデバイスの確認
    - [qiita : @naoppy Webカメラでこまったらまずこれをやれ](https://qiita.com/naoppy/items/74bdfa8216c7223f584b)

- 利用可能なカメラを列挙

```bash
$ v4l2-ctl --list-devices
```

- デバイスがサポートするフォーマットと フレームサイズとフレームレートの組み合わせを表示する

```bash
$ v4l2-ctl -d /dev/video0 --list-formats-ext
```

## openCVで画像取得
- [qiita : @kakinaguru_zo（ラズパイで）USBカメラの映像をPythonのOpenCVで高速に表示する](https://qiita.com/kakinaguru_zo/items/eda129635816ad871e9d)

-  usbカメラの場合、コーデックをMJPGにしないと動かなかった

```python
import cv2
capture = cv2.VideoCapture(0)
capture.set(cv2.CAP_PROP_FOURCC,cv2.VideoWriter_fourcc('M','J','P','G'))

if capture.isOpened() is False:
  raise IOError

while(True):
  try:
    ret, frame = capture.read()
    if ret is False:
      raise IOError
    cv2.imshow('frame',frame)
    cv2.waitKey(1)
  except KeyboardInterrupt:
    # 終わるときは CTRL + C を押す
    break

capture.release()
cv2.destroyAllWindows()
```

- キャプチャーボードがあればゲーム画面も取得できます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2525418/6a62131b-3ea3-87b2-fb1e-7d16c5c30741.png)


## 使用した機器

- [ロジクール Webカメラ C270n](https://www.amazon.co.jp/%E3%83%AD%E3%82%B8%E3%82%AF%E3%83%BC%E3%83%AB-%E3%82%A6%E3%82%A7%E3%83%96%E3%82%AB%E3%83%A1%E3%83%A9-C270n-%E3%82%B9%E3%83%88%E3%83%AA%E3%83%BC%E3%83%9F%E3%83%B3%E3%82%B0-2%E5%B9%B4%E9%96%93%E3%83%A1%E3%83%BC%E3%82%AB%E3%83%BC%E4%BF%9D%E8%A8%BC/dp/B07QMKND9M/ref=sr_1_3?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=1WZU8XUL79FRB&keywords=c270n&qid=1645875388&s=electronics&sprefix=c270%2Celectronics%2C180&sr=1-3)
     
- [KINGONE HDMI キャプチャーボード ゲームキャプチャー](https://www.amazon.co.jp/gp/product/B08L3F2818/ref=ppx_yo_dt_b_asin_title_o00_s00?ie=UTF8&psc=1)
    


## 参考文献

- [Zenn : PINTO WSL2 USBカメラ+他のUSB機器 2022年01月17日版](https://zenn.dev/pinto0309/articles/7c7ce81bea8b6c)

- [DevelopersIO : さかじ Raspberry Piに接続されているwebカメラの情報をv4l2-ctlで簡単に取得したい](https://dev.classmethod.jp/articles/raspberry_pi_v4l2_ctl_get_resorution_from_webcam/)
terraformでauroraをDestroyできない


## 原因
- ファイナルスナップショットの扱いが問題の模様
- 以下を指定するとよいと思う
    - `  skip_final_snapshot       = false`
    - `  final_snapshot_identifier = "${var.system_name}-${terraform.workspace}-${var.service_name}-finalsnapshot-${local.time}"`
- たぶん、リソースを作ったあとにこれらの変更をしてもだめで、リソースが作られるタイミングで組み込まれる設定みたい
- 困ったら手で消せばいいかな

## サンプル

```main.tf
resource "aws_rds_cluster" "default" {
  cluster_identifier = "${var.system_name}-${terraform.workspace}-${var.service_name}-aurora-cluster"
  engine             = "aurora-mysql"
  engine_version     = "8.0.mysql_aurora.3.01.0"
  availability_zones = [
    "ap-northeast-1a",
    "ap-northeast-1c",
    "ap-northeast-1d"
  ]
  database_name                   = "init"
  master_username                 = "admin"
  master_password                 = var.db_default_password
  backup_retention_period         = 7
  db_subnet_group_name            = data.aws_db_subnet_group.default.name
  db_cluster_parameter_group_name = "${var.system_name}-${terraform.workspace}-db-cluster-parameter-group-${var.majour_number}"
  vpc_security_group_ids = [
    aws_security_group.db_security_group.id
  ]
  enabled_cloudwatch_logs_exports = [
    "audit",
    "error",
    "general",
    "slowquery"
  ]
  final_snapshot_identifier = "${var.system_name}-${terraform.workspace}-${var.service_name}-finalsnapshot-${local.time}"
  skip_final_snapshot       = false
  deletion_protection = false

  tags = {
    Name = "${var.system_name}-${terraform.workspace}-${var.service_name}-aurora-cluster"
  }
  lifecycle {
    ignore_changes = [
      final_snapshot_identifier,
    ]
  }
}

resource "aws_rds_cluster_instance" "instances" {
  count              = var.instance_count
  identifier         = "${aws_rds_cluster.default.cluster_identifier}-${terraform.workspace}-${var.service_name}-instance-${count.index}"
  cluster_identifier = aws_rds_cluster.default.id
  instance_class     = var.instance_class
  engine             = aws_rds_cluster.default.engine
  engine_version     = aws_rds_cluster.default.engine_version
  promotion_tier     = count.index

  tags = {
    Name = "${aws_rds_cluster.default.cluster_identifier}-${terraform.workspace}-${var.service_name}-instance-${count.index}"
  }
}

```
ぼくがかんがえたえた"ほぼ"さいきょうのインタラクティブER図/テーブル定義のソース公開します

## はじめに

テーブル定義が100を超える様なプロジェクトを経験されていますでしょうか。
図やエクセルからかすかな記憶を頼りに目的のテーブルやカラムドにたどり着いていますでしょうか。
新メンバー投入の際に、宇宙を感じるERや100シートあるエクセルを熟読させていますでしょうか。

長らく、ERやテーブル定義はそういうものだと思いこんで何の疑問も持っていなかったのですが、nuxt や vuetify があんまりにも簡単でわずかなコードを書けば、インタラクティブで便利なER図やテーブル定義を作れるのではないかと思い作成して公開しました。

[erm-viewer](https://github.com/kaku3/erm-viewer)
[demo](https://kaku3.github.io/erm-viewer/dist/)


## erm-viewer

ER/テーブル定義用Eclipseプラグイン[ER Master](http://ermaster.sourceforge.net/index_ja.html)という神ツールで作成された erm ファイルを読み込み、そのER図やテーブル定義を **インタラクティブER図/テーブル定義** として表示するツールです。

相当快適なインターフェイスを提供できたと思うのですが、 **erm ファイルをお持ちではない方には無用の長物となります。**

また、erm ファイルを雰囲気で読み取って表示用データを作りましたので、読み込みができないケースもあるかもしれません。
その場合にはソース修正してご利用頂ければと思います。


## 機能

かなり基本的な機能しか作ってないです。
逆に言うと **『かなり基本的な機能すらないのが当たり前と思い込んでいた』** というDXが推進されない理由あるあるについて考えさせられながらのツール制作でした。

- ctrl + f で検索できるER図
ブラウザの検索機能で「図」を検索できます。
pngになった「図」は検索できませんが、html の div タグなどで描画したER図は、普通にブラウザのキーワード検索が利用できます。

- table 全文検索/インクリメンタルサーチ
部分一致でフィルタされるのは似て非なるテーブルが多い時に猛威を発揮します。
保守フェーズ突入後など、記憶が薄れ始めた時期や別メンバーに交代時などに猛威を発揮します。

vuetify の DataTable 組み込みの検索ボックスを置くだけで実装できました。

- ページ遷移なしで任意のテーブルの詳細確認
複数のテーブルの詳細を見たい時にシート移動しなくてよいのは単純に快適です。

vuetify の DataTable の expand 機能で、任意の行に追加で情報を記載可能なので、それを利用してテーブルの一覧/開閉可能なテーブルの詳細を実装しました。

- テーブル一覧とER対応
テーブル一覧で任意のテーブルをクリックすると、ER図上のそのテーブルを表示。
Excelや画像ビューワーを交互に開くのが面倒くさかったのでレイヤーのあるUIを作ってみました。
低コストで簡単に作れて効果が高いなと思いました。

知らないうちに実装されていた [Element.scrollIntoView()](https://developer.mozilla.org/ja/docs/Web/API/Element/scrollIntoView) でプラグインなどなしで簡単に実装できました。
※**もちろんIEは無視です。**

- 関連一覧表示/移動
関連を持つカラムをクリックすると、関連一覧を表示。
クリックで関連カラムを持つテーブルをスクロール表示。
画像を頑張ってドラッグして目的のテーブルにたどり着いたり戻ったりを無限に繰り返すのが地味にコストだなと思い作りました。

こちらも`Element.scrollIntoView()`で簡単に実装できました。


## おわりに
ER図やテーブルの理解のコストは、元々プロジェクトのコストとして計上されておらずエンジニアのセンスや努力や**残業**や**残業**によって暗黙的に賄われていることが多いと思います。

今回のツールでそのようなコストを削減する一助になれば幸いです。

DXでのコスト削減と世間ではバズワード的に語られていますが、仰々しいパッケージ製品を導入しなくても削減できるコストはたくさんあると思います。
エンジニアは、エンジニアリングによって人々の暮らしをよくする仕事をしていますが、自分たちの暮らしもよくすることをもっと考えてもいいと思います。その延長線上に見たことのない人々の暮らしや社会をよくすることはあると思います。

と、難しい話もしましたが、単純に nuxt や vuetify は簡単で楽しいので是非書いてみてください。
今回のツールも行数としてはかなり短いので学習の参考にもご利用頂けると思います。


(さいきょうのソース公開シリーズ)
- [ぼくのかんがえた"ほぼ"さいきょうのガントチャートツール(agile gantt)のソース公開します](https://qiita.com/kaku3/items/9a554779422f58b51591)
FargateからRDSへ接続する方法について

# はじめに
こんにちは、山田です。
今回は、FargateからRDSに接続する方法について記載していきます。

# 全体構成図
全体構成図はシンプルですが以下の通りです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1096361/94bb1c8e-8aa6-9f5d-ab89-f6f7d5a21669.png)
今回使用するデータベースは、``PostgreSQL``とします。

# スクリプト、DockerFileの作成
PostgreSQLに接続するスクリプトを作成します。
今回は接続に成功した場合にCloudWatchLogsにデータベースへの接続の結果をに出力させます。

```python:test.py
# -*- coding: utf-8 -*-
import psycopg2
import os

try:
    print ("Connection successful")　　#接続に成功した場合「Connection successful」と出力
except Exception as e:
    print ("Connection unsuccessful")　#接続に失敗した場合「Connection unsuccessful」と出力

def connect():　　#データベース情報
    con = psycopg2.connect("host=" + "database-2.cnujzrlbzwme.ap-northeast-1.rds.amazonaws.com" +
                           " port=" + "5432" +
                           " dbname=" + "testdb" +
                           " user=" + "postgres" +
                           " password=" + "password")

    return con
if __name__ == '__main__':
    con = connect()
```

同じフォルダにDockerFileを作成する。

```python:Dockerfile
FROM python
RUN pip install psycopg2 cryptography
COPY rds.py /
CMD [ "python", "/rds.py" ]
```
完成したらECRにDockerイメージをプッシュしておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1096361/38a34daa-293f-0202-0ee6-d9556db02fd8.png)


# コンテナの起動
ECSの管理コンソール上より、タスク定義を作成していきます。
起動タイプはFargateを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1096361/0ad5e5fa-2dcc-30bf-4c14-92bc0c09aec9.png)

タスク定義名を入力し、タスクロールは`RDSへのアクセス権限`が付与されているIAMロールを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1096361/acf436ef-db9c-af41-e837-cb229ae7ba04.png)

コンテナの追加よりコンテナ名を入力し、イメージURLには先ほど作成したDockerイメージのURLを入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1096361/7bab512d-00e2-f993-69f7-0f0e407bd8b0.png)

対象のクラスターを選択し、コンテナを起動させます。
# 動作確認
タスクの状態が停止状態になっていることを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1096361/a751177d-924e-32b0-6d8b-25179cd60ac7.png)
停止状態になっているタスクのログを確認し、``Connection successful``と出力されていれば接続が成功しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1096361/4cc1c366-e3e9-5626-48ea-39177516d119.png)
RDKitのSDMolSupplierがファイルを解放してくれない時の対処法

# はじめに
RDKitのSDMolSupplierでSDFファイルを読み込むと、ファイルを解放してくれない時があり、対処方法を調べたメモ

# 現象

例えば以下のようにSDFファイルを読み込んで処理をした後、SDFファイルのファイル名を変更してみる。

```python
tmpname = "hoge.sdf"
sdf_sup = Chem.SDMolSupplier(tmpname)
for mol in sdf_sup:
    if mol is not None:
        <省略>

#ファイル名を変更する。
os.rename(tmpname, "hogehoge.sdf")
```

するとこんなエラーが発生する。
```
PermissionError: [WinError 32] プロセスはファイルにアクセスできません。別のプロセスが使用中です。: 'hoge.sdf' -> 'hgoehoge.sdf'
```

どうやらファイルがきちんと解放されていないようである。

# 対処方法
2点方法が見つかったので、それぞれ記載する。

## 方法1
SDMolSupplierクラスはcloseメソッドを持たないため、明示的に解放することができない。そこで以下のようにSDMolSupplierのインスタンスを保持している変数にNoneをいれ、インスタンスそのものを解放してあげればよい。まさに目から鱗である。

```python
sdf_sup = None
```

これを実行した後であれば、ファイル名の変更がうまくいく。


## 方法2
もう一つは、参考文献にあるように、SDMolSupplierの代わりにcloseメソッドを持つForwardSDMolSupplierを利用する方法である。

この場合withを用いた以下のような処理となる。

```python
tmpname = "hoge.sdf"
with open(tmpname, 'rb') as reader:
    sdf_sup = Chem.ForwardSDMolSupplier(reader)

    for mol in sdf_sup:
        if mol is not None:
            <省略>

# ファイル名を変更する
os.rename(tmpname, filename)
```

これでもうまく解放されることが分かる。
なお、ForwardSDMolSupplierを用いるとzipファイルからも読み込んだりすることができるので便利である。
　

# 参考文献
- [Re: [Rdkit-discuss] Closing a file opened by Chem.SDMolSupplier?](https://sourceforge.net/p/rdkit/mailman/message/36960786/)