OmniAICreator/Qiita-1.07M huggingface dataset:
EC2のユーザデータでdockerサービスをインストールする

https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/user-data.html

上記ドキュメントを参考に、dockerのインストールコマンドをカスタムデータにあらかじめ仕込む際のスクリプトを記載する。

```
#!/bin/bash
sudo yum update -y
sudo amazon-linux-extras install docker
sudo systemctl start docker
sudo systemctl enable docker
sudo usermod -a -G docker ec2-user
```

EC2作成直後だとDockerサービスが起動しておらず、コマンドの流し込みにうまくいってない風に見えたが、数分するとサービスが起動していることが確認できた。その後一旦ログアウトし、再度ログインし`sudo`しなくてもDockerコマンドを実行できるか下記コマンドで確認できる。

```
docker info
```
CSRF

# ActionController::InvalidAuthenticityToken in "********"Controller#******* ＃ Can't verify CSRF token authenticity.

# クロスサイトリクエストフォージェリ(Cross Site Request Forgeries : CSRF)
XSSと同じく攻撃者が用意した```「ログインが必要なサイトに対して操作を行う」```リンクにユーザーがアクセスすることで被害を受ける攻撃です。
XSSと違う点は、目的が「ユーザーのWebブラウザに悪意あるスクリプトを送り込む」ことではく、```「本人になりすましてログインの必要なサイトを操作する」```ことです。
具体的な攻撃としては、ユーザーのパスワードを```攻撃者の指定するものに変えられ```てしまったり、```XSSのための悪意あるリンクを投稿する```ことなどか考えられます。

# クロスサイトスクリプティング(Cross Site Scripting:XSS)
```掲示板サイトのような、ユーザーの入力内容を表示するタイプのWebサイトの脆弱性をつく攻撃```です。
具体的には、攻撃者が```「脆弱性を持つWebサイトに対してスクリプトを書き込む」リンクを表示させるWebページを公開```します。そのリンクにアクセスしてしまうと、脆弱性のある```Webページを介してスクリプトがユーザーのWebアクセスに送り込まれ、クライアントサイド・スクリプトとして実行```されてしまいます。送り込まれるスクリプトとしては、セッションハイジャックのためのCookie情報を公開するものや、ウイルスをダウンロードするものが考えられる。

### 気づき
悪いサイトからログインしてしまっているのか。
それから自分になりすますのか。

# CSRFトークンとは？
CSRF（Cross-site Request Forgery）トークンとは、```正規のページからアクセスが行われていることを証明するための値```のことです。
いわば「合言葉」のようなもので、CSRFトークンが必要となるバックエンドと通信する際は、```必ずCSRFトークンを事前に取得```しておき、バックエンドへアクセスするときは```CSRFトークンも一緒に渡す必要```があります。
.
.
.
トークンを生成するか否かを判断し、```正式なアクセスのときだけトークンを返すという仕組みが肝```なのです。
### 出典
https://takabus.com/tips/1115/

# 感想
作成中のアプリにはまだCSRFトークンを取得していないからエラーメッセージが出たのかな。
これを取得すればいいのか。
まだやることがあるので今のところは
```skip_before_action :verify_authenticity_token```これでやり過ごす。

### 関連記事
https://developer.cybozu.io/hc/ja/articles/360001197206-CSRF%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B
【農家が作る】ビニールハウスの温度・湿度・気圧計（ATOM Lite, ENV III, Ambient）

# はじめに
最近では5万円程度の安いセンサーも販売されていますが、ATOM Liteを使って約3,000円で温湿度・気圧計を作ります。ちなみに単純なセンシングだけならSwitchbotの方が安くて簡単です。

