OmniAICreator/Qiita-1.07M huggingface dataset:
「テスト駆動開発」をC++で写経するためのCatch2によるテスト実行環境構築

## はじめに
[テスト駆動開発](https://www.amazon.co.jp/%E3%83%86%E3%82%B9%E3%83%88%E9%A7%86%E5%8B%95%E9%96%8B%E7%99%BA-Kent-Beck/dp/4274217884)の写経をC++でやりたい。
そのため、今回は以下のようなテスト環境の構築方法について説明する。
- [Catch2](https://github.com/catchorg/Catch2)を使ってテストを書く
  - 選定理由：個人的に使っている[OpenSiv3D](https://siv3d.github.io/ja-jp/)というフレームワークで使われてて自分も使ってみたくなった
- Visual Studio上でテストを実行する

最終的にこんな感じ：[リポジトリ](https://github.com/AngelCase/cpp-tdd/tree/qiita)

## プロジェクトの作成
テスト対象のプロジェクト`cpp-tdd`もテストを書くプロジェクト`test`も、同じソリューションの中にコンソールアプリとして作る。
![visualstudio_新しいプロジェクト.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3199929/c43d44b5-09c7-9f64-9fa2-9a7e22dad91b.png)
こんな感じの構成になる。
![visualstudio_テストプロジェクト_ソリューションエクスプローラー.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3199929/f248e694-fa0f-a5a5-2ae7-c5eac7d36297.png)

## Catch2のヘッダーを配置する
Catch2の現時点（2023/5/3）最新バージョンはv3だが、シングルヘッダーのライブラリを使いたいのでv2を使う。
デフォルトブランチである`devel`ブランチは最新バージョン（v3）なので、`v2.x`ブランチに切り替えて[catch.hpp](https://raw.githubusercontent.com/catchorg/Catch2/v2.x/single_include/catch2/catch.hpp)をとってくる。
これをテストプロジェクトのディレクトリ配下に配置。

## テストプロジェクトの下準備
以下のコードだけ書かれた`Test.cpp`を用意する。
```c++:Test.cpp
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
```
これで`main()`を自動的に作ってくれる。
自動生成される`main()`では、テスト実行時のコマンドライン引数を受け取る、みたいなことをやってくれてるみたい（[参考](https://github.com/catchorg/Catch2/blob/v2.x/docs/tutorial.md#what-did-we-do-here)）。

## 実際にテストを書く
「テスト駆動開発」の第1章を参考に書いていく。
テスト対象のクラス`Dollar`：
```c++:cpp-tdd/Dollar.hpp
#pragma once

namespace money {
	class Dollar {
	public:
		int amount;
		Dollar(int amount) {
			this->amount = amount;
		}
		void times(int multiplier) {
			amount *= multiplier;
		}
	};
}

```
テストを実行するコード：
```c++:test/MoneyTest.cpp
#include "catch.hpp"
#include "../cpp-tdd/Dollar.hpp"

namespace money_test {
	using money::Dollar;

	TEST_CASE("multiplication") {
		auto five = std::make_unique<Dollar>(5);
		five->times(2);
		REQUIRE(five->amount == 10);
	}
}
```

## F5でテストプロジェクトが実行されるようにする
ソリューションエクスプローラーからテストプロジェクトを右クリック、「スタートアッププロジェクトに設定」。
これでF5でテストを実行できる。
![catch2_pass.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3199929/0f799fbd-cf34-839c-7aae-91f8e81b3e83.png)
これでテスト環境はできあがり！

## おまけ：includeを楽に書く
テスト対象ファイルを`include`するとき、パスの指定がめんどくさい。
```c++
#include "../cpp-tdd/Dollar.hpp"
```
以下のように書きたい。
```c++
#include "Dollar.hpp"
```
手順は以下の通り。
ソリューションエクスプローラーからテストプロジェクトを右クリック、プロパティを開く。
「VC++ ディレクトリ」→「インクルードディレクトリ」、プルダウンから「編集」、「新しい行を追加」で`$(SolutionDir)/cpp-tdd`を追加する。
![visualstudio_インクルードディレクトリ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3199929/df3564fe-d447-ba50-0df0-9135a8b8999c.png)
これでソリューションのルートにある`cpp-tdd`ディレクトリ直下から直接`include`できる。

## まとめ
Catch2でテストを書くための環境構築の手順について説明した。
最終的なファイル構成はこちら：[リポジトリ](https://github.com/AngelCase/cpp-tdd/tree/qiita)

## 参考
[Visual Studio 2019でCatchを使ったC++単体テストを実現する - Gaming Life (hatenablog.com)](https://ai-gaminglife.hatenablog.com/entry/2020/03/29/110651)
[C++テンプレートの学習にstatic_assertだけで『テスト駆動開発』多国通貨を写経する - Qiita](https://qiita.com/tomoyuki-nakabayashi/items/f3ebf940967561a752c8)
[VisualStudioのincludeファイルパス設定について - Qiita](https://qiita.com/akurobit/items/b6c05723c407b6bfd7f1)
Xcode14.3でCommand PhaseScriptExecution failed with a nonzero exit codeが出る

Objective-Cのプロジェクトをビルドしてたら下記のエラーが出てしまったのですが、

```
Run custom shell script '[CP] Embed Pods Frameworks' 0.1 seconds
 AFNetworking.framework" failed: No such file or directory (2)
Command PhaseScriptExecution failed with a nonzero exit code
```

AFNetworking終了したかと思いきやそんなことなくて、Podsのここを修正すればOKです。

![xcodeerror.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4664/e631aced-1aac-2d17-3d9c-c41de1e5d294.png)
Error response from daemon: cannot stop container: ***: permission denied

# 忘れるのでメモ
```zsh
% docker-compose down
Error response from daemon: cannot stop container: nginx: permission denied

% sudo aa-remove-unknown
% sudo systemctl restart snapd.apparmor.service
% docker-compose down
/snap/docker/2746/lib/python3.6/site-packages/paramiko/transport.py:32: CryptographyDeprecationWarning: Python 3.6 is no longer supported by the Python core team. Therefore, support for it is deprecated in cryptography. The next release of cryptography (40.0) will be the last to support Python 3.6.
  from cryptography.hazmat.backends import default_backend
Stopping nginx ... done
Removing nginx ... done
```
別AWSアカウントのEC2インスタンスをまるっとさくっと複製する

# 実現したいこと
AというAWSアカウントで作成したEC2インスタンス（以下、旧EC2）を、BというAWSアカウント（以下、新EC2）でも複製利用したい

### どんなメリットがあるのか
今回の複製とは、簡単に言うとその中身までまるっとコピーされた状態になります。
例えば、旧EC2にあるソースコードが、そのまま新EC2にも反映されています！
開発環境用のEC2を作成して、それをもとに本番環境用のEC2を作成するときなどは便利かなと思います。


# Aアカウントでの作業
まず、AアカウントのAWSマネジメントコンソールにログインします！

## イメージを作成する
コピーしたいインスタンスを選択した状態で、
アクション > イメージとテンプレート > 「イメージを作成」 をクリックします。
![イメージの作成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2619923/bd695ba4-8ddb-f3e2-97ec-573f7c566eee.png)

イメージの名前などを記入します。
下記の画像のように「再起動しない」を「有効化」しないと、EC2が再起動されてしまうので、基本的にはチェックを入れるのが良いのかなと思います。
![スクリーンショット 2023-05-03 9.37.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2619923/235953e0-dbfd-d9f1-6284-196203659875.png)


「イメージを作成」をクリックすると、EC2 > イメージ > AMI に反映されます。
イメージ作成に少し時間がかかりますが、ステータスが利用可能（available）になるまで待ちます。

## イメージを共有する
利用可能になったら、イメージを選択した状態で、 アクション > AMI 許可を編集 をクリックします。
![スクリーンショット 2023-05-03 9.43.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2619923/bcbb0ca9-7985-9e76-4186-15814dbd4a8e.png)

設定は以下にしました。
- AMIの可用性 > 「プライベート」にチェック
- 共有アカウント > アカウントIDを追加 > アカウントBのAWSアカウント番号を入力
- 「アカウント許可を作成するときに、関連付けられたスナップショットに [ボリュームを作成] の許可を追加します」にチェック

保存すると、AアカウントのEC2のイメージはBアカウントへ共有された状態になります！

# Bアカウントでの作業
BアカウントのAWSマネジメントコンソールにログインします

## イメージをコピーする
今のままだと、イメージの所有者がAアカウントになっているので、共有されたイメージをもとに、Bアカウントが所有者となるようにします。

EC2 > イメージ > AMI　から
自己所有ではなく、「プライベートイメージ」を選択します。

Aアカウントから共有されているイメージを選択し、「アクション」から「AMIのコピー」を選択します。
- リージョンを選択
- AMIコピー名を入力
ができたら、「AMIをコピー」をクリックします。


## 所有者の確認
EC2 > イメージ > AMI　から
自己所有 を選択して、コピーされたイメージが表示されていればOKです

## インスタンスを起動
コピーしたイメージからインスタンスを起動します。
EC2 > イメージ > AMI　で、「AMIからインスタンスを起動」を選択します。

※旧EC2で使用しているキーペアがあれば、キーペアを新規作成する必要はありません。
EC2が起動できていれば成功です！！

# おわりに
調べたところAMI自体は料金はかかりませんが、一緒に作成されるスナップショットに料金がかかるようです。
一連の作業が終わって大丈夫そうだったら、AMIの共有を解除したり、削除した方が良いかも知れません。
【Re:マイナス１００億から始めるダメ人間生活】【もうおじさんにはならない】Quiitaでやりたいこと、目標

去る5/1、釜揚げうどんの日にどうしても釜揚げうどん(大)を食べている間、ずっと考えていた。

「どうやったら継続できるのか」、と。

~~１月末に書き始めたQuiitaも、内容がなく、薄すぎたので消してしまった。~~ 
いや。あれこれ書いても仕方がない。
吉野家の焼き鳥丼は美味いし、既に今日はGW初日だし、相変わらず始発電車に乗っている。

Quiitaで、薄っぺらくなく、読み返してためになって、成長を確認できて、早く実務年数とスキルをつけて転職したい。東京都の最低賃金(1072円)から脱出したい。

〜もう時間がない。更新したい記事を以下に記載する。未来の自分、書くのだ。〜

【今後更新する記事】　※未来の自分を信じる
- 東京都の最低賃金と地方によって最低賃金が異なる理由について
- 【命名規則】ケバブケース、パスカルケース、キャメルケース　※個人的には焼きまんじゅうケースが欲しい
- ライフサイクルについて完全に理解した件について　※本当に理解できた！人生始めないとサイクルもわからんな
- 【便利で色々試したい♪】markdown覚書
- Quiitaで目次にリンクつけれるかについて
- 【実装したいシリーズ】ヨルシカのサイトが神ってる
- 【実装したいシリーズ】丸亀製麺のハンバーガーメニューが壺超えて糠漬け
- 【ツンデレ】vueが嫌いだけど好きになりたいことについて
- 【草生やし検定】ダメ人間でも草生やしたい
- CSSでの縦線の書き方
- 【ダメ人間だけど】ポートフォリオ作ってみたい
- アトミックデザインについて
お母さんでもわかる演算子

### 論理積
`item = ex1 && ex2`
ex1の評価がtrueの場合はex2を返し、それ以外の場合はex2を返す。

### 論理和
`item = ex1 || ex2`
ex1の評価がtrueの場合はex1を返し、それ以外の場合はex2を返す。

### 条件演算子
`item = condition ? ex1 : ex2`
conditionを評価し、trueの場合はex1を、falseの場合はex２を返す。 

### 合体演算子
`item = expr1 ?? expr2`
expr１の評価がnullまたはundefinedの場合はexpr2を返し、そうでない場合はexpr1を返す。
Rustのtokioでワーカースレッドを起動する方法

Rustで非同期処理を行うために、`tokio`クレートを使用してワーカースレッドを起動する方法を説明します。以下に示すサンプルコードでは、`tokio::runtime::Builder`を使用してワーカースレッドを4つ作成し、それぞれで異なる待機時間のあとに終了するタスクを実行しています。

```rust
use tokio::runtime::Builder;
use tokio::runtime::Runtime;
use tokio::try_join;

fn main() {
    let local_runtime = Runtime::new().unwrap();
    let runtime = Builder::new_multi_thread()
        .worker_threads(4) // 4つのワーカースレッドを使用するように指定
        .enable_all()
        .build()
        .unwrap();

    println!("Main thread started");

    let thread1 = runtime.spawn(async {
        println!("Thread 1 started");
        // 1秒待つ
        tokio::time::sleep(std::time::Duration::from_secs(1)).await;
        println!("Thread 1 finished");
    });
    let thread2 = runtime.spawn(async {
        println!("Thread 2 started");
        // 2秒待つ
        tokio::time::sleep(std::time::Duration::from_secs(2)).await;
        println!("Thread 2 finished");
    });
    let thread3 = runtime.spawn(async {
        println!("Thread 3 started");
        // 3秒待つ
        tokio::time::sleep(std::time::Duration::from_secs(3)).await;
        println!("Thread 3 finished");
    });

    println!("Main thread finished");

    // 3つのスレッドが終わるのを待ち合わせる
    local_runtime.block_on(async {
        let results = try_join!(thread1, thread2, thread3);
        match results {
            Ok(_) => println!("All tasks have completed."),
            Err(e) => println!("An error occurred while waiting for tasks: {:?}", e),
        }
    });
}
```

## 解説

1. クレートを用意する: `tokio::runtime::Builder` と `tokio::runtime::Runtime`（プログラムで使用されるクレート）をインポートしています。

2. ランタイムを作成する: `Runtime::new()`と`Builder::new_multi_thread()`を使用して、それぞれ`local_runtime`と`runtime`を作成しています。`runtime`では、`.worker_threads(4)`でワーカースレッド数を指定し、`.enable_all()`ですべての機能を利用できるように設定しています。

3. `main` 関数内でメインスレッドが開始され、その後ワーカースレッドが次々と開始されます。

4. 各ワーカースレッドが別々の経過時刻で終了します（Thread 1は1秒後、Thread 2は2秒後、Thread 3は3秒後）。

5. 最後に、メインスレッドが終了し、`local_runtime.block_on`で全てのワーカースレッドが終了されたことを確認しています。結果が`Ok`であれば、すべてのタスクが終了したことを示し、`Err`であればエラー発生時の理由を表示します。

このサンプルコードを実行すると、指定された待機時間が経過した後に、各ワーカースレッドが次々と終了し、全てのタスクが終了したことが表示されます。これにより、Rustのtokioを使用してワーカースレッドを起動し、非同期処理を行う方法を学ぶことができます。
さくらのVPSでRocky LinuxのSELinuxを有効化する

さくらのVPSのRocky Linuxでは、SELinuxがカーネルパラメータから完全に無効化されています。

https://manual.sakura.ad.jp/vps/os-packages/rockylinux-9.html#selinux

よって、 /etc/selinux/config を書き換えるだけでは、有効化できません。

# ディストリビューションの確認

```
$ cat /etc/rocky-release
Rocky Linux release 9.0 (Blue Onyx)
```

# SELinuxのステータスの確認

```
$ sestatus
SELinux status:                 disabled
```

# configファイルの更新

```
$ sudo vi /etc/selinux/config

# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
#     enforcing - SELinux security policy is enforced.
#     permissive - SELinux prints warnings instead of enforcing.
#     disabled - No SELinux policy is loaded.
# See also:
# https://docs.fedoraproject.org/en-US/quick-docs/getting-started-with-selinux/#getting-started-with-selinux-selinux-states-and-modes
#
# NOTE: In earlier Fedora kernel builds, SELINUX=disabled would also
# fully disable SELinux during boot. If you need a system with SELinux
# fully disabled instead of SELinux running with no policy loaded, you
# need to pass selinux=0 to the kernel command line. You can use grubby
# to persistently set the bootloader to boot with selinux=0:
#
#    grubby --update-kernel ALL --args selinux=0
#
# To revert back to SELinux enabled:
#
#    grubby --update-kernel ALL --remove-args selinux
#
SELINUX=disabled　← enforcing に変更
# SELINUXTYPE= can take one of these three values:
#     targeted - Targeted processes are protected,
#     minimum - Modification of targeted policy. Only selected processes are protected.
#     mls - Multi Level Security protection.
SELINUXTYPE=targeted
```

# カーネルパラメータの変更

```
$ sudo grubby --args=selinux=1 --update-kernel=ALL
$ sudo reboot
```

# SELinuxのステータスの確認

有効化されていることが確認できます。

```
$ sestatus
SELinux status:                 enabled
SELinuxfs mount:                /sys/fs/selinux
SELinux root directory:         /etc/selinux
Loaded policy name:             targeted
Current mode:                   enforcing
Mode from config file:          enforcing
Policy MLS status:              enabled
Policy deny_unknown status:     allowed
Memory protection checking:     actual (secure)
Max kernel policy version:      33
```

カーネルコマンドラインに「selinux=1」が追記されていることを確認。

```
$ cat /proc/cmdline
BOOT_IMAGE=(hd0,gpt2)/boot/vmlinuz-5.14.0-70.17.1.el9_0.x86_64 root=UUID=xxxx ro consoleblank=0 console=ttyS0,115200 console=tty0 selinux=1
```
VSCodeでglTFファイルを開く方法

VSCode で glTF 形式の 3D モデルを確認する方法を探していたら、良さげな機能拡張を見つけたので紹介したいと思います。

## インストール方法

VSCode の「機能拡張」を選んで検索ボックスに "glTF" と入力し、この中の **glTF Tools** を選択してインストールを行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759747/0af02d3f-baa5-fd4c-f284-734a360295d3.png)

## 使い方

実際に glTF 形式の 3D モデルを VSCode で確認してみます。
glTF 形式のファイルは以下の Github リポジトリから取得できるので、こちらから拝借したいと思います。
https://github.com/KhronosGroup/glTF-Sample-Models

今回は「DamagedHelmet」という 3D モデルを拝借しました。
https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/DamagedHelmet/glTF

`.gltf` だけでなく `.bin` や `.jpg` など全てのファイルをダウンロードしてきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759747/09468b75-1631-ece5-0f90-c1d6ebcdcd55.png)

ダウンロードが終わったら VSCode で開き、VSCode 上で glTF ファイルを開きます。
そして、画面右上辺りに表示されている円錐っぽいアイコンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759747/feeaa54c-6e09-a9df-8df3-8c5abfc20df4.png)

すると、画面右側に 3D のモデルが表示されるかと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759747/9c62e7fd-d391-9b06-c93b-50eb9664826c.png)

glTF ファイルの中身を編集するとダイレクトにその結果が表示されるので、ちょっとした編集なんかの際に便利かなと思います。

## Debug モード

3D モデル表示中は、円錐っぽいアイコンがあったところに Debug モードに移行する虫のアイコンが表示されています。
これをクリックすると Debug モードに移行でき、ちょっとした確認作業なんかに使えるかと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759747/aefb48c6-d92b-06d7-70c1-f17e0148cca7.png)

## glb 形式でのエクスポート
コマンドパレットを開いて「glTF」と入力し、**glTF: Export to GLB (Binary file)** を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759747/541dfcb8-e0b7-e455-8dce-20535e1bb657.png)

次の画面で glTF ファイルを選択し「Convert to GLB」ボタンを押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759747/5bec6bac-284b-a98e-82e1-1a8bc28b106a.png)

次の画面で 出力先の glb ファイル名（と保存先パス）を入力し「保存」ボタンを押下すると、ファイルが保存されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759747/1b0d0e05-6259-c8f4-5d08-35d44a004545.png)

ここでは glTF を glb にエクスポートする例を紹介しましたが、逆に、glb から glTF へのエクスポートもできるので、
**glTF <-> glb の相互変換ツール**として利用することもできそうです。

## さいごに

実は、（筆者が使っている）Windows 10 Pro であれば glTF / glb ともにファイルをダブルクリックすると Windows 標準の 3D ビューワーが立ち上がり 3D モデルの表示確認ができます。
ただ単純に 3D モデルを確認したいのであれば、この方法でも良いと思います。
MySQL8.0.33のソースコードをビルドした

### はじめに
必要にせまられビルドした。インストールは考慮していない。

### 環境
* CentOS Stream 9(docker quay.io/centos/centos)
* MySQL 8.0.33

### 手順
1\.ソースコードダウンロードして解凍
MySQL Boostは必須。cmake時にダウンロードすることもできるが、MySQL Boost入りのものを使用。
```shell
wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-boost-8.0.33.tar.gz
tar -xzvf mysql-boost-8.0.33.tar.gz
```

2\.作業用フォルダ作成
解凍したフォルダ内でcmakeするとエラーになるので作業用のフォルダを作成。
```shell
mkdir work
```

3\.必要ソフトウェアインストール
実際は、cmake実行時のエラーメッセージの度に追加していった。
```shell
dnf install cmake
dnf install gcc-toolset-12-gcc gcc-toolset-12-gcc-c++ gcc-toolset-12-binutils gcc-toolset-12-annobin-annocheck gcc-toolset-12-annobin-plugin-gcc
dnf install openssl-devel
dnf install ncurses-devel
dnf --enablerepo=crb install libtirpc-devel
dnf install rpcgen
dnf install gcc
dnf install gcc-c++
```
libtirpc-develインストールについては、については、以下の情報を使用

https://centos.pkgs.org/9-stream/centos-crb-x86_64/libtirpc-devel-1.3.2-1.el9.x86_64.rpm.html

4\.workフォルダでcmake実行したのちmake
結構時間かかる。
```shell
cd work
cmake -DDOWNLOAD_BOOST=1 -DWITH_BOOST=../mysql-8.0.33/boost ../mysql-8.0.33
make

```
名前空間（ネームスペース）の同期【C#，.NET6 or .NETFramework】

今回はネームスペースの話をします。

この部分ですね．．．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3355359/56ae9dc9-c58f-064c-590a-dd3cb0f7cf32.png)



このネームスペースとフォルダー構成を合わせているという方も多いと思うのですけど、ファイルを移動とかしているうちにネームスペースとフォルダー構成が合ってないということが起こると思うのですけど、それをVisualStudio2022では自動でリファクタリングしてくれる、同期をとってくれるという機能がついているので、それを見ていきたいと思います。

まずClass1なのでこれなのですけど．．．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3355359/7deff55c-6823-d62e-97a3-2cf3745df38b.png)



