OmniAICreator/Qiita-1.07M huggingface dataset:
【ML初心者アドベントカレンダー】モデルのパフォーマンス分析について


# この記事の目的

モデルのパフォーマンス分析について、必要な知識を学ぶ。


# モデル評価手法



混同行列については過去記事でまとめてるので、それ以外のモデル評価手法についてまとめます。

https://qiita.com/ei_540/items/b5a49d463345ac972cb7

## ヒートマップ

混同行列を視覚化するためのグラフのこと。赤外線のあれみたいに、レインボーな色の濃淡で値を表現する。
モデルがどのクラスを正しく予測し、どこで誤っているかを視覚的に把握することができる。

Amazon QuickSight で作成できるらしい。（機械学習のメトリクスにも適用できるのかな？）

https://docs.aws.amazon.com/ja_jp/quicksight/latest/user/heat-map.html

## 二乗平均平方根誤差 (RMSE)

回帰モデルに使用する。
予測値と実測値の差（損失）の平均的な大きさを表す、損失関数のひとつ。
誤差の大きさを直感的に把握できるが、外れ値に大きな影響を受ける。

https://docs.aws.amazon.com/ja_jp/machine-learning/latest/dg/regression.html

https://ja.wikipedia.org/wiki/%E4%BA%8C%E4%B9%97%E5%B9%B3%E5%9D%87%E5%B9%B3%E6%96%B9%E6%A0%B9%E8%AA%A4%E5%B7%AE

## ROC曲線 (Receiver Operating Characteristic)

分類モデルに使用する。
モデルの予測能力をグラフで見る方法で、性能を視覚的に評価できる。

## ROC曲線下面積 (AUC)

こちらも分類モデルに使用する。
ROC曲線の下の面積を指し、値が1に近いほど性能が良い。



# モデルの過学習と学習不足を特定するには？

モデルが過学習状態か学習不足状態かは、損失の様子からある程度予測ができる。 


| 状態 | トレーニングデータの精度|検証データの精度|損失の様子|
| --- |---|---|---|
| 過学習|高い|低い|トレーニングデータの損失が減少し、検証データの損失が増加している|
| 学習不足|低い|低い|トレーニングデータの損失と検証データの損失が高いまま変化しない|
| 良いモデル|高い|高い|トレーニングデータの損失と検証データの損失が近い値でしかも低い|


## 過学習への対策

主に以下のような手段がある。

**データを増やす**

https://qiita.com/ei_540/items/cea988f32693246eb2dc#%E7%94%BB%E5%83%8F%E3%83%87%E3%83%BC%E3%82%BF%E3%82%BB%E3%83%83%E3%83%88%E7%AD%89%E3%81%AE%E3%82%AF%E3%83%A9%E3%82%B9%E4%B8%8D%E5%9D%87%E8%A1%A1%E3%81%AB%E5%AF%BE%E5%BF%9C%E3%81%99%E3%82%8B%E3%81%9F%E3%82%81%E3%81%AE%E6%89%8B%E6%B3%95

**正則化を利用する**

https://qiita.com/ei_540/items/0f4eec476823c665f86a#%E6%AD%A3%E5%89%87%E5%8C%96%E6%89%8B%E6%B3%95%E3%81%AE%E5%88%A9%E7%82%B9

**早期停止**

https://qiita.com/ei_540/items/0f4eec476823c665f86a#%E6%97%A9%E6%9C%9F%E5%81%9C%E6%AD%A2

その他、モデルが複雑になりすぎないようにする（パラメータを減らしたり、レイヤーを減らしたり）など。

## 学習不足への対策

**特徴量エンジニアリングを行う**

https://qiita.com/ei_540/items/dbbd0d1a99e47c30e198#%E7%89%B9%E5%BE%B4%E9%87%8F%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%83%AA%E3%83%B3%E3%82%B0

**エポック数を増やし、トレーシング時間を延長する**

https://qiita.com/ei_540/items/0f4eec476823c665f86a#%E3%82%A8%E3%83%9D%E3%83%83%E3%82%AF


その他、モデルをより複雑にする（パラメータを増やしたり、レイヤーを増やしたり）など。
【Unity】Arbor3で各ステートのコンポーネント取得が毎回大変なので、楽な方法を探す

# はじめに
この記事は[Hamster Output Advent Calendar 2024](https://qiita.com/advent-calendar/2024/game_hamster)の13日目の記事です！

ステートマシンを使っていて面倒...や大変と感じることはコンポーネント取得周り。Idle,Move,Attck見たいにステートがどんどん増える度に書くのが面倒になったので楽したいと思います。

# 参考にしたリンク
https://arbor.caitsithware.com/

# Arbor3でコンポーネント取得する方法
GetComponentでの取得や、インスペクタから直接アタッチ、ParameterContainerからのアタッチなど様々な方法がありますが、回数が増えてくると段々面倒くさくなります。稀に、インスペクタからアタッチした方が楽なコンポーネントもあります。

Animatorの取得はインスペクタからアタッチするとLayer指定やState指定ができるのでむしろ楽。ParameterContainerやGetComponentだとできない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588406/f26ba929-f613-43bc-5016-4529c2ac93ac.png)

# 結論
GetComponentを利用する or VContainerを利用する

# GetComponentを利用する場合
ステートのStart内にGetComponentを追加するだけなので簡単です。ArborFSMのStartが実行されるタイミングについてですが、ステートが実行されたタイミングで呼び出されるのでMonoBehaviourのStartと実行するタイミングが少し異なります。

# VContainerを利用する
GetComponentよりもさらに楽です。LifetimeScopeを作ってコンポーネントを登録して、Auto Injectの対象に入れて各ステートに[Inject]をつけるだけです。

ステートが沢山あったとしても、自動で取得してくれるので本当に楽です。というかArborFSMもInjectの対象なんだ.....
![スクリーンショット 2024-12-24 154224.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588406/ef4aa6f5-d9bb-bebd-aa3b-24fb543e9642.png)

# 使うならどっちが良いか
GetComponentの場合、GameObjectの階層が違うだけで利用できなくなる不便さとかもあったりと面倒な部分もあるので、使うならVContainerの方が良いと思います。

VContainerをとりあえず使うだけの記事も上げているので良ければ.............

https://qiita.com/game_hamster/items/41bbad3b4210e402be1f
[初学者向け]Rustを触ってみよう③ ~関数編~

## はじめに
こんにちは！ITスクールRareTECHにてCS（Customer Support）を担当している池村です。
今回の記事はRustの関数周りについて学習していった内容についてです。一応初学者向けなので、関数ってなんだ？という方向けの記事でもあります。

![62b7e9a5-449b-4542-aa0d-6ba4cb0dbd3f_720.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3798971/5d75c2a1-6563-0c91-9f78-f38e653a0dbf.png)


## 関数ってなんだ？という人に向けて
私は関数って何？と言われると基本的にこう答えています。

:::note info
関数は、何かを受け取って何かを返すものです。
:::

有識者ならわかると思いますが、何かを受け取らないこともあるし、何かを返さない場合もあります。なので基本的に私の説明は半分くらいしか正解ではありません。ただ、一言で答えるとするとこれがベストだと思っています。

関数は主に4種類あって、以下になります。
- 引数と戻り値があるもの（何かを受け取って何かを返すもの）
- 引数だけあるもの
- 戻り値だけあるもの
- 引数も戻り値もないもの

### 引数
この関数が呼び出される（実行される）時に渡される『**値**』です。何かしらのデータなことが多いです。

:::note warn
関数に関数を渡すようなこともできますが、初学者は理解しなくて大丈夫です。
:::


### 戻り値
この関数が呼び出された時に呼び出した先に返す『**値**』です。何かしらのデータなことが多いです。

### エクセルなどでも使われていますね
よく使うと言ったらエクセルでのSUM関数とかでしょうか？別にエクセルの中身に詳しいわけではないのですが、SUM関数を使うと、数字を二つ受け取ってその足し算した結果を返してくれます。
```Bash:SUM関数
SUM(12, 15)

結果：27
```

この12と15が引数で、27が戻り値です。内部的に足し算して返す関数が定義されているんですね。（内部見たことないのでわかりませんが、、）

## まずは関数を作ってみよう
関数の定義は`fn`から始まります。
今回は`main`関数の下に定義していきます。
```Rust:コードの内容
fn main() {
    println!("Hello World!");
    test_func()
}

fn test_func() {
    println!("Hello Rust!")
}
```

上記のコードを`cargo run`で実行します。結果は以下です。
```Bash:実行結果
Hello World!
Hello Rust!
```
`main`関数の中で、定義した`test_func`関数が呼び出されています。
今回は引数も戻り値もない関数になっています。

## 他の関数の定義
では4種類ある関数の残りの三つも定義してみましょう。
まとめてコーディングしていきます。

```Rust:書き方
fn test_func1() {
    println!("Hello Rust!");
}

fn test_func2(x: i32) {
    println!("xの値は{}", x);
}

fn main() {
    println!("Hello World!");
    test_func1();
    test_func2(20);
    let a_num = test_func3();
    let b_num = test_func4(a_num);
    dbg!(a_num);
    dbg!(b_num);
}

fn test_func3() -> i32 {
    15
}
fn test_func4(x: i32) -> i32 {
    x + 15
}
```
`test_func2~4`の関数を追加しました。
書いていて思ったのは、変数の型・戻り値の型をしっかり定義しないとダメなこと、`return`での戻り値は関数内の途中で返したい場合に使うということが少し特殊だなと。あと、コンパイラ的には`main`の関数より上に別の関数を定義しても問題なく実行されるということを押さえていきたいです。

```Bash:実行結果
Hello World!
Hello Rust!
xの値は20
[src/main.rs:15:5] a_num = 15
[src/main.rs:16:5] b_num = 30
```

