OmniAICreator/Qiita-1.07M huggingface dataset:
html書いたのでS3+CloudFrontする

## はじめに

フロントエンドとしてAPI GatewayにPOSTリクエスト投げるhtml書いたのでS3に置いてCloudFrontからアクセスさせるようにする
ベタな構成だけどちょい引っ掛かったのでメモ

https://qiita.com/Pretzelisa/items/2797294929301867ac09

## 1. S3側でバケット作る

汎用バケットしか使ったことないけど、1桁ミリ秒のレイテンシを求めるときはディレクトリバケットを使うらしい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3458266/f0ef796a-e33a-e262-c436-b2087d100230.png)

ディレクトリバケットの場合、データは単一のアベイラビリティゾーン内で冗長。90日間リクエストが無い場合、非アクティブ状態になり、読み取りと書き込みができなくなる。アクセスリクエストをすると数分でアクティブな状態に戻る。

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/directory-buckets-overview.html

レイテンシーは今回気にしないので汎用バケットで作る。
いったんパブリックアクセスは全ブロックで作って、CloudFrontの設定のときバケットポリシー制御に切り替える。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3458266/6cb6b90e-e6ab-4f1b-7b44-7a7e7798af8f.png)


## 2. CloudFrontでディストリビューションを作成する

ディストリビューション作成の際、オリジンアクセスでOrigin access control settings(OAC)を選択し、新規作成か既存のOACを設定する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3458266/3747ddb1-e914-0f76-6fa8-2d8718982379.png)

ディストリビューションを作成するとS3のバケットポリシー更新してねって出るのでコピーしておく
![qiitatmp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3458266/a670c864-884e-6dbd-93ec-7457b0e04b50.png)


コピーし損ねたらディストリビューションのオリジンタブでS3を選択して「編集」をクリックするとまたポリシーをコピーできる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3458266/eaa2cb8f-a1b8-ed1e-543f-3b0084fe2f02.png)


## 3. S3のバケットポリシーを変更する

コピーしたバケットポリシーをS3バケットに適用してディストリビューションのドメイン名にアクセス。

Access Denied…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3458266/581d86b9-02b1-a96e-a177-2059d95dfcaa.png)

ディストリビューションでデフォルトルートオブジェクト設定したらアクセスできるようになった。(S3オリジンのディストリビューションにAccess Deniedされる原因は複数あるけど、少なくともバケットポリシーは更新されていた)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3458266/4a14fbad-91c3-014d-9fd6-dc483232b824.png)

以下がindex.html
超シンプル、Submit!をクリックするとAPI GatewayにPOSTリクエストを送る。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3458266/f449613c-7650-1344-6910-b141a3d64b8c.png)

LambdaがBedrockのAPIを叩いてキーワードを元に(今回は"rainy")いい感じのプロンプトを作り、別のLambdaがそのプロンプトでまたBedrockのAPIを叩いて画像生成する。

レスポンスのところは実装できてなくて、作成された画像はS3に入る。
(今のところアプリはnullが返ってくるどうしようもない感じになっている)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3458266/bdf5bca9-125e-a3cd-4d62-b3108704d254.png)


ファイル名をプロンプトにしてるんだけど、前はいい感じにプロンプト作れてたのに、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3458266/cbacf99f-a124-fa59-73ed-430e6c1396c0.png)

最近余計なものが色々くっついてしまっている
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3458266/0cdc0491-63e3-c283-bf94-27d4ff5ef91d.png)

まあでもいい感じの画像ができた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3458266/66f46025-e500-a73b-9f7d-aff5774adbcf.png)


## おわりに
htmlは元々作ってたので今回はS3とCloudFrontの設定のみ実施。デフォルトルートオブジェクトのところで引っ掛かったけど作業時間は1時間弱くらい。

生成された画像をアプリ側でも表示させるようにレスポンスの処理をちゃんと書くのと、プロンプトを生成するLambdaを修正してきれいなプロンプトを作らせたい。
[git / vim] git コミットログに現れる "M^" を消去するため、改行コードを変更する

## 概要

- git diff したら、なんかすごいいっぱい`M^`が現れた
- どこかで誰かが、紛れ込ませたのだろう（恐らく自分だけど...）
- 全部消したい


## 対策

以下のようにして、消せる。

まず、バイナリファイルとして問題のファイルを開く。

```bash:bash
vim -b <問題のファイル名>
```

そうすると、 `M^` の存在を可視化することができる。

ファイルを開いた後は、 insert モードではなく、 Escape キーを押下して normal モードにした上で、

```vim:vim
:%s/\r//g
```

を実行すると、 `M^` が全部消えます。

これを保存するなり、 commit するなりすればよい！

## おまけ (記事化の経緯)

関連情報がまとまっている記事はいくつかあるが、情報量が多すぎて読むのに時間がかかったので、端的に必要な事項だけをまとめた。


## 参考資料

どちらも色々な関連情報がまとまっている。

https://www-creators.com/archives/2551#google_vignette

https://qiita.com/gorilla0513/items/e8ccb15bfc87e7ed8d5b
RailsアプリケーションにおけるDateパースの重要性

Railsの開発では、日付の扱いが意外とトリッキーな場面に遭遇することがあります。今回は、私が開発中の予約システムで直面した一つの問題と、その解決策について共有したいと思います。

## 背景

予約システムでは、様々なサービスに対して顧客が予約できるようにするため、日付を元に利用可能な時間帯やサービスの空き状況を確認する機能が必要です。特に、日付をパラメータとして受け取り、それを基にスケジュールを計算する必要がありました。

## 問題の発生

以下の`for_customer_index`メソッドでは、`@service_availability`の値が正常に取得できていましたが、`new`メソッドでは`@service_availability`が期待した値を返さないという問題が発生しました。

```ruby
def for_customer_index
  ...
  @date = Date.parse(params[:date])  # この行が重要
  ...
end
```

```ruby
def new
  @date = params[:date]
  ...
end
```

## 原因の特定と解決

原因は、`new`メソッド内で`@date`の値を`Date.parse`を使って明示的にパースしていなかったことでした。`params[:date]`から取得した値は文字列型であり、これをそのまま使用すると、後続のメソッドで日付として正しく処理されない場合があります。

解決策はシンプルで、`new`メソッド内で`@date`の設定を以下のように変更しました。

```ruby
@date = Date.parse(params[:date])
```

この変更により、`@service_availability`は正常に値を返すようになりました。
PHP-FPMとFastCGI: 効率的なウェブサーバー処理のためのチューニング

## PHP-FPMとは
PHP-FPM（PHP FastCGI Process Manager）は、webサーバー上でphpを実行するツールのことです。通常はリクエストのたびにページを生成してレスポンスとしてクライアントへ返します。しかし、毎回ページを生成していては非効率なので、あらかじめプロセスを起動しておいて効率よくリクエストを処理する役割を担っています。NginxやApacheなどのWebサーバーとPHPを連携してプロセスを管理します。



## FastCGIとは
PHP-FPMはFastCGIの拡張版なので、FastCGIについて簡単に解説します。
FastCGIの主な特徴として以下の４つがあります。

1. パフォーマンスの向上: FastCGIは、プロセスを維持して再利用することで、プロセスの生成と破棄が繰り返されるオーバーヘッドを削減します。
2. スケーラビリティ: FastCGIプロセスは、複数のリクエストを同時に処理する能力があり、負荷に応じてプロセスの数を増減させることができます。
3. 言語とプラットフォームの独立性: FastCGIはPHP、Python、Perl、Rubyなど多くのプログラミング言語で使用することができ、様々なオペレーティングシステム上で動作します。
4. 持続的接続: FastCGIプロセスは、長時間実行を維持することが可能で、一つのプロセスが複数のリクエストを扱うことができます。

## PHP-FPMチューニングについて
PHP-FPMはリクエストを効率的に処理するのに役立ちますが、設定を誤ると逆にサーバーへの負荷を高めてしまう可能性があります。

PHP-FPMの設定ファイルは /etc/php-fpm.d/以下にあります。これより設定ファイルのパラメータについて解説していきます。

## アイドルプロセス（Idle Process）とアクティブプロセス（Active Process）

### プロセス制御方式（PM）
プロセス制御方式を選択します。プロセス制御方式には以下の3つがあります。今回はstatic, dynamicについて解説します。

・static
・dynamic
・ondemand

・static
static設定を使用すると、PHP-FPMはサーバーが起動する際に設定された固定数の子プロセスを生成します。この数はpm.max_childrenで指定され、その数がサーバーのライフタイム中常に維持されます。つまり、サーバーが起動してから停止するまで、常に同じ数の子プロセスが動作していることになります。