AAAフォルダーの下にClass1があるので、このネームスペースは、WinFormsApp1.AAAにしたいわけです。手で「.AAA」って書けばOKなのですけど、そうしなくてもコントロールをしながら「.」ってやると．．．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3355359/efcb1405-44f6-e414-a366-389f263c0bdc.png)



フォルダー構造に合わせてネームスペース変更してくださいって出るので、これを選べばOKです。コントロール押しながら「.」じゃなくても、

こういう電球マークみたいなやつ．．．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3355359/44a3747b-b9a3-f29d-27fe-107ae5c6b41f.png)



が出てきたら、それを押して選んでもらってもOKです。

あとはドラッグドロップ、このソリューションエクスプローラでドラッグドロップしても、例えばルートにおきますと、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3355359/35ecba2e-f84a-9cce-8244-a6c7d3cdd87e.png)



そうしたら移動したファイルの名前空間変えますかって言ってくるので．．．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3355359/8e9d215d-3318-db5c-e605-4f03487cc32d.png)



これではいにしたらネームスペースが変わりますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3355359/b1f8d838-3dbb-5904-a996-cee0ec0d080b.png)



またAAAにかえしても、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3355359/5dbc4da6-73c6-9154-5bcd-e87f208b0f24.png)



AAAになっていますという感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3355359/f9c812db-7a4d-4dca-ff9d-45ceb46d5d53.png)