# 用意するもの
1. [ATOM Lite（約1,400円）](https://www.switch-science.com/products/6262)
2. [ENV III Unit（約1,000円）](https://www.switch-science.com/products/7254)
3. [USB TypeCケーブル（約650円）](https://amzn.asia/d/iYNpKsW)

# プログラミング（UIFlow）
プログラミングは無償で使えるブロックプログラミングの[UIFlow(M5Flow)](https://flow.m5stack.com/)を利用します。
![flow_20221030002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2951500/c184a082-8e39-9687-1f69-79c18c1263c5.png)

```python:Micro Python
from m5stack import *
from m5ui import *
from uiflow import *
import wifiCfg
from machine import WDT
import time
import unit

env3_0 = unit.get(unit.ENV3, unit.PORTA)

env3_temp = None
env3_hurmid = None
env3_press = None

wifiCfg.autoConnect(lcdShow=False)
wifiCfg.reconnect()

wdt = WDT(timeout=1000000)
while True:
  if not (wifiCfg.wlan_sta.isconnected()):
    pass
  env3_temp = env3_0.temperature
  env3_hurmid = env3_0.humidity
  env3_press = env3_0.pressure
  rgb.setColorAll(0x003300)
  import urequests
  def ambientSend(_channelId, _writeKey, _data):
    if isinstance(_data, list):
      __d = _data
    else:
      __d = [_data]
    try:
      req = urequests.request(method='POST', url=((('http://ambidata.io/api/v2/channels/' + str(_channelId))) + '/dataarray'),json={'writeKey':_writeKey,'data':__d}, headers={'Content-Type':'application/json'})
      _ret = True
    except:
      _ret = False
    return _ret

  ambientSend(******, '**********', {'d1':env3_temp,'d2':env3_hurmid,'d3':env3_press})

  wait_ms(5000)
  rgb.setColorAll(0x000000)
  wdt.feed()
  wait_ms(2)

```

### ポイント
* Ambientブロックの入手先→ https://github.com/AmbientDataInc/UIFlow
* Ambientブロックはファイルを開くたびに読み込む必要がある
* AmbientのwriteKeyはシングルクォートで括る
* WDTは約16分間ハングアップしたら強制リブート
* wifiは毎ループで再接続判定

# 設置
レゴの互換ブロック＋マグネットフックでビニールハウスの柱に設置しています。（写真手前のセンサーはCO2センサー）今後強制通風筒を製作してより精度を高めようと思っています。
![IMG-1658.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2951500/cbec3090-aedb-171d-72ff-f978401c39de.jpeg)


# 結果
[Ambient](https://ambidata.io/)に温度、湿度、気圧が蓄積され、スマホでいつでもどこでも確認できるようになりました。約1年間粉塵や農薬、ミストなどに曝されていますが正常に動作しています。
![ambient_20221030001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2951500/b07850e0-3cdf-43bb-24ef-bbc329b6dad4.png)

# 補足
ENV IIIに使用されているセンサーは温度湿度計がスイス センシリオン社のSHT30、気圧計は中国メーカー？のQMP6988という製品。
SHT30は温度精度0.3度、相対湿度精度3%RHと農業用としては十分な性能です。

https://docs.m5stack.com/en/unit/envIII

https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/datasheet/unit/enviii/QMP6988%20Datasheet.pdf
良く使うcondaコマンド一覧

# 背景
現在、研究開発でAnacondaを使っています。
前までJupyter Notebookを使っていたのですが、pythonファイルでプログラムを作成したくなりました。
Anaconda Navigatorから環境を変更するのではなく、Anaconda Promptからコマンドで操作する必要があるので、よく使うものを一覧にしました。

# 一覧
・仮想環境`hoge`を作成する(python)
```
conda create -n hoge python
```
・仮想環境`hoge`を作成する(pythonバージョン3.9指定)
```
conda create -n hoge python=3.9
```
・仮想環境の一覧を表示する
```
conda info -e
```
・仮想環境`hoge`へ変更する
```
conda activate hoge
```
・仮想環境内のパッケージ一覧を表示する
```
conda list
```
インストール可能なライブラリのバージョンを確認

pipでライブラリをインストールする際バージョンを指定することができます。

例えばDjangoのバージョン3.2.5をインストールする場合は次のようにします。

```
pip install Django==3.2.5
```

何も指定しなければ最新版がインストールされますが、安定版の中での最新版をインストールしたいことがあります。

例として、Djangoには現時点で最新版は4系以上が存在しますが、3.2系の中の最新版をインストールしたいとします。ただし、3.2系の最新版が3.2.1なのか、3.2.20なのかなどはわからないとします。

こんなときに手軽に確認するには次のようにします。

```
pip install Django==
```

このコマンドはバージョンを指定していないのでエラーがターミナルに表示されます。

エラー内容は
「バージョンを指定したいならこの中から選んでください。」
というような感じです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2706663/c4a181f6-5dc6-cf22-6d7e-24f28439c158.png)

そのエラーをよく見ると、今回知りたい3.2系の最新版は3.2.13であることがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2706663/a1880bc4-d320-d3b3-7f9e-3b6913c261f4.png)


インストールするバージョンがわかったのであとは次のようにインストールすればOKです。

```
pip install Django==3.2.13
```
React.js 学習備忘録③~CSS~

ワクチンの副反応で投稿が遅くなってしまった...。

# InlineStyle
・コンポーネント内に直接書き込む
・ハイフン繋がりのプロパティはキャメルケースになる

```App.jsx
export const App = () => {
    const inlineStyle = {
        color : 'red',
        fontSize: '16px', //キャメルケースにするのでfont-sizeはfontSizeとなる
    }
    return(
        <>
            //オブジェクトを定義して、styleに渡す
            <p style={inlineStyle}>Inline Style</p>
            //直接記入も可能
            <p style={ //jsであることを示す{}
                    { //オブジェクトであることを示す{}
                        color: 'blue',
                        fontSize: 18, //pxであれば数値だけでもOK
                    }
            }>Inline Style</p>
        </>
    )
}
```

# Css Modules
・コンポーネントに対応するcssを作成し、コンポーネント側で読み込んで使用する
・ファイル名はコンポーネント名.module.拡張子
・node-sassをインストールすることで、scss記法が可能
```App.jsx
import cssModules from "./App.module.scss";

export const App = () => {
    return(
        <>
            <p className={title}>Css Modules</p>
        </>
    )
}
```
``` App.module.scss
.title{
    color: red;
    font-size: 16px;
}
```

# Styled Jsx
・styled-jsxをインストールすることで使用可能
・styleタグで囲ってをcssを記入できる
```App.jsx
export const App = () => {
    return(
        <>
            <p className={title}>Styled Jsx</p>
            //Jsxであることを明記する
            <style jsx="true">{`
                .title {
                    color : red;
                    font-size: 16px;
                }
            `}</style>
        </>
    )
}
```

# Styled Component
・styled-componentsをインストールすることで使用可能
・スタイルを当てたコンポーネントを作成できる
・scss記法も可能
・作成したコンポーネントの継承もできる
```App.jsx
import styled from "styled-components"

export const StyledComponents = () => {
    return (
        //styled-componentsで作成した要素で作成できる
        <StyledP>Styled Components</StyledP>
        <StyledKidP>継承したコンポーネント</StyledKidP>
    )
}
//styled.要素名でスタイルを当てたコンポーネントを作成できる
//styled-componentsで作成したコンポーネントであることがわかるようにプレフィックスなどを付けると良い
const StyledP = styled.p`
    color : red;
    font-size: 16px;
`
// styled(作成したコンポーネント)でコンポーネントを継承
const StyledKidP = styled(StyleP)`
    background-color: blue;
`
```

# Emotion
・@emotion/reactと@emotion/styledをインストールすることで使用可能
・様々な書き方が可能
```App.jsx
import { css } from "@emotion/react"
import styled from "@emotion/styled"

export const Emotion = () => {
    //cssを記入
    const emotionCss = css`
        color : red;
        font-size: 16px;
    `
    //cssのオブジェクトを作成
    const emotionCssObject = css({
        color : blue,
        fontSize: 16px
    })
    //コンポーネントを作成
    const EmotionComponent = styled.p`
        color : 'orange';
        font-size: '5px';
    `
    return (
        <>
            <p css="emotionCss">emotionCss</p>
            <p css="emotionCssObject">emotionCssObject</p>
            <EmotionComponent>emotionComponent</EmotionComponent>

        </>
    )
}
```

前回：[再レンダリングについて](https://qiita.com/_takenoko/items/cd74f5c90f468f793b44)
次回：[React-Routerについて](https://qiita.com/_takenoko/items/26c47e14d5aa6f823584)
IPトンネリングをかんたんに説明してみる

# 勉強前の理解度
* パケットをうまい感じに伝送する技術
* VPNで使われた気がする

# IPトンネリングの概要

#### トンネリングとは
　**公衆回線上の２点間で、閉じられた通信経路を仮想的に作ること**。一般に、通信経路上のネットワークでサポートされていないプロトコルを使用して通信する場合に利用される。
　例えば、IPv6をサポートしていないネットワークを通信経路としてIPv6で通信をする場合などに利用する。

#### IPトンネリングとは
　**ネットワーク層の通信ヘッダに、さらにネットワーク層の通信ヘッダが続くトンネリング**。
　例えば、IPv6で通信する場合データにIPv6ヘッダが付与されるが、そのままだとIPv4のみをサポートしている通信経路を通過できない。そのため、「IPv6ヘッダとペイロード」をデータとしたIPv4ヘッダを付与することにより、IPv4のみをサポートするネットワーク内を通過できるようになる。

　なお、上記のような「別のプロトコルでヘッダとデータを包む」ことを**カプセル化**という。

# IPトンネリングの用途
* Mobile IP
(ノードが移動してもIPアドレスを固定化する仕組み)
* IPv4ネットワークを経由してIPv6パケットを送る
* IPv6ネットワークを経由してIPv4パケットを送る
* L2TP
(データリンク層でトンネリングを行うためのプロトコル)

# 参考
[マスタリングTCP/IP](https://www.ohmsha.co.jp/tbc/text_series_0201.htm)
[トンネリングとは？| ネットワークにおけるトンネリング | Cloudflare](https://www.cloudflare.com/ja-jp/learning/network-layer/what-is-tunneling/)
Google SitesでMathJaxの数式を入力できるようにする

Googleサイトには数式を入力する機能はありません。

MathJaxのHTMLを埋め込むことで、簡単に数式を$\LaTeX$形式入力することができます。

## 1. Google SitesでHTML埋め込み画面にする

Google Sitesで、任意の場所をダブルクリックして、左側の「埋め込む」のボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490087/8eeba10c-7199-61a3-a40e-8b57ff0dfd28.png)

## 2. 数式を入力する
「埋め込みコード」で、以下のように`$$`と`$$`の間に数式を書きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490087/b62cbefd-d0bb-569a-e2c4-17059f9c9b15.png)



```html
<body>
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" id="MathJax-script" async
src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js">
</script>
</head>
$$
\frac{-b\pm\sqrt{b^2-4ac}}{2a}
$$
</body>
</html>
```

すると、プレビュー画面で、きちんとMathJaxで数式が美しく表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490087/c2476c55-47d3-a43b-1c07-7da38b3a9dbe.png)

「保存」をクリックすると、無事に埋め込まれました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490087/71a4b6eb-5679-d343-25a6-77519cf709ef.png)



コピペ用にこちらをお使いください。


```html:コピペ用
<body>
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" id="MathJax-script" async
src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js">
</script>
</head>
$$

$$
</body>
</html>
```
ActionController::Redirecting::UnsafeRedirectErrorへの対処法

# 概要
質問投稿アプリを作っている最中、
投稿に失敗したら質問投稿画面に戻る仕様を実装してる際に、表題のエラーに捕まりました。

↓当該コード
```microposts_controller.rb
def create
  @micropost = current_user.microposts.build(micropost_params)
  if @micropost.save
    flash[:success] = "Micropost created!"
    redirect_to root_url
  else
    redirect_to 'microposts/new'
  end
end
````

# 結論

原因は下から3行目のredirect_to 'microposts/new'にありました。

エラーを解消する方法は以下の2つです。

**①リクエストを経由する必要がない場合はredirect_toをrenderに置換する**
　（そもそもrenderとredirect_toの違いって何だっけ？という方は以下へ）
　　https://qiita.com/koki_73/items/78b8cb2bde7930d55194

**②以下のコードをapplication_controllerに加える**
```application_controller.rb
  rescue_from ActionController::Redirecting::UnsafeRedirectError do
    redirect_to root_url
  end
```

# エラーの内容
リダイレクトした際に外部サイトへ遷移してしまうことを防ぐために
Railsが発生させたエラーでした。
②のコードもRailsがそのエラーへの解決策として用意したものです。
    
ただ②を加えると今後発生し得る全てのUnsafeRedirectErrorが
パスしてしまうため注意が必要です。

私は①で解決しました。単純な内容でつまずいてました…。

# 参考
↓より詳しい解説
https://blog.saeloun.com/2022/02/08/rails-7-raise-unsafe-redirect-error.html

↓②のGithub
https://github.com/rails/rails/commit/c3758a71af949db849d5b7f176677653e4e4fae9
【Ubuntu22.04】 Elixir Desktopのサンプルアプリ動かすまで

Ubuntu 22.04をインストールした素の状態からElixir Desktopのサンプルアプリを動くようにするまでのコマンド

Hyper-Vの仮想サーバで行いました


```
sudo apt -y install git
sudo apt -y install curl
git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.10.2
echo ". $HOME/.asdf/asdf.sh" >> ~/.bashrc
```
ログインしなおす

```
asdf plugin add erlang
asdf plugin add elixir
sudo apt-get -y install build-essential autoconf m4 libncurses5-dev libwxgtk3.0-gtk3-dev libwxgtk-webview3.0-gtk3-dev libgl1-mesa-dev libglu1-mesa-dev libpng-dev libssh-dev unixodbc-dev xsltproc fop libxml2-utils libncurses-dev openjdk-11-jdk
asdf install erlang 25.1.1
asdf install elixir 1.14.0-otp-25
asdf global erlang 25.1.1
asdf global elixir 1.14.0-otp-25
asdf plugin add nodejs
asdf install nodejs 19.0.0
asdf global nodejs 19.0.0
git clone https://github.com/elixir-desktop/desktop-example-app.git
cd desktop-example-app
cd assets/
npm install
cd ..
mix deps.get
mix assets.deploy
./run
```


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532235/e698d788-5c62-7061-e58d-104c6fc111cc.png)


nodejsは、Elixir Desktopで必須ではありませんが、サンプルアプリで必要だったので入れています。
Cloudflare R2 2022年10月時点での実用性

# この記事について
2022年10月時点で個人的にCloudflare R2を実際使えるか検討したので自分用のメモが主な目的ですが記事に残しておきます。

# 試してみた結果
将来的に非常に楽しみにしているサービスではあります。また料金が安いのも魅力的。ただ現時点（2022年10月時点）でバグが非常に多くすぐには個人的にはすぐに理由できませんでした。
-  決定的なのは通常のファイルへのアクセス、またはアップロードでランダムで10-20%で500エラーになり、自分の知る限り少なくともそのまま解決されていない。または日による

-  大容量ファイルのアップロード方法が限られている
S3のAPIが利用できるということで大容量ファイルをフロントエンドからアップロードする場合にはS3 api のPresigned-urlをサーバー再度で発行するがcontent-lengthをサーバー側で設定するとアップロード時にR2側のサーバーが受け付けない。よって悪意のあるユーザーは数TBのデータでもアップロードできてしまう。
それを避けるためには自前のサーバーをたててそこを経由させる必要があるが、そのためにサーバーを準備するのが難しく、大容量データもそのサーバーを経由させたくない。
Cloudflare Workerを経由してアップロードすることもできるがFree/Proどちらでも100MBの制限がある。

- 「グローバルに分散」「CloudflareのCDNと統合」とLPに記載があるので、自動的にCDNが利用できるか、もしくは簡単に実現できるような機能を期待しましたが現時点でそのような機能は無いようです。
実態としてはBucket作成した際にリクエスト元から最も近いRegionにBucketが自動で作成されることを謳っているということです。
リクエスト元から自動でRegionを決定する = Regionを選ぶことができないようです。この点はS3などのサービスと比較して大きなデメリットとなります。
SRT(Secure Reliable Transport)について色々ためす(3)

# 1. 概要
SRTを使った映像伝送に関して、色々を実験したい。そこで、手ごろな環境でどのようなことができるのか調べていく。今回は配信映像をカメラの映像にしてみる。

# 2. これまでの記事
https://qiita.com/tetsuoMikami/items/6f27de55b86b888f2fb4 

https://qiita.com/tetsuoMikami/items/b6dd5ec0dc445f507634

# 3. UVCカメラの映像を使う
# 3.1 準備
前回同様Listenerで待ち受け 
```
gst-launch-1.0 -v srtserversrc uri="srt://:17001" latency=100 ! srtserversink uri="srt://:17002"
```

カメラ映像を見る側はListener起動、カメラ映像配信開始後い操作する。
VLC Playerで「ネットワークストリームを開く」。これは今後、基本的には変わらない。
```
srt://192.168.1.2:17002
```
※192.168.1.2はListenerのIPアドレス

# 3.2 ソフトウェアエンコード
カメラの映像をカラーバーとほぼ同じパラメータで配信開始
```
gst-launch-1.0 v4l2src device=/dev/video0 ! videoconvert ! video/x-raw,format=I420,width=640,height=480 ! x264enc ! mpegtsmux ! srtclientsink uri="srt://192.168.1.2:17001" latency=100
```
ソフトウェアエンコードのせいか遅延が半端ない。実用に耐えないと思われる。

# 3.2 ハードウェアエンコード(YUV->H.264)
```
gst-launch-1.0 v4l2src device=/dev/video0 ! videoconvert ! video/x-raw,format=I420,width=640,height=480 ! omxh264enc ! mpegtsmux ! srtclientsink uri="srt://192.168.1.2:17001" latency=100
```
遅延はかなり減ったがそれでもそれなりにある。目視で1秒くらい。
ローカル環境であることを考えるともう少し遅延が小さくなってほしい。

# 3.2 ハードウェアエンコード(MJPEG->H.264)
MJPEGからならマシかも。
```
gst-launch-1.0 v4l2src device=/dev/video0 ! image/jpeg,width=640,height=480,framerate=10/1 ! omxmjpegdec ! omxh264enc ! mpegtsmux ! srtclientsink uri="srt://192.168.1.18:17001" latency=100
```
余計遅くなった。

# 3.3 VLC Playerのキャッシュ
よく考えたら再生側のVLC Playerにキャッシュ設定があった。0msにすると再生開始自体がうまくいかないが、デフォルトの1000msから500msにしたら遅延が減ってなんとか再生できるレベル。

# 4. 懸念点・疑問点
* 配信側の設定変更した後、Listener側も再起動しないとキャッシュやらで挙動がおかしくなる。
* やはり遅延。VGAでこれだとデモにも使いずらい。
* 見る側Caller、VLC Playerで停止したあとに配信側CallerやListenerにDropやPacket lostのログが出まくる。

# 5. 今後
遅延について調べるか、暗号化あたり調べる。
AWSについての個人的メモ

# はじめに
あくまで個人的メモです。特に記事を１つ作るほどでもないけど、覚えときたいことをちょこちょこメモする用です。

# 役立つ資料

https://dev.classmethod.jp/articles/aws-summary-2022/

https://docs.aws.amazon.com/ja_jp/index.html

https://aws.amazon.com/jp/architecture/?cards-all.sort-by=item.additionalFields.sortDate&cards-all.sort-order=desc&awsf.content-type=*all&awsf.methodology=*all&awsf.tech-category=*all&awsf.industries=*all

https://dev.classmethod.jp/articles/aws-beginner-docs/

https://speakerdeck.com/iselegant/sustainable-pcidss-operation-on-aws

https://speakerdeck.com/iselegant/business-optimization-for-credit-card-by-serverless

https://dev.classmethod.jp/articles/iam-policy-user-role-for-primary-of-aws/

https://www.ragate.co.jp/blog/articles/6833

## ハンズオン
### サーバーレスをサクッと作る
Lambda、API Gateway、DynamoDBを勉強したい人におすすめ。

https://aws.amazon.com/jp/builders-flash/202011/hajimete-serverless-handson/?awsf.filter-name=*all

https://aws.amazon.com/jp/builders-flash/202101/hajimete-serverless-handson-2/?awsf.filter-name=*all

https://aws.amazon.com/jp/builders-flash/202102/hajimete-serverless-handson-3/?awsf.filter-name=*all

# Cloud9とVSCodeで開発環境を構築する

https://qiita.com/hayate_h/items/93273285cdc7372ae569

## EC2インスタンスの選び方
### 非本番環境には`T3`がおすすめ

https://biz.nuro.jp/column/aws-mama-022/

汎用のインスタンスファミリーには、M系とT系がある。M系には`M5`、`M4`があり、T系には`T3`、`T2`がある。M系はCPUやメモリ、ネットワークをバランスよく提供するものなので、一般的な本番環境用であればM系が良さそう。T系はバースト機能がついている。スパイクが想定されるような本番環境であればT系が良さそう。

開発環境やステージング環境には、M系よりもT系がいい。もちろん開発環境やステージング環境ではバースト機能は必要ないことが多いがM系よりもT系の方が単純に安いので。

https://blog.turai.work/entry/20180831/1535674868

また`T2`よりも、`T3`の方が安く、パフォーマンスも高いので、特に理由がなければ`T3`を選ぶべし。

# RDSとDynamoDBはどっちを使うべき？

https://serverless.co.jp/blog/232

https://serverless.co.jp/blog/243/

DynamoDBは、RDSに比べて一貫性が低い代わりに、スケーラビリティが高い。ただ上記の記事で以下のように言及されているように、よほどトラフィックの多いサービス以外、あまり関係ないかと。なのでより扱いやすいフルマネージドのDynamoDBの方がいいのではと個人的には思う。

> しかし、極端にハイトラフィックへの対応や一貫性の確保が必要となるプロジェクトは、それなりにエッジケースではないかとも思っています。多くのシステムはRDSでもDynamoDBでも実装可能でしょう。

RDSを使用するにはVPCを構築する必要があるのと冗長化、容量などを考える必要があるが、DynamoDBはサーバーレスなのでVPCは必要ないし、冗長化や容量を気にしなくていい。ただ、DynamoDBはRDSに比べて検索がしづらいというデメリットがある。RDSとDynamoDBのそれぞれのメリットを活かすためにハイブリッド構成にするのもあり。

# ELBの種類

https://qiita.com/suzuki-navi/items/a8f4e21d75e685df51e9

# AmplifyとCloudFront+S3の違い

https://go-to-k.hatenablog.com/entry/2021/08/08/022528

# Lambda Function URLs vs. API Gateway
個人的には、Lambdaから別のLambdaを呼ぶときに、呼ばれる方のLambdaにLambda Function URLsを使うのがいいかなと思った。ユーザーによって直接叩かれるAPI（Lambda）に関してはAPI Gatewayを使うのがいいかと。

https://www.ragate.co.jp/blog/articles/12061

# Lambdaのレイヤー

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/configuration-layers.html

レイヤーをLambda関数から使用するのに手こずったのでメモ。Zip化したPythonパッケージをレイヤーにアップロードすると思うが、Zip化するディレクトリの構造は`python/<パッケージ>`にする必要がある。つまり、例えば`requests`というパッケージをレイヤーに加えたいときは、以下のようにディレクトリを作った上でZip化する必要がある。

```zsh
% mkdir python
% cd python
% python3 -m pip install requests -t .
```
このようにしてレイヤーを作り、Lambda関数にそのレイヤーを紐づけたら、以下のように普通にLambda関数内ではパッケージをインポートできる。

```python
import requests
```
本番DBのコピーをローカルに作成する方法

開発環境のデータは本番環境に近いものが望ましいです。セキュリティなどの観点から本場環境のデータを使っても問題ない場合はそのままコピーして使いたいです。

AWS RDSに本番環境のDBがあると想定します。

# 本番DBをダンプする

まず、RDSにアクセスできるEC2に入ってDBのコピーを作ります。次のコマンドでは、EC2へssh接続した後、`dump_target_db`というDBを`/var/tmp/dump.sql`にダンプしています。

```terminal
$ ssh hoge
$ mysqldump -h host_name -u user_name -p password dump_target_db > /var/tmp/dump.sql
```

# ローカルホストへダンプファイルを移動させる

次に、ローカルホストへ戻って先ほど作成した`dump.sql`をこちらへ移します。例として`dump.sql`としてローカルのカレントディレクトリに作成しています。

```terminal
$ scp -i "hoge.pem" ec2-user@ec2-00-000-000-00.ap-northeast-0.compute.amazonaws.com:/var/tmp/dump.sql dump.sql
```

# ダンプファイルを元にローカルホストでDB作成
最後に、本番DBから持ってきた`dump.sql`を元にDBを作成します。

```terminal
$ mysql -u user_name -p db_name < dump.sql
```

以上で本番DBのコピーをローカルに作成完了です。
プリザンターAPIで複数のテーブルをJOINしてデータを取得する方法

プリザンターAPIで複数のテーブルをJOINしてデータを取得する方法

# はじめに
[プリザンター](https://pleasanter.org) に格納された テーブルA と テーブルB の情報をJOINして取得することができます。マスタデータにある属性値などが簡単に取得できます。

# 前提条件
テーブルA と テーブルB はリンクの設定が必要です。

# テーブル構成
親テーブルA - サイトID: 6 
子テーブルB - サイトID: 7 (分類AでサイトID 6 にリンク)

# Request URL
子テーブルから親テーブルを参照する場合には、子テーブルのサイトIDをURLに指定します。

```url
https://servername/api/items/7/get
```

# Request Body
複数のテーブルからデータを取得する際には、以下の指定を行います。"ApiDataType"は必ず"KeyValues"とします。"GridColumns"には取得する項目をのテーブルと項目を指定します。"ClassA~6,ClassA" のカンマの前の部分は、分類Aに設定したサイトID: 6 へのリンクであることを示します。カンマの後ろの部分は、リンク先テーブルの分類Aを示します。

```json
{
    "ApiKey":"9318a126...",
    "View": {
        "ApiDataType": "KeyValues",
        "GridColumns": [
            "Title",
            "ClassA",
            "ClassA~6,ClassA",
            "ClassA~6,ClassB"
        ]
    }
}
```

# 取得結果
[デモ環境](https://demo.pleasanter.org) の「商談管理の例」で「顧客」と「商談」を取得した場合、下記のようにデータが取得できます。件名と顧客は サイトID: 7 から取得していますが、住所と連絡先は サイトID: 6 のマスタデータから取得しています。

```json
{
    "StatusCode": 200,
    "Response": {
        "Offset": 0,
        "PageSize": 200,
        "TotalCount": 7,
        "Data": [
            {
                "件名": "業務改善コンサルティング",
                "顧客": "働き方改革推進団体",
                "住所": "東京都立川市",
                "連絡先": "258-258-258"
            },
            {
                "件名": "文書管理ソフトウェアの販売",
                "顧客": "Webデータベース開発株式会社",
                "住所": "東京都練馬区",
                "連絡先": "789-789-789"
            },
            {
                "件名": "営業支援ツールの追加機能開発",
                "顧客": "情報共有イノベーション研究所",
                "住所": "東京都新宿区",
                "連絡先": "333-333-333"
            },
            {
                "件名": "グループウェアのクラウド環境への移行",
                "顧客": "株式会社プリザンター",
                "住所": "東京都中央区",
                "連絡先": "111-111-111"
            },
            {
                "件名": "AI技術の検証",
                "顧客": "マネジメント快適化ホールディングス",
                "住所": "東京都中野区",
                "連絡先": "123-123-123"
            },
            {
                "件名": "グラフ化ツールの販売",
                "顧客": "有限会社ノンプログラミング",
                "住所": "東京都国分寺市",
                "連絡先": "456-456-456"
            },
            {
                "件名": "食品工場内の環境モニタリングシステムの開発",
                "顧客": "株式会社オープンソーステクノロジー",
                "住所": "東京都豊島区",
                "連絡先": "555-555-555"
            }
        ]
    }
}
```

# その他
親の親や子を指定することも可能です。

親の親を指定する場合
```
ClassA~7-ClassA~6,ClassA
```

子を指定する場合
```
ClassA~~7,ClassB
```

# リンク項目を表す文字列の確認方法
「テーブルの管理」の「一覧」タブで他のテーブルの項目を追加し、ブラウザの開発者ツールを開き要素の「data-value」で確認することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78230/b5423273-4328-241e-4839-e99f9144b73a.png)
aws-vault: error: exec: aws-vault sessions should be nested with care, unset $AWS_VAULT to force

## 解決策

aws-vault を使っているとたまにでるこのメッセージ。

```bash
aws-vault: error: exec: aws-vault sessions should be nested with care, unset $AWS_VAULT to force
```

よくわからずこれが出るたびにターミナルを開き直している人もいるはず。

`unset AWS_VAULT`を実行すれば解決します。注意点としては`$`をつけないことです。

```bash
unset AWS_VAULT
```
AWS CDKでエラーを発生させたい

## 概要

[AWS Cloud Development Kit](https://aws.amazon.com/jp/cdk/) (以下、CDK) は、どの言語を使用していますか。私はTypeScriptを使用しています。TypeScriptを使用していると型のチェックをしてくれるので色々便利ですが、undefinedを考慮する必要があるので困ることがあります。

そういったときに強制的にエラーを発生させる方法について調べました。


## 困ったこと

別のスタックあるいは手動作成したDynamoDBテーブルに対してDynamoDB Streams => Lambdaといったアーキテクチャを持つスタックを新規で作成したいとします。


DynamoDBテーブルの情報を取得するために`Table.fromTableAttributes()`を使用することで`tableStreamArn`へアクセスすることができます。

```typescript: 

// 作成されているTable情報を取得
const dynamoDBTable:ITable = Table.fromTableAttributes(this, "hogehoge", {
  tableName: "hogehoge_table"
});

// IAMポリシーの作成
new Iam.PolicyStatement({
  actions: dynamoActions, // 事前に必要なアクションを定義しています。
  resources: [dynamoDB.tableStreamArn]
});

```

`tableStreamArn`はStreamが有効でない場合`undefined`が返ってくるのでもしDynamoDBのStreamsが有効でない場合はポリシー作成がおかしなことになりますし、`resources`はstringのみ許容しているため型チェックでエラーになってしまいます。

そもそもこの場合は`undefined`で返ってきた場合はデプロイするべきではないですし、Streamsを有効化するのを促す必要があります。


## どうやって解決したか

`throw new Error()`を使いました。次のようにすることで`undefined`が来た時にはメッセージと共にエラーが発生します。

```typescript: throw Error

// 作成されているTable情報を取得
const dynamoDBTable:ITable = Table.fromTableAttributes(this, "hogehoge", {
  tableName: "hogehoge_table"
});

// Stream情報が取得できるか確認。undefinedの場合はエラーを発生させる。
if (!dynamoDB.tableStreamArn) {
  throw new Error(
    "I cannot get an ARN for DynamoDB Streams. Please activate DynamoDB Streams from the console for manual creation or from the CDK for CDK creation."
  );
} else {
// undefinedのケースは除外されるため型制約に引っ掛からずに処理できる。
  new Iam.PolicyStatement({
    actions: dynamoActions,
    resources: [dynamoDB.tableStreamArn]
  });
}

```

```bash
/Users/hoge/Document/.../s3_stack.ts:68
      throw new Error(
            ^
Error: I cannot get an ARN for DynamoDB Streams. Please activate DynamoDB Streams from the console for manual creation or from the CDK for CDK creation.
    at new S3Stack 
....
```

また、[Annotations](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.Annotations.html#addwbrerrormessage)を使うとinfoやwarning, errorを見やすく出力できます。

```typescript
if (!dynamoDB.tableStreamArn) {
  Annotations.of(dynamoDB).addError(
    "I cannot get an ARN for DynamoDB Streams. Please activate DynamoDB Streams from the console for manual creation or from the CDK for CDK creation."
  );
} else {
  new Iam.PolicyStatement({
    actions: dynamoActions,
    resources: [dynamoDB.tableStreamArn]
  });
}

```

CDKでログを出すこと自体あまりないので正直使うことはないですが、逆にユーザが設定したエラーだとわかるのでそう言う意味ではこちらを採用する価値があるかもしれません。

```
[Error at /S3Stack/StreamArnFromTableName] I cannot get an ARN for DynamoDB Streams. Please activate DynamoDB Streams from the console for manual creation or from the CDK for CDK creation.
```


## 参考
https://github.com/aws/aws-cdk/discussions/21253

https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.Annotations.html#addwbrerrormessage
簡単！CORSの設定

# バックエンドにリクエストを送れないのはCORS設定できてないからかもしれない

例えばReact + Goで開発をしているとします。React`localhost:8000`でGoは`localhost:8080`で動いているとします。

ReactからGoへリクエストを投げてみます。すると次のエラーが出ます。

![Screen Shot 2022-10-30 at 14.28.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/949101/ccbf9810-613e-af38-f4ea-1fc6c2ce0eaa.png)


# オリジンが異なるときはCORSの設定が必要

エラーの原因はReactとGoでオリジンが違うことです。今回の場合で言うと

React：http://localhost:8000
Go：http://localhost:8080

がオリジンです。

違うオリジンにリクエストを送るにはCORS(Cross-Origin Resource Sharing)の設定が必要です。


具体的にはフロントエンド側でモードの設定、バックエンド側でヘッダーを付与する必要があります。


## モードの設定

クロスオリジンリクエストであることを指定します。クッキーを送信する場合は
`credentials: "include"`とします。

```js
fetch("http://localhost:8080/ping", {
      mode: "cors",  // クロスオリジンリクエストであることを指定
      credentials: "include",
    })
```

## ヘッダーを付与


`Access-Control-Allow-Headers`...許可するヘッダー

`Access-Control-Allow-Origin`...許可するOrigin

`Access-Control-Allow-Methods`...許可するメソッド

`Access-Control-Allow-Credentials`...クッキーを使うかどうか

今回はこのような感じで設定しました。

```go
func ForCORS(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Access-Control-Allow-Headers", "*")
		w.Header().Set("Access-Control-Allow-Origin", "http://localhost:8000")
		w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS")
		w.Header().Set("Access-Control-Allow-Credentials", "true")

		if r.Method == "OPTIONS" {
	    	w.WriteHeader(http.StatusOK)
			return
		}
		next.ServeHTTP(w, r)
	})
}
```

以上の設定で無事、フロントエンドからバックエンドへリクエストを送れるようになりました。
dbtのmacroを使用したクエリでsqlfluffのL001エラーが発生した場合の対処法

## 何が起こったのか？
dbt coreの以下のmacroを使用したクエリで、sqlfluffのリントを実行したらエラー`L001 | Unnecessary trailing whitespace.`が発生した。不要なスペースは無いし、インデントはおかしくないのになぜかエラーが出る。

```sql:models/test_query.sql
select
    column_1,
    column_2,
    {{ test_macro() }} as column_3 -- このマクロでL001のエラーが発生する
from source
```

```sql:macros/test_macro.sql
{% macro test_macro() %}
'test'
{% endmacro %}
```


```bash
# sqlfluffのリントを実行
$ sqlfluff lint models/test_query.sql
== [models/test_query.sql] FAIL                                                                   
L:   4 | P:   1 | L001 | Unnecessary trailing whitespace.
All Finished 📜 🎉!
```

試しにsqlfluff fixを実行したところ以下に自動修正されたが、これではインデントがあっていない。
```sql
select
    column_1,
    column_2,
{{ test_macro('test') }} as column_3
from source

```

### エラーが発生した環境
```
dbt-bigquery==1.1.0
dbt-core==1.1.2
sqlfluff==1.3.2
```

`.sqlfluff`の設定
```yml:.sqlfluff
[sqlfluff]
dialect = bigquery
templater = jinja

[sqlfluff:templater:jinja]
load_macros_from_path = macros

[sqlfluff:indentation]
indented_joins = True
indented_using_on = True

[sqlfluff:rules]
tab_space_size = 4
max_line_length = 80
indent_unit = space
comma_style = trailing
single_table_references = consistent
```


## 対応
以下のように、macroの`macro, endmacro`にダッシュ`-`が入れることでエラーが解消した。ちなみに、ダッシュを入れることにより、compile後のクエリも構成が少し変わるので確認すると面白い。
```sql:macros/test_macro.sql
{% macro test_macro() -%} -- 末尾にマイナスを入れる
'test'
{%- endmacro %} -- 頭にマイナスを入れる
```


## エラーが発生した原因
TBD
jinajaではダッシュをスペースの制御に使っているとこのこと。その関係でエラーが発生していると思われる。ただ、sqlfluffがどのようにマクロをパースして、上記エラーがでるのかは現在調査中。

## 参考資料
- [Stack Overflow Dashes in jinja templates](https://stackoverflow.com/questions/19401106/dashes-in-jinja-templates)
- [jinja whitespace control](https://jinja.palletsprojects.com/en/3.1.x/templates/#whitespace-control)
Azure Media Player API の使い方

# はじめに
Azure Media Player の API の使い方のメモです。


# Azure Media Player について
Azure Media Player とは、名前の通りで、Azure が提供している Media Player（Web ビデオプレイヤー）です。
[Azure Media Player の概要](https://learn.microsoft.com/ja-jp/azure/media-services/azure-media-player/azure-media-player-overview)

DASH、HLS、Smooth Streaming などのプロトコルへの対応や、AES などの動的暗号化も可能です。
また、API が用意されているので、Web アプリに埋め込んで利用することもできます。

# 試した内容
[App Service](https://learn.microsoft.com/ja-jp/azure/app-service/overview)  でホストする Web アプリを用意し、Azure Media Player の API を利用し、動画を再生すると開始地点から 10 秒後に再生されるように実装しました。
なお、ストリーミング配信は [Azure Media Services](https://learn.microsoft.com/ja-jp/azure/media-services/latest/media-services-overview) で VOD 配信したものを、Azure Media Player 内で再生しています。


## Azure Media Player API について
下記が試した Azure Media Player API です。
[Azure Media Player API のメソッド](https://learn.microsoft.com/ja-jp/azure/media-services/azure-media-player/azure-media-player-api-methods)

## 実行したコード
下記が試したコードです。
```javascript:sample
<script>
    var myOptions = {
      autoplay: true,
      controls: true,
      width: "640",
      height: "400",
      poster: ""
    };
    var myPlayer = amp("azuremediaplayer", myOptions);
    myPlayer.src([{ src: "//<YourStreamingURL>", type: "application/vnd.ms-sstr+xml" }, ]);
    myPlayer.addEventListener(amp.eventName.play, startTime);
    function startTime() {
      myPlayer.currentTime(10);
      myPlayer.removeEventListener(amp.eventName.play, startTime)
    }
</script>
```


再生すると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679419/84324f53-a426-8329-c07d-13c3b6d6da9d.png)

10 秒から再生されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679419/8ae5009a-ab25-5ee1-e037-5db5436ede14.png)


ポイントとしては、currenTime() を利用して動画の開始時間を指定するには、スクリプト内で Azure Media Player の src を設定した直後に、currentTime() を設定する必要があるっぽいです。


# まとめ
他にも色々メソッドも用意されているので、便利に利用できそうですね。
下記が Azure Media Player の API の詳細になりますので、ご参考になれば幸いです。
https://amp.azure.net/libs/amp/latest/docs/index.html
OpenAPI 定義からyup定義を生成するRust製CLIを作ってみた

https://github.com/igtm/openapi-yup-generator

OpenAPIv3の定義ファイルからyupのバリデーション定義を自動生成してくれるCLIを作ってみました

# 使い方

```sh
openapi-yup-generator -f openapi3.yaml -o yup-defs.js
```

すると以下のようなjsファイルが生成される

```js
/* tslint:disable */
/* eslint-disable */
//DO NOT EDIT MANUALLY

import { object, string, number, date, array, bool } from 'yup';

export const LightingSummary = object({
  zones: array().of(object({
    id: string().optional(),
    name: string().optional(),
    deviceId: number().integer().optional(),
    deviceType: string().optional(),
    zone: string().optional(),
  })).optional(),
  zoneStatus: array().of(object({
    id: string().optional(),
    name: string().optional(),
    lastUpdate: date().optional(),
    level: number().integer().optional(),
  }).label('the status of the lighting zone.')).optional(),
}).label('ok');

export const LightingZone = object({
  id: string().optional(),
  name: string().optional(),
  deviceId: number().integer().optional(),
  deviceType: string().optional(),
  zone: string().optional(),
});
```

# インストール方法

```shell
curl -sfL https://raw.githubusercontent.com/igtm/openapi-yup-generator/master/install.sh | sudo sh -s -- -b=/usr/local/bin
```

(M1Mac等で権限でエラーになる場合は 先頭に `sudo` をつけて実行してみてください)


# 作った背景

最近APIに[goa](https://github.com/goadesign/goa)、FrontにReact(Typescript)、FrontのAPIまわりは [openapi-generator-cli](https://github.com/OpenAPITools/openapi-generator-cli)で自動生成させる構成をよく使っていました。
ただ、SPAを採用するとAPIとFrontの2重でバリデーション定義を管理しないといけないのが辛いので、openapi3.yamlからyupの定義ができたら良いなと思っており今回Rustの勉強も兼ねて作ってみました。

yupでよく使う定義は対応したつもりですが、これも使いたいなどあれば気軽にissue/PR化していただければなと思います。
PHPのDB接続の時に、SQLSTATE[HY000] [2002] No such file or directory.と出た時の対応

# 環境
+ PC：Mac
+ サーバー：ロリポップサーバー
+ 言語：PHP8.1.7 

# 内容
ログインフォームなどで、DBに接続しようとすると、
```
SQLSTATE[HY000] [2002] No such file or directory
```
というエラーが出た。MySQL接続エラーらしい。

# 解決策
mysql:の後の空白を消してあげる。
```Login.php
$dsn = sprintf('mysql: host=%s; dbname=%s; charset=utf8', $db['host'], $db['dbname']);
```
消した後
```Login.php
$dsn = sprintf('mysql:host=%s; dbname=%s; charset=utf8', $db['host'], $db['dbname']);
```
これで無事にmysqlに接続ができ、ログインできました。

# 参考記事
https://xiyuan.jp/php/272/
SESでテストばかりさせられる

SESが悪いとされる理由に「テスト作業ばかりさせられる。」
という記事を見ることがあります。
今回はその事に関しておじさんエンジニアの個人的考えを書きたいです。

自分がこの業界に入った時はあまりそういう感覚はなく、
とにかく与えられたタスクをこなし、少しでも長く働かせて貰いたいと思い、
言われた事はなんでもするようにしていました。

それでも短期間で契約を解除されたり、
せっかく現場の人と打ち解けたのに、突然今月で終わりと告げられると、
かなりショックが大きかったです。

ちょっと話がズレましたが、
例えば経験が浅い為、開発作業はさせて貰えず、
テストばかりさせられるというケースについてです。

確かに辛いという考えもわかりますが、テストは大事です。
今後開発をさせて貰えるのであれば、テストをする事で、
そのシステムの使い方を理解するのはとても良い事です。

私などは開発するシステムの使い方をろくに理解せず（理解する時間を貰えず）
開発を始めるので、この機能ってなに？どういう目的？という事がよくあります。
お客さんとしては、そんなの分かって当たり前みないな感覚でいらっしゃるので、
それが作業遅延に繋がったりして苦労したりします。

なので、ただ記載された手順通りにテストするだけでなく、
このシステムってどういう物で、この機能はなんであるのか？
どういう風なプログラムで作っているのか？
という事を考えてテストするととてもスキルアップに有効だと思います。

あとは、テストでバグを見つけた時に、
そのバグを修正する為に必要な情報をしっかり残せる人はとても評価が高いと思います。
これは開発者側の意見ですが、バグ修正する場合に手順が曖昧で、
再現できなかったり、何が問題か分からなかったり、修正までにと時間がかかる場合があります。
意外とバグを発見した担当者によって書き方が違うので、
その辺が分かり易かったり、バグ発生時の条件やログなどを残すことを意識されている方は、
このテスターさんはデキル！！と思ったりします。

ちなみに、おじさんエンジニアとしてはテスト作業で同じお金を貰えるなら、
テスト作業は大歓迎と思ったりします。
Springの＠Transactional　備忘録

### ＠Transactionalとは？
Springではクラスやメソッド、及びインタフェースに付ける＠Transactionalというアノテーションを提供している。
＠Transactionalを付けたら、*宣言的トランザクションの管理対象になりトランザクションの属性が付与される。
*宣言的トランザクション：AOPを利用し、コードの外部からトランザクションの機能を付与し属性を指定できるようにすること。⇔明示的トランザクション
### 使用例
#### サービスレイヤー
＠Transactionalはどこでも使えるが、一般的にビジネスロジックに該当するサービスレイヤーのメソッドに書くのが良い。なぜなら、DBから取得したデータを取り扱うところがサービスレイヤーだからだ。
```
@Transactional(readOnly = true)  //★１
@Service
public class WordsService {
    private final WordsRepository wordsRepository;
    
    public List<WordHomeResponseDto> getAllWords() {
        List<Words> result = wordsRepository.findByDeleteFlg(GlobalVariable.FALSE);
        List<WordHomeResponseDto> words = result.stream().map(WordHomeResponseDto::new).collect(Collectors.toList());

        return words;
    }

    @Transactional　//★２
    public void edit(WordEditRequestDto requestDto) {
        Words word = wordsRepository.findByWordId(requestDto.getWordId()).orElseThrow(() -> new IllegalArgumentException("該当することばがありません。"));
        word.update(requestDto.getWordName(), requestDto.getWordPronunciation(), requestDto.getWordMeaning(), requestDto.getWordExample());
    }
}
```
★１：クラスレベルに@Transactionalアノテーションを付けるとその下のメソッドにも全て適用される。つまり`getAllWords()`にも@Transactionalが付与されている状態。
★２：★１のように、クラスレベルには全てのメソッドに適応される読み取り専用(readOnly = true)@Transactionalを宣言し、`insert, update, delete`をする場合は別途@Transactionalをメソッドに付与するのが良い。微妙な性能の改善ができるようだ。
#### テストコード
```
@Transactional  //★
@DataJpaTest
public class WordsRepositoryTest {
    @Autowired
    private WordsRepository wordsRepository;

    @Test
    public void updateWord() {
        //given
        String wordName = "test-word-update";
        String wordPronunciation = "test-pronunciation-update";
        String wordMeaning = "test-meaning-update";
        String wordExample = "did you update your test code?";

        List<Words> originalWordList = wordsRepository.findAll();
        Words word = originalWordList.get(0);

        //when
        word.update(wordName, wordPronunciation, wordMeaning, wordExample);
        List<Words> editedWordList = wordsRepository.findAll();
        word = editedWordList.get(0);

        //then
        assertEquals(word.getWordName(), wordName);
        assertEquals(word.getWordPronunciation(), wordPronunciation);
        assertEquals(word.getWordMeaning(), wordMeaning);
        assertEquals(word.getWordExample(), wordExample);
    }
}
```
@Transactionalをテストに付けたら、テストコードで行ったDBに対する作業(例えば登録・更新・削除)をテスト完了後ロールバックしてくれる。但し、`auto_increment`や`sequence`で増加した`id`はトランザクションの管理対象ではないので、ロールバックされない。
【実務6ヶ月】【AWS】期限を指定してS3からオブジェクトを取得する方法

EC2からもDCからもS3にアクセスする方法を備忘として記録しておきます。

# プライベートからS3にアクセスする方法

プライベートサブネットにEC2を設置し、S3を作成します。次にEC2を設置しているVPC内にゲートウェイ型のエンドポイントを作成します。


![スクリーンショット 2022-10-30 11.32.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1828177/b43daf41-6342-5154-2b19-765de67caf1d.png)

[VPC]-[エンドポイント]-[サービス]
エンドポイントの検索欄に「s3」と入力します。画面右にスライドさせると「タイプ」の一覧に「Gatteway」が表示されている「com.amazonaws.ap-northeast-1.s3」を選択します。


![スクリーンショット 2022-10-30 11.42.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1828177/36ae1431-7ef4-6f47-7cd2-df3e84e0eed5.png)

エンドポイントを作成するVPCを選択しどのルートテーブルを使用してS3にアクセスさせるかを選択します。


![スクリーンショット 2022-10-30 13.53.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1828177/1dbc6038-dd96-6296-ff68-f53954ac917a.png)

![スクリーンショット 2022-10-30 12.15.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1828177/ac60f899-df26-e49f-9d85-735574a20217.png)

ルートテーブルにエンドポイントが追加されているか確認します。

これでプライベートにあるEC2がゲートウェイ型エンドポイントを通ってS3にアクセスできるようになりました。

専用のIAMのユーザーを作成したり、バケットポリシーでアクセスを制限します。
こちらに関しては後日、書きたいと思います。（方法は参照記事にて）

# 期限を指定してオブジェクトを取得する方法
一時的にアクセスを許可して、指定した人にアクセスをさせる場合は署名付きURLを使用します。

一時的にアクセスを許可して、指定した人にオブジェクトを取得させる場合は署名付きURLを使用します。

![スクリーンショット 2022-10-30 13.05.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1828177/48492d24-02a4-2926-0ee9-521abb449e9d.png)
[Amazon S3]-[バケット]-[任意のバケット-チェックを入れる]-[アクション]-[署名付きURLで共有]

![スクリーンショット 2022-10-30 13.07.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1828177/aeda9d3a-63a4-0cca-d466-3887fd917d6e.png)

そうすると時間を指定することができます。

日数で指定することはできないのか・・

日数単位で指定したい場合はAWSCLIを使用します。

-AWS CLI コマンド リファレンス S3 署名する　より抜粋-

```
aws s3 presign s3://DOC-EXAMPLE-BUCKET/test2.txt \
    -- 有効期限 604800
```


有効期限　604800!?
なんのことかと思いましたがこれ秒数だったんですね、、

・7日間の場合
　60秒×60分＝3600秒
　3600秒×24時間＝86,400秒
　86,400秒×7日＝604,800秒

いやいや秒数でやると計算も面倒だし、日にちを指定できないかと調べてみるとありました。

-Amazon S3 署名済み URLAWS SDK for PHPバージョン 3から抜粋-
```
//Creating a presigned URL
$cmd = $s3Client->getCommand('GetObject', [
    'Bucket' => 'my-bucket',
    'Key' => 'testKey'
]);

$request = $s3Client->createPresignedRequest($cmd, '+20 minutes');

// Get the actual presigned-url
$presignedUrl = (string)$request->getUri();
```
サポートに問い合わせてみると
($cmd, '+20 minutes')の「minutes」を「day」もしくは「days」にすることで日数で指定できると回答が返ってきました。

1日なら1day、2日なら2days


### 他にもこんな記事書いています！
https://qiita.com/mmurm/items/0b763a5168159934b778

https://qiita.com/mmurm/items/068f8510019016880f5f

https://qiita.com/mmurm/items/1d42ab3d47dd85e4efbd


# 参照記事
【AWS】S3 バケットへのインターネット経由でのセキュアなリモートアクセス設定【バケットポリシー】
https://go-journey.club/archives/13053#インターネット経由でアクセスしてくる場合のセキュアな設定


【ゲートウェイ VPC エンドポイント】EC2からプライベート経路でS3などのAWSサービスへアクセスする
https://blog.serverworks.co.jp/2022/07/08/122037#ゲートウェイ-VPC-エンドポイントの作成今回選んだタイプ

AWS CLI コマンド リファレンス
https://docs.aws.amazon.com/ja_jp/cli/latest/reference/s3/presign.html

Amazon S3署名済みURLAWSSDK for PHPバージョン3
https://docs.aws.amazon.com/ja_jp/sdk-for-php/v3/developer-guide/s3-presigned-url.html
VS CodeでHTML/CSSを作成後、プレビューができない時の対処法

問題:
VSCodeにてHTML/CSSのプレビューが以下のようになり、上手くできませんでした。
・拡張機能LiveServerを用いて開いた場合
コードを変えてもリアルタイムで反映されず、ブラウザを更新しても'Live Reload is not possible as body or head tag or defined tag is missing in HTML'とエラーメッセージが表示され、更新されない
・HTMLテキストをブラウザで直接開いた場合
ブラウザを更新しても更新されない

対処法:
VSCodeの設定を開き、Auto Saveの項目をafterDelayに変える

以上でいずれの場合も解決しました。ブラウザで直接開いた場合も更新されなかったため、HTMLテキストが変更されるたびに保存されていないのでは?と考え設定を確認したところ、案の定Auto Saveがデフォルトではoffになっていたので設定を変えてみたところ問題が解決しました。

ひとこと：
YouTubeのチュートリアル動画を見様見真似でいじっていたのですが、動画と同じように動作せず、この問題に対する対処法を説明している記事等も日本語・英語のいずれでも見かけなかったため、初の寄稿をする運びとなりました。記事がもっと読みやすくなるレイアウトや、検索に引っかかりやすい工夫等あると思うのですが、その辺は後々勉強ということで:v:
SESで働くことで意識している事

最近はSESで働くことで意識しておくことがいくつかあります。
あくまで、おじさんエンジニアの私の考えです。

SESはシステム開発をする上で人手が足りないときに呼ばれて、
使えないと判断されたり、仕事がなくなれば契約解除される、
言わば使い捨てのような職業と考えています。

そのような職業と自覚した上で、どう考えて働いているかですが。
私は今の現場で少しでもスキルを吸収しようと考えています。

例えば、自分が未経験のプログラム言語を使う現場なら、
そのプログラム言語を覚えたり、
（あまり未経験の言語を経験させて貰える事はないですが）
プログラムの作り方を吸収したりします。教科書には載ってないような、
実践的なプログラムを見ることができるので、かなり勉強になります。
また、設計書を作成する仕事なら、このシステムを開発する場合、
どんな設計書があれば開発ができるかを理解したり。
また、チーム開発の一員であれば、チームのマネジメントはどうすれば良いか、
どうすれば失敗するかなど、実践的なマネジメントを勉強できます。
また、使ったことがない開発環境、ツールなどの経験もできます。

そう考えれば仮に短い時間で契約解除されたとしても経験として身に付くと思います。
経験が浅いときは、すぐに契約解除になる事は良くあるので、
すこしずつ経験を増やして長く契約して貰えるようになると良いと思います。

こういう事を考えるようになったのは、この業界を長く経験してきたからかなと思います。
若いときだったら、目の前のタスクをこなすのに精一杯でそんな余裕はなかったなと。
Laravel よく使う関数の返り値早見表

## 概要
Laravelを用いて開発をしている際に関数の返り値とその型がどのようなものであるかを毎回調べるのがめんどくさいので、よく使う関数については早見表にしました。
ご参考になさってください。

## 返り値早見表

|  関数名  |  返り値  |  型  |
| ---- | ---- | ---- |
|  get  |  コレクション（中身はModelのオブジェクト）  |  Illuminate\Database\Eloquent\Collection  |
|  all  |  コレクション（中身はModelのオブジェクト）  |  Illuminate\Database\Eloquent\Collection  |
|  pluck  |  コレクション  |  Illuminate\Support\Collection  |
|  find  |  Modelのオブジェクト  |  対象のモデルクラス or null  |
|  first  |  Modelのオブジェクト  |  対象のモデルクラス or null  |
|  create  |  新規作成されたレコード(Modelのオブジェクト)  | 対象のモデルクラス |
|  save  |  true or false  | bool |
|  insert  |  true or false  | bool |
|  insertGetId  |  新規作成されたレコードのid  | int |
|  update  |  更新されたレコード数  | int |
|  upsert  |  影響されたレコード数  | int |
|  delete  |  削除されたレコード数  | int |
 
## 参考
[Laravel API](https://laravel.com/api/9.x/[Global_Namespace].html)
【CocoaPods】インストール時に警告が出る

# 警告文
![スクリーンショット 2022-10-30 13.20.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/00cd8676-3996-a9cd-ac83-358b1cebce81.png)

```
[!] The `TwitterSearch [Debug]` target overrides the `OTHER_LDFLAGS` build setting defined in `Pods/Target Support Files/Pods-TwitterSearch/Pods-TwitterSearch.debug.xcconfig'. This can lead to problems with the CocoaPods installation
    - Use the `$(inherited)` flag, or
    - Remove the build settings from the target.

[!] The `TwitterSearch [Release]` target overrides the `OTHER_LDFLAGS` build setting defined in `Pods/Target Support Files/Pods-TwitterSearch/Pods-TwitterSearch.release.xcconfig'. This can lead to problems with the CocoaPods installation
    - Use the `$(inherited)` flag, or
    - Remove the build settings from the target.
```

# 解決方法
① プロジェクトを選択する
② ターゲットを選択する
③ 「Build Setting」を選択する
④ `OTHER_LDFLAGS`と検索する
⑤ 「Other Linker Flags」に`$(inherited)`を追加する
![スクリーンショット 2022-10-30 13.22.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/b2e93be9-41e6-69af-07d9-05ad2b2bb8a4.png)

# おわり
これが何かはわかってませんが、警告文は出ないに越したことはないので修正しておきました
SESの仕事について

私はSESで20年程働いています。
100％客先常駐で、今まで10社以上で仕事をしています。

今回はSESについて思う事を書きたいと思います。
ちなみに私の経験であり、他の方に当てはまるわけではないです。

SESって世間では悪いイメージもありますが、私はSESは悪くないと思っています。
SESの良い点は色々な現場で開発の経験ができる事と思っています。
なので、長くても2～3年で新しい現場に代わる方がよいと思っています。
1つの現場に長くいると、いざ新しい現場に変わった時に、
仕事の仕方が180度変わる事があり、ついていくのが大変と思います。

後は気楽な点です。（責任感をもって仕事をしていますが。）
仕事が出来ないとして契約が終わっても次の仕事で頑張ればよいので、
大きな責任を負わされる事が少ないです。
（請負か派遣の違いにより変わってはきますが。）

SESの悪い点は上流工程の仕事ができない事と思います。
私の場合は詳細設計～結合試験以外の経験はほぼ0です。
お客さんによっては他の工程も担当させて貰える場合もありますが。

ただ、上流工程をする事を目指していない場合は特に問題ないです。
この仕事をしていると上流工程の仕事をすることが良いとされていますが、
必ずしもそうであるとは言えません。技術で一生食べていく事もできると思いますし。

また、将来が不安な所と思っています。
将来不安な点は色々な現場を見ていても50代を超えて現場で働いている方を余り見ない所です。
自分が50代から定年まで働けるのか？というのはかなり不安です。
現場では若い方も多いので、技術で上回っていたとしてもいつ迄雇ってもらえるか不安です。
この問題は今もずっと悩んでいる所です。
参照型とプリミティブ型の比較は注意しよう

意識しておかないと間違いに気づかない可能性があるので、備忘録として保存する。

## 比較する場合の注意
```
int num = 100;
int anotherNum = 100;

Integer refNum = 100;
Integer refAnotherNum = 100;

//数値自体を比較するため、TRUE
System.out.println(num == anotherNum);
//プリミティブ型のラッパークラスはある範囲でインスタンスをキャッシュする。
//その範囲は-128～127なので、100の場合は上手くいってしまう。
System.out.println(refNum == refAnotherNum);
```

実は参照型は、インスタンス同士を比較するので`==, <=`などで比較しても正しい結果になるとは限らない。
それは知っている人が多いと思うが、逆にうまくいってしまうパターンもある。

特に「参照型」と意識しないと忘れやすいIngtegerは注意しておきたい。
「数値」という意識が主になってしまって、不等号で比較しないように気を付けよう。

## Integerを比較する場合
値の大小比較は、`compareTo`を使用
同値の比較は`equals`を使って比較しよう。

## 参考
https://www.jmuto.info/2022/05/java-integer-comparison.html
VScodeのMarkdown編集時に、複数行を一括で引用にする。（文頭に>つける）

こんにちは

# やりたいこと
VScodeのMarkdownで、複数行を一括で引用形式にしたい。
つまり複数行の文頭に一括で>をつけたい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100646/5577abc9-3ae0-262e-895c-5a6977981767.png)

これ、1行づつ文頭に>を描くのは面倒ですね。

# 解決方法
VScodeでは、`Ctrl` + `Alt` + `矢印キー` で、縦方向に選択ができます。
そこで、`Ctrl` + `Alt` + `↓`で1行目の文頭から縦方向に一括選択し、`>`を打てば、一括で引用形式になります。
![2022-10-30_12h55_55.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100646/be3b9fe2-a0dd-17b1-df47-827c5b52eb7d.gif)

# 余談
ところでQiitaにはこの機能が無いので、複数行を一括で引用したいときには困りますね。

# おわり
終わりです。お疲れ様でした。
OSSのRTSゲームエンジンをC#(WPF)で作り始めて四か月と少し。思ったこと。

# なぜ開発を開始したか
フリーゲーム界で有名なゲームエンジン「ヴァーレントゥーガ」のexe及びツールが
何者かに削除されるという事件があり、新規タイトル開発が正規の手続きでは出来なくなった。
……じゃあ新しく「〇〇トゥーガ」を作ったら良いんじゃね！？
せっかくだし、ローグライク要素や、技術・内政ツリーといった要素も含めて、
MITライセンスの最強のRTSゲームエンジンを作るかあ～～～～。

# 現在の進捗その１（顔絵はAIさんに描いてもらいました
https://twitter.com/fluffy_sheep_jp/status/1564567963897696256

# 現在の進捗その２
https://twitter.com/fluffy_sheep_jp/status/1585599873071472640

# 思ったこと。技術選定編
・せっかくのゲームエンジン、何を使って実装する？
Unityか、C++か、それともRust？
可視性、保守性、速度、いろいろと考慮すべきことはあるが……
・将来的にはスマホでも出来るようにしたいなあ、じゃあ、Unityか？
でもUnityプロジェクトでOSSって聞いたことがない。あるのか？
それに、ゲーム起動時にロゴが出るのがなあ……

少し調べる。うむ、.NET6だとクロスプラットフォーム開発が出来るんだとか。
MAUIというアーキテクチャーだとスマホ対応も可能！？
一番得意なC#で開発出来るなら、それで行くしかないか（現実的に考えて

# 思ったこと。インタープリタ編
・「ヴァーレントゥーガ」では独自言語からなる式をファイルに記載することで、内政やイベントを実装出来る。
それと同じことをするには、やはりコンパイラ、というかインタープリタを作るしかないようだ。

というわけで単純な木構造のインタープリタを作った。
しかし。
躓いた。
何故か。

メッセージウィンドウが原因である。
メッセージウィンドウはユーザーのクリックによって、次のメッセージやイベントが読み込まれるのだが……
インタープリタというものは、こういうユーザーとの対話を想定していないのである！
※例えば、ユーザーからの入力次第で次の処理に移ったり、処理そのものを変更するようなコンソールプログラムがありますかな？
scanf？まあ確かにそれはそうなんだが、GUI(WPF)にscanfはありませんぞ。
まあそういうわけです……

が、掲示板に泣く泣く開発停止を報告したら。
助けてくれる人がいた。
おかげで開発を続行することが出来た。
ありがとう、Yutaka-Sawada氏。

# 思ったこと。設計編。
・基本的に「ヴァーレントゥーガ」と同じ動きが出来れば良い……。
だからある意味では楽。
何しようかな、と思ったら「ヴァーレントゥーガ」を使ったゲームを起動すれば良いだけだから。
・とはいえ、独自色も出すから能天気にはしていられない
例えば要望にあったゲージ付き必殺技。
どう実装すれば良いかは思いつくが、これはゲームではなくゲームエンジンなのである。使われなければ意味がない。
ゲージ付き必殺技の呼び出し方（というかインターフェース？）について悩む日々。

# 思ったこと。負債編。
・日常生活を送りながらの開発である。
どうしても適当な部分が出る。
（というか適当であることを許せないとソロでの開発は厳しかろう。
・適当な部分が負債となって拙者に襲い掛かって……はいないが、いずれそうなるだろうなあ。
・単体テスト？自動テスト？でもGUIっすよ……。関係ない？ごもっとも。とはいえテストコード書くの大変だなあ。

# 思ったこと。やる気編。
・夏バテでやる気が出ない
・サウナ行くか
・そうだ！Discordで実装を配信しよう！
・見られているというプレッシャーにより、実装が進む

# まとめ
・お体に　お気を付けてね　OSS（俳句　

・ここまで読んで頂きありがとうございます。

# このゲームエンジンのリポジトリ
コミッター歓迎！簡単なC#なのでgithubデビューにもちょうどヨシ！

https://github.com/fluffySheepInX/WPF_Successor_001_to_Vahren
RN52にモノラルスピーカーを繋ぐ(1)

# 概要
BluetoothモジュールのRN52を使ってヘッドセットを作りたい。
RN52のスピーカー出力はステレオで、作りたいヘッドセットはモノラルなのでミキサーが必要。
まずはスピーカー出力を確認するところからスタートし、ミキサーと差動増幅回路でモノラルのシングルエンド信号に変換するところまでやってみる。

# 実行環境
シミュレーション：LTSpice XVII(x64) 17.0.35.0

# RN52のスピーカー出力
## 回路
RN52のメーカー推奨回路は下記。
※RN52のデータシートより引用。
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321914/9b8cbaf4-bc92-f71b-8e76-25a00d88e758.png)

RN52をBluetoothスピーカーとして使うだけなら下記の回路で動作した。
動作確認なので電源のパスコン等も省略している。
手持ちのLEDは高輝度タイプだったので、電流制限抵抗を大きめにして輝度を落としている。
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321914/fe9adb4a-3196-10f7-35dd-366a7acf9f67.png)
3.3Vに電源を接続し、SW1を押すことでRN52が起動、RED_LEDが一度点灯し、BLUE_LEDが点滅する。
SW1を長押しするとBLUE_LEDとRED_LEDが交互に点滅するペアリングモードに入る。
ペアリングモードに入った状態で、スマホ等でBluetooth機器を検索すると「RN52-XXXX」（XXXXはMAC IDの下4桁）という名前でヒットするのでペアリングする。
※上記の機器名は工場出荷時の初期値。変更している場合は変更後の名前で表示される。

ペアリングが完了するとRED_LEDの点滅に変わる。

## スピーカー出力の波形
作製した回路をスマホにペアリングして適当な音楽を流し、SPKRの出力に8Ω・500mWのスピーカーを接続して両端の電圧をそれぞれオシロスコープで確認。
※スマホ側の音量設定は最大。
CH1 : SPKR_L+ (黄)
CH2 : SPKR_L- (青)
![2022-10-29 (6).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321914/84ec0492-d560-68d7-108c-1e2497e486e5.png)
出力波形は710mV付近を中心とした差動信号になっていた。
8Ωのスピーカーを繋いでみたところ、かすかに音声が聞こえる程度だった。

## ステレオ⇒モノラル変換の方法を検討
まずはSPKRの右と左の波形を比較してみる。
ほぼ同位相の波形となっており、どちらも常に正電圧なので、ステレオ⇒モノラル変換は抵抗合成で問題なさそう。
![2022-10-29 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321914/53dcb479-6142-e30f-b918-981c4df7bbc8.png)

なので回路構成としては、
・左右の+同士、-同士を抵抗で合成してステレオ⇒モノラル変換する
・扱いやすくするために1倍の差動増幅回路でシングルエンド信号に変換
・スピーカーアンプで信号増幅

# シミュレーション
音声信号の扱いに慣れていないので、回路を組む前にシミュレーションで確認。

## ステレオ⇒モノラル変換
まずはステレオ⇒モノラル変換回路をシミュレーションしてみる。
結果は足して2で割った波形になりました。こんなもんでいいかな。
![2022-10-29 (7).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321914/b1d7ee4b-ba36-7c7e-ab99-c07c09c321f7.png)

次に差動増幅回路。オペアンプはNJM4580でシミュレーション。
ステレオ⇒モノラル変換で10kΩ相当の抵抗が付いていることを考慮して回路を組む。
※20kΩの並列接続なので、合成抵抗は10kΩ。
結果は+と-の差分と等しくなっているので計算通り。
![2022-10-29 (10).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321914/5303beb3-56c5-a1c5-cf23-af78e0d46228.png)

最後にスピーカーアンプ。
…と思ったが、デジタル信号に偏った私の知識ではすぐに作れそうにないので別記事で。
面倒になったらオーディオアンプICにお任せするかも。
アナログ回路って難しいなぁ…