:::note warn
処理自体はとても簡潔なものなので、どの関数がどういった動きをしているかは説明を省きます。
:::


## 式と文のお話
Rustは式指向言語ということで、Goのような文指向言語とは少し違います。
公式のドキュメントを見ていて、ここややこしいなと思いました。
#### 式
式は必ず値を返します。
```Rust:書き方
1 + 2
15
num
```
上記のようなものが式です。
では以下のコードはどうでしょう？
```Rust:書き方
fn main() {
    let y = 6 + 2;
}
```
:::note info
上記の場合、`6 + 2`の部分は式ですが、変数定義は文、`;`がつくのも文で、全体的に見て文です。
:::

Javaとかを触ってきた人（自分も含めて）にとって、`;`は文の終わりではあるんですが、Rustでは式を文に変更して値を切り捨てるという挙動をとることがあるようです。

```Rust:例えば
let y = {
    6 + 2
};

let x = {
    6 + 2;
};
```
この場合、上の変数`y`には8が入るのですが、下の場合変数`x`には`()`（ユニット型）が代入されると。ユニット型は値がないことを表す型ですね。8は切り捨てられました。

:::note warn
Rustの場合は式指向言語という認識をしっかり持って、これは式か文かをしっかり把握していかないと混乱していきそう。
:::
※チェーン代入（x = y = 6）とかがダメなのはGoと一緒なので問題なし。

:::note info
式指向言語は制御構造が値を返すもの。文指向言語は制御構造がただの命令文という違い。制御構造は`if`とか`for`のこと。
:::

```Rust:書き方
fn five() -> i32 {
    5
}
```
上記の形で全く問題ない関数ですが、違和感感じますね。



## おわりに
個人的な好みですが、プログラミングで一番好きなのは関数です。学習初期、関数の仕組みを理解したら『なんて美しいんだ！😭』となりました。何かを受け取って何かを返す。それだけで色々できてすごい。ちょっと変態のようなこと書きましたが、気にしないでください。（笑）
あとRustの公式ドキュメントの説明読みやす！😗
Luaでパターンマッチング

Lua ではテーブル同士の比較は同一比較(アドレス比較)になり、内容で比較することはできない。
tamale.lua を使うと、
『内容、構造を調べ、当てはまるなら、続く文(ブロック)を実行する』
ができる

[注意]このライブラリはもうずっとメンテナンスされていないため、lua5.2 から非対応の古いライブラリだが、
module 宣言を消し、return するテーブルを作ってそれに関数をはやしてゆけばlua5.4でも使うことができる

## サンプルコード

2048でタイルを左に詰めるコード(yuescript)
(lume.sliceはstring.subの配列版)
(...はテーブルを展開する)

```.moon
moveLeft = (ss)->
	recur, a = nil, tamale.var'a' -- matcher自体を再帰関数にするためには前方宣言が必要。
	recur = tamale.matcher{
		{[], []}
		{[0,], partial: true, (c)-> [...recur(lume.slice(c.input, 2)), 0]}
		{[a, a], partial: true, (c)-> [c.a*2, ...recur(lume.slice(c.input, 3)), 0]}
		{[a, 0, a], partial: true, (c)-> [c.a*2, ...recur(lume.slice(c.input, 4)), 0, 0]}
		{[a, 0, 0, a], (c)-> [c.a*2, 0, 0, 0]}
		{[a,], partial: true, (c)-> [c.a, ...recur(lume.slice(c.input, 2))]} -- lume.concatで{}を扱うとそのままつける？バグがある？
	}
	[recur(s) for s in *ss]

```

## ルール集の記述

- matcher にルール集(配列)を渡すと関数を返す
  上から(先頭から)順に調べる
- else 節の表記法はない(`[], partial: true`で代用)(`->true`でも)。else が何もしないなら省略できる
- ルールからmatcherを再帰呼出しをするには前方宣言が必要

## その中の一つのルールの記述

- 一つのルールは`{[1] = パターン, [2] = 実行する関数, partial = true}`のように配列に付加情報を同居させる形
- 数値などのほか`tamale.var'x'`で変数を定義、キャプチャできる
- `when = condition` でガード節を付けられる
- `partial = true`で一部のキーだけを調べてパターンに当てはまるか判断するようになる
  luaでは配列を記述することは`{[1] = .. , [2] = ..}`というキーがあることと同じなので、パターンに配列を書くことは『先頭から(パターンに記述した配列の長さ分)調べる』という意味になる
- 実行する関数はキャプチャしたものを引数として受け取る
  - `local a = tamale.var'a'`とすると、パターン中で使え、`capture.a`で受け取れる
  - `capture.input`でmatch関数に渡されたテーブルを受け取れる

### その他のパターンの書き方

- tamale.P で正規表現での比較がある
- 関数: マッチ関数に渡された値(input)を引数に呼ばれ真であれば成立

### その他の付加情報

- ids に値のリストを渡すと、同一比較(アドレス比較)になる
- index で優先的に比較するよう関数を定めることができる(高速化)?
- debug は stderr に色々出力するだけで動作は変わらない？

### 実行する関数の代わりに戻り値を書ける

- (関数の場合は多値が返せるが)返せる値は１つ、２番めには情報が返る

## 注意点

- N+1 のような便利な記述はない
- 網羅性チェックもない
- 無限を表すテーブルを渡すとスタックオーバーフローする(たとえ最初の１つ２つしか調べなくても)

## その他
他にもメンテナンスされていないライブラリがある

- luacombine(組み合わせ)
  少しの修正で動く
【Web】IDS・IPSとWAFの役割



## はじめに

こんにちは、アメリカで独学でエンジニアを目指しているものです。

現在Webについて勉強しているのですが、セキュリティについて少し学んでおりIDS・IPS・WAFという言葉が出てきたので、それについてまとめようと思い記事にしました。

---

## 1. IDS・IPSとは

### 1.1 IDS（Intrusion Detection System）

IDSは、ネットワークやホスト（サーバ／PC）へ不正アクセスや攻撃が行われていないかを\*\*“検知”\*\*するための仕組みです。たとえば以下のような機能を持ちます。

ショッピングサイトなどで、IPSの誤検知で遮断されてしまった場合、販売機会が損失するためIDSを採用する場合があります。

- ネットワーク上のパケットを監視し、不正アクセスや攻撃を検知します。
- 検知内容のログ化、警告の通知を行います。
- 管理者に対して攻撃発生の可能性を示唆します。

### 1.2 IPS（Intrusion Prevention System）

IPSは、IDSが検知した不正なアクセスや攻撃に対して自動的に防御アクションを行うシステムです。

ネットバンキングのような攻撃を受けたときのリスクが非常に高いサイトではIPSが使用されることがあります。

- 攻撃を検知した場合、対象の通信をブロックしたり、パケットを破棄したりします。
- 事前に定義されたルールに従ってリアルタイムで対処が可能です。

**違い**:

- IDSは「検知」に特化しており、実際の防御・遮断は別のシステムで行います。
- IPSは検知と防御を同じシステムで行うため、より迅速に対処が可能です。

---

## 2. シグネチャー型とアノマリー型

IDSやIPSがどのようにして不正行為や攻撃を見つけるかについては、**シグネチャー型**と**アノマリー型**の二つの手法が知られています。

### 2.1 シグネチャー型

\*\*既知の攻撃パターンや特徴（シグネチャー）\*\*をあらかじめデータベース化し、それとマッチする通信や挙動を検知して不正と判定します。SYNFlood攻撃のような特徴的な攻撃を検出できます。

- **メリット**:
  - 既知の攻撃を高精度で検知できます。
- **デメリット**:
  - 未知の攻撃には弱いです。
  - 定期的なシグネチャー更新が必要です。

### 2.2 アノマリー型

**正常な通信や挙動のパターン（ベースライン）を学習**し、そのベースラインから逸脱した行動を検知して不正と判断します。

アクセス数が急増するF5攻撃などを検出できます。

- **メリット**:
  - 未知の攻撃でも、異常なふるまいとして検出できる可能性が高いです。
- **デメリット**:
  - 誤検知（False Positive）が多くなる場合があります。

### **組み合わせの重要性**

通常の運用では、シグネチャー型とアノマリー型を適度に組み合わせることで、既知・未知の攻撃双方に対応できる柔軟な防御体制が構築されます。

---

## 3. WAF（Web Application Firewall）とは

WAFとは、**Webアプリケーションへの攻撃**を検知・防御するファイアウォールです。HTTPやHTTPSなどのアプリケーション層の通信を分析し、Webサーバへの攻撃（SQLインジェクションやクロスサイトスクリプティングなど）をブロックすることを目的としています。

### **従来のファイアウォールやIPSとの違い**

- 従来のネットワークファイアウォールやIPSでは、OSI参照モデルの下位レイヤ（ネットワーク層やトランスポート層）を中心に監視します。
- Webアプリケーション特有の攻撃（例: SQLインジェクション）には対応が不十分です。
- WAFは、Webサーバへのリクエスト内容自体を精査し、不正なパターンが含まれていないかを検査します。

---

## 4. ネガティブセキュリティモデルとポジティブセキュリティモデル

WAFのセキュリティモデルとして、「ネガティブセキュリティモデル」「ポジティブセキュリティモデル」の２つがよく取り上げられます。

### 4.1 ネガティブセキュリティモデル

**“許可されないもののみブロックする”** アプローチ。SQLインジェクションやクロスサイトスクリプティングなどの**既知の攻撃パターン**をルール化しておき、それらに一致するリクエストのみをブロックします。また、WAF開発元が提供したルール（リスト）を利用することになります。

- **メリット**:
  - 既知の攻撃に対して高い精度です。
  - ルールの更新や管理が比較的容易です。
- **デメリット**:
  - 未知の攻撃パターンに弱いです。

### 4.2 ポジティブセキュリティモデル