あとはプロジェクト丸ごと一括でというのもできますね。

Class1とClass2があって、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3355359/5805b91b-8921-e7d6-3117-d50461e906a1.png)



なんか変なネームスペースになっていますという場合も、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3355359/af83f4b5-44dd-95b1-65c0-d9d6eb4e1b47.png)



プロジェクト右クリックしたら名前空間の同期というのがあるので、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3355359/9891764e-ed44-2005-82b5-c2b8252011e0.png)



これ押したら一撃で変わりますね。

一撃で変わるのでちょっと不安だなという場合もあると思うので、そういう場合は1個1個、最初に言ったやり方でやってもらったらいいと思います。一撃で行くぜという人はやってみてください。あと注意点としては、このファイル移動でこう出てくるポップアップのメッセージで変えられるやつは.NETFrameworkではどうも使えないっぽいんです。

Windowsフォームアプリケーション（.NETFramework）というこの.NETFrameworkのやつでやると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3355359/4f2ebb8a-1b49-e193-a096-5b8efda06eb2.png)



ファイル移動しても変更されません。

このフォルダー丸ごと変換にも対応していないようです。

コントロールしながら「.」で、「名前空間変更します」があるので、これは使えそうです。.NET6の方でやる分には最初言ったやり方でできるのでそんな感じで使ってみてください。
Linux/Git,GitHub@学習まとめ

##### Linuxについて

https://qiita.com/mkame/items/fdb0654e6fa859b0cfbc

##### Viについて

https://qiita.com/mkame/items/1bb2ddf5d13bf829106d

##### Gitについて

https://qiita.com/mkame/items/86c351b9d39bb1334e27
vimについてまとめた

本番環境のサーバーには、GUI（マウスで操作する）が存在せず、CUI（コマンドで操作する）しかないことが大半である。CUIを操作するにあたり、ViやVimは避けては通れない。

viとは、Linuxにプリインストールされているテキストエディタのこと。
Linux上にあるテキストファイルをviで開き、内容の修正や確認をすることが多い。
vimは、viに拡張機能を持たせたもの。

ここでは、viのコマンドを備忘録としてまとめておきたい。

```text:sample
#コマンドモード
x       #カーソル位置にある1文字削除
dd      #1行削除
d100d   #100行削除
yy      #行をコピー
p       #コピーした行や文字をペースト
{}      #1ブロック移動
u       #変更を元に戻す
ctrl+t  #変更を進める
gg      #ファイルの一番上に移動
G       #最終行に移動
100G    #100行目に移動
/:      #下方向の文字を検索
?:      #上方向の文字を検索
:q!     #ファイルを保存せず終了
:w      #viは終了せずファイル保存
:wq     #viを終了してファイル保存

#コマンドモードと入力モード間を移動
i       #現在のカーソル位置から入力
I       #現在のカーソル位置から行頭入力
a       #現在のカーソル位置の1つ右から入力
A       #現在のカーソル位置の行末から入力
O       #1つ上に行を追加して入力
o       #1つ下に行を追加して入力
Esc     #入力モードからコマンドモードに移動

#コマンド機能
:sp ファイル名     #画面の水平方向に分割
:vsp　ファイル名   #画面の垂直方向に分割
ctrl+wを2回実行　　#分割した画面間を移動
:%s/old/new/g     #文字列を全て変換(oldのものをnewにする)
:set number       #ファイルの行番号を表示する
:!コマンド         #実行中にコマンドを実行する
:!%:p             #ファイルを実行する(実行する際には、:wでファイルを保存すること)

[各文字の意味]
:   コマンドモード
!   shellコマンドの実行
%   編集のファイルへのリンク
:p  絶対パス(rootディレクトリからのパス(/home/test/test.sh))を表す
```
boolean型にpresence: trueは使えない