pm.max_childrenの設定方法　この数値が高いほど同時にレスポンスを処理できます。ただし、CPUの処理能力を超えて設定しても処理しきれませんし、プロセスが多くなる分メモリ使用率が高くなってしまうのでCPUのコア数の範囲で設定します。
ちなみにpm.max_childrenはIdle ProcessとActive Processの合計値です。

・dynamic
dynamic設定では、子プロセスの数が動的に調整されます。このモードでは、pm.max_children、pm.start_servers、pm.min_spare_servers、pm.max_spare_serversという複数のパラメータが関連しています。pm.start_serversは起動時に生成する子プロセスの数、pm.min_spare_serversは最小限維持すべきアイドル状態のプロセス数、pm.max_spare_serversはアイドル状態で保持する最大のプロセス数をそれぞれ指定します。

pm.max_children　最大値　
staticでも解説している通りにCPUやメモリのリソースを考慮して設定する必要があります。リクエスト時に生成するプロセスの上限です。（Idle ProcessとActive Processの合計値の上限）
pm.start_servers　起動時に生成する子プロセスの数
pm.min_spare_servers　最小限維持すべきアイドル状態のプロセス数
pm.max_spare_servers　アイドル状態で保持する最大のプロセス数
Active Processが終了するとIdle Processに状態が変化します。
pm.max_requests 　各プロセスが処理するリクエスト数
一つの子プロセスが処理できるリクエスト数を指定します。この設定をすることでプロセスが蓄積してメモリを圧迫することを防ぐことができます。



参考記事

https://hackers-high.com/linux/php-fpm-config/

https://uzula.business/2019/04/15/post-2577/

https://zenn.dev/bs_kansai/articles/3706c12408160c#fastcgi%E3%81%A8%E3%81%AF

https://hackers-high.com/linux/php-fpm-config/#php-fpm-2

https://agaroot-itp.com/blog/1437/

EC2でPHP-FPMのチューニングをする際は以下CPUのコア数スレッド数を確認する必要があります。

https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/cpu-options-supported-instances-values.html
CKANで日付範囲を使って検索する

# はじめに

データセットのメタデータには作成日（metadata_created）や修正日（metadata_modified）といった値が含まれています。検索条件としてタイトルやタグの文字列を使うだけでなく、日付を使った検索が可能です。

# 日時のフォーマット

CKANのバックエンドでは検索エンジンとして Solr が動作しています。日時を表すフォーマットは
`YYYY-MM-DDThh:mm:ssZ`となっています。例えば2024年4月15日12時34分56秒は`2024-04-15T12:34:56Z`という形になります。

## 日時の範囲表記

日時の範囲を表すには、ブラケット[ ]の中に TO を挟んで日時を並べて、
```
[2024-04-01T00:00:00Z TO 2024-04-30T23:59:59Z]
```
といった形になります。

## Date Math 表記

具体的な数字以外に、**NOW**, **DAY**, **MONTH** といったキーワードを用いて日時を表すことが出来ます。

```
# 1週間前から今まで
[NOW-7DAY TO NOW]
```

```
# 1ヶ月までから10日前まで
[NOW-1MONTH TO NOW-10DAY]
```

詳細は [Apache Solr Reference Guide](https://solr.apache.org/guide/8_11/working-with-dates.html) を参照してください。

# 日時によるデータセット検索

CKAN API のデータセット検索 **package_search** で日付を使って検索してみます。

## 2024年4月15日から4月30日の間に作成されたデータセットを検索

```sh
curl -s "https://www.geospatial.jp/ckan/api/action/package_search?q=metadata_created%3A\[2024-04-15T00%3A00%3A00Z%20TO%202024-04-30T23%3A59%3A59Z\]"
```

## 7日前から今までの間に修正されたデータセットを検索

```sh
curl -s "https://www.geospatial.jp/ckan/api/action/package_search?q=metadata_modified%3A\[NOW-7DAY%20TO%20NOW\]"
```
semopyでは複数のDAGに対する評価はどうなるか？

semopyを使用したときに、複数のDAGが存在する構造方程式を渡すと評価値はどうなるのか気になったので確認してみた。今回は潜在変数がない状態のDAGを定義します。

## 必要なライブラリのインポート

まず、必要なライブラリをインポートします。

```python
import semopy as sem
import pandas as pd
import numpy as np
```

## 合成データセットの生成

以下のコードで、サンプルデータを生成します。`numpy`を使用して9つの変数を持つ100サンプルのデータを生成します。

```python
np.random.seed(123)
n_samples = 100
data = pd.DataFrame({
    'x1': np.random.normal(0, 1, n_samples),
    'x2': np.random.normal(0, 2, n_samples),
    'x3': np.random.normal(0, 200, n_samples),
    'x4': np.random.normal(0, 4, n_samples),
    'x5': np.random.normal(0, 5, n_samples),
    'x6': np.random.normal(0, 6, n_samples),
    'x7': np.random.normal(0, 7, n_samples),
    'x8': np.random.normal(0, 8, n_samples),
    'x9': np.random.normal(0, 100, n_samples)
})
```

## 潜在変数なしモデルの定義とフィット

**まずは3つのDAGを構造方程式で定義して計算してみます。**

```python
# モデルの定義（潜在変数なし）
model_desc = """
# Direct relationships among observed variables
x3 ~ x1 + x2
x6 ~ x4 + x5
x9 ~ x7 + x8
"""

# モデルの構築とフィット
model = sem.Model(model_desc)
results = model.fit(data)
```

パラメータやフィット指標を出力します。

```python
# モデルのパラメータとフィット指標の出力
display(sem.calc_stats(model))
print(model.inspect())
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456646/ba1f5548-61d4-27dc-675b-d272516771f9.png)

モデルを可視化します。

```python
# モデルの可視化
g = sem.semplot(model, "dag_model.png")
Image(filename='dag_model.png')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456646/1247a734-e3e3-b513-0fd3-2590000ad2cc.png)

---

**次は２つに減らします**

```python
# モデルの定義（潜在変数なし）
model_desc = """
# Direct relationships among observed variables
x3 ~ x1 + x2
x6 ~ x4 + x5
"""

# モデルの構築とフィット
model = sem.Model(model_desc)
results = model.fit(data)
```

パラメータやフィット指標を出力します。

```python
# モデルのパラメータとフィット指標の出力
display(sem.calc_stats(model))
print(model.inspect())
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456646/136497c7-9529-c5e6-0949-2c9e86e70d76.png)


モデルを可視化します。

```python
# モデルの可視化
g = sem.semplot(model, "dag_model.png")
Image(filename='dag_model.png')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456646/a23ff040-eb30-6e61-cfd5-75ffea1b1c33.png)


## まとめ

結果として

|DAGの数|RMSEA|
|-|-|
|3|0.119815|
|2|0.126984|

となりました。ちゃんと二つと三つでしっかり計算結果が変わっていますね。
逆にいうと、複数のグラフが含まれていても関係なく計算可能でもあるということです。