**“許可されたもののみ通す”** アプローチ。アプリケーションが受け付けるリクエスト形式・パラメータ内容など**正規の仕様**を定義しておき、そこから外れるものをブロックします。

- **メリット**:
  - 未知の攻撃にも対応しやすいです。
- **デメリット**:
  - 正しく設定しないと誤検知が増えます。
  - 管理に手間がかかります。

### **ハイブリッド方式**

多くのWAF製品は、ネガティブ・ポジティブ両方のモデルやハイブリッド方式をサポートしており、運用環境やアプリケーションの特徴に合わせて使い分けることが一般的です。

---

## 5. まとめ

1. **IDS（侵入検知） と IPS（侵入防止）**:

   - ネットワークレイヤでの不正アクセスに対して主に対応します。
   - 検知手法として、シグネチャー型（既知の攻撃に強い）とアノマリー型（未知の攻撃にも対応）があります。

2. **WAF（Web Application Firewall）**:

   - アプリケーション層の攻撃（例: SQLインジェクションやXSS）からWebサーバを守ります。
   - ネガティブセキュリティモデル（既知の攻撃をブロック）とポジティブセキュリティモデル（正規のリクエストのみ通す）の2つのアプローチがあります。
個人メモ（４）：S2Pのデモ、Fieldglass、

### 導入
個人メモです。

### S2Pのデモ
#### 申請者
1．esc(employee center) / shopping(shoppingHub) に遷移する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190712/29777b8b-bb91-635d-46e2-11b727a54692.png)

2.「I need a service」のカタログを選択する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190712/68607d65-0dca-e0a5-6f95-3eeb55e777d5.png)

3.Supplierなどの選択項目を入力し、提出する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190712/425ad24a-6df3-7c27-4408-d8220c80ecad.png)

※S2Pのカタログは社外向け（サプライヤ）に資金流出がする申請であることが特徴
　一般的なカタログは社内向けの申請であることが特徴です。
SRC（source request）が作成完了
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190712/160f7ed9-cd72-b84b-e7ea-6618b9cbd289.png)

#### 調達者
1.Source-to-Pay workspaceに移動
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190712/1f39cbb5-e55e-878b-dd28-301c6c2b338c.png)

2.SRCを探索して表示
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190712/7c9fa1b5-b3c4-de73-8fe9-36733fba2e7d.png)
3.必要事項を入力してContinueしていく
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190712/ae986984-4f90-5b7a-9026-8f97eb415b0f.png)
4.Purchase Line(購入明細書)が自動生成される。それをレビューする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190712/37b805b6-fff8-773f-1492-155de2e40202.png)
OKであればContinueを押下する
5.required approvalになるので承認する
6.Request internal workでTask（依頼事項）を作成する。終了したらContinue。
例ではSend NDA Contractをアサインしてみる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190712/edaa5215-df2a-072a-2a0d-5c0bd535ec5d.png)

#### 調達アプローチ
1.ソースアプローチを記載する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190712/45e5953b-263a-eb78-e55a-88dfbf63d277.png)
※基本的にタスクを順序的にこなしていくことで調達が完了される構成となっている。

#### 外部サプライヤ
1.外部サプライヤが作業を実施
2.実施されたら調達者にタスクが戻る
3.調達者はサインを実施して申請者に戻す

### SAP Fieldglass:外部人材管理
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190712/bcb13e7e-7e27-aa27-e312-aa650a32b376.png)
人の経験や単価を人づてに聞いていること。それらを一元管理すること。
Capability（できること）
1. タイムシート管理
2. 人材プロファイル管理
3. 契約管理
4. 予算・コスト管理
5. サプライヤー管理
6. 採用プロセス管理
7. コンプライアンス管理
8. レポーティング・分析

###
「id_ed25519.pub: invalid format」 と出てGitHubにSSH接続できなくなった。

突然以下のメッセージが出てGithubにSSH接続できなくなった。

```zsh
Load key "~/.ssh/id_ed25519.pub": invalid format
git@github.com: Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
```
思い当たる原因がなく少し調べたのでその備忘録として残します。

## 結論

SSHエージェントから秘密鍵が消えていたので、追加する。
以下のコマンドでちゃんと繋がるようになった

```zsh
# SSHエージェントを起動
$ eval `ssh-agent`

# 現在のSSHエージェントに登録されている秘密鍵を確認
$ ssh-add -l
> <何も出力されなかった>

# 秘密鍵をSSHエージェントに追加
$ ssh-add ~/.ssh/id_ed25519

# 接続確認
$ ssh -T git@github.com
Hi your name! You've successfully authenticated, but GitHub does not provide shell access.
 ```

# 試したり確認したこと

### permissionの設定

こちらは確認した上で再設定したがダメだった。

```zsh
$ sudo chmod 700 ~/.ssh
$ sudo chmod 600 ~/.ssh/id_ed25519
$ sudo chmod 600 ~/.ssh/id_ed25519.pub
```

### .ssh/config の確認

こちらも元から問題なさそうだった

```zsh
$ cat ~/.ssh/config

Host *
  UseKeychain yes
  AddKeysToAgent yes
  IdentityFile ~/.ssh/id_ed25519.pub
```

### 鍵の再作成

invalid formatとあるので新しく作り直せばいける？と思ったがダメだった。

#### 既存の鍵のバックアップ（一応）

```zsh
$ mv ~/.ssh/id_ed25519.pub ~/.ssh/id_ed25519.pub.bak
$ mv ~/.ssh/id_ed25519 ~/.ssh/id_ed25519.bak
```

#### 新しい鍵の作成・GitHubへ登録

```zsh
$ ssh-keygen -t ed25519 
```

https://github.com/settings/keys から新しい`id_ed25519.pub`を登録
毎日「物を書く」ことを続けていたら、考え方や行動が変わってきた話

こんにちは、ふるすです。
現在アドカレ期間ということもあり、毎日アウトプットにチャレンジしている最中です:santa_tone1:
昨日インプットの量を増やすためにやっていることをまとめたので、今日はアウトプットについて書いてみようかなと思います。

https://qiita.com/furusu_y/items/d8c1a59acc328d65efb0

##  私は何のためにアウトプットするのか

私の書いたものが誰のためになるのか？
書いたから何になるんだ？と最初は考えていました。
そのせいで何を書いたらいいのか思い悩み、なかなか発信ができない日々が続きました。

しかし毎日アウトプットすると決めてから、ある一つの結論にたどり着きました。
それは、**誰のためにならなくてもいい。絶対に自分のためになる**ということです。

もちろん誰かの迷惑になるような内容は論外ですが、それがポエムであっても、何気ないアイデアであっても、反省や振り返りであってもまずは書いてみることが重要だと考えるようになりました。
　　
「数を書けばいいってもんじゃないでしょ」と仰る方もいると思います。
一方で「量は質を凌駕する」という言葉があります。
少なくとも私は毎日なんでもいいから記事を書く、ということを始めてから色んないいことがありました。

・書くことへのハードルが下がった
・アウトプットするためのネタ探しをするために、情報の見方に変化が生まれた
・インプットの量や質が変わった

もちろん最初からクオリティの高いアウトプットができればそれに越したことはないですが、書かなければ質が上がることはないし、書くことで頭の整理になったり新たな改善点が見つかることもあります。自分の中にあるもやもやを形にすること自体も気持ちがいいです。

## その投稿が、もしかしたらどこかで誰かのためになっているかもしれない

母が闘病中なのですが、おなじような病気で闘っている方のブログやSNSの発信をたくさん読みました。
その方にとっては日々起きたことを何気なく綴っているだけだったかもしれませんが、私にとっては毎日投稿してくださっていることそのものも救いになりましたし、気づきもたくさんありました。

闘病ブログと日常ブログや技術ブログが同じ、なんてことを言うつもりはありませんが、頭の中にあることを外に発信するだけで、誰かが私の考えにアクセスできるようになります。そしてそれが誰かを助けることになるかもしれません。

## 書くためにやっていること

最後に、毎日書くためにやっているtipsを簡単にまとめて終わります。

**・ネタをためるツールはすぐ手元で開けるものに**
　PCやノートは開くためのアクションが面倒です。私はスマホのメモ帳に書き溜めています
**・頭の中に文章の構成が思い浮かんだら、後回しにせず殴り書きで書く**
　アイデアを書き溜めるのを後回しにすると思い出すことが困難になります。思い浮かんでいるところまででいいのですべて書き出してしまいましょう
**・完璧な文章は目指さない。7割くらいで発信しちゃえ**
　これは自分に言い聞かせていることでもあるのですが、ランキングに載るような完璧で素晴らしい文章を最初から目指さないことです。これをやろうとすると絶対に挫折します。継続すれば質が上がることを信じて、ある程度のところまでで外に出してしまいましょう。私も頑張ります！
VScodeでPython仮想環境を構築する

# はじめに
研究でVScodeを使ってPythonのシミュレーションを作成するにあたり、pythonはプロジェクトごとに仮想環境を作るとよいことを知ったので構築することにしました。
OSはWindows11です。