# 発端
```rb
validates :is_active, presence: true
```
boolean型のカラムに対して、presence: trueのバリデーションをかけていました。


is_activeをfalseにしてsaveすると．．．
```rb
example = ExampleModel.new(is_active: false)
example.save # => false

example.errors.full_messages
# => ["Is active can't be blank"]
```
***Is active can't be blank*** エラーが起きてしまいました。


# 原因
<strong>presence: trueは空かどうかの判定にblank?メソッドを使っているからです。</strong>
[presence.rb](https://github.com/rails/rails/blob/main/activemodel/lib/active_model/validations/presence.rb)にはこう記述されています。
```presence.rb
class PresenceValidator < EachValidator # :nodoc:
  def validate_each(record, attr_name, value)
    record.errors.add(attr_name, :blank, **options) if value.blank?
  end
end
```
つまり`value.blank?`の結果が`true`ならバリデーションエラーということですね。

今回はvalueにfalseが入っているので、`false.blank?`が実行されることになりますが,その結果が以下です。
```rb
value = false
value.blank?
# => true
```

# 疑問
### falseっていう値入ってるのに空の判定なの？

今度は[blank.rb](https://github.com/rails/rails/blob/main/activesupport/lib/active_support/core_ext/object/blank.rb#L18)を見に行きます。
```blank.rb
def blank?
  respond_to?(:empty?) ? !!empty? : !self
end
```
オブジェクトが`empty?`メソッドを持っていれば`empty?`を実行し、真偽値に変換しています。

しかし、falseのクラスであるFalseClassは`empty?`が使えません。↓
```rb
false.class #=> FalseClass
FalseClass.respond_to?(:empty?) # => false
```
<br>

そのためself（自分自身）であるfalseにnotをつけた`!false`が実行され、`false.blank?`はtrueとなるのです。

`false.blank?`がtrueということは、空だったと判定され、***can't be blank*** のバリデーションエラーが起きるようです。

# 解決方法
```rb
validates :is_active, inclusion: { in: [true, false] }
```
<br>

## 参考記事
https://note.com/ishibai/n/n2c27ff7288e3

https://shinkufencer.hateblo.jp/entry/2020/08/27/234050
UE デバッグコードをビルドに含めたくない場合にどうするか

# 概要
ゲーム開発していると、デバッグメニューを作成したりデバッグ用のユーティリティを作成したりしていきます。
最終的にビルドしてパッケージを作るときに、そういったソースコードを含めたくはないです。
純粋なC++でフルスクラッチしていれば、ビルドコンフィギュレーションを使用して
#if DEBUG ~ #endif
でクラスごと囲み定義を無くすことで対応します。
がUnrealC++の場合、UCLASSがあったりUFUNCTIONがあったりすると **#if  ディレクティブ** で囲むことができません。（正確には **#if WITH_EDITORONLY_DATA** のみ囲めるといった制限があります。）
このようなときどのような対応をすることができるのかを調べたメモです。

## 検証バージョン
UE5.2 preview2

# プラグイン化
UEでは機能ごとにプラグインを作成することができます。
https://docs.unrealengine.com/5.1/ja/plugins-in-unreal-engine/

プラグイン化することでデバッグ用のソースコード一式をゲームプロジェクトから分離します。

## コードの書き方
プラグイン化しているので、**#if UE_BUILD_DEBUG ~ #endif** で囲むなどする必要がありません。
ビルドコンフィギュレーション毎にプラグインを使うか使わないかを **Build.cs** で指定することになります。
```cpp
/**
 * UCLASSもそのまま書く
 */
UCLASS()
class DEBUGMENURUNTIME_API UDebugMenuRootObject : public UObject
{
	GENERATED_BODY()
};

```
## ゲームプロジェクト側からの利用
プラグインを作成しデバッグ用の処理を書いていきました。
ゲームプロジェクト側からプラグインの処理を呼び出さなければなりません。
ゲームプロジェクト側の **Build.cs** にプラグインの依存関係を記述します。
DebugとDevelopmentビルドで有効にしようとするなら以下のように記述します。
```csharp
using UnrealBuildTool;

public class MyProject : ModuleRules
{
	public MyProject(ReadOnlyTargetRules Target) : base(Target)
	{
		bUseUnity = true;
		PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
		PublicIncludePaths.Add("MyProject");
		PublicDependencyModuleNames.AddRange(new string[]
		{
			"Core",
			"CoreUObject",
			"Engine",
			"InputCore",
			"HeadMountedDisplay",
			"EnhancedInput",
			"GameplayAbilities",
			"GameplayTags",
			"GameplayTasks",
		});
		if (Target.Configuration == UnrealTargetConfiguration.Debug ||
		    Target.Configuration == UnrealTargetConfiguration.Development)
		{
			PublicDependencyModuleNames.Add("DebugMenuRuntime");
		}
	}
}
```
デバッグ用の処理を呼び出すゲームプロジェクト側は **#ifディレクティブ** を使って以下のように書きます
```cpp
#if UE_BUILD_DEBUG || UE_BUILD_DEVELOPMENT
```
これでパッケージビルドを行うとデバッグ用のソースコードは **DebugとDevelopmentの時はコンパイルされ、Shippingのときにはコンパイルされません。**

# 懸念
デバッグ専用処理はプラグイン化してコンパイル対象から外すことはできましたが、ゲームプロジェクト側のデバッグ専用関数はどうするべきでしょうか。
プレイヤーキャラクターを無敵にするなどゲームプロジェクト側の処理と関連深い処理はプラグイン側に定義することが難しい場合もあります。

定義はあっても呼び出しがなければDEADCODEとして消えてほしいですが、UnrealC++でどうなるかは確認していません。

# モジュールを使用
モジュールを使用しても同様にコンパイルされない動作になりました。
https://docs.unrealengine.com/5.1/ja/how-to-make-a-gameplay-module-in-unreal-engine/
プラグインとして他のプロジェクトにも使えるようにする必要がないならモジュール化で良さそうです
【Rails】Mysql2::Error: Table '' already existsをDBをリセットせずに対処する

## はじめに
`Mysql2::Error: Table '' already exists`というエラーが発生した際、調べたらそれなりに対処法は出てきたのですが、`db:migrate:reset`すれば解決と言っているものが多かったです。
しかしDBをリセットしたくない状況の時に困りそうだったので、リセットせずに解決できる方法を調べました。

## DBから直接テーブルを削除する
エラーの対象となっているテーブルがダブっていることが原因なので、DBから削除すれば解決できます。
まずDBにログインします。パスワードを設定していれば打ち込んでログインしてください。
```:ターミナル
$ bin/rails db

mysql> 
```
次にテーブルを参照します。
```:ターミナル
mysql> SHOW TABLE;

+-----------------------------+
| Tables_in_myapp_development |
+-----------------------------+
| ar_internal_metadata        |
| relationships               |
| schema_migrations           |
| tasks                       |
| users                       |
+-----------------------------+
5 rows in set (0.00 sec)
```
私の場合はrelationshipsテーブルでエラーが発生したので削除します。
```:ターミナル
mysql> drop table relationships;

Query OK, 0 rows affected (0.03 sec)
```
そしてもう一度マイグレートすれば上手くいきました。
```:ターミナル
$ bin/rails db:migrate
```

## 参考
https://bokunonikki.net/post/2018/0214_rails_mysql_error/
シリアライザを作りたいのでChatGPTさんと勉強する

# 自分でシリアライザ作りたい
ちょっとした “ギガ” も気になるお年頃なので少しでもデータ量を節約できるシリアライザを自作したい。私はZeroFormatterっていうなんかすごそうなシリアライザしか使ったことがないがあれは便利だった。あんなのを作ってみたい。
# 属性…どうやってつかうの？
属性に関して勉強しても、「このコードは誰が書いたか」とかの例しか出てこなくて、シリアライザすごく遠い目標に感じる。そのせいで学習意欲もわかない。
# そうだ、ChatGPT大先生に聞いてみよう
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286137/55dcc03e-bbeb-700e-c945-083a99ecf371.png)
以下は、属性が付けられたフィールドのみをシリアライズする例です。

まず、以下のような属性クラスを作成します。

```csharp
[AttributeUsage(AttributeTargets.Field)]
public class SerializeFieldAttribute : Attribute {}
```

この属性クラスは、`SerializeField`という名前でUnityエディターで使用されるものと似た名前になっていますが、本来は無関係なものです。

次に、シリアライズする対象のクラスを定義します。この例では、`Person`クラスを使用します。

```csharp
public class Person
{
    [SerializeField]
    public int Id;

    public string Name;

    [SerializeField]
    public int Age;

    public Person(int id, string name, int age)
    {
        Id = id;
        Name = name;
        Age = age;
    }
}
```

`Person`クラスには、`Id`と`Age`フィールドに`SerializeField`属性が付与されています。一方で、`Name`フィールドには付与されていません。

次に、以下のようなシリアライズ関数を作成します。

```csharp
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

public static class Serializer
{
    public static byte[] Serialize(object obj)
    {
        var fields = obj.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance);
        var serializeFields = new List<FieldInfo>();

        // SerializeField属性が付与されたフィールドのみをシリアライズする
        foreach (var field in fields)
        {
            if (Attribute.IsDefined(field, typeof(SerializeFieldAttribute)))
            {
                serializeFields.Add(field);
            }
        }

        using (var stream = new MemoryStream())
        {
            foreach (var field in serializeFields)
            {
                var value = field.GetValue(obj);

                if (value is int intValue)
                {
                    stream.Write(BitConverter.GetBytes(intValue));
                }
                else if (value is string strValue)
                {
                    var bytes = System.Text.Encoding.UTF8.GetBytes(strValue);
                    stream.Write(BitConverter.GetBytes(bytes.Length));
                    stream.Write(bytes);
                }
            }

            return stream.ToArray();
        }
    }
}
```

この関数では、`obj`引数に与えられたオブジェクトのフィールドを調べ、`SerializeField`属性が付与されたフィールドのみをシリアライズします。`int`型のフィールドには`BitConverter.GetBytes()`で、`string`型のフィールドには文字列のバイト数を先頭に付加してからUTF-8でエンコードしたバイト列を書き込んでいます。

最後に、以下のようにして`Person`クラスのオブジェクトをシリアライズしてみましょう。

```csharp
var person = new Person(1, "Alice", 20);
var bytes = Serializer.Serialize(person);
```

この場合、`bytes`には`person`オブジェクトの`Id`と`Age`フィールドがシリアライズされたバイト配列が格納されます。

# 本日はこれまで
ChatGPT先生がなんかそれっぽいコードと説明を書いてくれました。これからこのコードが正しいか、どんな関数が使われているかを勉強しようと思います。それにしても(これが正しかったら)凄いプログラムの勉強捗りますよね！ 大先生となら未経験の言語の求人にも応募できそうな気さえしてきます。ChatGPTを子供のころから学習のパートナーにできるこれからの子供は、とんでもなく賢くいろいろなことができる人間になるんだろうなあと、末恐ろしいです。
dataclassのフィールドとして宣言するには型ヒントが必要

# 環境
* Python3.11.2




# 起きたこと
dataclassでクラス`Task`を定義しています。フィールド`frame_count`にはデフォルト値を指定したいです。

```
In [48]: @dataclass
    ...: class Task:
    ...:     name: str
    ...:     frame_count: int = 10
    ...: 

In [49]: Task("foo", 5)
Out[49]: Task(name='foo', frame_count=5)
```

「`frmae_count`には`10`を代入しているので、型ヒントを指定しなくてもmypyは`int`と推論してくれるだろう」と思い、型アノテーション`int`を取り除いてみました。
すると、インスタンス生成時に「引数が多すぎる」というTypeErrorが発生しました。

```
In [51]: @dataclass
    ...: class Task:
    ...:     name: str
    ...:     frame_count = 10
    ...: 

In [52]: Task("foo", 5)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[52], line 1
----> 1 Task("foo", 5)

TypeError: Task.__init__() takes 2 positional arguments but 3 were given
```

# 原因
型アノテーションがないとdataclassのフィールドして扱われないから。

[dataclasses.dataclass](https://docs.python.org/ja/3.11/library/dataclasses.html#dataclasses.dataclass)には、以下の様に記載されていました。

>dataclass() デコレータは、フィールド を探すためにクラスを検査します。 フィールド は 型アノテーション を持つクラス変数として定義されます。 後述する２つの例外を除き、 dataclass() は変数アノテーションで指定した型を検査しません。


# 補足：型アノテーションは何でもよい
dataclassのフィールドとして利用するには型ヒントは必須ですが、Pythonは型を検査しないので、どんな型ヒントでもよいです。もちろんmypyなどには怒られると思いますが。


```
In [53]: @dataclass
    ...: class Task:
    ...:     name: ...
    ...:     frame_count: ... = 10
    ...: 

In [54]: Task("foo", 5)
Out[54]: Task(name='foo', frame_count=5)

```

https://death.andgravity.com/dataclasses
入力フォーム（Input）で文字数（桁数）の制限をかける

## （例）「金額入力フォーム」で４桁（９,９９９円）までしか入力させたくないとき。
### onChangeで`e.target.value.length <= 4`の場合のみステートにセットする。
#### → ４桁までしか入力できなくなる。（4桁までしかステートに保存できなくなる。）
```typescript
<PriceForm
  label={
    <div>
      金額
      <br />
      <div className="ml-14 text-xs">（円）</div>
    </div>
  }
  placeholder="300"
  witdh="w-1/3"
  type="number"
  min={0}
  max={9999}
  value={price}
  onChange={(e) => {
    if (e.target.value.length <= 4) {
      setPrice(e.target.value);
    }
  }}
/>
```
GitHub CodespacesでAWS CDK + ecspressoのコンテナ構築用環境を整備

## はじめに

以前書いたこちらAWS CDKとecspressoを利用してコンテナを簡単に構築する為の記事ですが、

https://qiita.com/yoyoyo_pg/items/5921801e7f674f4e1023

AWS CDKを触った事が無い方や、ecspressoを触った事が無い方にピンと来にくいかもしれないと思い、ハンズオン形式で`cdk deploy`や`ecspresso deploy`が実行できるように、リポジトリを公開しました。

https://github.com/yoyoyo-pg/cdk-ecspresso

単に環境構築手順を`README.md`に記載しても良かったのですが、以前から気になっていた**GitHub Codespaces**を利用し、ある程度環境構築を自動化しましたので、その紹介記事です。

## 用語の再整理

### ecspresso

**ECSサービス、タスクに関わる最小限のリソースをコード管理**する事ができるツールです。

https://github.com/kayac/ecspresso

https://zenn.dev/fujiwara/books/ecspresso-handbook-v2

### GitHub Codespaces

>codespace は、クラウドでホストされている開発環境です。 構成ファイルをリポジトリにコミットすることで、GitHub Codespaces のプロジェクトをカスタマイズできます (コードとしての構成とよく呼ばれます)。これにより、プロジェクトのすべてのユーザーに対して繰り返し可能な codespace 構成が作成されます。

https://docs.github.com/ja/codespaces/overview

以前はベータ版でしたが、現在は一般ユーザーも無料で使えるようになった為、今回初めて利用しました。

開発環境としてコンテナが利用される為、今回は`devcontainer.json`と`Dockerfile`を定義しています。

## 構築自動化の為に用意した内容

プロジェクトの`ルートディレクトリ/.devcontainer/`配下に以下2ファイルを用意しました。

```powershell
cdk-ecspresso-project/
├── .devcontainer
│   ├── Dockerfile
│   └── devcontainer.json
```

```Dockerfile:Dockerfile
ARG VARIANT="18"
FROM mcr.microsoft.com/vscode/devcontainers/typescript-node:0-${VARIANT}
RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" \
  && unzip awscliv2.zip \
  && ./aws/install \
  && rm -rf ./aws awscliv2.zip \
  && npm install -g typescript \
  && npm install -g aws-cdk \
  && npm install -g npm-check-updates
RUN curl -sL -O https://github.com/kayac/ecspresso/releases/download/v2.0.0/ecspresso_2.0.0_linux_amd64.tar.gz \
  && tar -zxvf ecspresso_2.0.0_linux_amd64.tar.gz \
  && sudo install ecspresso /usr/local/bin/ecspresso \
  && rm -f LICENSE README.md ecspresso ecspresso_2.0.0_linux_amd64.tar.gz
```

```json:devcontainer.json
{
  "name": "Node.js & TypeScript",
  "build": {
    "dockerfile": "Dockerfile"
  },
    "postCreateCommand": "npm install"
}
```

## コンテナ構築用環境の構築

**Code**タブからCodespacesを立ち上げるだけで、`.devcontainer`配下の内容を基に構築が始まります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411902/ca04d821-2e31-dce7-851f-c7360cc1759f.png)

- Codespacesが立ち上がってきた状態で既に`cdk`コマンドも`ecspresso`コマンドもインストールされています。  
- 最後に、`aws configure`だけ設定すれば直ぐにお手持ちのAWSアカウントにインフラリソースを構築できる状態となります。
- Codespaces内からAWSアカウントに対しての詳細のコンテナデプロイ手順はリポジトリ内の`README.md`に記載してあります。

<https://github.com/yoyoyo-pg/cdk-ecspresso/tree/master/.devcontainer>

## おわりに

- GitHub CodespacesはローカルPC上のVSCodeと遜色なく使えて、今回初めて使いましたが非常に便利だと感じました。
- 環境構築の備忘録として残しておく事も出来る為、devcontainer自体も今後活用していきたいです。
- 上記環境であれば気軽に`cdk deploy`と`ecspresso deploy`が試せるので、便利さを実感頂けると嬉しいなと思います。

## 参考文献

https://docs.github.com/ja/codespaces

https://github.com/kayac/ecspresso

https://zenn.dev/fujiwara/books/ecspresso-handbook-v2
OrangePi5(debian)でSSHできるようにする

# はじめに

OrangePi5を逐一液晶につながなくてもいいように、SSHできるようにする。要領はRaspberry Piでいうraspi-configとあまり変わらない。

# 環境

- OrangePi5
    - TP-Link TL-WN725Nでネット接続済み
    - SSHの設定は完了済み
- debian (Orange Pi 1.1.4 Bullseye with Linux 5.10.110-rockchip-rk3588)

ちなみに`TL-WN725N`は2.4GHzのwifiしか接続できないが、設定やドライバのインストールなしでUSBポートに挿してそのまま使用することができる。

https://www.tp-link.com/jp/home-networking/adapter/tl-wn725n/

# 結論

下記コマンドを実行する。初回であれば`orangepi`でsudoできる。

```
sudo orangepi-config
```

十字キー、Tabキー、Spaceキー、Enterキーで操作可能。要領はraspi-configと同じ。
まず最初に、`System`を選択する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271381/bdb67f74-78ef-dda6-2842-196a26b618e6.png)

