OmniAICreator/Qiita-1.07M huggingface dataset:
WinPython 過去バージョンのURLについて

Anacondaが有料化されたことで、便利なCondaの仮想環境が気軽に使えなくなりました。

Miniconda という選択肢もありますが、私は最近WinPythonをよく使っています。

WinPython はZIPファイルをダウンロードして解凍するだけで好きなPython環境を作ることができます。

色々な生成AIを実験的に試す場合、仮想環境の作成や切り替えたりといった作業が不要になるので、かなり便利です。

ついこの間まで「WinPython＋過去バージョン」というキーワードGoogle検索したらWinPythonアーカイブのURLがヒットしていましたが、最近は本家Python関連の情報しかヒットしなくなりました。

そこで、備忘録を兼ねてここにWinPythonの過去バージョンのURLを掲載しておきます。

[https://sourceforge.net/projects/winpython/files/](https://sourceforge.net/projects/winpython/files/)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3775985/fb940154-6467-2571-fb67-9ad8831f2649.png)

Qiita はSEOが強いので、きっと「WinPython＋過去バージョン」で検索したら、このページがヒットしてくれるでしょう。

参考サイト
[【最初の一歩】WinPythonの簡単インストール手順（スクリーンショットで解説）](https://ai.desktop.creative.favorite-worlds.com/%e3%80%90%e6%9c%80%e5%88%9d%e3%81%ae%e4%b8%80%e6%ad%a9%e3%80%91winpython%e3%81%ae%e3%82%a4%e3%83%b3%e3%82%b9%e3%83%88%e3%83%bc%e3%83%ab%e6%89%8b%e9%a0%86%ef%bc%88%e3%82%b9%e3%82%af/)
[【こりゃ便利】WinPythonでPython環境をUSB化しよう！](https://resanaplaza.com/2022/01/24/%e3%80%90%e3%81%93%e3%82%8a%e3%82%83%e4%be%bf%e5%88%a9%e3%80%91winpython%e3%81%a7python%e7%92%b0%e5%a2%83%e3%82%92usb%e5%8c%96%e3%81%97%e3%82%88%e3%81%86%ef%bc%81/)
Docker 上に RStudio + EZR の統計解析環境を作る【macOS】

# #0. 背景
私は放射線科専攻医で、秋の学会に向けてR + [EZR](https://www.jichi.ac.jp/saitama-sct/SaitamaHP.files/statmed.html) で統計解析を行うことになりました。ローカル環境を汚したくないので Docker 上で行うことにしたのですが、非常に躓いたので同じように困っている医療関係者も多数いらっしゃるだろうと思い、Qiitaに残すこととしました。
理解が不十分なまま、動いているから許容した箇所もあるため、ご容赦ください。
## #0-1. Rとは
皆様御存知の通り、統計解析に特化したプログラミング言語及びその開発実行環境の事です。
## #0-2. [EZR](https://www.jichi.ac.jp/saitama-sct/SaitamaHP.files/statmed.html)とは
自治医科大学附属さいたま医療センター血液内科の神田善伸が作成されたRのカスタマイズパッケージです。通常のRではスクリプトを打って操作する必要がありますが、EZRではマウス操作で簡単に医療統計を行うことができます。作者自身がEZRを用いた統計解析の本も出版しておられ、日本語マニュアルが充実していることも魅力です。
## #0-3. Dockerについて
Dockerの詳しい仕組みについては省略します。メリットを簡単に言うと、パソコン本体のOS(windows 10/11, macOS, Linux)に依存せず、同じ状態を使い回すことができます。そのため、パソコンを買い替えた際にも同じ状態を維持できますし、他の方が作られた統計解析を使いたいときも環境をそのままもらうこともできます。しかもローカル環境を汚さずに試行錯誤することができます。
# #1. 私の環境
m3 macbook Air(最近買い替えたのでローカルを汚したくない...)
OS: macOS Sonoma 14.4.1
メモリ: 16GB
SSD: 512GB
Docker: version 26.1.1
Docker image: [rocker/rstudio:4.4.0](https://hub.docker.com/layers/rocker/rstudio/4.4/images/sha256-ea482a2508bd83115e9f734aac96cb7eb38059988f3406959f2943f9cfd5b401?context=explore)
# #2. macOS側操作
[Homebrew](https://brew.sh/ja/)
[Docker](https://www.docker.com/ja-jp/)
[XQuartz](https://www.xquartz.org/index.html)
```zsh
# Homebrewをインストール
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# Hmebrew経由でDocerをインストール
brew install --cask docker
# RとRstudioがインストールされたdocker imageをダウンロード
docker pull rocker/rstudio:4.4

# Docker内で動くGUIアプリを表示(X Window Systemを利用)するためにXQuartzをダウンロード
brew install --cask xquartz
# XQuartzを起動
open -a XQuartz
# DockerコンテナからXQuartzを利用できるように設定を変更する
# setting>Security: Authenticate connections -> OFF, Allow connections from networck clients -> ON
# アプリを⌘cmd + Q で一度終了し、再度アプリを起動
open -a XQuartz

# Display変数が空になっているので、Dockerからアクセスできるように設定
export DISPLAY=:0.0

# Dockerでコンテナを起動
docker run --rm -ti -p 8787:8787 --net host -e DISPLAY=$DISPLAY -v ~/.Xauthority:/root/.Xauthority:ro rocker/rstudio
# --rmを消すと、コンテナをstopした後もコンテナを残すことができる
docker run -ti -p 8787:8787 --net host -e DISPLAY=$DISPLAY -v ~/.Xauthority:/root/.Xauthority:ro rocker/rstudio
```
# #3. Dockerコンテナ内での操作
```zsh
# aptを最新にアップデート
sudo apt update

# Rcmderをインストールするために基本的なパッケージをインストール
sudo apt install zlib1g-dev cmake libssl-dev libxml2-dev libcurl4-openssl-dev tcl8.6-dev tk8.6-dev

# GUIアプリを使うためにX11をインストール
sudo apt install xserver-xorg
# macOSのDisplayに出力するようにDisplayのパスを通す
export DISPLAY=host.docker.internal:0.0
```
# #4. RStudio内での操作
```R
# ブラウザで localhost:8787 にアクセス
# RStudioにログインする
 # ID: rstudio
 # PASSWORD: ターミナルに表示されているパスワード
 
# Rcmdrをインストール(依存関係もインストールされるので非常に時間がかかります
install.packages("Rcmdr", dependencies=TRUE)
# RcmdrPlugin.EZRをインストール(依存関係もインストールされるので非常に時間がかかります
install.packages("RcmdrPlugin.EZR", dependencies=TRUE)
# Rcommanderを呼び出す
library(Rcmdr)
# RcmdrのGUIからtool>load Rcmdr plug-in>RcmderPlugin.EZR をOKにし、設定を保存する
```
# #5. containerをimageとして保存し、使い回せるようにする
```zsh
# コンテナのIDを確認
docker container ls -a
# containerをimageとして保存
docker commit <container ID> <あなたがimageにつけたい名前>
```
# 6. 最後に
エクセルなどの保存された、好きな作業フォルダをマウントして、解析してください。この記事が、皆様のお力になれましたら幸いです。
PythonでAWS CLIコマンド実行の練習 (subprocess編)

# 目的
PythonでAWS CLIの実行方法を覚えるために、EC2のスループット値を取得してみる

# AWS CLIコマンドの確認
下記コマンドでスループット値を取得できることを確認
```:コマンド
aws cloudwatch get-metric-statistics \
--namespace AWS/EC2 \
--metric-name NetworkIn \
--dimensions Name=InstanceId,Value=i-085509fb0174af9cd \
--period 2628000 \
--statistics Maximum \
--start-time 2024-05-01T06:15:00+00:00 \
--end-time 2024-05-10T06:30:00+00:00
```
```bash:出力
[cloudshell-user@ip-10-130-39-130 ~]$ aws cloudwatch get-metric-statistics \
> --namespace AWS/EC2 \
> --metric-name NetworkIn \
> --dimensions Name=InstanceId,Value=i-085509fb0174af9cd \
> --period 2628000 \
> --statistics Maximum \
> --start-time 2024-05-01T06:15:00+00:00 \
> --end-time 2024-05-10T06:30:00+00:00
{
    "Label": "NetworkIn",
    "Datapoints": [
        {
            "Timestamp": "2024-05-01T06:15:00+00:00",
            "Maximum": 57597321.0,
            "Unit": "Bytes"
        }
    ]
}
```
# PythonでのAWS CLIコマンド実行
https://docs.python.org/ja/3/library/subprocess.html

subprocessモジュールを読み込んでsubprocess.run()で実行できそう

# Pythonスクリプトの作成
直近３ヶ月間の最大スループット値(In/Out)を取得する
```python:get_ec2_throughput_subprocess.py
import subprocess
import json
import time
from datetime import datetime, timedelta

def cloudwatch_get_metric(metric_name):
    # AWS CLIコマンドを構築
    command = (
        "aws cloudwatch get-metric-statistics "
        "--namespace AWS/EC2 "
        f"--metric-name {metric_name} "
        "--dimensions Name=InstanceId,Value=i-085509fb0174af9cd "
        f"--period {period} "
        "--statistics Maximum "
        f"--start-time {start_time_str} "
        f"--end-time {end_time_str}"
    )

    # AWS CLIコマンドを実行し、結果を取得
    result = subprocess.run(command, capture_output=True, text=True, shell=True)
    data = json.loads(result.stdout.strip())

    # データを処理する
    datapoints = data['Datapoints']
    maximum_values = [datapoint['Maximum'] for datapoint in datapoints]

    return maximum_values

start_script_time = time.time()

# 現在の日付から3か月前の日付を計算
end_time = datetime.utcnow()
start_time = end_time - timedelta(days=90)
period = 3600 * 24 * 90

# 日付をISO 8601形式に変換
start_time_str = start_time.strftime('%Y-%m-%dT%H:%M:%S')
end_time_str = end_time.strftime('%Y-%m-%dT%H:%M:%S')
print(f'{start_time_str=}')
print(f'{end_time_str=}')

NetworkIn = cloudwatch_get_metric('NetworkIn')[0]
NetworkOut = cloudwatch_get_metric('NetworkOut')[0]
print(f'{NetworkIn=}')
print(f'{NetworkOut=}')	

end_script_time = time.time()
elapsed_time = end_script_time - start_script_time

print(f'{elapsed_time=}')
```
# 実行
取得できました。
```bash:出力
[cloudshell-user@ip-10-130-39-130 ~]$ python get_ec2_throughput_subprocess.py 
start_time_str='2024-02-18T02:13:17'
end_time_str='2024-05-18T02:13:17'
NetworkIn=57597321.0
NetworkOut=68488705.0
elapsed_time=2.3432440757751465
```
Python Tkinterとラズパイで自分専用のダッシュボードアプリを作ってみる #3 時計を表示させる

# 概要
デジタルダッシュボード作成 第三弾。

https://qiita.com/inugami_sayo/items/61b0c5b95af840551b33

の続きです。
今回は時計の表示と実際に借りのダッシュボードに表示させてみます。

この記事はそのメモやまとめです。
すでに設定済みの項目がいくつかある。
試行錯誤しながらやったので、手順として不要かもしれない。**あくまでメモ用。**

# 実装したい内容とか整理
ここでは
・現在時刻(表示 大)
・日付(表示 小)
の表示を目指します。

## 作ってみる
https://atmarkit.itmedia.co.jp/ait/articles/2111/09/news015.html

の記事とchatgptを参考にしつつ、自分で必要な情報へと少し変更を加えます。
以下のようになりました。

```python:clock.py
import datetime

t_delta = datetime.timedelta(hours=9)
JST = datetime.timezone(t_delta, 'JST')
now = datetime.datetime.now(JST)

d_week = {'Sun': '日', 'Mon': '月', 'Tue': '火', 'Wed': '水',
          'Thu': '木', 'Fri': '金', 'Sat': '土'}
key = now.strftime('%a')
w = d_week[key]
d = now.strftime('%Y年%m月%d日') + f'（{w}）' #f'{now:%Y年%m月%d日}（{w}）'
print(d)  # 2021年11月04日（木）

t = now.time().strftime('%X')
print(t)  # 17:37:28
```
上記を実行すると以下のような結果を得ることが
```bash
RaspberryPi_Dashboard on  master [🌚‍] via 🐍 v3.11.0 took 31s 
at 22:53:15 ⬢ [Docker] ❯ python clock.py 
2024年05月12日（日）
22:55:44
RaspberryPi_Dashboard on  master [🌚‍] via 🐍 v3.11.0 
at 22:55:44 ⬢ [Docker] ❯ 
```

## ダッシュボード化
ある程度形が見えてきたので実際にダッシュボードにしていきます。

https://www.shido.info/py/tkinter2.html

を参考にしました。

```python:main.py
import tkinter as tk
from tkinter import ttk
import datetime

# メインウィンドウを作成
root = tk.Tk()
root.title("ダッシュボード")
root.geometry("1920x1080")

# 時計を定義
def update_clock():
    t_delta = datetime.timedelta(hours=9)
    JST = datetime.timezone(t_delta, 'JST')
    now = datetime.datetime.now(JST)

    d_week = {'Sun': '日', 'Mon': '月', 'Tue': '火', 'Wed': '水',
            'Thu': '木', 'Fri': '金', 'Sat': '土'}
    key = now.strftime('%a')
    w = d_week[key]
    day = now.strftime('%Y年%m月%d日') + f'（{w}）' #f'{now:%Y年%m月%d日}（{w}）'
    # print(d)  # 2021年11月04日（木）
    time = now.time().strftime('%X')
    # print(t)  # 17:37:28

    clock_label.config(text=time)
    day_label.config(text=day)
    root.after(1000, update_clock)

# 時計ラベルの設定
clock_label = ttk.Label(root, font=('Arial', 48), justify='center')
clock_label.pack(padx=10, pady=(10, 0), anchor=tk.W)  # 上に余白を設定

# 日付ラベルの設定
day_label = ttk.Label(root, font=('Arial', 24), justify='center')
day_label.pack(padx=10, pady=(5, 10), anchor=tk.W)  # 上下に余白を設定
update_clock()

root.mainloop()
```

実行するとこんな感じのウィンドウが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185852/20d68f14-e5b0-8636-9bcf-a68db4e237bc.png)

テストなんでウィンドウはめちゃくちゃでかいですが、表示位置や大きさなどはほとんどイメージ通りになりました。

# 次やりたいこと
- 室温気温測定用コード
- 天気予報とカレンダーをウィンドウに組み込む
- ニュースは別に必要なかったので今後は取り込まない
dict の特性(python)

[Python3.7](https://docs.python.org/3/library/stdtypes.html#dictionary-view-objects "Python3.7") 以降では dict の順序(追加された順番)が保証されるようになりました。


当たり前の動作に思えますが、Python のバージョンによっては当たり前ではないため確認する必要があり、実際に以下の動作になることが確認できました。


## next, iter
```
test = {"key1":"value2", "key2":"value1"}
test_iter = iter(test)
print(next(test_iter))
print(next(test_iter))

# 実行結果
key1
key2
```

```
test = {"key1":"value2", "key2":"value1"}
test_iter2 = iter(test.values())
print(next(test_iter2))
print(next(test_iter2))

# 実行結果
value2
value1
```

上記結果より、next, iter を使うことで、dict に追加した順の index を取得することが可能になります。


## mapping
Python3.10 以降 mapping が追加されたようです。
一言で言ってしまえば、values や key から元の dict に戻す感じですが、type が [types.MappingProxyType]("https://docs.python.org/3/library/types.html#types.MappingProxyType") になるようです。

具体的には以下の動作になります。

```
test = {"key1":"value2", "key2":"value1"}
print(test.values().mapping)
print(test.keys().mapping)

# 実行結果
{'key1': 'value2', 'key2': 'value1'}
{'key1': 'value2', 'key2': 'value1'}
```

異なる type になるようなので、以下になるようです。
```
print(type(test) == type(test.keys().mapping), type(test) ,type(test.keys().mapping))
print(test == test.values().mapping)

# 実行結果
False <class 'dict'> <class 'mappingproxy'>
True
```
type が異なっても、== は True になるところは興味深いです。


mapping と .values() は繰り返し使えそうなので試すと、繰り返し使えるようです。
```
print(test.values().mapping.values().mapping)

# 実行結果
{'key1': 'value2', 'key2': 'value1'}
```
[langchain] huggingfaceからembedding modelのダウンロード

```python
from langchain_community.embeddings.huggingface import HuggingFaceEmbeddings
import os

os.environ['HUGGINGFACEHUB_API_TOKEN'] = <HUGGINGFACEHUB_API_TOKEN>
embedding = HuggingFaceEmbeddings(model_name='intfloat/multilingual-e5-large')
```
技書博で「わからない」がわかる

# はじめに
5月12日の技書博で面白い本がありました。

技書博

https://gishohaku.dev/

# 「わからない」がわかる本
先生、わかりません！UNIX上級者ほど言われないとわからないコト
Rich Mikan 著

https://richlab.org/coterie/unb.html

私はUNIX上級者というわけではありませんが、
これは言われないとわからない。
これは私もわからなかった。
そういうことが書いてあって
初学者向けの勉強会などやろうとしているときに役立ちそうです。

iPhone 15 Proより薄いので8.25mm未満の厚みです。
この量なら読み切るのにそれほど時間もかからないでしょう。

ネットでも購入できるようなのでご興味のある方は上記リンクから！

# さいごに
そして開発協力者に名前を載せていただいたLinuxサーバー構築標準教科書 Ver.4.0.0の製本版も手に入れました。
他にも面白そうな本を何冊か購入しました。次の技書博が楽しみですね。

https://lpi.or.jp/news/press/page/20240201_01/
langchain webpageの読み込み＆分割

```python
# %%
from langchain_community.document_loaders.web_base import WebBaseLoader

web_paths = [
    'https://www.chiba-u.ac.jp/general/JoureiV5HTMLContents/act/content/content110000048.htm',
    'https://www.chiba-u.ac.jp/general/JoureiV5HTMLContents/act/content/content110000049.htm',
    'https://www.chiba-u.ac.jp/general/JoureiV5HTMLContents/act/content/content110001356.htm',
    'https://www.chiba-u.ac.jp/general/JoureiV5HTMLContents/act/content/content110000050.htm',
    'https://www.chiba-u.ac.jp/general/JoureiV5HTMLContents/act/content/content110000089.htm',

]
loader = WebBaseLoader(web_paths=web_paths)
documents = loader.load()
print(len(documents))
# %%
for document in documents:
    print(document)
# %%
from langchain_text_splitters.character import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(chunk_size=512, chunk_overlap=0)
splitted_documents = text_splitter.split_documents(documents=documents)
print(len(splitted_documents))
# %%
for document in splitted_documents:
    print('################################################################################')
    print(document)
# %%

```
CodeceptJS :  思ったよりJavaScriptでE2Eテストスクリプト作成が簡単

# Codeceptjsとは

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3790610/3fffc23f-63dc-5080-d9ad-a8826ef54c27.png)

[CodeceptJS](https://codecept.io/)とはテスト自動化のフレームワークです。Webアプリをテストするだけでなく、Appiumというエミュレータを使うことでモバイルアプリのテストも自動化できるそうです。 

- 初心者でも簡単に書ける
- コーディングが複雑ではない
- 同期的なテスト API の取り扱いもスムーズ

E2E テストが何か分からない方のために少し説明します。E2Eテストとは、ユーザーの実際の利用環境に基づき、システムが正しく動作するか・データフローが適切に機能するかどうかを、システム全体を通して検証するテストです。総合テストフェーズで実施されるもので、UIテストと呼ばれることもあります。

## CodeceptJSを使う理由は

1. シンプルな構文
CodeceptJSはシンプルでわかりやすい構文を提供し、I.click()、I.see()など、開発者がコードを書きやすく理解しやすいように設計されています。

2. 失敗時にスクリーンショット撮影
CodeceptJSは何も設定していなくてもテストが失敗したときに自動的にスクリーンショットを残してくれます。

3. Page Object & Step Objectを利用してCode Structureを管理しやすい
コードの保守性が向上し、再利用性が高まり、可読性が向上します。これにより、テストスイートの管理が容易になります。

4. Allureレポートのサポート
Allureを使うとテストの実行結果だけではなく、スクリーンショットやテストケース毎の実行時間など様々な角度からテスト結果をHTMLで確認することが可能になります。

## CodeceptJS 利用するライブラリ・フレームワーク

![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3790610/18ab8903-c130-d2b8-73e7-ba7c4cc1c030.png)

### 準備
事前に
- NodeJS v8.9以上
- npm
- vscode
をインストールしておいてください。

### 使ってみましょう。

- 最初はフレームワークをインストールします

![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3790610/7aaaf90a-15b5-d8af-e614-36735bac0d90.png)

- CodeceptJSの初期設定

![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3790610/5e6a46e8-e8a0-c0c6-dc4a-fe8de85133d2.png)

- WebDriverIO選択

![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3790610/241a42d1-9ac1-9f3b-deb8-6602591e86a3.png)

- テストケース作成

![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3790610/95778755-1d2c-997d-b9a0-aeeca0b3ba4e.png)

- コマンド実行すると Testcase が生成されます

![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3790610/e58f2c7b-b37c-189b-d5c3-7fe067168a58.png)

### コードの説明
"I.amOnPage" は、ウェブページを Google のページに開くように指示します。
 "I.fillField" は、Google の検索ボックスに 'codeceptjs' というテキストを入力するように指示します。
"I.pressKey" は、キーボードの Enter キーを押すように指示します。
"I.wait" は、待機するように指示します。

### 動作確認

![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3790610/6cc46c5a-5b4b-ff58-a6f2-7f19b4aaf27b.png)

- 確認しましょう!!

![1_jeItjML8K3ORj2hCRJlFsQ.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3790610/5141b461-6212-b3ea-a7ca-c3ad0ebbbf4c.gif)

### 結論
CodeceptJS は、JavaScript 上での E2E テストのもう一つの選択肢であり、理解しやすい構文を持ち、それほど難しくない。他のツールとは異なる利点や欠点がありますが、次回以降、CodeceptJS を実際のプロジェクトでどのように使用できるかを段階的に共有していきますので、お楽しみに。

でも、Codeceptjsのドキュメントを読むだけで簡単に実装できます！
http://codecept.io
http://codecept.io/basics/
https://github.com/codeception/codeceptjs/
M5Stackでの無線通信におけるソリューションへの制約

# M5Stackにおける無線通信手段と認証組織
無線通信は
・電波法(技適マーク)
が必須である。

ただし、ほかにも
・認証機関の認証
が必要になるケースがあり、開発できるからと言って幸せになるとは限らない。

| 無線通信手段 | 認証組織 | 認証組織Web |
|:-:|:-:|:-:|
| Wi-Fi  | Wi-Fi Alliance  | https://www.wi-fi.org/ja  | 
| BLE  |  Bluetooth SIG | https://www.bluetooth.com/ja-jp/  |
| Zigbee | CSA-IOT  | https://csa-iot.org/  |
| Matter  | CSA-IOT  |   https://csa-iot.org/  |
| Thread  | Thread Group  |  https://www.threadgroup.org/ |

無線通信は互換性の観点から認証組織が存在する。

# 認証なしによる罰金を科す組織

Bluetooth SIG


# 制定組織はあるが認証組織がない通信
| 無線通信手段 | 制定組織 | 認証組織Web |
|:-:|:-:|:-:|
| ESP-Now  |Espressif| https://www.espressif.com/ | 

# WiFiの認証は必要？
https://blog.espressif.com/wi-fi-certification-with-esp32-311e09dd06ff
ロゴあり出ないと運用できないケースがある。
また、互換性や信頼性の面から必須にする企業は存在する模様。


# まとめ
無線を使ったアプリが開発できるからと言って
ソリューションや製品として販売する際は認証が必要になる可能性があることに注意する。

基本的に制定組織や認証組織の知的財産を利用する形になるので
年会費を払って認証してもらうことが必要になる。

特に罰金を科す組織もあり、
いきなり数百万単位の認証or罰金を科せられて出荷停止を受ける可能性は無視してはいけない。

# 補足(ESP-NOW)
認証がないESP-NowもEspressif以外の製品で使用することについて言及がないので注意する。
NanoC6が比較手に入りやすいので
M5Stack →　NanoC6　→　USB or Grove
でGWを挟むことで無線システムの実現はできる。

商用利用については
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345260/67239f38-38f7-a849-9d82-9a60aed27e4e.png)
https://docs.espressif.com/projects/espressif-esp-faq/en/latest/application-solution/esp-now.html

The application for ESP-NOW does not require any special procedures.
→特別な申し込み不要
ということで20240513時点で問題はなし。
[過去記事]Amazon AppFlow でCSVの内容を一部変更してみる part1

## 注意点 

「Amazon AppFlowを触ってみた」の記事でも紹介しましたが、Amazon AppFlowはフローを実行するたびに料金が発生します。

ご注意ください。また詳しい内容は下記の「注意点」をご確認ください！

https://qiita.com/miriwo/items/ba3830030b5fbba6cdad

## 今回やること

今回やりたいことは前回の「Amazon AppFlowmを触ってみた」の時と同様にS3からS3に対してCSVを送ります。

ただし、その過程でpasswordのフィールドをマスクしてみようと思います！
今回使用するCSVは前回と同じ下記です。

```csv
id,first_name,last_name,password,age,
1,Tarou,Yamada,abcd1234,20,
2,Hanako,Yamada,abcd1234,22,
3,Tarou,Satou,abcd1234,24,
4,Hanako,Satou,abcd1234,16,
```

## 前提

前回の記事を参考にS3バケット間でCSVのやり取りができる状態になっていることを前提とします。

https://qiita.com/miriwo/items/ba3830030b5fbba6cdad

## 方法

AWSにログインしAppFlowのページを開きます。
既にフローが作成されているはずなので左サイドバーから「フロー」を選んでクリックします。

![Amazon_AppFlow-18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/81626586-a377-8b7e-ccec-f06f1bb23cbb.png)

フローの一覧が表示されるはずなので、前回作成したtest-flowにチェックを入れ「編集」をクリックします。

![Amazon_AppFlow-19-1024x158.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/6cb10099-04f6-61a3-a75e-b5d2ce03a7fd.png)

「次へ」をクリックしてゆき、手順3「データフィールドを編集」のページまで遷移します。

データフィールドを編集のページで下記の作業をしてマスクする値を設定します。

- マッピングされたフィールドのpasswordにチェックをつけ「値を変更」をクリック。

![Amazon_AppFlow-20-1024x413.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/cadbbb99-3fda-5411-a9bb-bf5fb97009ff.png)

- 「変更を選択」部分をクリックし、「値をマスクする」を選択

![Amazon_AppFlow-21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/b5af7ae6-37c9-9ee0-9704-99b4df1a9e64.png)

- 「すべての値をマスクする」を選択し「保存」をクリック

![Amazon_AppFlow-22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/d0b51dd6-fb9f-fce7-6e6e-3b8e2f4cd888.png)

更にフローそのものの設定も保存しましょう。

## 実行

それでは実際に設定を修正したフローを実行してみましょう！

フローの「フローを実行」をクリックしてフローを実行してみます。

![Amazon_AppFlow-23-1024x389.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/dcd5a28c-4111-b5c0-18d7-ce23ae0e920b.png)

フローの実行が完了したらout側のバケットを見に行きます。

![Amazon_AppFlow-24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/7cd26667-9adc-1483-5a4c-2bf92392ae60.png)

新しいフォルダが作成されていますのでそちらに入ります。

![miriwo-test-b_-_S3_bucket-2-1024x393.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/71b4ea15-6d02-186c-4362-8109ec7ef984.png)

設置されたファイルでダウンロードします。

![miriwo-test-b_-_S3_bucket-3-1024x336.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/b14ba9a1-c5a4-4d23-2882-ae18656a09df.png)

内容を確認するとマスクされた値が出力されていました！無事完了です！

- 送信元CSV

```csv
id,first_name,last_name,password,age,
1,Tarou,Yamada,abcd1234,20,
2,Hanako,Yamada,abcd1234,22,
3,Tarou,Satou,abcd1234,24,
4,Hanako,Satou,abcd1234,16,
```

- 送信先CSV

```csv
"id","first_name","last_name","password","age"
"1","Tarou","Yamada","********","20"
"2","Hanako","Yamada","********","22"
"3","Tarou","Satou","********","24"
"4","Hanako","Satou","********","16"
```

## おわりに

今回はAppFlowでのデータのマスクを実施してみました！ノーコードでちょっとクリックするだけですぐにデータがマスクできるのは便利だなーと思いました、、！

また、格納先CSVがわかりにくいので「フローの設定」から手順2「フロー設定を編集」ページの「ファイル形式の設定」の「ファイル名の設定」の「ファイル名にタイムスタンプを追加する」を選択して保存しておきましょう。

![Amazon_AppFlow-25-1024x707.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/2ba8c15a-d0dc-9837-1baf-2db772bd74f1.png)
Java・okhttp3ライブラリで簡単WebAPIアクセス・レスポンスデータはJavaオブジェクトに変換してみる

開発現場において外部サービスが展開するAPIにアクセスし、何らかの情報を取得するというケースはよくあることだと思う。
okhttp3というライブラリを使用すると簡単にアクセスできるが、細かい記述方法を忘れてしまうため備忘録としてまとめておく。
なお、今回はLINEが公開しているWebAPIにアクセスする形式を例として紹介する。

## okhttp3ライブラリを準備する
まずはアプリケーション上にライブラリ用意する。
build.gradleファイル内部のdependencies内に以下の記述を追加しよう。

```build.gradle
dependencies {
    ...
    ...
    implementation 'com.squareup.okhttp3:okhttp:4.9.2'
}
```
　使用しているIDEがintelliJの場合はファイル右上に象さんの更新アイコンが表示されるはず。
 クリックしてしばらくするとライブラリのインストールが完了するはず。


 ## WebAPIアクセス時に必要な情報を確認する
 WebAPIにアクセスする際は、特定の情報を添付してアクセスする事を求められるケースが多い。例として、LINEのWebAPIアクセスには、以下のような情報が必要となっていた。

 ### アクセスするURL
 ```URL
 GET https://api.line.me/v2/bot/channel/webhook/endpoint
 ```

### 必要な設定情報(ヘッダー情報)
上のURLにアクセスする際、必要となる情報は以下のとおり。

 ```shell
 curl -X GET \
-H 'Authorization: Bearer {CHANNEL_ACCESS_TOKEN}' \
-H 'Content-Type:application/json' \
https://api.line.me/v2/bot/channel/webhook/endpoint
 ```
 上はカールコマンドでWebAPIを叩く例だが、ざっくり言うと

 ・ヘッダー情報に特定のトークン(LINEのパスワードのようなもの。ここでは詳しく説明しない)の値を設定してアクセスすること
 ・ヘッダー情報にコンテンツタイプの情報をjson指定してアクセスすること

 上記2点を設定した状態で上のURLにアクセスしてね、という意味。


### 得られるレスポンス情報の例
上のAPIにアクセスすると、以下のようなレスポンス例が返却される.
```JSON
{
  "endpoint": "https://example.com/test",
  "active": true
}
```
 
 これらの点を踏まえた上、okhttp3を利用してAPIにアクセスする場のプログラムは以下のようになる。

 
```java

public void WebApiGetAccess() {

    // APIのリクエストに必要な情報を生成する
    // `header()`メソッドを使用して必要な情報をヘッダーに追加することができる
    // HTTPメソッドのGETを指定する場合は `get()`メソッドを使用するだけ
    Request request = new Request.Builder()
            .header("Authorization", "Bearer " + "LINEのトークン値")
            .header("Content-Type", "application/json")
            .get()
            .url(httpUrl)
            .build();

    // アクセス処理を行うために必要なCallオブジェクトを生成する
    // 上で生成したrequestオブジェクトは最終行で引数に設定する
    Call call = new OkHttpClient.Builder()
            .build()
            .newCall(request);

    try {
        // callオブジェクトに対して`execute()`メソッドで対象のWebAPIにアクセスする.
        Response response = call.execute();

        // レスポンスデータをJavaオブジェクトに変換するために必要なObjectMapperをインスタンス化する.
        ObjectMapper mapper = new ObjectMapper();

        // mapperインスタンスの `readValue()`メソッドを利用してJavaオブジェクトに変換する.
        // `radValue()`メソッドの第2引数に変換したいクラスの名前を指定する.
        ResponseToJavaObject responseToJavaObject = mapper.readValue(response.body().string(), ResponseToJavaObject.class);

        // 各フィールドに対してgetterを使用することでレスポンスデータを出力することができる
        System.out.println(responseToJavaObject.getEndpoint());
        System.out.println(responseToJavaObject.getActive());
    } catch (IOException e) {
            throw new RuntimeException(e);
    }
}

    /**
    * 返却されるレスポンスデータをJavaオブジェクトに変換するためのクラス
    * インスタンス化しなくても利用できるように`static`を付与しておく
    * 返却されるデータに応じた名前を持つフィールドを用意しておくこと
    */
    @Data
    public static class responseToJavaObject {

        private String endpoint;
        private String active;

    }

```

参考になれば幸いです。
[過去記事]SalesforceのAPI呼び出し制限の情報をまとめてみた

## 前提：Salesforceのエディションとライセンス

Salesforceのエディションとライセンスについて簡単にまとめておきます。

### エディション

これは大枠の契約内容のことです。下記の5種類のエディションが存在しています。

1. Essentials Edition
1. Professional Edition
1. Enterprise Edition
1. Unlimited Edition
1. Developer Edition

上記の各エディションの詳細の説明は下記の公式Helpページに任せますが、ようは料金によって使える機能使えない機能があるみたいな感じです。（Amazon Musicの通常会員とUnlimited会員とかMicrosoft officeの一般用とビジネス用があるみたいな感じです。）

https://help.salesforce.com/s/articleView?id=sf.overview_edition.htm&type=5

### ライセンス

これは簡単に言うと製品です。例えばMicrosoftのOfficeを思い浮かべてください。製品としてExcelやWordなどがありますよね。その「Excel」と「Word」が製品です。
ようはSalesforceと言われているものはNECが提供している製品の一部ということです。

https://www.nec-solutioninnovators.co.jp/ss/salesforce/products/

### エディションとライセンスのまとめ

MicrosoftのOfficeを例にすると分かりやすいかもですね！

| 提供会社 | エディション | ライセンス |
| --- | --- | --- |
| Microsoft | 365 Business Basic<br>365 Business Stanfard<br>365 Business Premium | Word<br>Excel<br>PowerPoint<br>...etc |
| NEC | Essentials Edition<br>Professional Edition<br>Enterprise Edition<br>Unlimited Edition<br>Developer Edition | Salesforce<br>Salesforce Platform<br>Sales Cloud<br>...etc |

## SalesforceAPIの制限について

SalesforceのAPIはどうやら3種類の制限を設定しているようです。

- 同時リクエスト制限
- タイムアウト制限
- 24時間のリクエスト合計数制限

上記それぞれについて詳しく見てゆきましょう！

※本内容は参考文献先の情報を筆者が咀嚼しまとめたものです。内容は記事記載時の最新情報です。閲覧時期や筆者の間違いで内容に誤りがある可能性があります。

## 同時リクエスト制限

名前の通り、同時に複数のリクエストを送信した場合の制限です。
契約しているSalesforceの組織の種類により上限数が異なります。
下記の制限数を超えたリクエストからはエラーが返ります。

| 組織の種類 | 制限数 |
| --- | --- |
| Developer Edition | 5 |
| Trial | 5 |
| Production | 25 |
| Sandboxes | 25 |

## タイムアウト制限

こちらも名前の通り、1回のリクエストで時間がかかりすぎるとタイムアウトエラーを投げるものです。
エディションによる差は無く、一律で10分となっています。10分をすぎるとSalesforceから例外が投げられます。
なので、SalesforceのAPIを実行してからレスポンスが帰ってくるまで10分以上かかった場合、タイムアウトエラーに伴う例外が返されることになります。

## 24時間のリクエスト合計数制限

こちらはSalesforceに用意されているAPIの呼び出しの24時間の回数制限です。

| エディション | 制限数/24h | 備考 |
| --- | --- | --- |
| Enterprise Edition | 1,000 | Salesforceライセンスのみの合計回数
（+100,000回） |
| Professional Edition | 1,000 | Salesforceライセンスのみの合計回数
（+100,000回） |
| Unlimited Edition | 5,000 | Salesforceライセンスのみの合計回数
（+100,000回） |
| Performance Edition | 5,000 | Salesforceライセンスのみの合計回数
（+100,000回） |
| Developer Edition | 15,000 | 全ライセンス合計回数 |
| Sandboxes | 5,000,000 | 全ライセンス合計回数 |

「Developer Edition」と「Sandboxes」のエディションとそれ以外のエディションで大きく仕様が異なります。

### 「Developer Edition」と「Sandboxes」以外のエディション

この場合、ライセンス（製品）ごとにAPIの呼び出し制限が決まっています。Salesforceのみで1,000回/24h、Salesforce Platformのみで1,000回/24hです。また全ライセンス合計のリクエスト数制限も設定されており100,000回/24hです。なので例えばEnterprise EditionでSalesforce1ライセンスの場合1,000回 + 100,000回 = 101,000回を超えるとアクセスできなくなる計算です。

### 「Developer Edition」と「Sandboxes」のエディション

ライセンスのリクエスト回数制限はありません。ただし、全ライセンス合計で1時間以内にDeveloper Editionだと15,000回、Sandboxesだと5,000,000回リクエストを送ってしまうと時間が経過するまで全ライセンスでリクエストを送ることができません。

## 参考文献

https://developer.salesforce.com/docs/atlas.en-us.salesforce_app_limits_cheatsheet.meta/salesforce_app_limits_cheatsheet/salesforce_app_limits_platform_api.htm

https://help.salesforce.com/s/articleView?id=sf.overview_edition.htm&type=5

https://help.salesforce.com/s/articleView?id=sf.users_licenses_overview.htm&type=5&language=ja

https://www.microsoft.com/ja-jp/microsoft-365/business

https://www.nec-solutioninnovators.co.jp/ss/salesforce/products/
MySQL 文字列最大長の設定について

## 文字列最大長の設定

CHAR型やVARCHAR型は文字列最大長の設定をする事ができます。

現在のMySQLの文字列カラムの文字列最大長の指定はバイト数ではなく文字数で指定します。（古いMySQLバージョンではバイト数指定）実際にテーブルにカラムを用意して振る舞いを見てみましょう！

## 体験

下記のSQLを実行してテスト用DBとテスト用テーブル・カラムを作成します。

```sql
create database string_test;
use string_test;
create table char_varchars(char_str char(10), varchar_str varchar(10));
```

上記SQLを実行すると下記のカラムが作成されます。

```
+-------------+-------------+--------------------+------+-----+---------+-------+---------------------------------+---------+
| Field       | Type        | Collation          | Null | Key | Default | Extra | Privileges                      | Comment |
+-------------+-------------+--------------------+------+-----+---------+-------+---------------------------------+---------+
| char_str    | char(10)    | utf8mb4_0900_ai_ci | YES  |     | NULL    |       | select,insert,update,references |         |
| varchar_str | varchar(10) | utf8mb4_0900_ai_ci | YES  |     | NULL    |       | select,insert,update,references |         |
+-------------+-------------+--------------------+------+-----+---------+-------+---------------------------------+---------+
```

上記のTypeを見るとchar型とvarchar型それぞれ文字コードutf8mb4で10文字の文字列最大長指定がされています。

insertできないことを前提にchar_strとvarchar_strカラムにそれぞれ「これはテスト文字列です」という11文字のinsertを試みてみましょう。

文字列最大長を超えているのでもちろんエラーになります。

```sql
mysql> insert into char_varchars (char_str) values ('これはテスト文字列です');
ERROR 1406 (22001): Data too long for column 'char_str' at row 1
mysql> insert into char_varchars (varchar_str) values ('これはテスト文字列です');
ERROR 1406 (22001): Data too long for column 'varchar_str' at row 1
```

では、char_strとvarchar_strカラムにそれぞれ「これはテスト文字列」という9文字のinsertをしてみましょう。

```sql
mysql> insert into char_varchars (char_str) values ('これはテスト文字列');
Query OK, 1 row affected (0.02 sec)
mysql> insert into char_varchars (varchar_str) values ('これはテスト文字列');
Query OK, 1 row affected (0.00 sec)
```

もちろん文字列最大長以下（9文字）なので値を格納する事ができます。テーブルで確認しても下記の様に値が入っています。

```
+-----------------------------+-----------------------------+
| char_str                    | varchar_str                 |
+-----------------------------+-----------------------------+
| これはテスト文字列             | NULL                        |
| NULL                        | これはテスト文字列             |
+-----------------------------+-----------------------------+
```

日本語は2バイト以上なので日本語で9文字 = 18バイト以上ということになります。今回char(10)とvarchar(10)と指定しても18バイト以上の文字列が格納できるのは、MySQLが文字コードを加味して文字数を用いた文字列最大長の指定ができるおかげということになります！
laravel postされた値が指定されたテーブルのカラムに存在するか確認するバリデーションルール

## 使うバリデーションルール

今回のチェックをしたい場合、existsルールを使います。下記の様に記載します。

```php
'キー名' => 'esists:テーブル名,カラム名'
```

## 例

ここでユーザーidがpostされたときのバリデーションを考えてみましょう。

postされる時の値のキー名：id
テーブル名：users
カラム名：id

上記の情報を用いてユーザーidがpostされたとき、その値はテーブルに存在しているかのチェックをしてみましょう。

```php
'id' => 'estates:users,id'
```

これでバリデーションを設定する事ができます。またバリデーションルール記載時にテーブル名とカラム名の間の区切り前後でスペースを開けないようにしましょう！

## 参考文献

https://readouble.com/laravel/9.x/ja/validation.html#rule-exists
Git フックを使ってみる

## Gitフックって？！

Git フックはGitを用いた特定の作業に応じて処理を走らせるものです。

Git フックを活用することで「コミットをする前にlinter（コードの自動補正ツール）を走らせる」なんてことも可能です！

多分この記事より、公式ドキュメントを見たほうが分かりやすいのでリンクを置いておきます！

https://git-scm.com/book/ja/v2/Git-%E3%81%AE%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%9E%E3%82%A4%E3%82%BA-Git-%E3%83%95%E3%83%83%E3%82%AF

## Gitフックの基礎

- Git フックの処理はシェルスクリプトで記載します。
- フック処理のファイルに拡張子は必要ありません。

## Gitフックの種類

### コミット系

#### git commitコマンド実行直後に実行されるフック 〜pre-commitフック〜

- pre-commitフックはgit commitコマンド実行直後に動作させたい処理を記載します。
- 公式ドキュメントによると「いまからコミットされるスナップショットを検査したり、何かし忘れた事がないか確認したり、テストが実行できるか確認したり、何かしらコードを検査する目的で使用されます。」だそうです。
- このフックで0以外が帰った場合コミットプロセスが停止します。
- なのでlintチェックやローカルテストの実行などはこのタイミングで実施すると良さそうです。そもそもチェックが通らないとコミットできないので品質を上げる事ができます。
- ただ、すべてのテストコードを実施することはやめたほうが良さそうです。すべてのテストが完了するまでコミットができない状態は開発体験としてあまりいいものではありません。
- すべてのテストコードの実行はPR作成時のCIチェックで実施すべきかもしれません。
- ちなみにgit commitコマンドに--no-verifyオプションを付与して実行することでpre-commitフックを回避することも可能です。

#### コミットメッセージエディタ起動直前に実行されるフック 〜prepare-commit-msgフック〜

- prepare-commit-msgフックはgit commitのコミットメッセージ入力エディタの起動直前に動作させたい処理を記載します。
- 修正内容をフックに記載する処理で判断して自動でコミットメッセージを記載したい場合などに重宝します。

#### コミットメッセージ保存直後に実行されるフック 〜commit-msgフック〜

- commit-msgフックはユーザーがコミットメッセージを入力して保存した直後に動作させたい処理を記載します。
- 例えばコミットメッセージがチームで決めたルールに沿っているかの確認などが行なえます。
- チェックに弾かれ0以外の値が返されたとき、コミットプロセスが中断します。

#### コミット完了時に実行されるフック 〜post-commitフック〜

- post-commitフックはコミットプロセスが完了した場合に動作させたい処理を記載します。
- コミットプロセスが完了しているのでなにかをチェックするというよりは通知に使うことが主なようです。

### その他

#### リベースする直前に実行されるフック 〜pre-rebaseフック〜

- pre-rebaseフックはリベースを実施する直前に動作させたい処理を記載します。
- このフックが0以外を返したときリベースを中断します。

#### コミットを書き換えた直後に実行されるフック 〜post-rewriteフック〜

- post-rewriteフックはgit commit --amendやgit rebaseを実行した後に動作させたい処理を記載します。

#### チェックアウト直後に実行されるフック 〜post-checkoutフック〜

- post-checkoutフックはgit checkoutが正常に終了した後に動作させたい処理を記載します。
- checkoutしたコードをもとにドキュメントを生成する場合などに重宝します。

#### マージ直後に実行されるフック 〜post-mergeフック〜

- post-mergeフックはマージが正常に終了した後に動作させたい処理を記載します。
- リモートブランチの名前と場所を引数に取ります。（git commit -vで確認できる内容）
- 主に通知処理に使われます。
- このフックが0以外を返したとき通知は行われません。

#### プッシュ実行直後に実行されるフック 〜pre-pushフック〜

- pre-pushフックはgit pushを実行した直後の変更内容がリモートに転送される前に動作させたい処理を記載します。
- リモートに変更が加わる前に更新内容をチェックすることに使用できます。

## Git フックを体験してみよう

それでは一通り使いそうなフックが分かったところでGitフックを体験してみようと思います。！

体験にはpre-commitフックを用います。

### 準備

下記の内容を実施し準備を行います。

1. フック体験用のディレクトリを用意します。
1. フック体験用のディレクトリに移動します。
1. `git init`コマンドを実行してローカルリポジトリを作成します。
1. `cp .git/hooks/pre-commit.sample .git/hooks/pre-commit`コマンドを実行してpre-commitフックのサンプルファイルをコピーします。
1. `.git/hooks/pre-commit`ファイルをエディタで開きます。
1. 内容をすべて削除し下記の内容を記載します。

    ```
    #!/bin/sh
    echo 'pre-commit hookです。'
    ```

### 体験

実際にコミットを行ってpre-commitフックの振る舞いを確認してみましょう。

1. フック体験用のディレクトリで`touch test.txt`コマンドを実行してファイルを作成します。
1. `git add .`コマンドを実行してtest.txtをステージに上げます。
1. `git commit`コマンドを実行してコミットを作成します。（このとき、コミットエディターが開く直前に「pre-commit hookです。」の文字がターミナル上に出力されます。）
1. 任意のコミットメッセージを入力してコミットプロセスを完了させターミナルに戻ると下記の様に「pre-commit hookです。」の文字が`git commit`コマンドの直後に出力されているはずです。

![pre-commit_—_git.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/c14063e8-4829-7916-a353-5441afbc07a5.png)
Ubuntu に Powershell をインストールする方法

以下のスクリプトでインストール可能

```shell
# apt のリポジトリをアップデート
sudo apt update

# Linux 版 Powershell に必要なライブラリをインストール
sudo apt install -y wget apt-transport-https software-properties-common

# Microsoft のリポジトリを追加し、公開鍵をインポートします。
wget -q "https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/packages-microsoft-prod.deb"
sudo dpkg -i packages-microsoft-prod.deb

# Powershell をインストール
sudo apt install -y powershell

# Powershell を起動
pwsh

```
Unity 6 で Cinemachine をインポートしたら could not be found になる方へ

Unity 6 の Cinemachine はデフォルトでバージョンが 3 系みたいです。
そして 3 系は API の破壊的変更があります。
対応しましょう。

# ステップ１

cf.

https://docs.unity3d.com/Packages/com.unity.cinemachine@3.0/manual/CinemachineUpgradeFrom2.html

> Namespaces have changed to be more consistent with Unity standards.
> - Cinemachine is now Unity.Cinemachine
> - Cinemachine.Editor is now Unity.Cinemachine.Editor
> - Cinemachine.Utility has been folded into Unity.Cinemachine.

以上のように API を書き換えたり、そのあとに書かれている色々なこと（必要に応じてコンポーネントを変えるとか、ツールで一括置き換えするとか）をします。

例：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253921/d1733a1c-2364-af01-fc65-87657c664ce0.png)
※↑で一括アップグレードするときはバックアップ必須。


# ステップ２

https://assetstore.unity.com/packages/essentials/starter-assets-character-controllers-urp-267961

例えば、こんなアセットを入れてると「ステップ１」だけじゃコンパイルエラーが消えないかもしれません。
エラーが出ているスクリプトの置かれたフォルダを見に行って、asmdef（Assembly Definition）がないか探してください。

上記アセットの場合、Assets/Starter Assets/Editor にあります。

![2024-05-12-20-43-17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253921/d32b8ed0-c494-b703-cf5c-3e1699cd928f.png)

`➕`ボタンで Assembly Definition References に新しく Unity.Cinemachine を追加し、`➖`ボタンで Missing Reference を削除してください。

![2024-05-12-20-44-21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253921/f825d4d9-b66f-1273-9d5d-f3a670fe1379.png)

そのあと忘れずに当該 Assembly Definition のインスペクター上で Apply してください。

![2024-05-12-20-32-34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253921/1e6d95ba-35a0-3f6c-344a-83721e16610e.png)

最後に Assets/Refresh すれば多分解決します。

# 謝辞

https://stackoverflow.com/questions/63895695/namespace-name-cinemachine-could-not-be-found

> This ended up being an easier fix than I expected. The problem ended up being that the developer that I'm working with had created an assembly file that I didn't know existed. So I just had to add Cinemachine to that assembly file in the scripts folder.

状況は違うけど同じ仕組みでエラーが出ていた事例。asmdef は盲点だった。ありがとうございます。
ROS2メッセージをBuilderパターンで書く

## 実装例

下記の例では，ROS2のパラメータ記述に関するクラス（ROS2 メッセージ）を，提供されているBuilderパターンを使って実装した例

```cpp
#include <rclcpp/rclcpp.hpp>
#include <rcl_interfaces/msg/parameter_descriptor.hpp>
#include <rcl_interfaces/msg/floating_point_range.hpp>
#include <rcl_interfaces/msg/integer_range.hpp>

// ...

node->declare_parameter(
      "sigma_x",
      1.0,
      rcl_interfaces::build<rcl_interfaces::msg::ParameterDescriptor>()
        .name("sigma_x")
        .type(rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE)
        .description("The sigma of gaussian in x axis")
        .additional_constraints("")
        .read_only(false)
        .dynamic_typing(true)
        .floating_point_range(
          rosidl_runtime_cpp::BoundedVector<rcl_interfaces::msg::FloatingPointRange, 1>(
            {
              rcl_interfaces::build<rcl_interfaces::msg::FloatingPointRange>()
                .from_value(1.0)
                .to_value(100.0)
                .step(0.001)
            }))
        .integer_range(
          rosidl_runtime_cpp::BoundedVector<rcl_interfaces::msg::IntegerRange, 1>()));
```

https://github.com/fugashy/image_proc_chain/blob/develop/src/image_proc_chain/image_processors.cpp

## 普通にやるときとの違い

普通とは？ 下記のような感じ

```cpp
rcl_interfaces::msg::ParameterDescriptor param_desc;
param_desc.name = "sigma_x";
param_desc.type = rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE;
param_desc.description = "The sigma of gaussian inn x axis";
param_desc.additional_constraints = "";
param_desc.read_only = false;
param_desc.dynamic_typoing = true;
rcl_interfaces::msg::FloatingPointRange fpr;
fpr.from_value = 1.0;
fpr.to_value = 100.0;
fpr.step = 0.001;
param_desc.floating_point_range = { fpr };
```

ROS2の...builder.hppで提供されるBuilderパターンを用いる場合は：

- すべてのプロパティの値を明示的に書かなければならない（すべてをコードで示す）
    - 実装例にあるような，rcl_interfaces::msg::ParameterDescriptorにはデフォルト値があるため，普通のやり方で書く場合は，変更不要なプロパティには触らなくても良い
        - param_descを宣言した時点で設定される値があるので，その値が求める値なら省略してもよい
    - ROS2メッセージの...builder.hppでは，すべてのプロパティについて値を設定しながらメッセージを構築する仕様であるため，省略ができない
- Builderパターンを使うと，行数が多くなりがちではあるが，バージョン変更に伴うデフォルト値の変更の影響を受けなかったり，すべてが明示されるためプロパティの制約を把握しやすいなどのメリットはあると思う
    - プロパティそのもの存在がなかったことになったり，追加されたりした場合はその限りではない
- 個人的には人に依るさじ加減が入る余地がないので好き

## 注意すべきこと

- 含まれるbuiltinではないメッセージ型のヘッダはincludeすること
    - https://stackoverflow.com/questions/75390687/ros2-c-message-builder-pattern-error-use-of-auto-before-deduction-of-auto

## 参考

- Builderパターンを活用しているOSS
    - Autoware Universe
        - https://github.com/autowarefoundation/autoware.universe/blob/1e829e2948222e8b9c60a2af3bf3008ffbe2ec6d/common/tier4_autoware_utils/include/tier4_autoware_utils/geometry/geometry.hpp
SPAの遷移にページ内遷移の為のscroll-behavior: smooth;が影響してしまう際の解決法

## 概要
スムーズなページ内遷移を実現するために、scroll-behavior: smooth;を使うことがあるかと思います。しかし、SPA（Single Page Application）では、このプロパティが遷移時に意図しない動作を引き起こすことがあります。

本記事では、SPAでscroll-behavior: smooth;が影響する問題を解決する方法について説明します。

## 実現したい挙動
ページ内遷移では連続的にスムーズに移動し、ページ間の遷移はパッと一番上に切り替わる。

## 問題
まず、SPAでscroll-behavior: smooth;がどのような影響を与えるかを説明します。

SPAで何も指定せずにvue routerの設定をすると、前のページのスクロール量が残ってしまいます。下記の画像のようなイメージです。

![スクリーンショット 2024-05-12 2.26.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734276/ea39ddde-006c-0396-a67e-6c001e40ceb6.png)

本来は下記のようなイメージで遷移したいですよね

![スクリーンショット 2024-05-12 2.29.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3734276/ce04ad30-247a-9801-95b7-5ec12064dda5.png)

二つ目のイメージのように遷移するたびに一番上へ遷移するような設定をした際、下記のようにscroll-behavior: smooth;を指定していると二ページに遷移するときパッと切り替わって欲しいのにスムーズなスクロールが入ってしまいます。

```.css
html {
  scroll-behavior: smooth;
}
```

一番上へ遷移するような設定に関しては下記を参考にさせていただきました。

https://qiita.com/kazukinagahira/items/347a63cf0c84624c0cfd

## 解決策の提案

**①aタグでページ内遷移を行う
②JavaScriptを使用してスクロール制御を行う**

### ①aタグでページ内遷移を行う

そもそもaタグでページ内遷移を行う時の為にscroll-behavior: smooth;を指定するわけですが、その解決法を提案します。

- aタグを押した瞬間だけCSSにscroll-behavior: smooth;を指定する
- setTimeout等でscroll-behavior: auto;に戻す

```A-tag.vue
<script setup lang="ts">
const smoothScroll = () => {
  document.documentElement.style.scrollBehavior = 'smooth';
  setTimeout(() => {
    document.documentElement.style.scrollBehavior = 'auto';
  }, 100);
}
</script>

<template>
  <a @click="smoothScroll" href="#anchor">↓ aタグです　</a>

  <div id="anchor" class="page page4">
      <p>4</p>
  </div>
</template>
```


### ②JavaScriptを使用してスクロール制御を行う
aタグでidで指定した要素にページ内遷移する方法ではなく、JavaScriptでwindow.scrollToを使用してページ遷移を行う方法を使用する方法です。

https://developer.mozilla.org/ja/docs/Web/API/Window/scrollTo

```JS.vue
<script setup lang="ts">
import { onMounted, ref } from 'vue';

const anchor = ref<HTMLElement | null>(null);

onMounted(() => {
  anchor.value = document.querySelector('#anchor');
});

const smoothScroll = () => {
  const element = anchor.value;
  if (element) {
    window.scrollTo({
      top: element.offsetTop,
      behavior: 'smooth'
    });
  }
}
</script>

<template>
  <button @click="smoothScroll">↓ Clickイベントです　</button>

  <div id="anchor" class="page page4">
      <p>4</p>
  </div>
</template>
```

scroll-behavior: smooth;の代わりにJavaScriptを使用して、ページ遷移時のスムーズスクロールを制御します。

## 実際に実装してみた

https://vue-a-tag-ojagaos-projects.vercel.app/

実際に両方見てみても挙動に差は見られません。

開発者ツールでソースを確認すると、aタグで遷移する方のページではaタグを押した際に`<html>`タグでstyleの変更が行われているのが見えると思います。

## どちらを使うのがいいの？
私個人の感想になりますが、②のJavaScriptを使用してスクロール制御を行う方が違和感がないかなという気がしています。クラスの付け替えを頻繁に行うのはそういう動きが必要でない場合には気持ち悪い感じがしますが、プロジェクトに合わせて選んでいただければと思います。私は今後②の方法を主に使用していくと思います。

## 最後に

今回の問題以外にもSPAならではの問題が多々あるので都度ベストプラクティスを求めて解決していきたいです。SPAでaタグを使用すると他にも弊害が起きていたので、現象の再現と解決策が見つかりましたらまた書きたいと思います。

最後までありがとうございました。
ソフトウェア

ソフトウェアを学ぶ
ソフトウェアは３つに分けられる。
基本ソフトウェア：
ミドルウェア：
応用ソフトウェア：アプリ、ワードとかエクセル。

OS:GUI,視覚的に操作するときに使われる。

CPUはOSが管理している。
①
どんな管理があるか？
タスク管理：
記憶管理：主記憶装置はOSが行っている。
ファイル管理：ファイルやフォルダの管理もOSが行っている。
②

ジョブ管理とタスク管理
ジョブ管理とは、人間がコンピューターにタスク管理の命令を実行すること。
タスク管理とは、OSがCPUに作業させること。
どのようにCPUはタスクを実行させるのか？
CPUがHDDにプログラムを移して、CPUは他の作業に映る。
    単位時間あたり処理できる仕事量はスループットが向上する。

タスクが処理される順番。
①実行可能状態：タスクが生成される。
②実行状態：処理が完了するとタスクが消滅。反対に、処理がとまると「待機状態」にはいる。
③待機状態：実行可能状態に映る前の状態。
ディスパッチゃ：CPUの割当を実行するプログラム。
ディスパッチ：CPUを割り当てること。

スケジューリング：タスクのルール
①到着方式：実行可能上地になった順番にCPUをに使用権を割り当てる。　ノンプリ
②優先方式：優先度の高いｃPUに使用権を割り当てる。プリ
③ラウンドロビン方式：一定時間ごとにPCUに使用権を割当てる。プリ
プリエンプティブ：OSがPCUを管理し、実行中にタスクを中断して実行権を与える。
ノンプリエンプティブ：他に実行中のタスクを中断せずに実行権を与える。
実記憶管理：
①固定
１６MB→４mb，６ｍｂ，６ｍｂとMBを分ける。
メリット：処理速度が早い。デメリット：処理の効率が悪い。理由：１２ｍBのプログラムを処理したいとき、使えない。
②可変式
必要な分だけ処理をする。
HDDを処理する動作→デフラグ
　メモリコンパクション：主記憶装置をぎゅっとする。
 デフラグ：HDDにをぎゅっとする。

 スワップアウト：主記憶装置→補助記憶装置　補助記憶装置に移動するので、プログラムの処理速度が遅い。
 スワップイン：補助記憶装置→主記憶装置　メモリリーク：OSのバグ等により処理をしていない使用不可領域が発生。
 またこれらのメモリリークを開放することを（ガーベージコレクション）。

 仮想記憶：実記憶（メモリ１６MB)、仮想記憶：好きなだけプログラムを読み込める。
 どうつくられてる？
 主記憶と補助記憶の両方を使用して仮想に見せかけている。
 これらを変換することをDATという。

 ページング：必要なプログラムは主記憶に保存される。↔一旦不要なメモリは補助記憶に保存する。
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3779552/1b44c8e5-1a18-7cdb-d536-467ba49927fb.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3779552/4fe4e62c-16d7-34ef-546c-3e0db54389b5.png)
これらの動作が頻繁に起きるとスラッシングアウトが起きる。
ページングはプロセスの通常の実行中に継続的に行われ、スワッピングはメモリが不足した場合にのみ行われることが一般的です。

アルゴリズム：
FIFO
LIFO
LRU→最も使われていないページを出す。
LFU→使われているページを追い出す。
[過去記事Vue.js v-onとv-showを使って要素の表示・非表示を切り替えてみよう

## やること

v-on、v-showディレクティブを用いて「アンテク」の文字列の表示非表示を切り替えてみる。

## 準備

下記の内容をindex.htmlファイルを作成して記載し、保存します。

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="css/app.css">
    <script src="https://cdn.jsdelivr.net/npm/vue@2.*/dist/vue.js"></script>
</head>
<body>
    <div id="app">

    </div>
    <script>
        let app = new Vue({
            el: '#app',
            data() {
                return {

                }
            },
        })
    </script>
</body>
</html>
```

## 方法

下記の様に記載をして当該のファイルをブラウザで開いてみましょう。

```html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.*/dist/vue.js"></script>
</head>
<body>
    <div id="app">
        <button @click="viewChange()">表示切り替え</button>
        <div v-show="isShow">
            アンテク
        </div>
    </div>
    <script>
        let app = new Vue({
            el: '#app',
            data() {
                return {
                    isShow: false,
                }
            },
            methods: {
                viewChange () {
                    this.isShow = !this.isShow;
                }
            }
        })
    </script>
</body>
</html>
```

下記のように「表示切り替え」ボタンをクリックすると「アンテク」の文字が表示されたり表示が消えたりすると思います！

![Document.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/09ada388-f66a-9814-b5c7-77ae871e9688.png)

また、定義されているviewChange()関数ですが、このレベルの処理ならv-onに直接記載しても良いかもしれません！下記のように書き換えても同じ挙動をします！

```html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.*/dist/vue.js"></script>
</head>
<body>
    <div id="app">
        <button @click="isShow = !isShow">表示切り替え</button>
        <div v-show="isShow">
            アンテク
        </div>
    </div>
    <script>
        let app = new Vue({
            el: '#app',
            data() {
                return {
                    isShow: false,
                }
            },
        })
    </script>
</body>
```
[ODC]Exception Handler下のFlowでExceptionが発生した時の動作

Exception Handler下のFlowで再びExceptionをRaiseしたい時がある。
この時の動作を確認する。
[OutSystemsの無限ループ問題](https://qiita.com/jyunji_watanabe/items/91db13dcd4ce0536b60e)でも検討したように、OutSystemsで無限ループを発生させると厄介なので、「無限ループが発生しないこと」を確認したい。
（例えば、AllExceptionsのHandler下で再びExceptionをRaiseしたときに、同じException Handlerに再び捕まらないか）

# 環境情報
ODC Studio(Version 1.4.7)

# 確認結果
今回の検証で以下のことが確認できた

- Exception Handler下のFlowで発生した例外は同じException Handlerではハンドルされないようだ
- 同じAction内のException Handlerでハンドルしたあと、更にExceptionをRaiseした場合、Action内にそのExceptionをハンドルしうる別のException Handlerがあってもそこではハンドルされないようだ
- （参考）Global Exception HandlerのAllExceptions下のFlowでさらに例外を発生させると、GEHを設定せずに例外を発生させたときと同じ画面になる（＝GEHの外側まで例外が届いてしまっている）

# （Action内の）AllExceptions Handlerの下でExceptionをRaiseした場合
## 動作確認用Action Flow
以下の通り、
1. メインのFlowでTestExceptionをRaiseする
1. AllExceptionsのException Hanlderへ遷移
1. Settingに用意した、Boolean型のLoopStopperを見て、TrueならEndへ（ログから流れを確認できるようにLogMessageも配置）
1. LoopStopper=Falseなら、Sleepした上で再びTestExceptionをRaiseする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/9fedc9ea-931a-2551-a5a5-d4f293cfada2.png)

LoopStopperを持たせているのは、仮説が間違っていて無限ループになったら止めるため。
（AllExceptions下で再びRaiseしたTestExceptionをAllExceptionsでHandleしてしまうようなら無限ループになる。幸い以下の確認結果の通りそうはならなかったが）
Sleepで5秒止めているのも無限ループ対策。無限ループになっていたら、LoopStopperをTrueにするまでの時間を稼ぐ。

## 確認結果
Action内のAllExceptionsのException Handlerではハンドルされず、上位（今回はGlobal Exception Handler）でハンドルされた。
つまり、Exception Handler下のFlowで発生した例外は同じException Handlerではハンドルされないようだ。

# Action中の具象Exception
## 動作確認用Action Flow
上のバリエーションで、具象例外に対するException Handlerを追加し、その下のFlowでも同様にExceptionをRaiseしてみる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/80857784-674e-4739-cec5-55b6baefa9d9.png)

## 確認結果
処理は
1. メインフローでTestExceptionをRaise
1. 同じActionのTestExceptionに対するException Handlerでハンドル
1. その下のFlowで再びTestExceptionをRaise
1. 同じActionのAllExceptionsではハンドルされず、上位（GEH）AllExceptionsに対するException Handlerでハンドルされた

つまり、同じAction内のException Handlerでハンドルしたあと、更にExceptionをRaiseした場合、Action内にそのExceptionをハンドルしうる別のException Handlerがあってもそこではハンドルされないようだ。

# Global Exception Handlerでハンドル後、再びExceptionをRaiseしてみる
## 動作確認用Action Flow
Global Exception HandlerのAllExceptionsに対するException Handler部分だけ見ると以下の通り。
AllExceptions下で具象例外をRaiseしてみた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/271aa481-4e77-9e34-e02c-1f986311aa6b.png)

## 確認結果
Global Exception Handlerを設定せず、かつ例外が発生したときと同じ画面になる。
つまりGEHの外側まで例外が届いてしまっている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/192ae528-57b2-09ed-0e5b-e9051774b3a7.png)