# VScodeのインストール
1. [Download Visual Studio Code](https://code.visualstudio.com/download)へアクセス
2. 「Windows」をクリック
3. exeファイルがダウンロードされるので実行

# Pythonのインストール
1. 「[Windows版Pythonのインストール](https://www.python.jp/install/windows/install.html)」を参考にインストールする。

私は「Pythonランチャー」というセクションまで進めればよいかなと思います。（pythonを使えるようになるためではなく、仮想環境を構築できるようになるためなので）

# VScodeの拡張機能を追加
画面左のL字型に四角が並んで一つだけ飛んでいるアイコン（拡張機能）をクリックすると、拡張機能の一覧や検索をすることができる。
以下の拡張機能があれば、ひとまず仮想環境を構築できます。
* **Japanese Language Pack for Visual Studio Code**
言語を日本語にしてくれる拡張機能
* **Python**
microsoftのものを選んでください。色々なPythonの拡張機能が出てくるので注意

# 仮想環境をまとめるフォルダを作成
私は研究で使うために仮想環境を構築したいので

C:\Users\・・・\Documents

の中にstudyというフォルダを作成しました。
これは一例なので、自分の好みでフォルダ作成場所やフォルダ名を決めてください。また、このフォルダは仮想環境ではなく、仮想環境をまとめておくフォルダなので、その認識だけ注意しましょう。

# VScodeで仮想環境を作る
まずは、**仮想環境をまとめるフォルダを開きます**。
先ほど作成したフォルダをVscode上で開きます。
1. 左上の「ファイル」をクリック
1. 「フォルダを開く」 をクリック
1. 先ほど作成したフォルダ（私ならstudy）をクリック
    

次に**仮想環境を作成**します。
仮想環境はターミナルで作成することが出来ます。
1. 上の「ターミナル」→「新しいターミナルを開く」
1. 「python -m venv dev01」 と入力してEnter
1. フォルダ内にvenv01というフォルダが作成されたことを確認


# 仮想環境をactivate
先ほど作成した仮想環境を**アクティベート**して使用可能な状態にします
- ターミナルを開く
- 以下のコマンドで作成した仮想環境に移動
    ```
    cd dev01
    ```
- 以下のコマンドでアクティベート
    ```
    .\Scripts\Activate.ps1
    ```

# 参考文献
この記事は以下の情報を参考にして執筆しました

- [WindowsのVSCodeでPythonの仮想環境を作る手順](https://qiita.com/Channnel4/items/6a9efa65e5bd51bd532e)
自宅サーバーのWireGuard初期設定メモ

備忘録ナリ。
# 環境
- Ubuntu Server 24.04.01 LTS
- 自作マシン
# 手順
[参考サイト](https://gihyo.jp/admin/serial/01/ubuntu-recipe/0614)
上記サイトを参考に自身の環境に合わせたもの。
## 1.インストール
```shell
$ sudo apt install -y wireguard
```
## 2.サーバー側鍵生成
```shell
# 秘密鍵生成
$ wg genkey | sudo tee /etc/wireguard/server.key

# 秘密鍵から公開鍵生成
$ sudo cat /etc/wireguard/server.key | wg pubkey | sudo tee /etc/wireguard/server.pub

$ sudo chmod 400 /etc/wireguard/server.key /etc/wireguard/server.pub
```
## 2.クライアント側鍵生成
```shell
# 秘密鍵生成
$ wg genkey | sudo tee /etc/wireguard/client1.key

# 秘密鍵から公開鍵生成
$ sudo cat /etc/wireguard/client1.key | wg pubkey | sudo tee /etc/wireguard/client1.pub

$ sudo chmod 400 /etc/wireguard/client1.key /etc/wireguard/client1.pub
```
クライアントは今後増えることを想定してナンバリングしておく。
# 3.サーバー側設定

### 設定ファイル
```shell
$ sudo nano /etc/wireguard/wg0.conf
```

```config
[Interface]
PrivateKey = サーバーの秘密鍵
Address = 10.0.0.1
ListenPort = 51820
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o enp3s0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o enp3s0 -j MASQUERADE

[Peer]
PublicKey = クライアントの公開鍵
AllowedIPs = 10.0.0.2/32

[Peer]
PublicKey = クライアントの公開鍵
AllowedIPs = 10.0.0.3/32
.
.
.
```

`Ctrl+x`→`y`→`Enter`で保存。

注意としては、
- PostUpとPostDownのenp3s0の部分は各自のNICの名前を入力すること
- クライアントは下に増やしていけば良い
- IPアドレスは適当に連番とかで

どうでもいいけどNVMe SSD挿すと`enp3s0`の数字変わって鬱陶しい。


### LAN内へのルーティング
```shell
$ sudo nano /etc/sysctl.conf
```

`sysctl.conf`で以下の行を見つけてコメントアウト解除
```diff
- # net.ipv4.ip_forward=1
+ net.ipv4.ip_forward=1
```

反映させる。
```shell
$ sudo sysctl -p
```

### 起動と有効化
```shell
$ sudo systemctl enable wg-quick@wg0
$ sudo systemctl start wg-quick@wg0
```

### ポート開放
```shell
$ sudo ufw allow 51820
```

## 4.クライアント側設定
`client1.conf`とか適当に名前つける。スマホ想定。
```config
[Interface]
PrivateKey = クライアントの秘密鍵
Address = 10.0.0.2
DNS = 1.0.0.1

[Peer]
PublicKey = サーバーの公開鍵
EndPoint = サーバーのIPアドレス:51820
AllowedIPs = 0.0.0.0/0
```

保存したらクライアント側で読み込む。
メモアプリを作ってみた話

# はじめに
みなさんはじめまして。かわはるといいます。長野高専で学生をしてます。
開発を始めてから３か月の超初心者です！
今回は@crysta1221からのお誘いで参加しています。
初記事なので拙いところや誤植などあると思いますが、大目に見てください笑。
# 今回の話題
今回は3か月ほど前から開発を始めたメモアプリ「Memoapp-Overlay」の開発について、躓いた点について話していきたいと思います！

:::note warn
（アプリ作ったとは言ってますが、皆さんだったら多分１日で作れるんじゃないかなっていうところまでしか進んでません。ご容赦ください。）
:::

# 使用技術
Tauri
https://v2.tauri.app/
Next.js
https://nextjs.org/
rust
https://www.rust-lang.org/ja

# Tauriについて
Tauriとは、WEBフロントエンド（Next.js/React/Svelte/Qwikなど）と、Rustnのバックエンドで、Windows/macOS/Linux/Android/iOSを対象にしたデスクトップアプリケーションが作れるフレームワークです。
https://ja.wikipedia.org/wiki/Tauri

# Memoapp-Overlay概要
このアプリは開発の途中から根本的に使うフレームワークを変えたり、方向性を分けたりしてます。
そのため、①初期バージョンのアプリと②現在開発中のアプリが存在します。
まずは①初期バージョンの方から紹介します。

## 初期バージョン
### アプリについて
このアプリは「ゲーム中に簡単にメモが取れたらなー」という思いから作りました。
アプリでは完全に透明なウィンドウにNotionぽくブロックでメモができるというものです。
Ctrl+Alt+Vを押すことで編集モードと表示モードを切り替えます。
使用例としては、ウィンドウフルスクリーンのゲーム上で攻略情報を常に見ながらプレイする。
WEB会議中にウィンドウを分割することなくメモが取れる。
などを想定して作りました。
一応デモということで配布をします（exeファイル直貼りというやってはいけなさそうな配布の仕方ですが）

https://drive.google.com/file/d/1jfAX-WeNWngS4Mb7X8jjTWbhnKh1TTNz/view?usp=sharing

ちなみにメモ機能にはTipTapというNotion-likeなテンプレートを用いました。

https://tiptap.dev/

### 開発について
今回の開発では、Tauriの環境構築とバージョンの調整にめっちゃ時間がかかりました。特に、Tauri APIでWindow EventをListenする際、必要な権限の設定に苦労しました。また、データベースに関してはわからず終いでした。あと、Rustくんが何言ってるかわからなかったです。

さらに、Reactの構造をどのように書けば良いのか全く分からず、設計に手間取りました。加えて、パッケージマネージャーの種類が多すぎて、ネット上で拾った情報が役に立たないこともありました。このような状況下で、どの情報を参考にすればうまくいくのか分からず、作業が停滞してしまうことも少なくありませんでした。

### 初学者がアプリを作るには...
・Udemy等の開発のすべての工程が載っているコンテンツで実践すること
・同じようなアプリをGitHubで探してソースコード丸パクリしてみること
・環境構築が複雑すぎるものに手を出さないこと

やっぱり一から実践できるコンテンツが本当に大事だなと。友達に付きっきりで教えてもらうわけにもいかないし。

## 現在開発中のバージョン
### 構想について
こちらはオーバーレイできるメモアプリというところには変わりはありません。
今度はここに「共有機能」を盛り込んだものにしようと考えました。
共有はDiscordのチャットにヒントを経て、画像や動画などの資料に対してノーションライクなリッチテキストでチャットのようにメモができる機能を持たせようと考えました。
めちゃくちゃ雑で申し訳ないのですが、一応Figmaで作ってみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3950690/0a539953-9d9c-63a6-681a-ef7d5ca1425e.png)
コンセプトとしては「Notion風テキストフォーマットの見易さ」×「時系列整理できるチャット」×「画像とテキストの関連が見やすいJamboard風フィールド」といった立ち位置でしょうか。

一応構想はこんな形で練っています。
この構想にたどり着くまでにいろいろFIgmaでこねました。
作る中でそれなりによいプラグインを見つけたので共有しておきます。

・html.to.design
WEBサイトをFigmaファイルにインポートできる。

https://www.figma.com/community/plugin/1159123024924461424

・shadcn/ui

https://www.figma.com/community/file/1203061493325953101

・Dot Grid
先ほどの画像の背景にあるような丸方眼を再現できる

https://www.figma.com/community/plugin/795474715778185230/dot-grid

### 開発について
現状では初期バージョンの環境(Vite,Tiptap)から現在の環境(Nextjs,TailwindCSS,Shadcn,Platejs)
に移植して、透明にする機能だけが実装できている段階です。
これからはUIの作成、データベース設計などやっていこうと考えています。

# 最後に
今回は初心者がアプリを作ってみた話でした。
やっぱり最初からRustが絡んでくるTauriに足を突っ込むのはやめた方がよさそうですね。
最後まで読んでくれた方、ありがとうございます！！
記事の投稿遅れてごめんなさい！
【Unity】Arbor3でAnimatorのAnyStateのような強制ステート遷移がしたい

# はじめに
この記事は[Hamster Output Advent Calendar 2024](https://qiita.com/advent-calendar/2024/game_hamster)の12日目の記事です！

今回、Arbor3のステートマシンを利用している時にAnimatorのAnyStateのようなステートの強制遷移ができるのかと疑問がでたので、調べたことを書きます。

# 参考にしたリンク
https://arbor.caitsithware.com/

<iframe width="560" height="315" src="https://www.youtube.com/embed/0JJhLpeB3TY?si=-xOYO80b2AYv2Nhr" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

# 結論
### ・スクリプトから強制ステート遷移するなら
ArborFSMを参照 → StateLink変数を作成してArborFSMのFindStateメソッドでステートを取得 → ArborFSMのTransitionメソッドの引き数にStateLinkを渡す

### ・ArborEditorから強制ステート遷移するなら
ResidentStateを作り遷移条件を作成する

現時点で自分が知っている方法はこの辺り...下の方でもう少しだけ詳細に手順を書いていきます

# スクリプトから強制ステート遷移
先ほどの内容をソースコードにするとこんな感じです。Findなので、名前間違いやステートが存在しない場合エラーになるので注意。

```C#
using Arbor;
using UnityEngine;

public class Test : MonoBehaviour
{
    [SerializeField]
    private ArborFSM _arborFSM;
    private State _anyState;

    private void Start()
    {
        // ステートを取得
        _anyState = _arborFSM.FindState("Idle");
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            // ステートの遷移
            _arborFSM.Transition(_anyState);
        }
    }
}
```

# ArborEditorから強制ステート遷移するなら
Residentは常駐という意味です。ArborEditor上でもResidentに追加した処理や条件が達成するとResidentに強制ステート遷移ができます。

作り方はとても簡単で、ArborEditor上で右クリックを押してResidentStateを作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588406/2fee3df6-be94-52b7-3808-a5dd85edb296.png)

ResidentStateに遷移条件を追加する...これだけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588406/1f17b5ba-bc6b-d226-2fd5-94b2c2e8ed3c.png)

# 使うならどっちが良いか
簡単な強制ステート遷移ならResidentState。強制ステート遷移だけど、連続で呼ばれたくなかったり調整をしたいならスクリプトから強制ステート遷移を作成する。
Ubuntu20.04にMySQL5.7をインストールする

仕事上、必要になったので、仕方がなくMySQL5.7をインストールすることに。それとPHPが5.6系。

おそらく2024年12月だけしか使えないと思われる。

## Ubuntu 20.04 と PHP5.6

`Ubuntu18.04`はOndrejさんがサポート外とのこと。
なので`Ubuntu20.04`を選択。

いつものPHPインストールでPHP5.6を設定。

## MySQL5.7

◆まずは、このブログ「[UbuntuでMySQL 5.7をインストールする方法](https://hitori-josys.me/entry/ubuntu-install-mysql57)」を参考にインストールを進める。

だが、途中で動かなくなってしまった。

◆仕方がないので、今度はこちらのエントリー「[Is it possible to install MySQL Server 5.7 on Ubuntu 19.10?](https://askubuntu.com/questions/1199495/is-it-possible-to-install-mysql-server-5-7-on-ubuntu-19-10)」を参考にして作業。

下記ファイルを直接修正した。

`/etc/apt/sources.list.d/mysql.list`

書き込む内容は、こちら。

```
deb http://repo.mysql.com/apt/ubuntu/ bionic mysql-apt-config
deb http://repo.mysql.com/apt/ubuntu/ bionic mysql-5.7
deb http://repo.mysql.com/apt/ubuntu/ bionic mysql-tools
#deb http://repo.mysql.com/apt/ubuntu/ bionic mysql-tools-preview
deb-src http://repo.mysql.com/apt/ubuntu/ bionic mysql-5.7
```

◆そして`sudo apt update`

◆エラーメッセージから、こちらのブログ「[public key is not available: NO_PUBKEY](https://zenn.dev/gomo/articles/7f6c28d002837c)」を参考にして下記を実行

```
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys <PUB_KEY>
```

◆最後にインストールできたかを確認

```
$ sudo apt-cache policy mysql-server
$ mysql --version
```

ひとまず動いた。
【AWS re:Invent 2024】参加レポート

こんにちは、AWS Jr. Championsnの安達です。

12月2日から12月6日にかけて、アメリカ ネバダ州ラスベガス開催された **AWS re:Invent 2024** に初参加してきました！

今回が初参加だったので、現地での体験を重視した予定を立てて回りました。
出遅れましたが、AWS re:Inventで開かれたセッションや会場での催し物を通しての学びをふりかえります！！

## AWS Jam を楽しむ

Exam Prep For AWS Certified AI Practitioner の Jam に参加しました。
内容は、2024年に始まったAIF試験の準備ができるゲーム型学習です。

【シナリオ】
宇宙船がカフェに墜落して、その衝撃でカフェ内のシステムが損傷！？
その影響で宇宙船が出発できない！！カフェのシステムを直して、脱出しよう！

![スクリーンショット 2024-12-03 7.36.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3762239/48273329-598d-22bb-0ea2-2ab97dd9d93d.png)

全文英語でWeb翻訳も使えなかったのでシナリオのニュアンスは違うかもしれないですが、　ストーリーに沿ってキャラクターを操作して、AWSコンソールからAI・ML系のサービスを触ったり、試験問題に答えたりしました。

AIF試験には合格していますが、業務内や試験勉強ではAWSコンソールでの操作はできていなかったので、非常に良い学びになりました！

３時間のセッションで参加者は100人くらいいましたが、脱出できたのは十数名程度でした。
英語の解読に時間がかかり、私も脱出できず…

またできる機会があれば、脱出リベンジしたいです！

## GameDay で大苦戦…

AWS Jr. Championsメンバーとチームを組んで、GameDayにも参加しました。

GameDayに参加した経験は１回しかなかったのですが、その１回がとても楽しかったので、re:Inventでも参加することにしました。

> AWS GameDay: Cloud operations (sponsored by Dynatrace) GHJ304

Dynatrace が主催のGameDayに参加し、アプリケーションのオブザーバビリティとセキュリティを管理できるプラットフォームを提供するSaaS製品を含めた課題に取り組みました。

一緒になったメンバーはインフラ寄りのメンバーが多く、運用面の対応にかなり苦戦しましたが、全員で協力しながら取り組めたので最高に楽しかったです。
軽食も用意されて、コーヒーを飲んだりブラウニーを食べたりしながら、リラックスした雰囲気で学ぶことができました！！

GameDayは楽しいとともに、できないところがあるととても悔しい気持ちになり、次の参加機会があれば好成績を挙げられるようにとAWSを使い学ぶモチベーションに繋がるのでとてもおすすです。


## EXPOで、SWAG集め！
最初から行こうと決めて訪れたブースは、

- CircleCI
    - DevOpsの実践に対応した継続的インテグレーション&継続的デリバリー（CI/CD）プラットフォーム
- TiDB
    - MySQL互換の分散型データベース
- サイバーセキュリティクラウド
    - 攻撃遮断くんやWafCharm, CloudFastenerといったセキュリティサービスを提供

の３社であとはぶらぶら気の向くままに回りました！

**Can I have one?** を多用して、サービスについて簡単に紹介してもらいながら、SWAGを集めていました。

Tシャツに、靴下に、ミント、グミ、ガム、水筒、缶ケース、3WAYケーブル、サコッシュ、コースター、シールなどなど、色々と集まりました。

なんだかんだでいろんなSaaS製品について知ることができ、データベースやセキュリティの流行りや勢力図的なものを知ることができました。

ただ、最終日にブースを回りだしたので圧倒的に時間が足りませんでした…
じっくりと話を聞けない焦りがあり、初日から回るべきだったとシンプルに後悔しています…

次回、EXPOに行く際にはもっと企業や製品の下調べをしていきたいです。

## re:Play を満喫！！
最終日に夜には **re:Play** に参加してきました。

宿泊していたMGMグランドから会場まではバスで30分ほど揺られ、どこに連れていかれるのか若干の不安がありましたが、ライトアップされた会場とDJの演奏が聞こえてくるにつれて、re:Inventのラストに向けての盛り上がりを感じました。

しっかりと学んだあとには思いっきり楽しもうということで会社の方やre:Inventを通して仲良くなった方と交流を深めることができました！！

翌日の帰国の集合時間が早朝５時ごろだったため、複数の催し物がある中で早めに切り上げてしまったのが心残りです…

## 最後に

今回の **AWS re:Invent 2024** は、初参加ということもあり全てが新鮮で、学びと刺激に満ちた一週間でした。

セッションやGameDay、Jam、EXPOなどを通して、AWSの進化を体感するとともに、AWSを利用している世界中の方々と直接交流し、コミュニケーションを取る機会を得られたことが非常に印象的でした。特に、同じ分野で活躍している方々との技術的な会話や、日頃自分が触れていない領域への挑戦ができたことは、大きなモチベーションとなりました。

また、今回の反省点として、スケジュールの立て方や時間の使い方が課題でした。参加したセッションの数や内容には満足していますが、EXPOや特定の深堀りをしたかった分野に時間を割く余裕が足りなかったため、次回以降はより戦略的な計画を立てて臨みたいと思います。SWAGももっとたくさん集めたい…

来年以降もこの学びを活かし、さらに自分のスキルを磨き、業務やプロジェクトでの成果につなげていきたいと感じています。そして、次回の AWS re:Invent では、さらに成長した自分で新しい挑戦をしたいと考えています！

# 最後にひとこと：AWS re:Inventは最高！来年もぜひ参加したい！
最後まで読んでいただきありがとうございます。
TypeScriptのGenerics(ジェネリクス)の使い方

### この記事を読むのにオススメな対象者
- TS触り始めてまもない人
- ジェネリクスを使ったことがない人
- フロントエンドに興味を持ち始めた人

## Genericsとは
ジェネリクスとは型の決定を遅延させ、型をより柔軟に使いこなすシステムです。
ジェネリクスに割り当てた型定義に対し、`< >`の中に型を指定することで型を利用する時に、可変にできる便利な機能です。

TS開発していく上で、知っていると型を共通化したり何かと便利なので
そんな機能もあるんだという気持ちで見ていってください！

#### 基本的な書き方
ジェネリクスを利用する場合には、型の名称に続いて括弧`< >`の中に`T`などの型エイリアスを指定して利用します。
```ts
interface Text<T> {
  value: T
}
```

#### 使い方
先ほど作成した、型エイリアスのに利用したい型を指定します。
今回の場合、`string`で指定しています。
```ts
const text: Text<string> = { value: 'hoge' }
```

#### エラーになる使い方
先ほど作成した型には、ジェネリクスが必須のため、省略した記述をするとエラーになる。
また、ジェネリクスで定義している型と値の型が一致しない場合にも同様エラーの扱いになります。
```ts
// Error!!: ジェネリクスが指定されていない
const text: Text = { value: 'hoge' }

// Error!!: numbeer型にstring型の値は入れられません。
const text: Text<number> = { value: "fuga" }
```

#### ジェネリクスに初期設定の値を定義する方法
ジェネリクスにも初期値が設定できるため、初期の型を設定したい場合には
`<T = string>`のように定義すると利用側でジェネリクスを指定しなくても利用できるようになります。

```ts
interface Text<T = string> {
  value: T
}
```
初期値を指定していると、ジェネリクスを省略して記述しても
初期値に入れた型として処理されるため、エラーにならない。
```ts
// ジェネリクスがstringとして処理されるためエラーにならない。
const text: Text = { value: 'hoge' }

// Error!!: 初期値のstring型で定義されるため、number型の値を入れるとエラーになる。 
const text: Text = { value: 100 }
```
また、ジェネリクスの何でも利用側で型定義できてしまうのを制御したい要望もあると思います。その場合には`extends`シグネチャを利用する事で型に制約を加える事も可能です。

```ts
interface Text<T extends string | number> {
  value: T
}

const text: Text<string> = { value: "huge" }
const text: Text<number> = { value: 100 }

// Error!!: string型または、number型にboolean型を入れられません。
const text: Text<boolean> = { value: false }
```

もちろん型制約しながら初期値を定義する事も可能です。
 ```ts
// 初期のジェネリクス型をstringとしたい場合
interface Text<T extends string | number = string> {
  value: T
}

// 初期のジェネリクス型をnumberとしたい場合
interface Text<T extends string | number = number> {
  value: T
}
 ```

 ジェネリクスを上手に利用すると、一部の型では共通化する事ができ整理する事もでき結果的に保守のしゃすいコードが書きやすくなります。


#### 関数でのジェネリクスの使い方
```ts
function handleTextChange<T>(text: T) {
  return { value: text };
}

const strText = handleTextChange('hoge');
const numText = handleTextChange(100);

// このように推論される。
strText: { value: string };
numText: { value: number };

// アロー関数での書き方
const handleTextChange = <T>(text: T) => {
  return { value: text };
}
```
また、関数の場合だとジェネリクスを使っていても、利用する際に型指定は必須じゃないため省略して記載してもエラーになる事はなく型推論されます。



```ts
function handleTextChange<T extends string | number>(text: T){
  return { value: text }
}

handleTextChange('hoge');
handleTextChange(100);

// string | numberにbooleanを入れようとしたのでエラーになる。
handleTextChange(false);
```
また、関数でも`extends`を利用して型制約を入れる事が可能なため
何でも型を指定して欲しくないケースなどでは、`extends`をうまく利用して制御することが可能です。


### 最後に
ジェネリクスは便利な機能です。使いこなせるようになると
コードを大幅に縮小できたり共通化できたり、保守しやすいコードが
書けるようになり、TSを書くのが楽しくなります。ぜひTSを普段触っていてジェネリクスをまだ触ってない人がいれば一度、試しにプロジェクトで利用してみてください！
文字化けの原因

## 概要
私は2024年4月入社の新人社員である。
プログラム経験は趣味でチラッと学んだ程度で、ないと言って差し支えない。
現在はAI系エンジニアとして働いているが、そもそもプログラムすらかけないので何もできない状態が続いている。現状を脱するためにこうして自身で学んだものをアウトプットする場を設けた。
初めての投稿なので多めに見てくれるとありがたいです。

## 今回の議題
今回はAIの簡単なRAGをpythonで作っていた時に発生した問題である。

これは実際のコードである。　※外部公開用に少し簡略化、縮小してるので動くかは不明
<details><summary>コード</summary>

```
# Logger setup
def setup_logger(log_dir="logs", level=logging.INFO):
    os.makedirs(log_dir, exist_ok=True)
    logger = logging.getLogger("app")
    logger.setLevel(level)
    handler = RotatingFileHandler(
        os.path.join(log_dir, "app.log"), maxBytes=1_000_000, backupCount=3
    )
    handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
    logger.addHandler(handler)
    return logger

app = Flask(__name__)
logger = setup_logger()

# Simple search simulation
def perform_search(query, top_n=3):
    logger.info(f"Performing search for: {query}")
    return {"results": [f"Result {i+1}" for i in range(top_n)]}

# LLM response simulation
def query_llm(context, user_input):
    logger.info("Querying LLM...")
    return f"LLM response for '{user_input}' with context."

@app.route("/api/query", methods=["POST"])
def handle_request():
    try:
        data = request.get_json()
        user_input = data.get('msg')
        if not user_input:
            raise ValueError("Missing 'msg' parameter")
        
        results = perform_search(user_input)
        context = "\n".join(results["results"])
        response = query_llm(context, user_input)
        
        return jsonify({"response": response})
    except Exception as e:
        logger.error(f"Error: {str(e)}")
        return jsonify({"message": "An error occurred."}), 500

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000, debug=False)

```
</details>
簡単なflaskを使ったコードで、"request"を別ページで受け取り、json形式にしたのちlocalhostのurl経由で受け取っている。
その時にログを一つ一つ確認していると"msg"と"results"の内容が代替文字(黒いひし形のはてなマークなど)に置き換わってしまっていた。

コードにもある通り直前でutf-8にデコードしているし、入力側でもutf-8にするよう明示的に処理をしている。
これの原因がわからず苦戦していたのが今回の問題である。

## 原因
原因はかなり簡単なもので、ログを記述するときに文字化けが発生したようだった。そのため、ログ以外の処理はそのままでも問題なく進んでいた。
かなりの時間悩んでいたので拍子抜けだったが、こういう部分も確認しなくてはならないのだなと勉強にはなった。

## 解決策
解決策はシンプルで、ログの関数部分で文字コードを指定してするだけで解決できた。
(ログを関数にする意味あるか？という疑問は置いておいてください、、) 

```
def setup_logger(name=__name__, log_dir="logs", level=logging.DEBUG):
    if not os.path.exists(log_dir):
        os.makedirs(log_dir)

    logger = logging.getLogger(name)
    logger.setLevel(level)
    logger.handlers.clear()

    log_file = os.path.join(log_dir, "app.log")
    handler = RotatingFileHandler(
        log_file, 
        maxBytes=10_000_000, 
        backupCount=5,
        encoding="utf-8")
    formatter = logging.Formatter(
        "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
    )
    handler.setFormatter(formatter)
    logger.addHandler(handler)

    return logger
```

これで出力してみると、無事文字化けせずにログを出力することができた。

## まとめ
今回はかなり初歩的な内容でしたが、みんなこういった道を通っているのだと信じて進みたいと思う。
第一歩としてこういった備忘録は大事だと常々感じさせられるので、三日坊主にならず気づいたことがあれば投稿していきたい。
最初のつたない記事をお読みいただきありがとうございました。
Juniper機器のnegotiationについて

## はじめに
Juniper機器使ってますか？
いろいろな会社の製品を使いますが、初心者にとって、一番とっつきやすいのが、Juniper社の製品だと思います。
（もちろん、どの会社の製品も一長一短があります）
そんなJuniper社の製品を使用していて遭遇したnegotiationの問題について書いていきます。

## リンクが上がらない Vol.1（事象発生編）
ある日、機器でリンクが上がらないとの申告を受けました。
この時点での状況は以下です。
- SFP：1000BASE-LX
- 光レベル：送受信ともに正常
- 弊社側機器状態（EX4300）：
    - Link Down
    - negotiation：auto-nego
    - 対向機器のMAC：見えない
- 対向側機器状態（Catalyst9300L）：
    - Link Down
    - negotiation：auto-nego
    - 対向機器のMAC：見えない

この時は、弊社側機器IFをdisable/delete disableすることで事象は解決しました。

## リンクが上がらない Vol.1（切り分け編）
一旦は上記の手順で解決しましたが、事象の切り分けを行うことになりました。結果だけ書きます。
- 両端 no-negoでは発生しない
- 両端 auto-negoで発生するが100%ではない
    - EX4300のdisable/delete disableで解決する
- EX4300 auto - C9300 no-negoで事象発生
    - EX4300 down - C9300 up
    - EX4300のdisable/delete disableで解決する
- EX4300 no-nego - C9300 autoで事象発生
    - EX4300 down - C9300 up
    - EX4300のdisable/delete disableで解決する

また、SFPのメーカーによる結果差分はありませんでした（純正でも3rd-Partyでも結果は変わらず）

## リンクが上がらない Vol.2（事象発生編）
先程とは違う日、機器のテストを行っていた際に、テスト用機器との間でリンクが上がらない事象が発生しました。
この時点での状況は以下です。
- SFP：1000BASE-LX
- 光レベル：送受信ともに正常
- 機器状態（QFX5120）：
    - Link Down
    - negotiation：auto-nego
    - 対向機器のMAC：見えない
- テスト用機器状態（C4948）：
    - Link Down
    - negotiation：auto-nego
    - 対向機器のMAC：見えない

先程の知見があったため、IFをdisable/delete disableしましたが、事象が解決しませんでした。
そのため、両端をno-negoにすることで事象が解決しました。
また、回避策として、両端でSFP-T（LANケーブルに変換）を使用することで両端auto-negoでもリンクアップが確認できました。

## リンクが上がらない Vol.2（切り分け編）
一旦は上記の手順で解決しましたが、事象の切り分けを行うことになりました。結果だけ書きます。
- QFX no-nego　～ 対向機器 auto-negoの場合、QFX側はUP、対向機器側はDOWNとなる場合があった
    - EX2300、Cisco機器が対象
- リンクが上がっているときに、no-negoに設定されている機器側の送り（TX）を抜去した場合、送り側を抜去された側はリンクダウンしない（リンクアップし続ける）
    - EX4300とMX240はauto-negoの場合でも発生する

また、SFPのメーカーによる結果差分はありませんでした（純正でも3rd-Partyでも結果は変わらず）

詳しい検証結果は、需要があれば書きます

## 得られた知見まとめ
今回の切り分けを通じて得られた知見や調べて分かったことをまとめます。

- Juniperのnegoには３種類ある
    - Default Mode
        - 設定不要で、元々のJUNOSと同じ動作
        - ≒auto-nego
    - Explicit Disable(gigether-options/ether-options配下でno-auto-negotiation)
        - Auto-negotiationを明示的にDisable
        - no-nego
    - Explicit Enable(gigether-options/ether-options配下でauto-negotiation)
        - Auto-negotiationを明示的にEnable
        - flow-control、remote-faultも設定可
        - auto-nego
- EX4300やQFX5120で、1000BASE-LX（1G Fiber SFP）を使用する際は、no-negoを推奨
- SFPメーカ（純正 or 3rd party）による結果の差分はない！
- QFX5120-48Yは、1000BASE-LX（1G Fiber SFP）ではauto-negoをサポートしない
    - OSの問題ではなく、筐体としてサポートしていない
    - SFP-Tでは、auto-nego使用可能
    - https://www.juniper.net/documentation/us/en/software/junos/interfaces-ethernet-switches/topics/topic-map/port-speed-qfx-switches.html#id-channelizing-interfaces-on-qfx512048y-switches


## まとめ
1G のIFはnegoの問題があって大変
negotiationの概念がない10G IFを使おう！
(最近は3rd party製だと数千円で買える！)
JDBCとは

↓動画で解説
[![JDBCとは](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3680113/9b8d2f50-b5b1-5ea4-81a6-237f52acbccb.png)](https://youtu.be/Fd-fVHXGrLA)

Java Database Connectivityの略称。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3680113/0db9092d-2be5-e6c8-1bfd-6baafe363895.png)


**JDBCAPI**
Javaからデータベースを操作するためのプログラムの集まり

**JDBCドライバ**
・データベースごとに用意されている
・ドライバを設置するだけでデータベースにアクセスできる



---
Udemyで講座を公開中！
https://zenn.dev/codek2/articles/e9e44f3e0023fb

X(旧Twitter)
https://twitter.com/kunchan2_

Zenn
記事→https://zenn.dev/codek2
本→https://zenn.dev/codek2?tab=books

新入社員研修向け！JDBC・Servlet/JSP・ソフトウェアテスト・Webアプリケーション開発講座
https://www.udemy.com/course/jdbcservletjsp/?referralCode=88203EF7B00BDA28198C


note
https://note.com/codek2
Polarsで他のカラムを維持しながらgroup_by操作を行う方法

# やりたいこと
`Polars`を使って、他のカラムを維持しながらgroup_by操作を行う方法を探しました。具体的には、`mDice`を計算し、各`Fold`ごとに`mDice`が最大の`Epoch`を取得したい、という内容です。

ぱっと思いつかなかったので、備忘録として記事にまとめます。

---

## データ確認
以下のようなデータを対象としています。ここで、`mDice`を計算し、各`Fold`ごとに`mDice`が最大の行を抽出したいとします。

| Epoch | Fold  | Label1 | Label2 | Label3 |
|-------|-------|--------|--------|--------|
| 1     | 1 | 0.8    | 0.75   | 0.85   |
| 2     | 1 | 0.85   | 0.78   | 0.9    |
| 3     | 1 | 0.88   | 0.8    | 0.92   |
| 1     | 2 | 0.76   | 0.74   | 0.78   |
| 2     | 2 | 0.84   | 0.8    | 0.85   |
| 3     | 2 | 0.9    | 0.85   | 0.88   |

```python
data = {
    "Epoch": [1, 2, 3, 1, 2, 3],
    "Fold": [1,1,1,2,2,2],
    "Label1": [0.8, 0.85, 0.88, 0.76, 0.84, 0.9],
    "Label2": [0.75, 0.78, 0.8, 0.74, 0.8, 0.85],
    "Label3": [0.85, 0.9, 0.92, 0.78, 0.85, 0.88],
}
df = pl.from_dict(data)
```
---

# `method A`
最も簡潔な方法として、`filter`を活用します。この方法では、`over`を使って`Fold`ごとに`mDice`の最大値を計算し、その行を抽出します。

```python
result = (
    df
    .with_columns(mDice = pl.mean_horizontal(pl.exclude("Epoch", "Fold")))
    .filter(pl.col("mDice")==pl.max("mDice").over("Fold"))
)
```
水平方向の平均には便利な`.mean_horizontal()`を使用します。

# `method B`
`group_by`で`mDice`の最大値を計算し、それに対応する行をjoinで取得する方法です。この方法は、手続き的で構造が明確です。

```python
tmp_df = df.with_columns(mDice = pl.mean_horizontal(pl.exclude("Epoch", "Fold")))
cols = tmp_df.columns
result = (
    tmp_df
    .group_by("Fold")
    .agg(pl.max("mDice"))
    .join(tmp_df, on=["Fold", "mDice"])
    .select(cols)
)
```
この方法だと列の並びが変わるので`.select`でもとに戻しています。


# まとめ
個人的には、`method A`の方が簡潔で読みやすいと思います。ただ、`method B`も明確な手順で処理を行うため、状況によってはありかも。

もし他に良い方法や改善案があれば、ぜひコメントで教えてください！

---

## バージョン
このコードは以下のバージョンで動作確認済みです：
- `Polars`: **1.15.0**
Forms の回答の完了時刻を Power Automate で SharePoint Lists に転記したら、時刻が9時00分になってしまった時の対処法

ごきげんよう、百合宮桜です。
今日はForms の回答の完了時刻を SharePoint に転記する方法をお伝えします。

Forms のアンケートで得た回答にはこのようにアンケートへの記入開始時刻と完了時刻が記録されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3032705/e35a664c-b584-372e-9854-2f7e9ea91bc5.png)


このデータを Power Automate で SharePoint Lists に転記した時に SharePoint Lists 側で時刻が 9:00 で固定されてしまうという現象があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3032705/3e81927e-a095-242e-a0f4-7d657c3221a6.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3032705/5b3d2159-c880-b11c-914f-02fae3e67502.png)

おそらく何らかの原因で、DayTime 列で完了時刻が日付だけしか認識できず、「2024/12/24 00:00」という値で格納され、それを SharePoint サイトのタイムゾーン設定で日本時間に変換しているので、「2024/12/24 9:00」の表記になってしまったと考えられます。

なので応答の詳細を取得する > Submission time （完了時刻）で取得した値を SharePoint の日時書式に強制的に変更することで、正しい完了時刻が表記できるようになります。

日時書式の指定は formatDateTime 関数で行うことができます。

https://learn.microsoft.com/ja-jp/power-automate/date-time-values

以下のような形で式を入れて、強制的に日時書式を指定すると、正しい完了時刻が表示されました。

```
formatDateTime(
    outputs('応答の詳細を取得する')?['body/submitDate'],
    'yyyy-MM-ddThh:mm:ssZ'
)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3032705/97a6130b-3b5c-7df8-a855-abba33f4e939.png)

私の持っている環境では事象再現ができなかったため、どのような条件でこのような事象が発生するのかは分かりかねます。
ただ、このような事象が発生する時もあるようなので、もし遭遇したら、この対処法を試してみてください。

それでは、ごきげんよう。
RTKの設定メモ

## 前回の記事内で少し用語解説したり図もある

https://qiita.com/ono-honeycomb/items/1436d97c15bcf52e5f90

## 資料リンク

https://www.switch-science.com/products/8288

https://cdn.sparkfun.com/assets/f/7/4/3/5/PM-15136.pdf

- 大事な（よく見た）箇所ピックアップ
    - 4.1.6 Position Fix Flags
        - GGA：4→RTK使っててFixed、5→RTK使っててFloat
    - 4.2.5 GGA
        - $xxGGA,time,lat,NS,lon,EW,quality,numSV,HDOP,alt,altUnit,sep,sepUnit,diffAge,diffStation*cs<CR><LF>
            - 1 time - hhmmss.ss 092725.00 UTC time, see note on UTC representation
            - 2 lat - ddmm.mmmmm4717.11399 Latitude (degrees & minutes), see format
            description
            - 4 lon - dddmm.mmmmm00833.91590 Longitude (degrees & minutes), see formatdescription
            - 6 quality - digit 1 Quality indicator for position fix, see positionfix flags description
            - 8 HDOP - numeric 1.01 Horizontal Dilution of Precision
            - 9 alt m numeric 499.6 Altitude above mean sea level
        - Example:
            - $GPGGA,092725.00,4717.11399,N,00833.91590,E,1,08,1.01,499.6,M,48.0,M,,*5B

https://rtk.silentsystem.jp/

## 各デバイスの設定

設定方法の基本

1. u-center立ち上げ
2. 設定したいポートにconnect
3. メニューの「View / Configuration View」
4. Configuration viewウィンドウ内左下の「send」ボタン押す
5. デバイスが再起動しても設定を残すように「CFG」項目内の「FLASH」も選択されているのと「Save current configuration」が選択されている状態で左下「send」
    1. VALSET内など一部では項目内でFLASHに書き込めたりする

### 移動局

最悪デフォルト設定でも取得できるが、おすすめの設定

1. 「RATE」
    1. 200msくらいに
2. 「NMEA」
    1. High precision mode

### 基準局

1. 「RATE」
    1. 200msくらいに
2. 「NMEA」
    1. High precision mode
3. 「VALSET」
    1. たぶんRTCMのデータをoutputする設定
    1. [![Image from Gyazo](https://i.gyazo.com/6ee7f6139561d96408e120771289f82c.png)](https://gyazo.com/6ee7f6139561d96408e120771289f82c)
    1. [![Image from Gyazo](https://i.gyazo.com/15ed2dc15f56cd426c081ee7ccc4e7b8.png)](https://gyazo.com/15ed2dc15f56cd426c081ee7ccc4e7b8)
4. 普通のGPSデータをoutputしない設定
    1. こうしたほうが望ましいらしいが、これしたらうまくいかないかった感じだったので、やらなかった
    1. View -> Message View を開いて、NMEA を右クリックし、Disable Child Message

## 開発方法

- Unityでやってみた
    - https://qiita.com/ono-honeycomb/items/1436d97c15bcf52e5f90

## 所感

- 自前で用意した基準局だと、RTK判定にもならないときもあって結構シビア
- 善意の基準局もfix難しい
    - 移動局が外だとfixしやすい気がするので、屋内だと難しそう
    - 10cmのずれがよく見るし、どんどん遠ざかっていくので屋内だったり、近くにものがあると厳しい気がする
- 天候とかに左右されているかも
    - いい日と悪い日がある
Python 大量リストから一意なデータを高速に取得する方法

# Python 大量リストから一意なデータを高速に取得する方法

## はじめに

Pythonで大量のリストから一意なIDを基準にデータを取得する際、
データ量が増えるとパフォーマンスが大きく低下することがあります。
そのような課題を解決した方法を記載します。

###  課題

例えば、以下のように一意なIDを条件にリストを絞り込むコードでは、
大量データの場合にパフォーマンスが大きく低下することがあります。

```python
target_id = "{id}"

results = [] # 対象リスト
result = [x for x in results if x.id == target_id]
```

リスト内のすべての要素をチェックするため、サイズが大きいほど処理時間が長くなります。

### 解決策①: リストの形式を変更できない場合

リストの形式を変更できない場合、nextを使用すれば効率的に最初の一致するデータを取得できます。

```python
result = next((x for x in results if x.id == target_id), None)
```

- 条件に一致する最初の要素だけを返すため、無駄なループを削減できます
- 条件に一致するデータがない場合は None を返します

### 解決策②: リストデータを辞書形式に変換できる場合

リストデータを辞書形式に変換できる場合、一意なIDをキーにすることで、
パフォーマンスを大幅に向上させることができます。

```python
# 辞書型に変換
results_dict = {x.id: x for x in results}
# 辞書型からデータを取得
result = results_dict.get(target_id)
```

- 辞書型の検索はキーアクセスで行われるため、リスト検索に比べてパフォーマンスが向上します
- 辞書化自体のコストはありますが、同じリストデータに対して複数回検索を行う場合はパフォーマンスが向上します

### 結果

リストデータ数やループ回数の詳細は失念しましたが、以下のようなパフォーマンスの改善を確認しました。
- 解決案②
  - 適用前：0.7秒
  - 適用後：0.0015秒

この方法を活用することで、一意なIDを持つリストの取得パフォーマンスを向上させることができます。
Biomeを導入してJSの書き方がちょっと変わった話

* VSCodeにEditor ConfigやPrettierの拡張機能をインストールして、保存時に自動整形くらいはしていました
* [Stylelint](https://stylelint.io/)の入門記事に出会って、JSでもPrettier以外に何かないかなとさまよいました

# Biome

[Biome](https://biomejs.dev/)はJSなどの問題検出や自動修正がおこなえるLintツールです。
間違いを指摘してくれたり、こう書くといいかもよと提案もしてくれて、JSの書き方がちょっと変わりました。

## インストール

```
npm install --save-dev --save-exact @biomejs/biome
```

## 設定

biome.jsonに設定を記述します。

```
npx @biomejs/biome init
```

あまりカスタマイズしなくても使えるbiome.json。

```json:biome.json
{
    "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
    "organizeImports": {
        "enabled": true
    },
    "linter": {
        "enabled": true,
        "rules": {
            "recommended": true
        }
    },
    "formatter": {
        "enabled": true,
        "indentStyle": "space",
        "indentWidth": 4
    }
}
```

## 使い方

```
npx @biomejs/biome lint --write <files>
```

## Biomeを使うようになって知ったJSの書き方

```javascript:使用前
const exampleElement = document.querySelectorAll(".example");
document.addEventListener("scroll", function () {
    const exampleLeft = exampleElement.scrollLeft;
    exampleElement.forEach((element) => {
        element.classList.add("show");
        element.style.left = exampleLeft + "px";
    });
});
```

```javascript:使用後
const exampleElement = document.querySelectorAll(".example");
document.addEventListener("scroll", () => {
    const exampleLeft = exampleElement.scrollLeft;
    for (const element of exampleElement) {
        element.classList.add("show");
        element.style.left = `${exampleLeft}px`;
    }
});
```

* アロー関数式を使おう
* テンプレートリテラルを使おう
* forEachよりfor...ofを使おう

Biomeのルールによって書き方が統一されたり、知らないJSの書き方も出てきたりして、ツールを使いながら勉強にもなってます。
【Minecraft】Prometheus+Grafanaでサーバの状況を可視化

ブラウザ越しにMinecraftサーバの状況を監視できたらイケてるかなと思いツールを探したら見つかったので導入してみました。

# 環境
- Ubuntu 22.04 LTS amd64
- PaperMC 1.21.3

# Prometheusの導入
aptを使えば一発で入ります。
```bash
sudo apt update
sudo apt install prometheus
```
# Prometheusの設定
[sladkoff/minecraft-prometheus-exporter](https://github.com/sladkoff/minecraft-prometheus-exporter)をダウンロードしてプラグインサーバの`plugins`ディレクトリに入れて起動します。起動後、必要に応じて`PrometheusExporter/config.yml`を編集しサーバを再起動してください。
`/etc/prometheus/prometheus.yml`を編集してMinecraftサーバを監視対象にします。
```yml:prometheus.yml
- job_name: 'minecraft'
    static_configs:
      - targets: ['localhost:9940']
        labels:
          server_name: 'minecraft_server'
```
Prometheusを再起動します。
```bash
sudo systemctl restart prometheus
```

# Grafanaの導入
こちらはリポジトリの追加が必要です。
```bash
sudo wget -q -O /usr/share/keyrings/grafana.key https://packages.grafana.com/gpg.key
sudo echo "deb [signed-by=/usr/share/keyrings/grafana.key] https://packages.grafana.com/oss/deb stable main" | sudo tee -a /etc/apt/sources.list.d/grafana.list
sudo apt update
sudo apt install grafana
```

# Grafanaの設定
Grafanaの設定ファイルは`/etc/grafana/grafana.ini`ですが、今回は特に設定の変更はしません。
リバースプロキシを設定したい場合は[こちら](https://qiita.com/zawanume/items/321d20313cab5b21361f)を参照。
ブラウザで`http://[サーバのIPアドレス]:3000/`にアクセスするとGrafanaのログイン画面が表示されます。初期ユーザー名`admin`、初期パスワード`admin`でログインできます。必要に応じて別の管理者アカウントを作成しログインし直した上で、ユーザー`admin`を無効にするなどしてください。
`Admin`権限を持つユーザーでログインし右側のメニューから「Connections」をクリックします。「Add new connection」から「Prometheus」を選択します。右上の「Add new data source」をクリックします。`Connection`に`http://localhost:9090`を入力します。名前は任意です。一番下の「Save & test」をクリック。
![Screenshot 2024-12-24 at 14-24-14 prometheus - Data sources - Connections - Grafana.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370590/39fce66e-cee8-de15-3093-c1358149b41c.png)

左側のメニューから「Dashboards」をクリックします。右上の「New」から「Import」を選択します。[minecraft-server-dashboard.json](https://raw.githubusercontent.com/sladkoff/minecraft-prometheus-exporter/master/dashboards/minecraft-server-dashboard.json)をアップロードするかペーストします。一番下の「Load」をクリックします。
![Screenshot 2024-12-24 at 14-14-47 Import dashboard - Dashboards - Grafana.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370590/b1586375-b1a2-4c8e-a372-b0b090b7e0f3.png)

出てきた画面でPrometheusのdata sourceを選ぶようになっているので、先程作成したものを選択します。
![Screenshot 2024-12-24 at 14-27-38 Import dashboard - Dashboards - Grafana.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370590/00c3db7e-1818-6423-2c2f-964218a0f110.png)

「Import」をクリックすれば完成です。
![Screenshot 2024-12-24 at 14-37-08 Minecraft - Dashboards - Grafana.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370590/dcd245bf-5197-7972-5ef8-59146ae15fc6.png)
1人ログインした途端にチャンクの読み込み数とエンティティ数が増えているのが見て取れます。

# 参考
1. [ナ組Minecraftサーバーの監視について —マイクラサーバー監視2020—](https://blog.unasuke.com/2020/monitoring-minecraft-server/)