十字キーでSSHへカーソルを移し、Enterキーで選択する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271381/a7ed8581-1b38-25b7-c9b0-f5eabd4e46b2.png)

`PermitRootLogin`、`PasswordAuthentication`、`PubKeyAuthentication`にSpaceキーでチェック`*`を入れる。その後、TabキーでSaveを選択し、Enterキーで決定する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271381/4c6b2ccf-a591-df1a-43fc-d140d0599e79.png)

同じ画面に戻されるため、十字キーで`Cancel`にカーソルを移してEnterキーで選択する。その後、促される通りに再起動する。

## 確認

`ip a`などのコマンドでIPアドレスを調べ、クライアントからsshを実行する。
初回設定であればユーザーは`orangepi`、パスワードも`orangepi`となる。
また、fingerprintについては`y`を入力しておく。

```
ssh orangepi@<IPアドレス>
```

orangepiにそのまま入れればOK。

## 余談

このタイミングではまだ名前解決はされていないため、IPアドレスの代用としてホスト名.localを使ってログインすることはできない。
名前解決してホスト名.localでログインする方法は下記の記事にて記載しました。

https://qiita.com/Inoue_Minoru/items/60b0c3b7a7abe03a500c
NeurIPS2022の枝刈り論文 (2)

# 概要

この記事では、[NeurIPS2022](https://nips.cc/)の枝刈り論文を紹介します。[^1]
[その1](https://qiita.com/kyad/items/35e72f05c9423e8f1b08)

[^1]: 画像や数式は論文から引用しています。

# Pruning’s Effect on Generalization Through the Lens of Training and Regularization

https://openreview.net/forum?id=OrcLKV9sKWp

- 概要：枝刈りで汎化性が上がるか調査した
- 研究機関：Google
- 新規性：汎化性向上は枝刈り以外(繰り返し学習等)にあることを示した。
- キモ：Augmented training algorithm (刈らずに繰り返し学習だけする)で汎化性能向上を切り分けた。
  (補足：OpenReviewでの議論も参照のこと)


# Weighted Mutual Learning with Diversity-Driven Model Compression

https://openreview.net/forum?id=UQJoGBNRX4

- 概要：オンライン蒸留によるモデル圧縮の改良
- 新規性：従来のメモリが多い課題を解決
- キモ：学習の教師・精度で重み共有し、刈る割合を変えて蒸留する。これをpyramidでやる。最適化はbi-level optimizationとする。
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2448931/80b7ae8b-bdf3-dcab-b49d-fa2c16f91549.png)
- 評価：MobileNetV2, ResNetで実験


# SInGE: Sparsity via Integrated Gradients Estimation of Neuron Relevance

https://openreview.net/forum?id=oQIJsMlyaW_

- 概要：枝刈りの評価指標の提案
- 新規性：そのニューロンより先の情報も加味して測る
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2448931/292c18a7-24e5-0056-fe00-ef5b024897a3.png)
- キモ：説明可能性の分野(Grad-CAM等)から取ってきたアイデアのIntegrated Gradientを使った
- 評価：ImageNetで他と比較