【参考文献】
- [Mediumの元記事](https://cr-c.medium.com/analysis-and-visualisation-of-sem-using-python-semopy-structural-equation-modeling-bd6c3f463e33)
ローカルTCPサーバをインターネットからアクセス - ngrok

# 日記
トンネリングリバースプロキシサーバ。
[Discord SDKのQuick Start](https://github.com/discord/discord-example-app#set-up-interactivity)で[ngrok](https://dashboard.ngrok.com/)を知った。
動的URLでよければフリー。

# 環境
- Raspberry PI 4
- Ubuntu 20.04


# ngrok agentの導入と初期化

[公式参考](https://dashboard.ngrok.com/get-started/setup/linux)
```sh:
curl -s https://ngrok-agent.s3.amazonaws.com/ngrok.asc \
	| sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null \
	&& echo "deb https://ngrok-agent.s3.amazonaws.com buster main" \
	| sudo tee /etc/apt/sources.list.d/ngrok.list \
	&& sudo apt update \
	&& sudo apt install ngrok
```
```sh:snapを使用する場合 (非推奨; service installコマンドでエラーが出た)
sudo snap install ngrok
```

[ngrok](https://dashboard.ngrok.com/)にサインインし、 [[Getting Start] - [Your Authtoken]](https://dashboard.ngrok.com/get-started/your-authtoken) でTokenを取得(*1)
```sh:
NGROK_TOKEN=2g4MxxxxdX8D4HDouBlxxxxd77y_7nFHxxxxxkhwm7mcrZjvA #(*1)
ngrok config add-authtoken $NGROK_TOKEN
```

# 例:ローカルHTTPサーバを公開
[Node.js](https://qiita.com/shokkaa/items/6f83b3a107006ccc598b)で簡単なテストサーバ起動。
```sh:テストサーバ起動 (カレントフォルダが公開されてしまうのでご注意)
npx -y http-server
```
例えばローカルでHTTPサーバ `http://192.168.11.44:8080` が起動した場合、
```sh:トンネル接続
ngrok http http://192.168.11.44:8080
```

URL(例えば`Forwarding https://e025-2001-f77-d3e0-12f0-dea6-32ff-fe7a-aaaa.ngrok-free.app/`)が表示されるので、インターネット側からアクセスできることを確認

手軽なSSL化としても便利。なおSSLの認証局はLet's Encryptの`ISRG Root X1 R3`。

# 例:ローカルssh(TCPサーバ)を公開
前提: sshdが起動している
```sh:sshサーバ側
ngrok tcp 22
```
例えば `Forwarding tcp://0.tcp.jp.ngrok.io:10599 -> localhost:22` と表示された場合
```sh:sshクライアント側
ssh <login-id>@0.tcp.jp.ngrok.io -p 10599
```

# 例:設定ファイルに公開ポートを定義
生成される設定ファイル(`ngrok config check` で表示される)(*2)を編集。
例えばID`ssh1`でローカルのポート22をインターネットに公開する設定を追加。

```sh:
NGROK_CONF=`ngrok config check |sed -e 's/.*file at //'`
nano $NGROK_CONF 
```

```yml:/(*2)/ngrok.yml 編集
version: "2"
authtoken: xxxxxx  # *1
metadata: '{"id":"rp4/ubuntu"}' # 識別用の適当なJSON文字列
tunnels:
  ssh1:
    addr: 22
    proto: tcp
```
```sh:サーバ側
ngrok start ssh1
```

# 例:OSのサービスとして登録
上記設定ファイルでOS起動時に自動的にポート公開したい場合。
```sh:
sudo ngrok service install --config $NGROK_CONF
sudo ngrok service start
```

動的に開かれたngrog側のTunnelのアドレスを取得するには、
[[Tunnels] - [Agents]](https://dashboard.ngrok.com/tunnels/agents) - Agentの詳細からmetadataで判断

```sh:サービスの状態を確認する
systemctl status ngrok.service
```
`active (running)`が見えれば起動中
LLaVA Docker試行メモ

## 概要
LLaVAをWSL2(ubuntu22)のDockerコンテナで動かしたときのメモを残しておく。
とりあえず動かしただけなので、あとで見直したい。
(2024/5/6時点)

## 参考サイト
https://llava-vl.github.io/blog/2024-01-30-llava-next/

https://github.com/haotian-liu/LLaVA

## 手順

### コード取得
```bash
git clone https://github.com/haotian-liu/LLaVA.git
cd LLaVA
```

### ubuntuイメージ取得
```
docker pull nvidia/cuda:11.8.0-devel-ubuntu22.04
```

### コンテナ起動
```
docker container run -it --gpus all -h "llava_test" --rm -p 80:8888 -v /mnt/c/work:/mnt/work --name llava_test nvidia/cuda:11.8.0-devel-ubuntu22.04 /bin/bash
  # -it : インタラクティブモード
  # --gpus <gpu指定> : 使用するgpuの指定 (番号を指定する例 : --gpus device=0,2)
  # -h <ホスト名> : コンテナ内のホスト名
  # --rm : 終了時にコンテナを自動で削除
  # -p <ホスト側port>:<コンテナ側port> : ポートマッピング
  # -v <ホスト側dir>:<コンテナ側dir> : ディレクトリマウント
  # --name <コンテナ名> : コンテナ名
  # 末尾から2つ目の引数 : イメージ名:タグ名
  # 末尾 : 起動時コマンド 
```


### コンテナ内でライブラリインストール
```
cd /mnt/work/LLaVA

apt-get update
apt-get install -y --no-install-recommends python3-pip git python3-dev curl vim less wget python3-wheel
pip install --upgrade pip

pip install -e .
pip install protobuf
```

実行例
```bash
# llava-v1.5-7b
python3 -m llava.serve.cli --model-path liuhaotian/llava-v1.5-7b --image-file "https://llava-vl.github.io/static/images/view.jpg" --load-4bit

# llava-v1.5-13b
python3 -m llava.serve.cli --model-path liuhaotian/llava-v1.5-13b --image-file "../1.jpg" --load-4bit    
```

以上



----- 

Dockerfile
※できてないので未使用。Dockerfileを使用した手順に変えたい

```Dockerfile
# for LLaVA
FROM nvidia/cuda:11.8.0-devel-ubuntu22.04

ENV FORCE_CUDA="1"
ENV MMCV_WITH_OPS=1

RUN apt-get update && apt-get install -y --no-install-recommends \
    python3-pip     \
    git             \
    python3-dev     \
    curl            \
    vim             \
    less            \
    wget            \
    python3-wheel
RUN pip3 install --upgrade pip
    && pip3 install   \
          protobuf    \
        setuptools

COPY . /llava
WORKDIR /llava

RUN pip3 install -e .

# ENTRYPOINT [ "python3", "demo.py" ]
# CMD [configs/pretrain/${MODEL}, weights/${WEIGHT}]

```
【ROS2 ログTips】一定時間間隔でログを出力する方法

# はじめに

ROS2ログを使って、一定時間間隔でログを出力する方法。C++とPythonそれぞれ紹介する。

# 動作確認環境
* ROS2 Foxy

# 方法
C++、Pythonでそれぞれ一定時間間隔を指定する引数があるので設定する。

## C++ (rclcpp)

ログレベルで、それぞれ関数が用意されている。
```cpp
RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_THROTTLE
```
ストリームを使う場合
```cpp
RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_STREAM_THROTTLE
```

例：　1秒おきにエラーログを出力

```cpp
#include "rclcpp/rclcpp.hpp"

std::string test_err_str = "test error"
RCLCPP_ERROR_THROTTLE(
  this->get_logger(), 
  *this->get_clock(),
  1000,
  "Some error message: %s",
  test_err_str.c_str()
);
```
* 第1引数(logger)：ROSロガー
* 第2引数(clock)：ROSクロック
* 第3引数(duration)：出力間隔時間（**ミリ秒**）
* 第4引数：メッセージ

:::note info
ポイント  
第2引数のROSクロックは実体で渡す必要があるので、this->get_clock()を使う場合は、*this->get_clock()として実体で渡す。
:::

## Python (rclpy)

追加の引数「throttle_duration_sec」で指定する。
例：1.0秒間隔でエラーログを表示
```python
import rclpy

self.get_logger().error('Some error message', throttle_duration_sec=1.0)
```
* throttle_duration (float)
  * 出力間隔時間（**秒**） 

参考までに、rclpyのログの引数には以下が指定可能。
```python
:Keyword Arguments:
    * *throttle_duration_sec* (``float``) --
      Duration of the throttle interval for the :py:class:Throttle: filter.
    * *throttle_time_source_type* (``str``) --
      Optional time source type for the :py:class:Throttle: filter (default of
      ``RCUTILS_STEADY_TIME``)
    * *skip_first* (``bool``) --
      If True, enable the :py:class:SkipFirst: filter.
    * *once* (``bool``) --
      If True, enable the :py:class:Once: filter.
```
* https://github.com/ros2/rclpy/blob/foxy/rclpy/rclpy/impl/rcutils_logger.py#L262-L271

# まとめ
 ROS2で一定時間間隔でログ出力する方法を紹介した。ログの量が多くなりすぎる場合に、重宝する。

# 参考

https://docs.ros.org/en/foxy/Tutorials/Demos/Logging-and-logger-configuration.html#logging-throttled

https://docs.ros2.org/latest/api/rclcpp/logging_8hpp.html#a53cee4093804b13f31cb1516087400eb

https://github.com/ros2/rclpy/blob/foxy/rclpy/rclpy/impl/rcutils_logger.py#L262-L271
LINE bot のクイックリプライ機能をPythonで実装する

# LINE Messaging APIを使ってインタラクティブなボットを作成しよう！

LINE Messaging APIは、LINEのメッセージングプラットフォームを利用して、ボットやアプリケーションを開発するためのAPIです。この記事では、LINE Messaging APIの概要と、その中でも特に便利な「クイックリプライ機能」について詳しく解説します。

## クイックリプライ機能で、ユーザーとのコミュニケーションを円滑に

クイックリプライ機能は、ユーザーにボタン形式の選択肢を提示することで、簡単かつ迅速な応答を可能にします。この機能を活用することで、ユーザーとのコミュニケーションをよりスムーズに行うことができます。

例えば、ユーザーからの質問に対して、いくつかの選択肢を提示することで、ユーザーは簡単にボタンをタップするだけで回答できます。これにより、ユーザーの利便性が向上し、ボットとのやり取りがより円滑になります。

## Python SDKを使ってクイックリプライ機能を実装しよう

LINE Messaging APIを使ってクイックリプライ機能を実装するには、以下の手順が必要です。

### 1. 必要なパッケージをインストール

まず、以下のコマンドを使って、必要なパッケージをインストールします。

​```
pip install line-bot-sdk
​```

### 2. クイックリプライ機能を実装

次に、以下のようなコードを記述することで、クイックリプライ機能を実装できます

```python
items = ["選択肢1", "選択肢2", "選択肢3"]
quick_reply_buttons = []

for item in items:
    quick_reply_buttons.append(QuickReplyButton(action=MessageAction(label=item, text=item)))

quick_reply = QuickReply(items=quick_reply_buttons)
message = TextSendMessage(text="以下の選択肢から選んでください。", quick_reply=quick_reply)
line_bot_api.reply_message(event.reply_token, message)
```

このコードでは、`items`リストに選択肢となる文字列を定義し、`quick_reply_buttons`リストに`QuickReplyButton`オブジェクトを追加しています。その後、`QuickReply`オブジェクトを作成し、`TextSendMessage`オブジェクトに追加することで、クイックリプライ機能を持ったメッセージを送信しています。

## クイックリプライ機能を活用して、ユーザーとのエンゲージメントを高めよう

クイックリプライ機能は、ユーザーとのエンゲージメントを高めるために非常に効果的です。例えば、以下のようなシーンで活用できます。

- ユーザーの好みや興味を知るためのアンケート
- ユーザーの問い合わせ内容を絞り込むための選択肢の提示
- ゲームやクイズなどのインタラクティブなコンテンツ

LINE Messaging APIとクイックリプライ機能を活用して、ユーザーとのコミュニケーションをより円滑かつ効果的に行いましょう！

# 最後に
記事の全体像は私で作成しましたが、Claudeで全体を添削してもらっています！
【AWS×Webアプリ】S3静的Webサイトホスティング（Terraform）

## 目的
・AWS上にS3バケットを作成し、静的Webサイトホスティング設定を行う。

## 前提条件
・Terraformを使用してAWS上にリソースを作成する。
・作成する内容は以下と同様

https://qiita.com/Haruki-N/items/68661c410bb4a143183a

## TFファイル
```terraform:aws_s3.tf
resource "aws_s3_bucket" "WebBucket" {
  bucket = "terraform-webbucket-${data.aws_caller_identity.self.account_id}"
}

resource "aws_s3_bucket_versioning" "versioning_WebBucket" {
  bucket = aws_s3_bucket.WebBucket.id
  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_s3_bucket_public_access_block" "public_access_block_WebBucket" {
  bucket = aws_s3_bucket.WebBucket.id

  block_public_acls       = false
  block_public_policy     = false
  ignore_public_acls      = false
  restrict_public_buckets = false
}

resource "aws_s3_bucket_website_configuration" "website_configuration_WebBucket" {
  bucket = aws_s3_bucket.WebBucket.id

  index_document {
    suffix = "index.html"
  }
}

resource "aws_s3_bucket_policy" "allow_access_to_WebBucket" {
  bucket = aws_s3_bucket.WebBucket.id
  policy = data.aws_iam_policy_document.allow_access_to_WebBucket.json
}

data "aws_caller_identity" "self" { }

data "aws_iam_policy_document" "allow_access_to_WebBucket" {
  statement {
    principals {
      type        = "*"
      identifiers = ["*"]
    }

    effect = "Allow"
    actions = ["s3:GetObject"]
    resources = ["${aws_s3_bucket.WebBucket.arn}/*"]
  }
}
```

・WebBucket：S3バケット
・versioning_WebBucket：バージョニング有効化
・public_access_block_WebBucket：パブリックアクセス有効化
・website_configuration_WebBucket：静的Webサイトホスティング設定
・allow_access_to_WebBucket：バケットポリシー設定

## 動作確認
①作成されたS3バケットへ「index.html」をアップロードする
```index.html
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>My Website Home Page</title>
</head>
<body>
  <h1>Welcome to my website</h1>
  <p>Now hosted on Amazon S3 using Terraform!</p>
</body>
</html>
```
②Webサイトへアクセス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3637963/20b2bad1-c66a-3bce-f9f4-e468e4e380b2.png)
asdf-javaのJAVA_HOME切替スクリプトがzshの遅延に影響していた

# TL;DR
- zshのプロンプト表示に遅延が発生していた
- 原因：[asdf-java](https://github.com/halcyon/asdf-java)が提供する、`JAVA_HOME`切替スクリプトがprecmdフックにより毎回動いていたため
- [asdf](https://github.com/asdf-vm/asdf)が直接の原因ではないが、これをきっかけに[mise](https://github.com/jdx/mise)に移行した

# 環境
- macOS 13.1
- zsh 5.9 (x86_64-apple-darwin21.3.0)
- [asdf](https://github.com/asdf-vm/asdf) 0.11.0
- [asdf-java](https://github.com/halcyon/asdf-java)
- [mise](https://github.com/jdx/mise) 2024.5.2 macos-x64

# asdfとは
複数のツールのバージョンを管理してくれるCLI

https://github.com/asdf-vm/asdf

プロジェクトごとに`.tool-versions`を置いてあげるといちいち違うプロジェクトのためにバージョンを切り替えなくて済む

```zsh
$ cd a_directory && node -v
# node version written in a_directory/.tool-versions
$ cd ../other_directory && node -v
# node version written in other_directory/.tool-versions
```

そして、asdfのプラグインの一つである[asdf-java](https://github.com/halcyon/asdf-java)でJavaのバージョン管理をしていた

https://github.com/halcyon/asdf-java

# 現象
プロンプトが表示されるのに遅延がある なんだ〜？
![terminal.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/473717/a90e0da1-15f2-3905-3208-001a7394a597.gif)

# 原因
asdf-javaが提供する[set-java-home.zsh](https://github.com/halcyon/asdf-java/blob/master/set-java-home.zsh)がprecmdで実行されるように登録されていた。つまり、プロンプトを表示する直前に毎回このスクリプトが走るようになっていた

https://github.com/halcyon/asdf-java/blob/b7bc26473dee6d078976b1e9cbea76023585891b/set-java-home.zsh#L12

issueも上がっていたので、辿ってみると...

https://github.com/halcyon/asdf-java/issues/193

> try rtx, it updates JAVA_HOME without any need for set-java-home.zsh and doesn't have any meaningful performance impact.

じゃあそうしますか！＾

※rtxはmiseの前身の名前で、NVIDIAのグラフィックボードRTXと混同するから改名しただけらしい(法的にではなく、単にググラビリティの問題とのこと https://mise.jdx.dev/rtx.html)

# asdfからmiseに移行
asdf-javaが提供する1スクリプトを改善すれば済む話なのでasdf本体には関係ないのだが、issueコメントに誘われmiseに移行してみる

https://github.com/jdx/mise

[Quickstart](https://github.com/jdx/mise?tab=readme-ov-file#quickstart)からインストール

新たに管理するプラグインは`config.toml`が生成されて管理されるらしい
既存の`.tool-versions`にも対応しているのが嬉しい
```zsh
$ mise list
Plugin  Version                 Config Source              Requested             
java    adoptopenjdk-8.0.312+7  ~/.tool-versions           adoptopenjdk-8.0.312+7
java    17.0.2                                                                   
jq      1.6                     ~/.tool-versions           1.6                   
node    20.11.1                                                                  
node    20.12.2                 ~/.config/mise/config.toml 20
```

フックの登録無しで`JAVA_HOME`も変わってる。嬉しい
```zsh
$ cd a_directory && echo $JAVA_HOME
/Users/macaroni/.local/share/mise/installs/java/adoptopenjdk-8.0.312+7
$ cd ../other_directory && echo $JAVA_HOME                      
/Users/macaroni/.local/share/mise/installs/java/17.0.2
```


たぶん[ここら辺](https://github.com/jdx/mise/blob/40e82be7e187cb09d2dad1c0d8b61078c4f7cebe/src/plugins/core/java.rs#L379)とかかな・・・
teraterm: dockerでsshサーバを立ててテストを行う

## はじめに：teratermとは

teratermとは、Windows向けのターミナルソフト。
WSL2が普及する以前、Windowsの場合はこのツールがSSHターミナルソフトの主流だった。

## なぜ、sshサーバを立てなければならないのか

- teratermは通常、単体テストを行えない。なぜなら独自のTera Term Language (TTL)を使用しているため
  - teraterm対応の自動テストフレームワークがないから、ユニットテストを行えない
  - 一方、サーバがなければ結合テストができない
  - 複数人で使用する環境には、(テスト環境といえども)未テストの資材を持ち込みたくない

## では、どうするか(WSL2+dockerでサーバを立てればよい)

もっとも手軽な解決策として、dockerでサーバを立てる方法が挙げられる

## sshサーバの立ち上げ

今回は、`linuxserver/openssh-server`イメージを選定した。

https://hub.docker.com/r/linuxserver/openssh-server

:::note warn
  teratermのテストを目的としているため、volumeを使用しない。したがって、データは永続化されない。
:::

```compose.yml
services:
  openssh-server:
    image: linuxserver/openssh-server:version-9.6_p1-r0
    container_name: openssh-server
    hostname: openssh-server #optional
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Asia/Tokyo
      - PASSWORD_ACCESS=true 
      - USER_PASSWORD=**password**
      - USER_NAME=**user**
      - LOG_STDOUT= #optional
    ports:
      - 2222:2222
    restart: unless-stopped
```

起動はいつも通り

```bash
docker compose up -d
```

## 起動後のアクセス確認

### docker composeの場合(同一ネットワーク)

テストには、`arukiidou/sshpass`イメージを使用する。

https://hub.docker.com/r/arukiidou/sshpass

```compose.yml
services:
  openssh-server:
    #(略)
  openssh-sshpass:
    image: arukiidou/sshpass:1.10-alpine3.19
    container_name: openssh-sshpass
    tty: true
```

```bash
sshpass -p '**password**' ssh -o StrictHostKeyChecking=no -p 2222 **user**@openssh-server
```
### WSL2(コンテナ外、同一ホスト)の場合

```bash
sshpass -p '**password**' ssh -o StrictHostKeyChecking=no -p 2222 **user**@localhost
```

### teratermの場合

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/161516/2289c6a9-e5d2-52e7-842a-fb944ccdd8e2.png)
C# 非同期について

# 目的
現場でC#を使っています。非同期処理について自分なりに学習した記録です。誤りがあれば指摘をお願いします。

# 同期処理
一つずつ順番に処理を実行する方法。前の処理が終わるまで次の処理は実行されない。

# 処理の流れ
①button1をクリック
②検索処理(GetData()メソッド実行)
③表示
途中でcheckBoxにチェックを入れることはできない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3185982/bef04f66-d6a2-c4ac-afbf-67dff6400969.png)
```C#:Form1.cs
private void button1_Click(object sender, EventArgs e)
{
    dataGridView1.DataSource = GetData();
}
private List<DTO> GetData() 
{
    var result = new List<DTO>();
    for (int i = 0; i <5; i++) 
    {
        Thread.Sleep(1000);
        result.Add(new DTO(i.ToString(),"Name" + i));
    }
    return result;
}

```

# 非同期処理
前の処理が終わる前に、次の処理も実行する方法。
処理の流れは同期処理と同じですが、大きな違いは途中でもチェックボックスにチェックを入れることができる。
メインスレッド（UIスレッド）に加えて、非同期処理を行うワーカースレッドが生成される。
主な方法としては、三つある。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3185982/bf908d20-e99d-35a9-2b92-e5b9774ce493.png)
# ①Threadクラスを用いた方法
```C#:Form1.cs
private void button2_Click(object sender, EventArgs e)
{
    var t = new Thread(GetData); // Threadクラスの引数に非同期で処理したい関数(GetData)を指定。
    t.Start(); //非同期処理開始
}

private void GetData()
{
    var dtos = new List<DTO>();
    for (int i = 0; i < 5; i++)
    {
        Thread.Sleep(1000);
        dtos.Add(new DTO(i.ToString(), "Name" + i));
    }

    //dataGridView1.DataSource = dtos; //エラーが発生する
    //UIスレッド外のスレッドから直接UIコントロールを操作することは避ける

    this.Invoke((Action)delegate ()
    {
        //UIスレッド上で実行するコードを記述する
        dataGridView1.DataSource = dtos;
    });
}
```

# ②Taskを用いた方法
①と比較すると複雑そうにみえますが、文構造としてはシンプルです。
```C#
Task.Run(非同期メソッド).ContinueWith(action, context)
```
action→非同期メソッド完了後に行う処理
context→メインスレッドの環境（ざっくりいうと）
Task.Run→非同期処理を開始
ContinueWith()メソッドで非同期処理後に行う処理を指定

```C#:Form1.cs
{
    public partial class Form1 : Form
    {
        private void button2_Click(object sender, EventArgs e)
        {
            var context = TaskScheduler.FromCurrentSynchronizationContext();
            Task.Run(
                () => GetData()
            ).ContinueWith
            (
                dtos =>
            {
                dataGridView1.DataSource = dtos.Result;
            }, 
                context
            );
        }
    }
}
```
# ③awaitを用いた方法
最もコード量が少なく、使用頻度も高い方法。
async→非同期メソッドになる
await→非同期処理に対する実行制御を行う。コード例でいうと、非同期処理(GetData()メソッド)の完了を待ってから、UIスレッド上で処理(dataGridView1.DataSource = dtos;)が行われる。

```C#:Form1.cs
private async void button3_Click(object sender, EventArgs e)
{
    var dtos = await Task.Run(() => GetData());
    dataGridView1.DataSource = dtos;
}
```
# 参考資料
[非同期プログラミング](https://anderson02.com/main/#toc5)
[C#の非同期処理 基礎編](https://qiita.com/TaitoKudo/items/130949f41eece8ef4d80)
SBOM導入時における3つの論点

2024年4月26日に「サイバー攻撃への備えを！「SBOM」（ソフトウェア部品構成表）を活用してソフトウェアの脆弱性を管理する具体的手法についての改訂手引（案）を公表します」として経済産業省から手引書が公表されました。
SBOMを用いた具体手法が記載されておりわかりやすいものの、依然として専門家でないとわからない脆弱性管理対応が求められるような作りとなっています。

ここでは、まだSBOMを導入していない企業へ向け、導入時に検討するべき3つの論点を整理しました。
これらを議論の材料に活用いただければ光栄です。

![20240505_セキュリティニュースまとめ_SBOM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3299903/4268fdea-7125-e4a6-2b02-516b2d4cc3af.png)

## 参考
ソフトウェア管理に向けたSBOM（Software Bill of Materials）の導入に関する手引ver 2.0（案）,経済産業省, https://www.meti.go.jp/press/2024/04/20240426001/20240426001.html
Reactでのコンポーネント分割の際の読み込みエラーを解決

# はじめに
UdemyでReactを学習していて、コンポーネント分割をした際にエラーが発生したためその解決方法を備忘録として残します！

# 発生したエラー
```react
[plugin:vite:import-analysis] 
Failed to parse source for import analysis because the content contains invalid JS syntax. 
If you are using JSX, make sure to name the file with the .jsx or .tsx extension.
```

# 原因
日本語訳すると「コンテンツに無効な JS 構文が含まれているため、インポート分析のためのソースの解析に失敗しました。 JSX を使用している場合は、ファイル名に必ず .jsx または .tsx 拡張子を付けてください。」です。
生成したファイルの拡張子が "App.js" だったため上記のエラーが起きていました！

# 解決
ファイルの拡張子を「App.jsx」に変更するとエラーが解消されて、無事に表示されるようになりました！
エラー文様々ですね！
discordのdeveloper portalにbotをホストしているURLを登録しようとしてもバリデーションエラーではじかれた話

結論から書きます
`INTERACTIONS ENDPOINT URL`のValidationに誤りがあり、アンダーバー`_`を含んだサブドメインがエラーになってしまいます

自分の場合、cloudflareのWorkersの名前をaaa_bbbのような名前にしていたため、サーバーがaaa_bbb.username.workers.devのようなurlでホストされていました
それをdiscordのdeveloper portalに入力しようとして謎のエラーに苦しめられていました

```
Validation errors:
interactions_endpoint_url: URLの形式が正しくありません。
```

![error.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3785521/17c790fd-07b0-e20b-3ec7-b5724c44bb44.png)


解決策は簡単で、別ドメインでホストするか、Workersのなまえのアンダーバーをハイフンに変えるだけです
【Try Hack Me】vulnversityをサイバーキルチェーンに当てはめて

こんにちは、今日はサイバーキルチェーンに当てはめて

Try Hack Meのこの単元のマシンを攻略していきたいと思います。自身で答えを導き出した方がためになると思うので問題の答えは書きません。

※もちろん攻略用に用意されたマシンです。他サイトに向けてここに書かれている攻撃を試すと不正アクセス禁止法により罰せられます。


偵察
-----
まずは偵察です。

攻撃用のマシンを起動したら
どういった脆弱性があるか、どこを起点にして攻撃を仕掛けるか情報収集をしましょう。
このときに使用するツールはNmapです。

Nmap は、コンピューター ネットワーク上のホストとサービスを検出するために使用される、無料のオープンソースのスキャンツールです。



nmap -sV 攻撃対象のIP

をターミナルに入力します。



ここで注意して確認したいのは
・何番ポートが開いているか
→不要なポートは閉じる
・サービスは何か
→脆弱性のあるサービスは使用を停止する
・サービスのバージョンは何か
→やむを得ない場合は最新のバージョンにしておく
・OSは何か
→OSごとの脆弱性も情報になる
・WEBサーバは何番で実行されているか
→攻撃の起点になる
です。

それぞれ調べ終わったらさらに情報収集です





情報収集
-----

OSとサービスがわかりました。

OS固有の仕組みによって権限昇格ができそうなのでリバースシェルをアップロードするためのディレクトリを
gobusterと呼ばれる高速検索ツールを使って総当たり検索していきます


その時のコマンドは
gobuster dir -u ht◇tp://攻撃対象のIP:3333 -w <単語リストの場所>
となります。


恐らく何個か見つかるはずです。



ファイルをアップロードできそうな場所を見つけられたらリバースシェルをアップロードしていきます。


武器化、配送、攻撃、インストール
-----
ここではシェルをアップロードするディレクトリに対して攻撃をしていってとりあえずサーバに入り込みます。
サイバーキルチェーンだと武器化、配送、攻撃、インストール
をいっぺんに行います。

Burpの機能を使いますがBurpについて説明すると多分日が暮れるので割愛します。
Try Hack MeにもBurpの単元があるので調べてみてください。

まず調べるのが

アップロード許可される拡張子です。

そのためにBurpで送った攻撃に対するステータスコードやレスポンスの長さ（レングス）に注目します。


一つ違うものがあるはずなのでその拡張子を使います。

そしたら
ncコマンドでリバースシェルの通信をリッスンします。

リバースシェルをアップロードしたら

http:/　/攻撃対象のIP:3333/該当ディレクトリ/uploads/php-reverse-shell.拡張子
をアドレスバーにに入力します
(これでリバースシェルが実行される)
※リバースシェルのダウンロード方法については単元のページに書いてあります。



そうすると
ターミナルのncコマンドが動いてシェルに入った状態になるはずです。


入れたので情報を集めましょう

自分が使ったコマンドは
ls
cd
cat
pwd
whoami
find

です

これを駆使してどのファイルが一番最初に調べたOS特有の権限管理による問題を使えるのか調べましょう

・どんなユーザーがいるのか
・ユーザ配下のディレクトリにフラグはないか？

について調べることでで最初の目標は達成できるはずです

遠隔操作
-----

ここから問題がいきなり投げやりになって難しいはずなので（自分はここだけで４時間かかりました、、、、笑）


https://gtfobins.github.io/
これだけ共有しておきます！！

どこかのバイナリファイルにSUIDが設定されていると権限昇格の維持ができるみたいです。
基本になるコードは調べれば出てくるので調べてみましょう
ここだけ答え書くと怪しいのは/bin/systemctlです、

目標達成
-----


無事に二つのユーザーフラグをとれましたか？

ここで考えることは攻撃者も同じことを考えて脆弱性を突いてくるということです。
それぞれのフェーズにあった対策をしっかり取捨選択していって、よりよいセキュリティライフを送ってください。


今回はフラグの奪取でしたがこれが個人情報だったり社外秘の経営上秘密だったりしたら莫大な被害になりますよね。

次はWindowsマシンのハッキングにチャレンジしようと思います。
【HTML/CSS】marginとpaddingについて

:::note warn
エンジニア初心者の投稿であるため、間違っている箇所が多くあるかと思います。
何かお気づきの点がありましたらご教授いただけると幸いです。
:::

# はじめに
HTML/CSSを使って見た目を組み立てるときに必ず使う*margin*と*padding*がある。
どちらも意味的には余白を作るプロパティになっているがそれぞれの使い方が曖昧になっていたり、よくわかっていないところがあったので、調べてみることにした。

# marginとpaddingとは
marginとpaddingについては以下のサイトが非常に詳しく、わかりやすく説明してるので、わからなくなった時はそこから確認する。

https://blog.codecamp.jp/margin_padding

# marginを使ったレイアウト作りをするときのコツ
marginは**topとleftのみ**を使うようにすると保守性が高まる。
marginでは上下左右どちらにどのくらい余白を空けるかを設定することができるが、むやみに全ての方向に値を設定して余白を作ってしまうと保守性が悪くなる。そこで余白を空ける部分を限定にする。具体的にはmarginの設定を上と左に固定することである。

```CSS:CSS
//悪い例 (全ての方向に余白を設定してしまっている)
.wrong__margin {
margin: 10px 20px 10px 20px;
}

//良い例 (topとleftのみに余白を設定している)
.good__margin {
margin: 10px 0 10px 0;
}

または

.good__margin {
margin-top: 10px;
margin-left: 10px;
}
```

# marginとpaddingの使い分け
marginは兄弟要素間、paddingは親子間の余白を設ける。

親子要素と兄弟要素については以下の記事がわかりやすく説明をしている。

https://it-hack.net/development/markup-language/html/element-relation

実際にソースコードで書いてみるとこのようになる。
```HTML:HTML
 <div class="parent__box">
    <div class="children__box">子要素</div>
    <div class="children__box">子要素</div>
 </div>
```

```CSS:CSS
//親要素にはpaddingをつける
.parent__box {
  color: white;
  background: black;
  width: 300px;
  height: 300px;
  padding: 100px; 
}

//子要素にはmarginをつける
.children__box {
  background: white;
  width: 250px;
  height: 100px;
  margin: 0 0 100px 0;
}

```

上のソースコードの結果がこれ↓
![スクリーンショット 2024-05-06 20.11.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784901/d989e92e-3b71-4854-92d5-ef627b31eef1.png)

なかなかに細かい設定の仕方だが、実際にこの方法でレイアウトを作ると頭なの中で位置を組みやすかった。



# 終わりに
これまでHTML/CSSでレイアウトを組み立てる時はGoogleの検証ツールを用いて、ゴリ押しスタイルで余白をつけていたため、レイアウトがおかしくなった時にどこを直せば良いのかがわからなくなり、混乱することがあった。
上記で述べたルールを守ることによって、より効率よくCSSを活用したい。

# 参考文献

https://blog.codecamp.jp/margin_padding
https://it-hack.net/development/markup-language/html/element-relation
Kotlinインアクション(アウトプット目的)

※Kotlinインアクションを読み、アウトプットとして忘れない程度に記載

# 式と文の違い
値が返る処理機構は式。
式を組み合わせて処理を行うものが文。

# 変数
## プリミティブ型
```kotlin
Int
Long
Double
Float
Short
Byte
Boolean
Char
```

## リファレンス型
```kotlin
String
Array
List
Set
```

## 動的, 静的, 定数
val: 静的読み取り用の値
var: 動的読み書き用の値
const val: 定数

## 読み取り範囲
```kotlin
private 
protected
public
```

## null許容型
変数に?をつけて宣言することでnull許容型になる
```kotlin
val name: String? = null
```

# 関数
### 基本的な宣言方法
引数に型の設定可能。デフォルト引数の設定も可能。
返り値の型の宣言が可能。
```kotlin
fun functionName(name: String = "太郎"):Unit {
    println("Hello World ${name}")
}
```

## 返り値の種類
一般的な変数種別や宣言したクラスに加えて以下を宣言可能
Unit: 正常終了して返り値がない
Nothing: エラーなどが返る前提で、正常終了しない
Any: 任意の型

## クラス

## クラス種別
enum: 列挙型
annotation: アノテーション型
interface: インターフェース
data: データ型
object: シングルトンクラスとして利用するクラス
abstract
open
final
override

## カスタムアクセサ
クラスの引数にはget,setのアクセサを設定できる

## 基本構文
```kotlin
## 条件処理
if (条件) {
    処理
}
when(対象の値) {
    条件1 -> 処理1
    条件2 -> 処理2
    条件3 -> 処理3
}

## 繰り返し
for(i in 0..10) {
    println(i)
}
while(条件式) {
    処理
}
do {
    処理
} while(条件式)

## 例外処理
try {
    処理A
} catch(e: Exception) {
    処理B
} finally {
    処理C
}
```
# ラムダ関数

# スコープ関数
対象のレシーバに対してスコープ内の処理を行うため、スコープ関数と呼ぶ。

```kotlin
/**
 * You can edit, run, and share this code.
 * play.kotlinlang.org
 */
fun main() {
    val amount = 300.0;
    val withRes = with(JapanCalcTax(0.05)) {
        this.printTax()
        println(this.calcTaxAmount(amount))
        this
    }
    println("withRes: ${withRes}")
    println("===========================")
    val letRes = JapanCalcTax(0.1).let {
        it.printTax()
        println(it.calcTaxAmount(amount))
        it
    }
    println("letRes: ${letRes}")
    println("===========================")
    val runRes = JapanCalcTax(0.15).run {
        printTax()
        println(calcTaxAmount(amount))
    }
    println("runRes: ${runRes}")
    println("===========================")
	val applyRes = JapanCalcTax(0.2).apply {
        printTax()
        println(calcTaxAmount(amount))
    }
    println("applyRes: ${applyRes}")
}

class JapanCalcTax(
    private var taxRate: Double
){
    override fun printTax():Unit {
        println("taxRate: ${taxRate}")
    }
    
    override fun calcTaxAmount(amount: Double): Double {
        return amount * taxRate
    }
}

```
# 関数種別
## 匿名関数/無名関数/ラムダ関数

```kotlin
fun main() {
    val x = 10
    val y = 20
    val plus = fun(x: Int, y: Int): Int {
        return x + y
    }
    println(operation(plus))
    val minus = {x: Int, y: Int -> x - y}
    println(operation(minus))
}
fun operation(func1: (x: Int, y: Int) -> Int): Int {
    return func1(2, 1)
}
```
## 拡張関数
クラスに定義せず、外側から既存クラスに関数を追加することができる。
```kotlin
fun main() {
    val x = 10
    val y = 20
    x.printInt()
    y.printInt()
}

fun Int.printInt(): Unit {
    println(this)
}
```
参考資料
https://vegcale.com/2023/05/15/kotlin-lambda-and-anonymous-function/

# ジェネリクス
処理の外側から処理内部で利用する変数の定義を設定、注入することができる。

# 演算子オーバーロード
クラスのメソッドにplusやtimesを利用することで演算子である+や*を利用して処理を記述することができる。
multerのエラー：Error: Unexpected end of formの解決法

# 環境

- Node.js、Express
- multer

# multer について

- HTTP 通信で送られた Content-Type が multipart/form-data のファイルを処理するためのミドルウェア
- ファイルをメモリやストレージに保存することができる

# やりたいこと

- HTTP リクエストで受信した multipart/form-data のファイルをメモリに保存した後、ストレージに保存したかった
- 以下の方法で実現しようとした
  - メモリに保存するときは、multer.memoryStorage()
  - ストレージに保存するときは、multer.diskStorage()

![2024-05-06-19-30-10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601978/31d30872-2129-6462-6d5a-b56a0e6fadc2.png)


# 事象

- multerの.single() を 2 つミドルウェアに定義するとエラーになる



```js
router.post(
  "/upload/new",
  // メモリに保存
  multerMemory.single("markdown"),
  async (req, res, next) => {
    req.headers.uuid = crypto.randomUUID();
    req.headers.userEmail = await getUserEmail(user_id);
    req.headers.filedir = `${process.env.STR_DIR_PATH}/users/${req.headers.userEmail}/files/${req.headers.uuid}`;

    // ディレクトリを作成
    await fs.promises.mkdir(req.headers.filedir, { recursive: true });

    next();
  },
  // ストレージに保存
  multerStorage.single("markdown"),
  // 以降のミドルウェアは省略
```

# エラー文

```
Error: Unexpected end of form
    at Multipart.\_final (アプリのパス\node_modules\busboy\lib\types\multipart.js:588:17)
    at callFinal (node:internal/streams/writable:698:12)
    at prefinish (node:internal/streams/writable:710:7)
    at finishMaybe (node:internal/streams/writable:720:5)
    at Writable.end (node:internal/streams/writable:634:5)
    at onend (node:internal/streams/readable:705:10)
    at process.processTicksAndRejections (node:internal/process/task_queues:77:11)
```

multipart/form-data のストリームが完全に終わる前に終了してしまうとこのエラーが出るようです。

![2024-05-06-19-47-14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601978/1ac6558f-1995-c98e-9629-dda69c9fa067.png)


# 解決

なぜストリームが途中で終わってしまうかの原因はわかりませんでしたが、メモリに保存したファイルを fs でストレージに移すように変更したことで解決しました。

![2024-05-06-19-31-28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601978/389efe7d-bd68-2a72-c650-de6c4a41b44d.png)


multer でメモリ保存されたファイルは、req.file.buffer に格納されるので、それを使用しました。

```js
router.post(
  "/upload/new",
  multerUpload.single("markdown"),
  async (req, res, next) => {
    try {
      if (!req.file) {
        return res.status(400).send("No file uploaded.");
      }

      const buffer = req.file.buffer;
      const uuid = crypto.randomUUID();
      const userEmail = await getUserEmail(user_id);
      const filedir = `${process.env.STR_DIR_PATH}/users/${userEmail}/files/${uuid}`;
      const filename = decodeURIComponent(req.file.originalname);
      const filePath = path.join(filedir, filename);

      // UUIDのディレクトリを作成
      await fs.promises.mkdir(filedir, { recursive: true });

      // ファイル保存
      await fs.promises.writeFile(filePath, buffer);
```

## Tips

ちなみに、multerの.single()はそれ自体がミドルウェア関数で、以下のようにミドルウェア関数の中に入れることはできないです。

```js
async (req, res, next) => {
    multerMemory.single("markdown")
    next();
},
```
【初心者】npxとは？

## npxってなに？
```
npx XXXX
```
みたいにコマンドを書くけど、npxとはなんなのさっぱりわからない。今回を機に勉強&備忘録。

## ってか、npmもある。　npxとnpmの違いは？
npm は、```node package manager```、ノードパッケージのマネージャー、つまりパッケージの管理ツール。
npx は、```node package executer```、ノードパッケージの執行者、つまりパッケージの実行するツール。

### もっと、細かくいうと、

npmは、Node.js をインストールするとすぐに利用できる依存性+パッケージのマネージャー。
npxは、インストールされてないパッケージでも自動的に探してインストールして実行してインストールしたパッケージの削除する。

うーん、npxはピンときたが、npmはまだよくわからん。依存性って何。
npmは、必要な機能をインストール。npxは、インストールして実行する。って感じかなぁ。
https://qiita.com/kohta9521/items/ee3ed4a2360add80ad79

って調べていたら、
### こちらの図でかなりスッキリとしました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3692854/cd326b27-80b8-edbf-1a45-7dadaa929432.png)
かなり良い整理された表!ｽﾊﾞﾗｼｲ!!!
https://iret.media/97888


### ってか、そもそもnodeって何？
「ノード（node）」とは、結び目・節・集合点・中心点といった意味で、いろいろな分野で幅広く利用されている単語。
https://kagoya.jp/howto/it-glossary/server/node/
ｳｰﾝ、続きはまたあとで勉強します。
Node.js のExpressで学ぶミドルウェア

## ミドルウェア

#### ミドルウェアって何？

- コンピュータの分野で、コンピュータの基本的な制御を行うOSと書く業務処理を行うアプリケーションソフトウェアとの中間に入るソフトウェアのこと
- ある特定の分野に属するアプリケーションに対して、その分野に共通する機能や処理をアプリケーションに提供する


ググった結果、以上の説明がなされるが、あまり理解できない。

### 毎回行っている同一の処理を共通化し、再利用するためのしくみ

という説明に納得した。

```sample.js
const express=require('express'),
app=express();

const logger=require('morgan');

app.use(logger('dev'));
app.use(function(req,res,next){
    console.log('this is middleware');
    next();
})

app.get('/hello',function(req,res){
    res.send('Hello World");
});

app.get('/bye',function(req,res){
    res.send('Good bye');
});

app.listen(8080);
console.log('server listen ...');

```

- ここでは、app.useの部分がミドルウェアの役割を果たしている
- /hello にアクセスしても、/byeにアクセスしても、
HTTPリクエストをした際のアクセスログと、自作した「this is middleware」という表示がログとして出力される

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2733402/cac133c1-a087-55f7-3e61-d1a21b66dc14.png)


[参考](https://www.dailyupblog.com/backend_development/1030/)
aws アカウント作成 方法

# アカウントを作成しましょう。

awsを使用するためにはアカウントを作成する必要があります。

https://aws.amazon.com/jp/register-flow/
上記urlでアカウントを作成できます。

希望のメールアドレスで作成しちゃってください。
アカウント作成手順については上記url内で詳しく説明してくれています。
手順に従って作成してしまえば完璧です！

# 多要素認証(MFA)を設定しましょう。

作成したaws アカウントはメールアドレスとパスワードさえ分かっていれば誰でもログインできてしまうかなり危険な状態です。(アカウント詳細ページで手順の際に登録したクレジットカード情報がガッツリ表示されています。)
多要素認証を使用してアカウントのセキュリティを高めてあげてください。ここでは二段階認証ソフトウェアを使用した方法で登録します。

まずはあなたの携帯に
google-authenticatorアプリをインストールしてください。(私はgoogle-authenticatorを使用しただけです！ほかの二段階認証ソフトウェアでも可能です。)
https://apps.apple.com/jp/app/google-authenticator/id388497605

aws のページでiamと検索してiamのページに遷移してください。

[![Image from Gyazo](https://i.gyazo.com/f825d11f537829a98ffd66888bb01a79.png)](https://gyazo.com/f825d11f537829a98ffd66888bb01a79)
多要素認証が設定されているとこのように表示されます。
tauri1.xのソースを2.xにし、androidのアプリとしてデバッグできるようにする

tauriで、まだbetaでありながらandroid,iosアプリにできるようになっていたので、androidのアプリとしてビルドできるところまで試してみました

(また、tauriでビルドしたアプリも問題なくgoogleの審査を通すことができました
名前が公開されることを知らなかったので現在は非公開にしていますが...)

基本的には以下の二つだけでいいはずですがうまくいかなかったので、詰まった点も含めて書いていきます

https://beta.tauri.app/blog/tauri-mobile-alpha/

https://beta.tauri.app/start/upgrade--migrate/from-tauri-1/

0.環境
本来はwsl2上でビルドしたかったのですが、うまくいかなかったのでwindows上でビルドすることにしました

1.まず以下のコマンドを実行します
```sh
npm install @tauri-apps/cli@next @tauri-apps/api@next
```

2.次にsrc-tauri/Cargo.tomlに以下を追記します
```src-tauri/Cargo.toml
[lib]
name = "app_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
```

3.`src-tauri/src/main.rs` を `src-tauri/src/lib.rs`にリネームします

4.lib.rsのmain関数を以下のように書き換えます
```rs
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    // your code here
}
```

5.`src-tauri/src/main.rs`を作成し、以下の内容にします
```rust:src-tauri/src/main.rs
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

fn main() {
  app_lib::run();
}
```

6.以下のコマンドを実行し、プロジェクトをmigrateします
```sh
npm run tauri migrate
```

7.androidの開発環境を作り、エミュレーター上でデバッグできるようにします
```
npx tauri android init
npx tauri android dev
```

本来ならこれで正しくビルドできるはずですが、私のプロジェクトではうまくいかなかったので、追加でプロジェクトに変更を加えました



#### warning: output filename collision.
```
warning: output filename collision.
The lib target `app_lib` in package `proj_name v0.0.3 (D:\code\proj_name\src-tauri)` has the same output filename as the lib target `app_lib` in package `proj_name v0.0.3 (D:\code\proj_name\src-tauri)`.
Colliding filename is: D:\code\proj_name\src-tauri\target\x86_64-linux-android\debug\libapp_lib.rlib
The targets should have unique names.
Consider changing their names to be unique or compiling them separately.
This may become a hard error in the future; see <https://github.com/rust-lang/cargo/issues/6313>.
```
これはCargo.tomlのcrate-typeからrlibを消すとうまくいきました
しかし、なぜこれでうまくいくかは謎です...
```diff_toml:src-tauri\Cargo.toml
- crate-type = ["staticlib", "cdylib", "rlib", "lib"]
+ crate-type = ["staticlib", "cdylib", "lib"]
```

#### 自動migrationのスクリプトの問題？
/src/App.tsxでのimportがなぜか
```
import { invoke } from "core";
```
となっていたので、以下のように修正
```
import { invoke } from "@tauri-apps/api/core";
```

#### <b>Warn</b> Waiting for your frontend dev server to start on http://... で止まってしまう
まず、package.jsonに以下を追記します
```diff_jsondoc:package.json
/// ...
  "devDependencies": {
/// ...
+    "internal-ip": "^7.0.0"
  }
/// ...
```
そしてnpm iします
```sh
npm i
```

そしてvite.config.tsに以下を追記します
```diff_typescript:vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
+ import { internalIpV4 } from "internal-ip";

+ const mobile = !!/android|ios/.exec(process.env.TAURI_ENV_PLATFORM);

export default defineConfig(async () => ({
  server: {
    // ...
+    host: mobile ? "0.0.0.0" : false,
+    hmr: mobile
+      ? {
+          protocol: "ws",
+          host: await internalIpV4(),
+          port: 1421,
+        }
+      : undefined,
+    watch: {
+      ignored: ["**/src-tauri/**"],
+    },
    
    // ...

}));
```
pyautoguiで画像ファイルから特定の画像を探す方法

### 結論
pyautoguiで画像ファイルから特定の画像を探すには`pyautogui.locate(対象画像ファイル, 探したい画像ファイル)`を使うとOK
`対象画像ファイル`に`探したい画像ファイル`が含まれているとあれこれ返ってくる

これだけだとあまりにもそっけないので、今後の自分のために発見までの道のりを記しておく

### マニュアルを読んだ

マニュアルには現在表示されている画面から特定の画像を探すときに使う`pyautogui.locateOnScreen`についての説明がある

たとえば以下の画像`quitBtn.png`が画面上にあるか探し出したいとする

![quitBtn.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3740499/e505cd13-182b-f735-18a9-d1bca803ac98.png)

そんな時は

```python
pyautogui.locateOnScreen('quitBtn.png')
```

とすると場所情報などが返ってくる

似たような関数として画面上から画像を探し出し、その中心座標を返す`pyautogui.locateCenterOnScreen`というのもある

けれど、ある画像ファイルの一部に特定の画像が含まれているから探す関数というのが見当たらない

例えば、次のような`rrScreen.png`というのが保存されていたとして

![rrScreen.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3740499/b3fbe910-d05d-4686-e684-338d1719decb.png)

この中に`quitBtn.png`が含まれているか（画像ファイルの右下にある）、含まれているならその場所情報を返す、という方法がわからなかった

### ソースコードを読んでみた
するとtestのコードに以下のようなのがあるではないか

```python:github.com/asweigart/pyautogui/tests/test_pyautogui.py
        pyautogui.useImageNotFoundException()
        with self.assertRaises(pyautogui.ImageNotFoundException):
            pyautogui.locate("100x100blueimage.png", "100x100redimage.png")
        # Commenting out the locateAll*() functions because they return generators, even if the image can't be found. Should they instead raise an exception? This is a question for pyscreeze's design.
        # with self.assertRaises(pyautogui.ImageNotFoundException):
        #    pyautogui.locateAll('100x100blueimage.png', '100x100redimage.png')        
```

これか！と思って試してみたらビンゴでした

```python:ptpython
>>> pyautogui.locate('tests/images/quitBtn.png', 'tests/images/rrScreen.png')
Box(left=336, top=844, width=261, height=73)
```

こうして無事にとってこれました
How to run systemd in a container(podman)

Linuxのコンテナエンジン podman で、コンテナを systemd で起動する方法。
podman container の --systemdオプションは podman 固有なので docker では使えないことに注意。

1. ホスト側でSELinuxが有効な場合は以下のコマンドを実行しておく
```
# setsebool -P container_manage_cgroup true
```
　
2. コンテナを起動 or 作成する
```
# podman container run --name <コンテナ名> --systemd=true -d <イメージ> /sbin/init
# podman container create --name <コンテナ名> --systemd=true <イメージ> /sbin/init
```
　
3.  コンテナを作成した場合は起動する
```
# podman container start --name <コンテナ名>
```
　
4. コンテナに接続する場合
```
# podman container exec -it <コンテナ名> /bin/bash
```
podman container attach では接続できないので注意。

https://armadillo.atmark-techno.com/forum/armadillo/18066

https://developers.redhat.com/blog/2019/04/24/how-to-run-systemd-in-a-container#enter_podman
Slackアプリで絵文字追加したのに反映されないときの対処法


Slackアプリで絵文字を追加したのに、Slackアプリに反映されないことがたまにあります。
あれこれやってみて対処方法が分かったのでメモ。

`ヘルプ` -> `トラブルシューティング` -> `キャッシュを消去して終了` で終了してから起動すると改善されました。

数時間待つと改善される等の対処方法もあったのですが、キャッシュ消すのが一番早そうです。

https://slack.com/intl/ja-jp/help/articles/205138367-%E6%8E%A5%E7%B6%9A%E5%95%8F%E9%A1%8C%E3%81%AE%E3%83%88%E3%83%A9%E3%83%96%E3%83%AB%E3%82%B7%E3%83%A5%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0#:~:text=Step%201%3A%20Clear%20your%20cache&text=Desktop%20Browser-,Open%20the%20Slack%20desktop%20app%2C%20then%20click%20Help%20in%20your,Click%20Clear%20Cache%20and%20Restart
AppRouterなNext.jsで作った静的なコンテンツを出力する

## 背景

近年、Github PagesやCloudflare Pagesなどの、
静的なコンテンツを無料で公開できるサービスが多くなってきことに加え、
AppRouterなNext.jsに関する書籍が手元にあるので、
まず静的なコンテンツを出力してみたい。

## やること

まず、Next.jsを取ってきます。

```bash
nox create-next-app
```

きかれたことに答えます。ここでは素直にAppRouterを選びます。

次に、`next.config.js`に`output:'export'`を加えて全部静的にします。

```js
/** @type {import('next').NextConfig} */
const nextConfig={
	output:'export',
};

export default nextConfig;
```

あとはお好みのSPAを作って`npm run build`します。
すると`out`というフォルダができてビルド成果物が出力されます。