# Sparse Winning Tickets are Data-Efficient Image Recognizers

https://openreview.net/forum?id=wfKbtSjHA6F

- 概要：データ数が少ない時、当たりくじからの学習は元々のDNNを上回ることの実証
- 研究機関：Google
- 新規性：データ数が少ない場合で実証した。
- キモ：IMPで当たりくじを見つけ、データ数が少ない場合とInbalanceな場合で実験した


# Most Activation Functions Can Win the Lottery Without Excessive Depth

https://openreview.net/forum?id=NySDKS9SxN

- 概要：強い宝くじ仮説の証明における深さの改良
- 新規性：強い宝くじ仮説の証明時に当たりくじを得る前のDNNの深さが、従来の$2N$から$N-1$で良くなり、幅の係数も対数で済むことを示した。
  ReLU付きのDNNでも適用できるようにした。
- キモ：証明した。
- 評価：ResNet18, TinyImageNetで実験した。


# おまけ

## Axiomatic Attribution for Deep Networks

http://proceedings.mlr.press/v70/sundararajan17a/sundararajan17a.pdf

- 概要：Integrated Gradients. DNNの入出力関係を知る指標
- 学会：ICML2017
- 新規性：SensitivityとImprementation Invariance (同じDNNなら同じ値を取る性質) を満たすべき性質とし、これを満たす指標を提案
- キモ：定義(1)式
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2448931/ce35aaed-edef-0774-1dc6-93c433c01a4d.png)
- 評価：Gradientと比較 (下図は論文のFig. 2より引用)
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2448931/d5ed7f81-4a13-d832-7c39-38c48ff7c9d3.png)
PostgreSQL テンポラリテーブルの削除

A5 Mk2で疑似命令の実行に失敗するとテンポラリテーブルが残って鬱陶しいことに。

下記コマンドで消しましょう。

```sql
drop table if exists "$$sample_table" cascade;
```

sample_tableのところが可変値です。

スキーマ名も指定する場合は下記。

```sql
drop table if exists スキーマ名."$$sample_table" cascade;
```
Vim 入門

軽率に Emacs から Vim に乗り換える事にしました。覚える事が膨大なので操作を忘れた時のメモを書きます。ちなみにこれはまだ Emacs で書いてます。

## :help の使い方

* `:h` と入力すると画面半分にヘルプ画面が出る。
* よく使う操作
    * `:q` help を終了。
    * `Ctrl-]` (コントロールを押しながら `]`): `|bars|` のように縦棒で囲まれた tag (ハイパーリンクの事) にジャンプ。
        * `:h bars` のように `:help` の引数として指定してもジャンプできる。
    * `Ctrl-O`: ジャンプ先から戻る。Old と覚える。
    * `Ctrl-I` または `<Tab>`: 戻った後ジャンプ先に進む。
    * `Ctrl-D`: ページ下半分移動。Down と覚える。
    * `Corl-U`: ページ上半分移動。Up と覚える。
    * `:ju[mp]` : 訪問ヒストリ。この数字を打ってから Ctrl-O や Ctrl-I するとヒストリを移動できる。
* ヘルプ画面の最初の Get specific help に、ヘルプの使い方が書いてある。
    * Normal mode のあるキー(例えば x) の意味を知りたい時: `:h x`
        * 例えば Ctrl-O の意味を知りたい時は `:h ^O` だが、この `^O` を打つときに実際に Ctrl-O と打っても `^` `O` と打っても良い。
    * コマンドラインコマンド(例えば `:quit` の意味を知りたい時。: `:h :quit`

## わからない事リストと解決法

以下ランダムにわけの分からない事を書きます。

* `:help` を起動すると画面が二分割されて見にくい。もう一つの画面に行く方法も分からない。
    * `:h usr_08`: に説明があります。分割画面は Windows と呼ぶらしい。
    * `:only` で画面を一つにできます。
    * `C-w C-w` で隣の Window に移動。
* `:helpgrep` でヘルプを全文検索できるが、次の検索結果に行けなくてわけが分からない。
    * 検索結果は `:h quickfix` というのになるらしい。
        * `:cn` 次を表示, `:cp` 前を表示
        * `:clist` quickfix list 表示
        * `:cc [nr]` quickfix list の nr 番目を表示
    * `:lh[elpgrep]` を使うと quick fix の代わりに location-list というものに検索結果が保存される。
        * `:lne` 次を表示, `:lpr` 前を表示
        * `:lli[st]`: location-list 表示
        * `:ll [nr]`: location-list の nr 番目を表示
* ファイルを複数開けたときに２つ目以降のファイルが見えない。
    * ファイルを複数開けた時、Vim の buffer list と VI 由来の args の二つのリストが作られる。
    * `:h buffer-list`
    * `:ls` リスト表示
    * `:bn[ext]` 次のバッファ `Ctrl-^` トグル
    * `:b [nr]` [nr] 番目のバッファを表示
* 編集するファイルを追加したい。
    * `:hide edit` でいける。
* カーソルを画面の真ん中にしたい。
    * `zz`: `h: scroll.txt` 参照
* 細かいジャンプ
    * `''` を使うと細くジャンプできる。
* たまに `recording q@` みたいな画面が出てどうしたら良いのか分からない。
    * 無意識に `qq` をタイプしていてマクロを `q` レジスタに記録していた。
    * 再度 `q` をタイプすれば脱出出来る。参考: `:q usr_10` 
    * `q{0-9a-zA-Z}` マクロ開始
    * `@{0-9a-zA-Z}` マクロ再生
* 勝手に補完して欲しい。
    * 挿入モードで `Ctrl-P` `:h usr_24` 参照
* シンタックスハイライト
    * `:syntax enable` `:h syntax.txt` 参照
    * どうやら Mac OS 付属の vim に問題があるらしく、`brew install vim` するのが正解らしい。
        * https://apple.stackexchange.com/questions/320287/how-do-i-enable-syntax-highlighting-in-vim-on-mac
* オプションの設定方法が分からない。
    * 例えば検索結果を表示する hlsearch の場合
        * 設定: `:se[t] hlsearc`
        * 解除: `:se[t] nohlsearc`
* デフォルト設定がどこで設定されているか?
    * /usr/local/share/vim/vim90/defaults.vim
    * .vimrc を書く時は`:h 05.3` を参照して defaults.vim を source する。
* 他のアプリにペーストしたい
    * `set clipboard=unnamed` を .vimrm に書く (参考: `:h clipboard`)
* `:ls` などのコマンド結果をコピペしたい。
    * `:read !ls` でいける。
    * `!}` 段落をコマンドにフィルタする。
* たまに`::::[Command ]` みたいな画面が出てどうしたら良いのか分からない。
    * これは `q:` で起動する command-line window というものだった。
    * `:q` で閉じる事ができる。
* ディレクトリを grep したい。
    * `:grep -r 検索語 ディレクトリ` 行番号を追加する `-n` オプションは vim が勝手に追加する。
* 適当な文字数で折り返す。
    * `:set tw=80` `gq}`
* Insert 中にペースト
    * `Ctrl-R"` Insert モード中の Crtl-R は計算機機能など他に面白い機能が沢山ある `:h i_^R`
* Command 中にカーソルを先頭や末尾に移動
    * `Ctrl-B` 先頭 `Ctrl-E` 末尾

## Mac OS 付属 vim より brew の vim の方が良いこと

* Mac OS 付属 vim は defaults.vim が効いていない気がする。
* `syntax enabled` が有効になっている。
* `incsearch` が有効になっている。
OrangePi5(debian)でホスト名.localでSSHできるようにする

# はじめに

OrangePi5でsshする際にIPアドレスを直打ちするのは少し面倒。
なので、名前解決してホスト名でログインできるようにした。

# 環境

- OrangePi5
    - TP-Link TL-WN725Nでネット接続済み
    - SSHの設定は完了済み(IPアドレスでSSHできる状態)
- debian (Orange Pi 1.1.4 Bullseye with Linux 5.10.110-rockchip-rk3588)

# 結論

下記の4コマンドを実行すればOK。

```shell
sudo apt-get install avahi-daemon
sudo systemctl enable avahi-daemon
sudo systemctl start avahi-daemon
systemctl status avahi-daemon
```

また、ホスト名は`sudo orangepi-config`で設定可能。セットアップしてから意図してパスワードを設定していない場合は、`orangepi`をパスワードとして入れればOK。

上下キーとEnterキーを使って`Personal`→`Hostname`を選択すると、好きなホスト名を設定することができる。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271381/b128f35d-1565-eea5-e75c-7433df870b7a.png" width="500px">

一通り入力したらTabキー＆Enterキーで設定を終了する。再起動すればホスト名でSSHできるはず。

# 参考にしたページ

こちらのページを参考にした。orangepi-configの下り以外はラズパイと同じ要領で実施できる。

https://iot-plus.net/make/raspi/ssh-connect-using-mdns/?fbclid=IwAR1v-pj-42HaM_Jr9XKEG85ahJoPMUgzmzeOinJPSKPf5ihfcrgbQzMqBj8
mysqlコマンドメモ(ormを一旦卒業)

orm(gorm:go,SQLAlchemy:python,typeorm:typescript)に頼りすぎたの初歩に戻って勉強！！
MySQLでよく使われる基本コマンドの一覧と、それぞれのコマンドを使用したサンプルコマンドを示します。
ただのメモです笑

| コマンド | 説明 | サンプルコマンド |
|---------|------|----------------|
| SHOW DATABASES | データベースの一覧を表示 | `SHOW DATABASES;` |
| CREATE DATABASE | データベースを作成 | `CREATE DATABASE sample_db;` |
| USE | データベースを選択 | `USE sample_db;` |
| SHOW TABLES | テーブルの一覧を表示 | `SHOW TABLES;` |
| CREATE TABLE | テーブルを作成 | `CREATE TABLE users (id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL, email VARCHAR(255) UNIQUE NOT NULL);` |
| INSERT INTO | データを挿入 | `INSERT INTO users (name, email) VALUES ('Taro Yamada', 'taro@example.com');` |
| SELECT | データを検索・取得 | `SELECT * FROM users;` |
| UPDATE | データを更新 | `UPDATE users SET email='new-email@example.com' WHERE id=1;` |
| DELETE FROM | データを削除 | `DELETE FROM users WHERE id=1;` |
| DROP TABLE | テーブルを削除 | `DROP TABLE users;` |
| DROP DATABASE | データベースを削除 | `DROP DATABASE sample_db;` |
OrangePi5(debian)でリモートデスクトップする方法

# はじめに

OrangePi5でRDPするためのメモを記録します。

# 環境

- OrangePi5
    - TP-Link TL-WN725Nでネット接続済み
    - SSHの設定は完了済み
- debian (Orange Pi 1.1.4 Bullseye with Linux 5.10.110-rockchip-rk3588)

# 結論

SSHでログインし、下記コマンドを実行する。

```shell
sudo set_vnc.sh
```

パスワードの入力が問われたら、任意のパスワードを入れる。
その後、view-onlyパスワードが問われるが、特に需要がなければ`n`にしておく。

```shell
You will require a password to access your desktops.

Password: 

Verify:   

Would you like to enter a view-only password (y/n)? n

New 'X' desktop is *********:1

Creating default startup script /root/.vnc/xstartup
Starting applications specified in /root/.vnc/xstartup
Log file is /root/.vnc/*********:1.log

Killing Xtightvnc process ID 4832

New 'X' desktop is *********:1

Starting applications specified in /root/.vnc/xstartup
Log file is /root/.vnc/*********:1.log
```

クライアントがmacOSであれば、Chromeブラウザのリンク欄やFinderのツールバーから`移動`→`サーバーへ接続`で出てくるURL欄に下記を入れてEnterすればリモートデスクトップでつなげに行くはず。

ちなみにポート番号は`デスクトップ番号+5900`で決まる。デスクトップ番号は`New 'X' desktop is *********:1`の`:`以降の末尾の数字です。

```
vnc://<IP address>:5901
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271381/9583ce9d-71cd-b1ee-e439-56928729abcb.png" width="500px">

パスワードは、`set_vnc.sh`を実行したときのパスワードを入れる。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271381/654eb61f-bc6d-c0e0-ae16-cfd49783db55.png" width="500px">

リモートデスクトップの画面が出てくればOK。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271381/60eab906-36a2-415a-dc76-9dde116d67f1.png" width="600px">

# すぐterminateされてしまうとき

Jetson系と同じアプローチで設定しようとして下記のパッケージをインストールしていると失敗する。

```
- tigervnc-common
- tigervnc-standalone-server
- tigervnc-scraping-server
```

もしインストールされていた場合は、下記コマンドで設定ファイルごと消しておく。
消えたら困るファイルがある場合はご確認の上、慎重に...

```shell
sudo apt-get --purge remove tigervnc-common tigervnc-standalone-server tigervnc-scraping-server
```

# 実を言うと...

公式マニュアルの`OrangePi_5_RK3588S_User Manual_v0.7.pdf`にて`sudo set_vnc.sh`の方法について記載されている。

https://drive.google.com/drive/folders/1Bre2q0bGgXQuQlYaYDMvwstpvtHLmcgX

このマニュアルを見ずにJetsonと同じ要領でtigervncを入れようとしたため、詰まってしまった。

# おわりに

公式マニュアルはちゃんと読もう！
【Mac】コマンドでFinderを開く方法

## カレントディレクトリをFinderで開く
次のコマンドを使用することでカレントディレクトリをFinderで開けます。

```terminal
open .
```

## 指定したディレクトリをFinderで開く

次のコマンドを実行することで指定したディレクトリをFinderで開けます。

```terminal
open path/to/dir
```
Node.js v18.16.0 npm ERR! code ECONNREFUSED #npm iコマンドがエラーで返ってくる時の対処方法。

# 追記 バージョンダウンしました。 2023年5月18日

Node.js v18.16.0 をバージョンダウンしました。

ネットワーク接続エラーが外でも発生したので
Node.js v18.16.0 が原因ではないと思いました。
そこで、

Windows 環境から Node.js を完全に削除する方法をやってみた | DevelopersIO

https://dev.classmethod.jp/articles/completely-uninstall-nodejs-from-windows/

を参考にNodeを完全アンインストールして。

> node -v
v16.20.0

をインストールしました。

現在の所、エラーが起こった部分は乗り越えましたが、まだ観察中です。

Node 14->18->16と移行したことになります。

追記終了 2023年5月18日


Node.js v18.16.0(推奨版)をインストールしたところ下記のエラーが発生しました。
解決方法がちょっと特殊だったのでメモしておきます。

# エラーログ

```エラーログ.log
npm i
npm ERR! code ECONNREFUSED
npm ERR! syscall connect
npm ERR! errno ECONNREFUSED
npm ERR! FetchError: request to https://registry.npmjs.org/@supabase%2fauth-helpers-nextjs failed, reason: connect ECONNREFUSED 2606:4700::6810:1123:443
npm ERR!     at ClientRequest.<anonymous> (C:\Users\＊＊＊＊＊\AppData\Roaming\npm\node_modules\npm\node_modules\minipass-fetch\lib\index.js:130:14)
npm ERR!     at ClientRequest.emit (node:events:513:28)
npm ERR!     at TLSSocket.socketErrorListener (node:_http_client:502:9)
npm ERR!     at TLSSocket.emit (node:events:525:35)
npm ERR!     at emitErrorNT (node:internal/streams/destroy:151:8)
npm ERR!     at emitErrorCloseNT (node:internal/streams/destroy:116:3)
npm ERR!     at process.processTicksAndRejections (node:internal/process/task_queues:82:21)
npm ERR!  FetchError: request to https://registry.npmjs.org/@supabase%2fauth-helpers-nextjs failed, reason: connect ECONNREFUSED 2606:4700::6810:1123:443
npm ERR!     at ClientRequest.<anonymous> (C:\Users\hi\AppData\Roaming\npm\node_modules\npm\node_modules\minipass-fetch\lib\index.js:130:14)

```

## 環境

Windows10
VSCode
Node.js v14.??.??

※Node.js v14.??.??では npm i は成功していました。

## ダウンロード
Node.js

https://nodejs.org/ja

ダウンロード Windows (x64)
18.16.0 LTS
推奨版

## 再現手順

1. Node.js v14.??.??をインストールしていた。
1. バージョンアップするため Node.js v18.16.0 をインストールを実行
1. npm i を実行
1. エラーが表示される。

## 原因

IP アドレスとドメイン名が紐づいてなかった。

## 解決方法

`C:\Windows\System32\drivers\etc\hosts`

このファイルを VSCode で開き、下記のように追記します。

```hosts
104.16.20.35 registry.npmjs.org
104.16.20.35 registry.npmjs.org

```

`npm i`を実行すると無事にインストールできました。

## 参考

npm で ECONNREFUSED とか言われた - Qiita

https://qiita.com/raiga0310/items/ba198156cfb5a5f8462b

※この Qiita 記事は Mac の場合の対応方法でした。
見えているのを検出するのではなく、出現を予見する検出の必要性

車載関係の物体検出を調査していて気がついたことがある。
そのことを述べようと思う。

# 検出の目的は、事故を未然に防ぐこと
- 安全な運転をするには、安全な運転者の注意の向け方をまねることが必要だ。
- 停車中のクルマがあったときには、そのクルマの先には、人が隠れているかもしれない。
- そういう可能性を検討して、人が出てきたときにも、安全に停止できるようにクルマの速度を落としている。


# 検出問題としてとらえてしまうと起こること
- 狭い意味の歩行者検出としてとらえてしまうと、明らかに人であることがわかってからの対応になってしまう。
- 十分に人であることがわかりやすい状況での検出率の評価に、評価の重みをおいてしまいやすい。
- クルマに隠れている状況から、半分人が見ていている状況での検出の評価データが少なくなってしまう。
- そこで急に人が出てきたら、どれくらいで止まりうるかを評価できない。

# 自車の走行に影響を与える人が隠れているかもしれない場所の検出問題
- 人が隠れているかもしれない場所
- 人が隠れているかもしれない場所への距離
- 人が隠れているかもしれない場所への到達時間（＝衝突予想時間）
- 「かもしれない」運転
- おそらくは、カメラ画像から距離を推定し、距離の不連続箇所から、そこに人が出てくるかもしれないという場所という候補をつける。
    - 動画のシーケンスで、実際に人が出てきたかどうかは重要ではない。
    - そこから人が出てきうるかどうかを判断することである。

## 付記
- 自動運転の見返りが期待しやすい自動車専用道路での自動運転にだけ注力しすぎると、人がいる分野での基礎的な技術の開発がないがしろになって、技術的な部分を他社に特許でがんじがらめにさせられてしまう可能性があることに注意しなくてはならない。

 
[「かもしれない運転」とは？「だろう運転」がおこす動静不注視の事故](https://www.zurich.co.jp/car/useful/guide/cc-driving-attitude/)
Azure StorageにSASを使用してアクセスする

# はじめに
溜まっていた下書きの棚卸しです🤦‍♂️
Azure BlobにSAS(Shared Access Signature)を使ってリソースにアクセスしてみたいと思います。
その中でも今回はユーザ委任SASを使ってアクセスします。

https://learn.microsoft.com/ja-jp/azure/storage/common/storage-sas-overview

# Azure Storageにアクセスする方法
Azure Storageに格納されているファイルには、クライアントがHTTP/HTTPS経由でアクセスします。
Azureでは、格納されているデータにアクセスするための各クライアントの承認要求を確認します。
BLOBストレージに使用可能なオプションは4つあるみたいです。
* パブリックアクセス
* Azure Active Directory(Azure AD)
* 共有キー
* Shared Access Signature(SAS)

# Shared Access Signature(SAS)とは
読取り専用/書き込みアクセス、有効期限など、Azure Storage内のファイルへの詳細なアクセスを許可できます。
Azure Storageでは、次の3種類のShared Access Signatureがサポートされています。
* ユーザ委任SAS　★今回はこちらを使ってアクセスしたいと思います
* サービスSAS
* アカウントSAS

# SAS認証の生成してアクセスする
①テストデータをBlobに配置します
![スクリーンショット 2023-05-02 222042.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819390/999a0b1f-9aec-a0c4-991c-9b498cbeae3b.png)


②PowerShellを起動し、Azureに接続します
```powershell
# Azure PowerShell要インストール
Connect-AzAccount
```

③トークン生成用のコマンドを作成します
```powershell
# ストレージコンテナに格納されているファイルにSASを付与する
$RG = "<リソースグループ名>";

# ストレージアカウント名を設定
$storageAccountName = "<ストレージアカウント名>"

# ストレージアカウントのキーを取得
$storageAccountKey = (Get-AzStorageAccountKey -ResourceGroupName $RG -Name $storageAccountName).Value[0]

# ストレージアカウントのコンテキストを作成
$ctx = New-AzStorageContext -StorageAccountName $storageAccountName -StorageAccountKey $storageAccountKey

# コンテナ名を設定
$containerName = "images"
$BlobName = "BodyPart_5c618d0e-dde5-48dd-baa9-13da7d77753a.jpg";

# SASの有効期限を設定
$expiry = (Get-Date).AddMinutes(5);

# SASの権限を設定
$permissions = "r"

# SASを生成
$sas = New-AzStorageBlobSASToken `
        -Container $containerName `
        -Blob $BlobName `
        -Permission $permissions `
        -ExpiryTime $expiry `
        -Context $ctx `
        -FullUri

# SASを出力
Write-Output $sas
```

④アクセスしてみます
トークンありだとファイルがダウンロードできることが確認できます。
SASトークンなしだとエラーが出ることが確認できます。
![スクリーンショット 2023-05-02 222932.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819390/c46569ff-83a1-2a2f-650f-f191d69f2bbd.png)

# 所感
今回はBlobにSASを付与することでアクセス制限ができ、セキュリティを強化することを学びました。
外部からBlobにアクセスしたいときや、Congnitive ServiceなどのAIサービスでSASトークンを付与することでより安全にアクセスできると思います。
(いずれ実装してみたいです...)
また実装するときは、有効期限のサイクルを意識する必要があると思うので気を付けたいと思います。
また何か学んだことがあれば投稿したいと思います。