OmniAICreator/Qiita-1.07M huggingface dataset:
requestAnimationFrameのループはPromiseに変換すると簡潔に書ける（かも）

[requestAnimationFrame()](https://developer.mozilla.org/ja/docs/Web/API/Window/requestAnimationFrame)は、フレームレートを考慮してコールバック関数を実行する関数です。主にcanvasなどのアニメーションで使います。

この`requestAnimationFrame`ですが、コールバックに渡した関数は1度だけ実行されます。

```ts
globalThis.requestAnimationFrame(()=>{
  // この部分は1度だけ実行される
  console.log("raf!");
});
```

これをループさせて、複数回実行させるためには、例えば[このサイト](https://liginc.co.jp/web/js/130758)では以下のような方法が紹介されています。

```ts
const startTime = Date.now(); //描画開始時刻を取得
(function loop(){
  globalThis.requestAnimationFrame(loop);
  console.log(startTime - Date.now()); //経過時刻を取得
})();
```

名前付きの即時関数を使って実装されているのですが、ループしていることが一目では分かりづらくなっています。
ループを抜けるための条件分岐が足されると、更に読みづらくなってしまいそうです。

ループを簡潔に書くためにはどうしたらよいでしょうか。

## requestAnimationFrameをPromiseに変換する

requestAnimationFrameをPromiseに変換することで、ループが書きやすくなります。

```ts:requestAnimationFrameをPromiseに変換
function animationFramePromise() {
  return new Promise<number>((resolve) => {
    globalThis.requestAnimationFrame(resolve);
  });
}
```

Promiseに変換した後は、ループを以下のように書くことができます。

```ts
const startTime = Date.now(); //描画開始時刻を取得
while (true) {
  await animationFramePromise(); // この行で、次の更新タイミングまで待つ
  console.log(startTime - Date.now()); //経過時刻を取得
}
```

`while`文を使うことで、ループであることが一目で分かるようになりました。

ループを抜ける条件を書く時は、while文のbreak条件の部分に指定するか、`if`文で`break`するだけです。

```ts
const startTime = Date.now();
while (startTime - Date.now() < 10000) { // 終了条件
  await animationFramePromise();
  console.log(startTime - Date.now());

  // 終了条件はこう書くこともできる
  // if (startTime - Date.now() > 10000) {
  //   break;
  // }
}
```

## setTimeoutにも応用できる

ちなみにこの方法、setTimeoutやqueueMicrotaskにも応用できます。

```ts
function delay(ms) {
  return new Promise<number>((resolve) => {
    globalThis.setTimeout(resolve, ms);
  });
}

const startTime = Date.now(); //描画開始時刻を取得
while (true) {
  await delay(1000);
  console.log(startTime - Date.now()); //経過時刻を取得
}
```

setTimeoutをPromise化する際の注意点として、時間計測のスタート地点が「前の処理が終了した時刻」になります。そのため、例えば1000ミリ秒を指定してもぴったり1000ミリ秒間隔になるわけではありません。

GUIの操作など、「ぴったりn秒」が要求されない箇所では、setIntervalよりこちらのほうが書きやすい事もあるかもしれません。

## まとめ

- requestAnimationFrameを使ったループ処理はPromiseに変換すると簡潔に書ける
- setTimeoutやqueueMicrotaskも同様にPromiseに変換すると簡潔に書ける
Firewalld の要の設定

1)Default Dropという設定
Firewalldは、ルールに記載があってもDefault Acceptの状態（＜ー記載記事は少ないですね）
これを変えないとPortFIlterな設定を入れてもスカスカとなる。
以下の通り、targetをDefaultからDropへ変更する。

（設定の確認）
```
#firewall-cmd --list-all
public (active)
  target: Default

（以下略）
```

（変更のコマンド）
 sudo firewall-cmd --permanent --zone=public --set-target DROP

```
#firewall-cmd --list-all
public (active)
  target: DROP

（以下略）
```

ちなみにRejectの選択も可能。


２）次にポート番号とSourceIPでの組み合わせ設定について

以下の例では、管理対象に導入したZABBIXとの通信を行うための設定。

```
#firewall-cmd --permanent --zone=public --add-rich-rule="rule family="ipv4" source address="161.202.109.214/32" port protocol="tcp" port="10050" accept"
```

ちなみに、以下のコマンドで表示される「Service」「ports」で表示されている設定の場合、送信元制限はされずにアクセスを許可するという意味。上記、Rule Familyより優先されるため注意が必要（記載しているサイトは少ないですね）

```
#firewall-cmd --list-all

public (active)
  target: DROP
  icmp-block-inversion: no
  interfaces: ens3 ens4
  sources: 
  services: cockpit dhcpv6-client http https
```

以上。
Vue.js 状態管理用の変数を実装してみた

# 実現したかったこと
aタグなどの要素としては同じだが遷移先が異なるものについてcomponent化
生成した数に応じて、表示位置を制御するクラスを適用する

# 実装
```html:index.html
<!-- // ... -->
   <body>
        <!-- // ... -->

        <vue_header></vue_header>

        <!-- // ... -->
    </body>
    
    <script type="text/javascript">
        Vue.component('vue_a', {
            props: [
                'list',
                'position_class',
            ],
            template: `
                <a
                    v-bind:class="position_class"
                    v-bind:href="list.href"
                >
                    {{ list.name }}
                </a>
            `,
        });
        
        Vue.component('vue_header', {
            beforeCreate: function () {
                this.count = 0;
            },
            data: function () {
                return {
                    lists: [
                        {
                            href: "/href1",
                            name: "リンク1",
                        },
                        {
                            href: "/href2",
                            name: "リンク2",
                        },
                        {
                            href: "/href3",
                            name: "リンク3",
                        },
                    ],
                };
            },
            methods: {
                getPositionClass: function () {
                    if (this.count < 2) {
                        this.count++;
    
                        return "left";
                    } else {
                        this.count++;
    
                        return "right";
                    }
                }
            },
            template: `
                <header>
                    <div>
                        <vue_a 
                            v-for="(list, index) in this.lists"
                            v-bind:key=index
                            v-bind:list=list
                            v-bind:position_class="getPositionClass()"
                        >
                        </vue_a>
                    </div>
                </header>
            `,
        });

        <!-- // ... -->
    </script>

<!-- // ... -->
```

# つまずいたところ
`vue_header`のdata内に`count`を保持していたが、
`count`の更新と共にtemplateが再描画されてしまう

`beforeCreate`内で変数初期化することで、
処理内で更新しても`template`は更新されない

# さいごに
他にいい対応方法ありましたら、コメントで指摘して頂けるとありがたいです...
若手を壊しに来るマンの特徴

そろそろ春になり、新しいエンジニアたちが生まれてくる季節となった！

そんな若手エンジニアたちの弾む心を壊しに来る「若手を壊しに来るマン」がどの会社でも蔓延っている！

今回は、そんな若手を壊しに来るマンの種類と対策を紹介するぞ！


# 気軽にお願いするマン

「気軽にお願いするマン」は相手の都合も考えずに、涼しい顔で重い作業をお願いしてくるぞ！

とても面倒な作業でもお構いなし！本人は作業量が分からないから、納期も短いことが多いぞ！

「気軽にお願いするマン」と戦うためには、短納期すぎるとか、今は別の作業をしているからなど、できないを説明して、長めに納期を言うのが効果的だ！

また、納期を短くするための代替え案なども話し合うのも良いぞ！


# 自分の理解が追い付かないとキレるマン

「自分の理解が追い付かないとキレるマン」の特徴は説明する側は相手が理解できるように努めるべきだという思いが強すぎて、理解できないとキレるぞ！

本来理解できない側も、わからないポイントを説明して歩み寄る姿勢が大切だが、それが全くないぞ！

そのため、説明が難しい仕事を頼みづらいばかりか、説明のために膨大な時間を割いて資料作成なども発生する！

「自分の理解が追い付かないとキレるマン」と戦うためには、全員で理解度を合わせる場を設けるのが効果的だ！

「理解していないのは貴方だけ」とわからせることで、自分の説明は平均的な理解度の人はカバーできると裏付けできる！

みんなで集まってる場で理解できないことを晒すのは恥ずかしいのが日本人だ！

「相手の理解が追い付かないとキレるマン」という双子の兄弟も確認されているらしいぞ！気を付けろ！


# 縄張り意識が強すぎるマン

「縄張り意識が強すぎるマン」は、自分の担当以外は1ミリも手を出さず、断ってくるぞ！

「自分の縄張りかどうかグレーな時は相手の仕事だと認識するマン」という亜種もいる！

何かと理由を付けて、自分の仕事ではないと主張するのが特徴だ！

担当以外を引き受けてしまうと、今後もずっと自分の担当になってしまうことに強い恐怖感を持っているのだ！

「縄張り意識が強すぎるマン」には、定期的に発生する作業は担当を明確にするようその上司に掛け合ってもらうのが得策だ！

基本的に上司から言われた作業は自分の担当だと認識するぞ！


# 自分で考えてみてマン

「自分で考えてみてマン」は、ググってもわからない質問を聞いても何一つ答えてくれないぞ！

会社の決まり事など、世間一般の常識ではなくても「考えろ」が口癖なのだ！

特に新人は、なるべく忙しい上司に気を使って、考え抜いた結果聞きに来ていると思うが、それでもまた考えろと言う！

教育係だった場合はかなりしんどいぞ！

「自分で考えてみてマン」には、既に考えてみたことを伝えたうえで、適当に考えを言って、正解を聞くのが効果的だ！


# 昔はこうだったマン

「昔はこうだったマン」は、若手を捕まえて役立たない情報をひたすら話して、時間を奪ってくるぞ！

問題の解決策や仕事に役立つ情報だったらよいが、そもそも「昔はこうだったマン」の目的は、ただ話がしたいだけなのだ！

過去の栄光、仕事で辛かった思い出、昔働いてた人など、とにかくありとあらゆる話をマシンガンのように続ける！

しかも、このタイプには年配者やお偉いさんが多いためか、無下にすることもできないところがまた厄介だ！

中でも、「昔はもっと大変だった。お前たちは恵まれている。もっとガッツを出せ！」と体育会系のタイプには要注意だ！

このタイプが上司だと、デスマ確定と言っても過言ではないぞ！

「昔はこうだったマン」には、必要以上に関わらないのが正解だ！

あまり丁寧に話を聞く姿勢を持ってしまうと、「こいつは話を聞いてくれる！」と調子付いて、余計に毒牙にかかりやすくなるぞ！



# おわりに

意図して壊しに来るタイプは雰囲気で感じ取れるが、怖いのは若手を壊そうとは意図して思っていないタイプだ！

この手のタイプは本人は気づかないうちにダメージを与えていて、何度も繰り返すのが特徴だ！

今回はこの辺でお別れだ！

また新種の「若手を壊しに来るマン」が確認された場合には、この記事を更新していくぞ！

それじゃ、若手のみんな。折角エンジニアになれたのだから、若手を壊しに来るマンなんかに負けずに生き抜いていこうではないか！

さらばだっ！
WordpressにGoogle AnalyticsとSearchConsoleを連携

SEO対策をしてサイト設計を自動化するツールを作るため、自分の個人ブログサイトをGoogle AnalyticsとGoogle SearchConsoleに連携してみた。
ほぼ他の記事の力のおかげだが、Diviを使ってる人には役立つかも

### Google Analyticsとの連携
方法はこちらの記事を参考にしてやった
[Google analyticsをWordpressと連携させる](https://www.conoha.jp/lets-wp/wp-analytics/#section04)

Wordpressの`<head>`要素にanalyticsのコードを埋め込む必要がある。
参考記事では「Cocoon Child（Cocoonの子テーマ）」というのを使っていたが、僕はDiviというテーマを使っていたので[こちらの記事](https://nocodeweb.jp/divi/google-analytics-without-plugin/)を参考に埋め込みました。

### Google SearchConsoleと連携
方法はこちらの記事を参考にしてやった
[Google SearchConsoleと連携](https://www.conoha.jp/lets-wp/wp-search-console/#section05-01)

htmlタグの場所が少し分かりにくかったので　設定＞所有者の確認 で見れた。
![スクリーンショット 2022-03-22 19.44.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2048531/28e74da7-a169-c7ad-7ed0-42ee837476a4.png)
FLOPS ってなに？

## 勉強前イメージ

いやわからんけど、クロックなのか浮動小数点的な？

## 調査

### FLOPS とは

FLoating-point Operations Per Second の略で、コンピュータの処理速度をあらわす単位のひとつです。
「1秒間に浮動小数点演算を何回できるか」という能力を表しています。
GBやMBと同様に、値が大きくなると1MFLOPSなどと記載をします。

- 1GFLOPS
	- 10億回/sの浮動小数点演算を実行することができる
	- 10^9回 → 1,000,000,000回
- 1TFLOPS
	- 1兆回/sの浮動小数点演算を実行することができる
	- 10^12回 → 1,000,000,000,000回
- 1PFLOPS
	- 1000兆回/sの浮動小数点演算を実行することができる
	- 10^15回 → 1,000,000,000,000,000回

ちなみに、スーパーコンピューターの京は
1秒間に 10PFLOPS(10^16回 = 10,000,000,000,000,000回 = 1京回) の計算ができます。
京の由来は↑の1秒間にできる計算の回数から来ています。

## 勉強後イメージ

全然クロックとは違った...
そんな単位があるのね、、初めて知った。

## 参考

- [FLOPS 【FLoating-point Operations Per Second】 フロップス](https://e-words.jp/w/FLOPS.html)
- [ペタフロップス（PFLOPS）って、何ですか？](https://www.fujitsu.com/jp/about/businesspolicy/tech/k/qa/k02.html)
MiniPupper ROS: catkin_makeで [Errno 13] Permission denied になってしまう

## 環境
* windows10
* Ubuntu20.04 on WSL2
* ROS noetic

## 概要
ミニぷぱの公式ドキュメント [ SLAM & Navigation ] を参考にしてホストPCにROS環境を構築しようとしたらmnpp_wsでのcatkin_make (Step 2.5) で "[Errno 13] Permission denied: /home/user/mnpp_ws/build" になった。
＊ちなみにLidarを使用する予定はないのでStep 2.4 Cartographer_ros は行っていない。

## 解決策
現在の所有権を確認

```:shell
cd
ls -l
```
おそらくmnpp_ws のところが root:root になっていると思う
次にmnpp_ws以下のすべてのユーザー所有権とグループ所有権を自分にする

```:shell
sudo chown -R user:user ~/mnpp_ws
```
所有権を確認

```:shell
ls -l
```
user:userに変わっていると思う
これでcatkin_makeできるはず

```:shell
cd ~/mnpp_ws
catkin_make
```
## 参考
[Mini Pupper Document](https://mangdang-minipupperdocs.readthedocs-hosted.com/en/latest/index.html)
【mac】networkQualityコマンドの紹介

# networkQualityコマンドとは？

ネットワークの品質を手軽に確認することができるコマンドです。

pingコマンドや回線スピードをテストするサイトなどの代わりに使うことができそうです。

macOSのMontereyから追加されました。


# 何がわかるか

コマンドを実行すると、1分間あたりのラウンドトリップ数がわかります。

ラウンドトリップとは、ネットワーク通信でメッセージを送って、返答が送信元に戻ってくるまでのことを云います。

networkQualityでは1分間あたりのラウンドトリップ数を教えてくれます。

同時にアップロードとダウンロードの処理能力も測定してくれます。

（実行結果サンプルの再下行の（348 RPM）がラウンドトリップ数/分）


## 実行結果サンプル

```
$  networkQuality
==== SUMMARY ====                                                                                         
Upload capacity: 71.466 Mbps
Download capacity: 115.972 Mbps
Upload flows: 20
Download flows: 16
Responsiveness: Medium (348 RPM)
```

## ヘルプ

```
$  networkQuality -h
USAGE: networkQuality [-C <configuration_url>] [-c] [-h] [-I <interfaceName>] [-s] [-v]
    -C: override Configuration URL
    -c: Produce computer-readable output
    -h: Show help (this message)
    -I: Bind test to interface (e.g., en0, pdp_ip0,...)
    -s: Run tests sequentially instead of parallel upload/download
    -v: Verbose output
```
Google Cloud MonitoringのメトリクスをPythonで取得する

作ってみましたが、若干詰まったので書いておきます。

# コード
コード

```py
from datetime import datetime, timedelta
from google.cloud import monitoring_v3

project = "your-project"  # プロジェクト名
project_id = f"projects/{project}"
instance_name = "instance-name"  # インスタンス名

# 今現在から過去1時間分のデータを取得
now = datetime.now()
time_span_hour = 1
start_ts = int((now - timedelta(hours=time_span_hour)).timestamp())
end_ts = int(now.timestamp())


# フィルターには色々設定可能はこちらを参照 https://cloud.google.com/monitoring/api/v3/filters
metric_filter = f'metric.type = "compute.googleapis.com/instance/cpu/utilization" AND metric.labels.instance_name = "{instance_name}"'

metric_interval = monitoring_v3.TimeInterval(start_time={"seconds": start_ts}, end_time={"seconds": end_ts})
metric_cli = monitoring_v3.MetricServiceClient()
res = metric_cli.list_time_series(
    request=monitoring_v3.ListTimeSeriesRequest(
        name=project_id,
        filter=metric_filter,
        interval=metric_interval,
        view=monitoring_v3.ListTimeSeriesRequest.TimeSeriesView.FULL,
        aggregation=monitoring_v3.Aggregation(
            alignment_period={"seconds": 300},
            per_series_aligner=monitoring_v3.Aggregation.Aligner.ALIGN_MAX,
        ),
    )
)

for r in res:
    header = f"{r.resource.type} - {r.metric.labels['instance_name']} - {r.metric.type}"
    print(header)
    print("----------------")
    for p in r.points:
        print(f"{p.interval.start_time} : {p.value.double_value}")
```

出力例

```
gce_instance - instance-name - compute.googleapis.com/instance/cpu/utilization
----------------
2022-03-22 10:32:27+00:00 : 0.017728691561633998
2022-03-22 10:27:27+00:00 : 0.01778202000300553
2022-03-22 10:22:27+00:00 : 0.018050090768349493
2022-03-22 10:17:27+00:00 : 0.01823367738704557
2022-03-22 10:12:27+00:00 : 0.01796084328830716
2022-03-22 10:07:27+00:00 : 0.020671517363830577
2022-03-22 10:02:27+00:00 : 0.018054658292775607
2022-03-22 09:57:27+00:00 : 0.01777473582532707
2022-03-22 09:52:27+00:00 : 0.017657926236356994
2022-03-22 09:47:27+00:00 : 0.017721036677447916
2022-03-22 09:42:27+00:00 : 0.020249340092521113
2022-03-22 09:37:27+00:00 : 0.0206812247338424
```

 # 参考

https://github.com/googleapis/python-monitoring

https://cloud.google.com/monitoring/api/v3/filters
cscの作法 その55

# 概要
cscの作法、調べてみた。
走る人やってみた。


# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/1160cdbd-5f34-bbb8-b26a-10bf328a41b2.png)


# サンプルコード

```
using System;
using System.Windows.Forms;
using System.Drawing;

class form1: Form {
	PictureBox pb0;
	Image img;
	int ind;
	form1() {
		Text = "WebBrowser";
		ClientSize = new Size(420, 320);
		pb0 = new PictureBox();
		pb0.Width = 85;
		pb0.Height = 102;
		img = Image.FromFile("sprite-animation4.png");
		pb0.Image = img;
		ind = 0;
		Controls.AddRange(new Control[] {
			pb0
		});
		Timer timer = new Timer();
		timer.Interval = 100; 
		timer.Tick += new EventHandler(timerTick);
		timer.Start();
	}
	void timerTick(object sender, EventArgs e) {
		int x;
		int y;
		Text = DateTime.Now.ToString("HH:mm:ss");
		ind = ind + 1;
		if (ind > 29) 
			ind = 0;
		x = (ind % 6) * 85;
		y = (ind / 6) * 102;
		Bitmap canvas = new Bitmap(pb0.Width, pb0.Height);
		Graphics g = Graphics.FromImage(canvas);
		Rectangle srcRect = new Rectangle(x, y, 85, 102);
		Rectangle desRect = new Rectangle(0, 0, srcRect.Width, srcRect.Height);
		g.DrawImage(img, desRect, srcRect, GraphicsUnit.Pixel);
		g.Dispose();
		pb0.Image = canvas;
		this.Invalidate();
	}
	[STAThread]
	public static void Main() {
		Application.Run(new form1());
	}
}




```


以上。
RubyとPHPの比較(文字列と変数)

# この記事の概要
Rubyで学習を始めてPHPに言語チェンジをする人が一定数いるみたいなので
そういった人達が参考にするための記事です。

# 簡単な出力
簡単な出力からそれぞれ見ていきます。

⚫︎```Ruby```

```rb
puts "プログラミング"
```

⚫︎```PHP```

```php
<?php
// Your code here!
echo "プログラミング";
?>
```
PHPだと```最後に;```がつくのが特徴です。

両方とも***プログラミングと出力***されます。

⚫︎補足
""だけでなく''で囲むことも可

# コメントの書き方
　コメントの書き方がそれぞれ違います。

⚫︎```Ruby```

```rb
#コメント
puts "Ruby"
```
Rubyでは```#```を使います。

⚫︎```PHP```

```php
<?php
// Your code here!
//コメント
echo "PHP";
//コメント
?>
```
PHPでは```//```を使います。


# 文字列と数値の連結

⚫︎```Ruby```

```rb
puts "Ruby"+"の"+ "勉強するぞ"
#Rubyの勉強するぞ と出力

puts "1" + "00"
#100と出力

puts 100 + 50 
#150と出力
```
```""で囲むと文字列```になり```+をつけると連結```できます。
数値に""を付けない場合は通常通り数値のままで計算されます。

⚫︎```PHP```

```php
<?php
// Your code here!

echo "PHP"."の"."勉強をするぞ";
//PHPの勉強をするぞ

echo"1"."00";
//100の出力

echo 100 + 50;
//150と出力
?>
```
```文字連結は.を使います。```

# 変数
何かしらの値を入れておくための箱で、
変数があることで読めるプログラムが書けるようになります。


⚫︎```Ruby```

```rb
#数値の計算
a = 100      #イコールで変数に代入できる
b = 50
puts a + b  #150と出力

#文字の連結
a = "Ruby"
b = "楽しい"

puts a + b
```

⚫︎```PHP```
PHPの場合、***変数の前には```$```をつけます***。

```php
<?php
//数値の計算
$a = 100;
$b = 50;
echo $a + $b;  //150と出力


//文字の連結
$a = "PHP";
$b = "楽しい";
echo $a.$b; //PHP楽しいと出力
?>
```

### 変数の上書き
変数は下のように上書きをすることができます。
***変数が同じ場合は下の変数の中身が出力***されます。

⚫︎```Ruby```

```rb
programming_study = "Ruby"
programming_study = "Rubyを学習するぞ"
puts programming_study  
```

***Rubyを学習するぞ***が出力されます。


⚫︎```PHP ```

```php
<?php
$programming_study = "PHP";
$programming_study = "PHPを学習するぞ";
echo  $programming_study;
?>
```
***PHPを学習するぞが出力***されます。

### 注意
PHPの場合は```変数に$を忘れずに```

### 変数の繰り返し
変数は繰り返し使うことができます。

```rb
programming_study = "Ruby"
puts programming_study + "を習得するぞ！"
puts programming_study + "は楽しい"
```
出力すると

```
Rubyを習得するぞ！
Rubyは楽しい
```
と出力されます。


```php
<?php
$programming_study = "PHP";
echo $programming_study ."を学習するぞ";
echo $programming_study ."は楽しい";
?>
```
と出力すると

```
PHPを学習するぞPHPは楽しい
```
と出力されます。


### 変数の文字列展開
変数を使い文字列展開することができます。

⚫︎```Ruby```
変数の文字列展開をする時には```#{変数}```を使います。
***(変数は自分や他人がわかりやすいもので自由に決められます。)***

```rb
day = 5
program = "Ruby"

puts "週に#{day}日#{program}を学習します"
```
***週に5日Rubyを学習します***が出力されます。

⚫︎```PHP```
PHPの場合は```{変数}```で文字列展開ができます。

```php
<?php
$day = 5;
$program = "PHP";
echo "週に{$day}日{$program}を学習します"
?>
```
***週に5日PHPを学習します***が出力されます。

⚫︎補足
***変数は自分や他人がわかりやすいもので自由に決められます。***
 

⚫︎変数の参考資料
https://qiita.com/Hashimoto-Noriaki/items/d705a3fb92db14de4742


# PHPのHTMLへの組み込み
PHPのHTMLファイルの組み込み方です。

```php
<p>
<?php echo "PHP";?>の学習
</p>
```
PHPの***開始タグ***が```<?php```で***終了タグ***が```?>```です。

```
<p>
PHPの学習
</p>
```
が出力されます。


# 参考資料
⚫︎Ruby

https://www.youtube.com/watch?v=8NI57SrDiVw&list=PL-1KBX2gDRuiDf7R-YR4NjFf_nYu3RG3y&index=15

⚫︎PHP

https://www.youtube.com/watch?v=J_pASVQXElA&list=PL-1KBX2gDRuiDf7R-YR4NjFf_nYu3RG3y&index=24
CygwinにPythonをインストールする方法

# CygwinにPythonをインストール
Cygwinの設定をするため、**setup-x86_64.exe** を起動する。
**Python39**、**wget** を追加する。

# Cygwinにpipをインストール
以下の記事を参考

https://qiita.com/fujisystem/items/68c6b9dfdc5e0328c882

※ 2022年3月23日に追記
**setup-x86_64.exe** で **python39-pip** を追加するだけ大丈夫みたいです。

以上、CygwinにPythonをインストールする方法でした。
C++におけるcsvファイル⇔二次元配列の変換

# はじめに
自分用の覚書です。二次元配列をcsvに読み書きする方法。
まともな記事は「c++ csv」で検索すれば山程見つかるのでそちらを参照されてください。

# 環境
割愛
MacOS
C++14,17

# 読み取り(csv ➡ 二次元配列)
```c++
#include <fstream>
#include <sstream>
#include <string>
#include <vector>

// input を delimiter で分割する関数
std::vector<std::string> split(std::string &input, char delimiter) {
  std::istringstream stream(input);
  std::string field;
  std::vector<std::string> result;
  while (getline(stream, field, delimiter)) result.push_back(field);
  return result;
}

// csvファイルの読み取り
void read() {
  std::string path = "/Users/usr/.../input.csv"; // 読み取り元
  std::ifstream ifs(path);                       // 読み取り用ストリーム
  std::vector<std::vector<int> > data;           // 読み取ったデータの格納場所
  if (ifs) {
    std::string line;

    // 一行目がラベルの場合
    // getline(ifs, line);
    // std::vector<std::string> strvec = split(line, ',');

    while (getline(ifs, line)) {
      std::vector<int> datvec;
      std::vector<std::string> strvec = split(line, ',');
      for (auto &&s : strvec) datvec.push_back(std::stoi(s)); // セルの文字列を数値に変換
      data.push_back(datvec);
    }
  }
}
```

# 書き出し(二次元配列 ➡ csv)
```c++
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

// csvファイルの書き出し
void write() {
  std::string path = "/Users/usr/.../output.csv";                   // 書き出し先
  std::ofstream ofs(path);                                          // 書き出し用ストリーム
  std::vector<std::vector<int> > data(10, std::vector<int>(10, 2)); // csvにしたいデータ
  if (ofs) {
    for (size_t i = 0; i < data.size(); i++) {
      for (size_t j = 0; j < data[i].size(); j++) ofs << data[i][j] << ",";
      ofs << std::endl;
    }
  }
}
```

# 参考文献
https://cvtech.cc/readcsv/
  
https://qiita.com/iseki-masaya/items/70b4ee6e0877d12dafa8

# おわりに
pythonだとsplitがあるので楽です。
read()で作られるdataはアクセスが悪いのでそのまま使うのは少しモヤります。
GCP nodejs環境構築手順(nodeのバージョンを指定)

ほぼ自分用のメモです。

## １インスタンス作る 
インスタンスがmicroだと手順5で　out of memoryになって進めない

## ２sshでコンソールに入る
これはSSHクリックするだけ
## 3gitをインストールする
```
$ sudo apt-get update # いろいろパッケージを更新
$ sudo apt-get install git # git インストール
$ which git
```
参考
https://zenn.dev/suyaa/scraps/a0e84f9cbf403b

## 4homebrewをインストール
```
git clone https://github.com/Homebrew/brew homebrew
```
```
eval "$(homebrew/bin/brew shellenv)"
```
```
brew update --force --quiet
```
↑ちょっと時間かかる
```
chmod -R go-w "$(brew --prefix)/share/zsh"
```
参考
https://docs.brew.sh/Installation


## 5nodebrewをインストール
```
mkdir ~/.nodebrew
mkdir ~/.nodebrew/src
brew install nodebrew
```
参考
https://qiita.com/kyosuke5_20/items/c5f68fc9d89b84c0df09


## 6nodebrewでnodeをバージョン指定してインストール

参考
https://qiita.com/kyosuke5_20/items/c5f68fc9d89b84c0df09

## 7 ssh接続切って　インスタンスを一旦停止して再起動
これで　ようやく
node -v
でnodeが動いていることを確認

## 8その他メモ
IPアドレスの予約で　ポートを開く
foreverコマンドは別途インストールが必要
Juliusで自作N-gramを使う方法

# bingram形式のN-gramを作成
N-gramの作成方法の詳細は前回の記事[**PythonでN-gramを作る（Julius）**](https://qiita.com/phiniki/items/9d4fe40389e054385787)に記載しております。

今回は作成したN-gramを **C:/Julius/my_julius/** にコピーしておく。

# 自作N-gramの適用
過去の[**Windows環境のPythonで大語彙連続音声認識エンジン Julius を使う方法**](https://qiita.com/phiniki/items/9729da0b66c16a7020d2)でダウンロードした音声認識パッケージ（dictation-kit-4.5）の **/dictation-kit-4.5/am-dnn.jconf** を開く。

以下の部分を見つける。
```am-dnn.jconf
# The phone set of DNN-HMM is different from GMM-HMM, so requires another dictionary
-v model/lang_m/bccwj.60k.pdp.htkdic
```

**-v**は単語辞書です。自作の単語辞書を使いたい場合はここを変更する。
自作N-gramを使う場合は以下のように一行追加する。
```am-dnn.jconf
# The phone set of DNN-HMM is different from GMM-HMM, so requires another dictionary
-v model/lang_m/bccwj.60k.pdp.htkdic

-b C:/Julius/my_julius/n-gram.bingram  ### 追加
```

以上で設定完了。
あとは、過去の[**Windows環境のPythonで大語彙連続音声認識エンジン Julius を使う方法**](https://qiita.com/phiniki/items/9729da0b66c16a7020d2)と同様にJuliusを実行する。

以上、Juliusで自作N-gramを使う方法でした。
CloudFormationで作成したIAMポリシーのステートメント内のプロパティはいつも同じ順番になる／コンソールで作ったものとも順番が異なる

## はじめに

IAMポリシーのステートメント内のプロパティの順番について、IAMポリシーを作成する方法が異なるとその順番も異なるということに気が付きました。

## 前提

CloudFormationのテンプレートでステートメント内のプロパティを以下のようにしたとします。

* myPolicy1: Sid, Action, Effect, Resource の順に記述
* myPolicy2: Sid, Effect, Action, Resource の順に記述

## テンプレート

ActionやResourceは適当な内容にしています。

```YAML
AWSTemplateFormatVersion: "2010-09-09"

Resources:
  Policy1:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: myPolicy1
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          # Sid, Action, Effect, Resource の順に記載
          - Sid: myPolicy1
            Action:
              - ec2:*
              - s3:*
            Effect: Allow
            Resource: "*"

  Policy2:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: myPolicy2
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          # Sid, Effect, Action, Resourceの順に記載
          - Sid: myPolicy2
          - Effect: Allow
            Action:
              - ec2:*
              - s3:*
            Resource: "*"
```

スタックを作成して、実際に出来上がったIAMポリシーを見ると、ステートメント内のプロパティの順番はテンプレートに書いた通りの順番とはならず、上記のテンプレートで記載した4つのプロパティの場合はAction, Resource, Effect, Sidの順になります。

### myPolicy1

```JSON
{
    "Version": "2012-10-17"
    "Statement": [
        {
            "Action:" [
                "ec2:*",
                "S3:*"
            ],
            "Resource": "*",
            "Effect": "Allow",
            "Sid": "myPolicy1"
        }
    ]
}
```

### myPolicy2

```JSON
{
    "Version": "2012-10-17"
    "Statement": [
        {
            "Action:" [
                "ec2:*",
                "S3:*"
            ],
            "Resource": "*",
            "Effect": "Allow",
            "Sid": "myPolicy2"
        }
    ]
}
```

## AWS管理コンソールで作成した場合

なお、AWS管理コンソールでIAMポリシーを作成すると、以下のようになります。

```JSON
{
    "Version": "2012-10-17"
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action:" [
                "S3:*",
                "ec2:*"
            ],
            "Resource": "*",
        }
    ]
}
```


## 何故このようなことを確認しようと思ったか？

テンプレートでIAMポリシーを作成する前に、AWS管理コンソールを操作して作成してあったIAMポリシーがありました。そのIAMポリシーをテンプレートに記載して、スタックに含めておいてほしいという要望があり、対応をしました。両者は同じAWSアカウント上に作っています。

AWS管理コンソールで作成したIAMポリシーは試作で、試作した結果を受けてスタックに取り込んでほしいという要望でした。

それでスタックは無事に作成でき、テンプレートからIAMポリシーを作成できたのですが、その後で「スタックで作ったIAMポリシーの内容が違う」と言われました。

そう言われて内容を確認したところ、ポリシーとしては何も違うところはなかったのですが、ステートメント内のプロパティの順番が両者で異なっていたのです。「スタックで作ったIAMポリシーの内容が違う」と述べた人は、プロパティそのものの内容までは見ておらず、プロパティの順番・見た目が違うだけで両者の内容が違うと言っていたことがわかりました。

その人はAWSに触れて日が浅かったために、単に見た目が違うというだけでそれぞれのポリシーは別物だと判断されてしまったのですが、見た目が違っていたのは確かだったので、それで調べてみたということでした。
AWS Certified Data Analytics - Specialty(DAS) を取得しました

# はじめに
AWSの資格を毎年1～2個取得していますが、AWS Certified Data Analytics - Specialty(DAS)にチャレンジしてみました。
取得済みの資格は下記の通りとなります。
- Cloud Practitioner
- Developer Associate
- Solutions Architect Associate
- SysOps Administrator Associate
- DevOps Engineer Professional
- Solutions Architect Professional
- Advanced Networking Specialty
- Security Specialty

# AWS Certified Data Analytics - Specialty(DAS) とは
[この辺](https://aws.amazon.com/jp/certification/certified-data-analytics-specialty/)を参照ください。

# 結果
- 結果:合格(886点)

# 教材
1. [Udemy:AWS Certified Data Analytics Specialty 2022 - Hands On!](https://www.udemy.com/course/aws-data-analytics/)
    - 講義+問題集です。
    - 全て英語です。
    - データ分析関係のAWSサービスの基礎を学ぶことができます。
    - 問題集はやや古く、且つ通常の試験よりも難易度が低いと思います。
1. [Udemy:AWS Certified Data Analytics Specialty Practice Exams](https://www.udemy.com/course/aws-certified-data-analytics-specialty-practice-exams-amazon/)
    - 100問の問題集です。
    - 全て英語です。
    - 上記よりも若干難易度高めです。より試験に近い難易度かと思います。
1. [試験問題サンプル](https://d1.awsstatic.com/ja_JP/training-and-certification/docs-data-analytics-specialty/AWS-Certified-Data-Analytics-Specialty_Sample-Questions.pdf)
    - 公式の試験問題サンプルです。
1. [Exam Readiness: AWS Certified Data Analytics – Specialty-A2T](https://explore.skillbuilder.aws/learn/course/51/exam-readiness-aws-certified-data-analytics-specialty)
    - 公式の講義+問題集です。
1. [AWS Certification Official Practice Question Sets](https://amazonwebservices.benchprep.com/app/aws-certification-official-practice-question-sets-japanese)
    - 公式の模擬試験です。
    - 無償となり、DAS以外も受けることができます。

# 受験までの流れ
- 仕事がとても忙しいこともあり、2カ月弱かけて勉強しました。上記各試験の正答率が100%になってから試験に挑みました。
- 最初は自宅で受けましたが、音声が届かない&試験アプリが起動するとCPU高負荷となると言った、システムトラブルによりキャンセルとなりました。2回目は近くの試験会場で受けました(再度システムトラブルになるのを避けたかったため)。
- 試験では、1週目は80分、2週目(見直しフラグの確認)は30分の計110分程要しています。

# 感想
- 難易度はやや高く、上記教材と同じような問題は3割程度、よく考えれば分かるが4割、でした。普段扱っていないと分からないような細かい設定の問題もありました。
- 上記教材ではカバーできていないサービスも登場しています。
- 次は、Database SpecialtyかMachine Learning SpecialyかCKS(Certified Kubernetes Security Specialist)を狙っていますが、初めて触る別のクラウドを扱う仕事を担当することになったため、そちらのキャッチアップに精一杯で難しいかもしれません。
ABC244のA, B, C, D問題 を Elixir で解く

https://qiita.com/obelisk68/items/925c08d3135f2a16bd7e

これに続いて、Elixir 超初心者が AtCoder に挑戦してみました。

https://atcoder.jp/contests/abc244

御指導よろしくお願いします。

# A問題: Last Letter

```elixir 
defmodule Main do
  def main do
    IO.read(:line)
    IO.read(:line) |> String.trim()
    |> String.at(-1)
    |> IO.puts()
  end
end
```

与えられた文字列の最後の文字を取り出すということで、`String.at(str, -1)`でいいのですね。負数のインデックスが後ろからの指定になるのは、Ruby と同じです。

# B問題: Go Straight and Turn Right

```elixir 
defmodule Main do
  def main do
    IO.read(:line)
    IO.read(:line) |> String.trim()
    |> solve()
    |> IO.puts()
  end
  
  defp solve(str) do
    str
    |> String.codepoints()
    |> Enum.reduce({0, {0, 0}}, &doit/2)
    |> output()
  end
  
  defp doit("S", {0, {x, y}}), do: {0, {x + 1, y}}
  defp doit("S", {1, {x, y}}), do: {1, {x, y - 1}}
  defp doit("S", {2, {x, y}}), do: {2, {x - 1, y}}
  defp doit("S", {3, {x, y}}), do: {3, {x, y + 1}}
  defp doit("R", {dir, tpl}), do: {rem(dir + 1, 4), tpl}
  
  defp output({_, {x, y}}), do: "#{x} #{y}"
end
```

0, 1, 2, 3 の数字で「右下左上」の方向を表しています。右回転なので 1 を足していけば回転します。

# C問題: Yamanote Line Game

```elixir 
defmodule Main do
  def main do
    IO.read(:line) |> String.trim() |> String.to_integer()
    |> solve()
  end
  
  defp solve(n), do: Enum.to_list(1..2 * n + 1) |> doit()
  
  defp doit([x | xs]) do
    IO.puts(x)
    IO.read(:line) |> String.trim() |> String.to_integer()
    |> doit(xs)
  end

  defp doit(0, list), do: nil
  defp doit(i, list), do: List.delete(list, i) |> doit()
end
```

まさかのインタラクティブ問題。手続き型言語なら無限ループさせておいて終了条件がきたらループを出ればいいだけですが、関数型言語なので工夫が要りました。再帰でやっています。それから、問題にある標準出力の`flush`はどうすればよいかよくわかりませんでしたが、ここではテキトーに`IO.puts/1`でよかったようです。

# D問題: Swap Hats

```elixir 
defmodule Main do
  def main do
    s = IO.read(:line) |> String.trim()
    t = IO.read(:line) |> String.trim()
    
    solve(s, t) |> IO.puts()
  end
  
  defp solve(s, t), do: judge(s) * judge(t) |> output()
  
  defp judge("R G B"), do: 1
  defp judge("G B R"), do: 1
  defp judge("B R G"), do: 1
  defp judge(_), do: -1
  
  defp output(1), do: "Yes"
  defp output(-1), do: "No"
end
```

これは対称群の知識があるとよいです。`"R G B"`からの偶置換を`1`、奇置換を`-1`として、`s` `t` それぞれについての値の積が`1`（偶置換）になればよいというのを実装しました。

# 最後に

Elixir で AtCoder をやる人が少なくて、参考にしたくてもなかなか…。それから、僕の実装が悪いのかも知れませんが、速度的にもきびしいです。コードの改善など、きびしい指摘を待っております。
【Swift】テキストフィールド数値のみの入力制限

# 

iOSでよく扱う入力制限についての備忘録です


```swift
@IBOutlet private weak var quantityTextField: UITextField!

override func viewDidLoad() {
        super.viewDidLoad()

        quantityTextField.delegate = self
}

//省略

extension ViewController: UITextFieldDelegate {
    
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        
        guard let text = textField.text else {
            return false
        }
        
        if textField === quantityTextField {
            //数値のみチェック
            guard string.isEmpty || !string.match(#"^\d+$"#).isEmpty else {
                return false
            }
            //レンジを取得
            guard let textRange = Range(range, in: text) else {
                return false
            }
            // `textRange`の範囲をstringへ置換
            let updatedText = text.replacingCharacters(in: textRange, with: string)
            //桁数チェック
            guard
                updatedText.count < 6 else {
                return false
            }
            //最初に0は拒否
            guard updatedText.match(#"^0"#).isEmpty else {
                return false
            }
        }
        
        return true
    }
    
}

```

正規表現を扱うのでStringクラスを拡張
```swift
extension String {
    func match(_ pattern: String) -> [String] {
        guard let regex = try? NSRegularExpression(pattern: pattern),
              let matched = regex.firstMatch(in: self, range: NSRange(location: 0, length: self.count))
        else { return [] }
        return (0 ..< matched.numberOfRanges).map {
            NSString(string: self).substring(with: matched.range(at: $0))
        }
    }
}
```


おかしいところがもしあれば教えてください🙇‍♂️
Linuxのディストリビューションを調べる

`hostnamectl`コマンドを使う

アウトプット
```
  Static hostname: my-server
         Icon name: computer-desktop
           Chassis: desktop
        Machine ID: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
           Boot ID: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  Operating System: Ubuntu 18.04.6 LTS
            Kernel: Linux 5.4.0-100-generic
      Architecture: x86-64
```
Python: コール可能なオブジェクト（関数とか）かチェックする

```python

def f():
    pass


async def af():
    pass


class C:
    @staticmethod
    def s():
        pass

    @classmethod
    def c(cls):
        pass

    def i(self):
        pass


if __name__ == "__main__":
    print(callable("x")) # False
    print(callable(None)) # False
    print(callable(lambda x: x + 1)) # True
    print(callable(f)) # True
    print(callable(af)) # True
    print(callable(C.s)) # True
    print(callable(C.c)) # True
    print(callable(C.__init__)) # True
    print(callable(C().i)) # True
    print(callable(C.i)) # True
```
Rails ルーティングの順序で失敗した件

# はじめに
JSONを返すRailsAPIを作成中に、意図しないコントローラーのアクションを読み取りに行ってしまった事があった。
エンドポイント(api/v1)を設定していた為、URI Pattern に被る部分が起きた為のミスだった。

# 状況

### ディレクトリ構成
```
app/
　├ contorollers/api/v1/articles/drafts_controller
　  contorollers/api/v1/articles_controller
```

### コントローラー
```drafts_controller.rb
module Api::V1
  class Articles::DraftsController < BaseApiController
    def index
      ~
    end
    def show
      ~
    end
  end
end
```
```articles_controller.rb
module Api::V1
  class ArticlesController < BaseApiController
    def index
      ~
    end
    def show
       ~
    end
  end
end
```

### ルーティング
```routes.rb
Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      resources :articles
      namespace :articles do
        resources :drafts, only: [:index, :show]
      end
    end
  end
end
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/693042/db3e4669-e749-df3b-6aac-e3a17eba259d.png)



# エラー内容
上記の状況で、`drafts_controllerのindexアクション`を実行しようとしたら、
意図せず`articles_controllerのshowアクション`が実行されてしまった。

この２つはHTTPメソッドが同じGETであり、URI Pattern が上記画像の通り、
目的のindexが`api/v1/articles/drafts`、期待してないshowが`api/vi/articles/:id`。
共通の部分が`/api/v1/articles/`である。

ルーティングの記述は上から順番に読み込む性質がある。なので、先に記述されてる`/:id`の部分をdraftで読み取ってしまった為、`api/vi/articles/:id`としてarticles_controllerのshowアクションが実行されてしまったようだった。

# 解決策
シンプルにルーティングの順番を下記のように書き直したら、解決した。
```routes.rb
Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      namespace :articles do
        resources :drafts, only: [:index, :show]
      end
      resources :articles
    end
  end
end
```
# 参考文献
- [rails routes（ルーティング）の順番の解説](https://qiita.com/harinezumi-html/items/46931ffc56c101fc6a4e)
第5回 OSI参照モデル

OSI参照モデル
---
<table>
<tr>
<th>第７層</th>
<td>アプリケーション層</td>
</tr>
<tr>
<th>第６層</th>
<td>プレゼンテーション層</td>
</tr>
<tr>
<th>第５層</th>
<td>セション層</td>
</tr>
<tr>
<th>第４層</th>
<td>トランスポート層</td>
</tr>
<tr>
<th>第３層</th>
<td>データリンク層</td>
</tr>
<tr>
<th>第２層</th>
<td>データリンク層</td>
</tr>
<tr>
<th>第１層</th>
<td>物理層</td>
</tr>
</table>
[Table04-02:OSI参照モデル7階層]

階層分けした理由
---
各レイヤはそれぞれ機能が違い、独立してい
る。だからこそ、他レイヤのことを考える必
要がない。


7つのレイヤの機能
---
レイヤ7は最上位に位置し、ネットワークと
いうサービスを提供する。

レイヤ6は、データの形式を決定する。例え
でいうと使う言語を決定する。

レイヤ5は、セッションを管理する。これは
こっちが話して、向こうが話し返してくる。
このラリー(まとまり)を指す。

レイヤ4は、信頼性の高い通信サービスを保
証する層。エラーや相手に確実に届けるとい
うことを保証する。

レイヤ3は、離れた場所に存在する相手との
間でデータの転送や運ぶルートの決定、宛先
の決定などを行う。

レイヤ2は近くの機器とのデータの伝送制御
を行う。レイヤ3と違うのは、より直接的な
接続を行うという観点。
【AWS】buildspec.ymlとは

# buildspec.ymlとは
``buildspec.yml``とは、ビルド実行時に実行するコマンドを記述した**YAML**形式のファイルのこと。
このファイルをソースコードのルートディレクトリに配置することで**CodeBuild**が``buildspec.yml``を読み込んで実行される。
**CodeCommit**を使用する場合は、リポジトリのルートディレクトリに配置する。

``buildspec.yml``には以下のような項目が記述される。
## version
これは必須項目で**buildspec**のバージョンを指定する。参考文献では``0.2``が推奨されていた。
```yml
version: 0.2
```

## env
これはオプションの項目で環境変数を設定する。DB接続に関する情報などを設定したりする。
```yml
env:
     <環境変数名>:<値>
```

## phases
これは必須項目でビルド実行時に**CodeBuild**が実行するコマンドを記述する。
また、**CodeBuild**ではフェーズごとに実行する内容を以下のように分けることができる。

■ **install** 
インストールの際に**CodeBuild**が実行する必要なコマンドを記述する。
■ **pre_build** 
build前に**CodeBuild**が実行する必要なコマンドを記述する。
■ **build**
build実行中に**CodeBuild**が実行する必要なコマンドを記述する。
■ **post_build** 
build後に**CodeBuild**が実行する必要なコマンドを記述する。

```yml:例
phases:
    pre_build:
        commands:
            - <コマンド>
```


## artifacts
ビルドの出力結果に関するデータの保存先情報を記述する。
■ **files**
これは必須項目でビルド環境でのビルド出力アーティファクトを含む場所を表す。

# 参考
今回は自分が使った項目だけをまとめたが、他の項目や詳細は以下を参照。
[https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/build-spec-ref.html](https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/build-spec-ref.html)
ADOPをAWSのubuntuで動かす（ただしカクカク）

フォトグラメトリのADOPをAWSで動かすことに成功したので備忘メモ

# ADOPとは
以下です。
https://github.com/darglein/ADOP
フォトグラメトリのライブラリで、NvidiaのGPUが載ったubuntu18とubuntu20に対応しています。

これをAWSで動かそうと悪戦苦闘したら動いたので報告

# 動作環境

動作確認環境は、 g3.4xlargeとg4dn.4xlarge。
ubuntu 20.04で動作確認。18では動作確認してません。

# ハマリポイント

## NICE DCVでUbuntuに繋ぐ必要がある
これは使用するOpenGLの問題で、RemoteDesktop(RDP)でつなぐとどうしてもOpenGLがMesaになってしまってうまく動作してくれない。
最初、MacからRDPでアクセスしても最後の最後でOpenGLエラーで動かなくなってしまっていたが、
Ubuntu内にNICE DCVサーバを立てて、NICE DCVでつなぐと、NvidiaのOpenGLが動いてくれる。

以下を参考にしながらインストールすると良い。
https://docs.aws.amazon.com/ja_jp/dcv/latest/adminguide/setting-up-installing.html

## Adop viewerがビルドされないときはライブラリが足りない
このIssueが役に立つ
https://github.com/darglein/ADOP/issues/43
以下を入れると生成される。
```
sudo apt install qtbase5-dev qttools5-dev-tools qt5-default
```

## LD_LIBRARY_PATHはフルパスで指定する

README( https://github.com/darglein/ADOP/tree/main/src )には以下にように指定するように書いてあるが
```
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:~/anaconda3/envs/adop/lib #動かない可能性あり
```
このままでは動作しなかった。
正しくは以下のように指定する必要がある（[user]部分はご自身の環境に合わせて書き換えてください）

```
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:/home/[user]/anaconda3/envs/adop/lib
```

上記の３つのポイントを押さえると、うまく動かすことができます。
（ただし動作はカクカクです）

# インストール手順

上記の注意点を押さえながら公式の手順に従ってインストールをしていけばOKです。
https://github.com/darglein/ADOP/tree/main/src

ただ、ここに書かれていないこととして `build_adop.sh`というファイルがあります。
これは、**4.Build ADOP** の部分を補完してくれるので、`install_pytorch.sh`が済んだあとは`build_adop.sh`を実行しちゃえばOKです。

# 実行

ビルドさえ済んじゃえば、あとは　sceneとexperimentsをダウンロードしてきて配置し、書いてあるとおり実行すれば動作します。

ただし、**実行するときはNICE DCVで接続することをお忘れなく**

NICE DCVでつなぐときは、SSHでubuntuにつなぎ、以下を実行
```
# サーバ起動
sudo systemctl start dcvserver
# セッションの作成 (my-session部分は好きに変更可能)
dcv create-session my-session
```
できたら、NICE DCVを起動して
接続先を以下のようにする（[ubuntu_ip]部分はご自身のEC2のIPにしてください）
```
[ubuntu_ip]:8443#my-session
```
すると、なんかのSSLの警告画面が出るのでTrustかProseedを選ぶとID/PASSを入力する画面が出るので
作成したユーザでログインすればOKです。
MySQL with Python connector

#### MySQL
##### 記事の背景
MySQLに関する記事は多いのですが、途中まで躓いているものも多い。  
本記事は、Windowsに限定してインストール、初期設定、データベース作成  
そしてPythonからデータベースへのアクセスまで網羅します。

PandasとMySQLのデータのやり取りは以下参照
https://qiita.com/k2J/items/aad2c1805dfd844a1122

AnacondaとPowerShellの設定
https://python0to1.com/499/

insert_data_bulk
https://itips.krsw.biz/python-mysql-bulk-insert/

#### 1.install 
MySQLダウンロードサイト
https://dev.mysql.com/downloads/installer/
ログインについては、スキップする
画面左下の「No thanks, just start my download.」からダウンロードする。

#### 2.インストール設定

Custom
☑MySQL Server
☑MySQL Shell
☑Connector/Pythonを選ぶ

Type & networking
☑Development Computerを選択
☑TCP/IP Port:3306
☑X Protocol Port:33060

☑Open Windows Firewall port for network access

認証設定
☑Use strong password Encryption for Authentication
Use　Legacy Authentication Method

#### 3.認証関連
☑ルートパスワード入力
☑ユーザ名追加
User Name：
Host:locahost
Role:DB Admin

パスワード変更(8.0) ※パスワードに@を含んでいる場合、PGMでログインに影響あり
```sh
mysql > USE mysql;
mysql > ALTER USER 'root'@'localhost' identified BY 'hoge';
```

#### 4.MySQL 基本コマンド

☑データベースを表示する
show databases;

☑データベースを作成する
CREATE DATABASE `データベース名`;

☑データベースを選択する
use `データベース名`;

☑テーブルを表示する
SHOW TABLES;

☑テーブルを追加する
CREATE TABLE　`テーブル名` (id INT(10) AUTO_INCREMENT, `カラム名` VARCHAR(10));

☑テーブルを削除
DROP TABLE IF EXISTS `削除するテーブル名`;

☑テーブル名変更
RENAME TABLE `変更するテーブル名` TO `変更後のテーブル名`

☑テーブルデータをクリアする（インクリメントもクリア）
TRUNCATE TABLE `削除するテーブル名`

☑テーブルデータ全削除（インクリメントクリアしない）
DELETE FROM `削除するテーブル名`

☑項目更新
UPDATE `テーブル名` SET `カラム名` = `値` WHERE `条件`

☑カラム名変更
ALTER TABLE `テーブル名` RENAME COLUMN `変更前カラム名` TO `変更後カラム名`;

☑カラム名移動
ALTER TABLE `テーブル名` MODIFY `移動するカラム名` TEXT AFTER `移動する位置`;

☑カラム名削除
ALTER TABLE `テーブル名` DROP COLUMN `カラム名`;

☑インクリメントをリセット（テーブルを空にしている場合）
ALTER TABLE `テーブル名` auto_increment = 1;

☑テーブル名の定義を見る
SHOW CREATE TABLE `テーブル名`;

☑重複なしでカラムを取り出す場合
SELECT DISTINCT(`カラム名`) FROM `テーブル名`;

#### 4.Python connector
```sh
pip install mysql-connector-python
pip list | grep mysql
mysql-connector-python 8.0.28
```
#### 5.Flask SQLAlchemy
SQLAlchemyはデータベースに左右されずに共通文法として使用できるメリットがあります。
基本的な使い方は[ここ](https://qiita.com/ariku/items/75799665acd09520bed2)
対応DB：MySQL/Postgres/Oracle/SQLServer/SQLite/MongoDB
Flaskに接続する場合の設定例は下記参照：
```python
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql+pymysql://{user}:{password}@{host}/{db_name}".format(**{
  'user': Admin_Param.user,
  'password': Admin_Param.passwd,
  'host': Admin_Param.host,
  'db_name': Admin_Param.management_database,
  'charset': 'utf8'
})
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_ECHO'] = True
```
#### 6. ソースサンプル

https://github.com/akeyi2018/Restful-Flask-mysql/blob/main/mysql_test.py
【HTML】スタイルシートの構文

## 固定スタイルシート
常に適用させるスタイルシート。優先スタイルシートから代替スタイルシートに切り替えても、固定スタイルシートは適用される。

~~~html
<link href="URL" rel="stylesheet" type="text/css" >
~~~

## 優先スタイルシート
ページ読み込み時に、優先的に適用されるスタイルシート。
title属性に、優先スタイルシートのグループ名を指定する。同じグループ名のスタイルシートは、同時に適用される。

~~~html
<link href="URL" rel="stylesheet" type="text/css" title="優先スタイルシートのグループ名">
~~~

## 代替スタイルシート
優先スタイルシートの代替となるスタイルシート。ページ読み込み時には適用されず、ユーザーがブラウザを操作し、スタイルシートを切り替えたときに適用される。
rel属性に、stylesheet alternateを指定する。
title属性に、代替スタイルシートのグループ名を指定する。同じグループ名のスタイルシートは、同時に適用される。

~~~html
<link href="URL" rel="stylesheet　alternate" type="text/css" title="代替スタイルシートのグループ名">
~~~

## 最後に
間違っている点がありましたらご指摘お願いします。
ミリ秒が四捨五入されて日付が繰り上がってしまう問題

# 目次
[何が起こったのか](#何が起こったのか)
[原因](#原因)
[解決策](#解決策)

# 何が起こったのか
openpyxlを使ってExcelに日付を出力（表示形式： yyyy-m-d）したところ、データベースの値（期待値）と異なる結果になってしまった。

・Excel
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526938/4082a2d0-3296-0118-496a-1150741e5cd3.png)

・データベース（期待値）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526938/aadf5782-3bcb-8ce9-5031-79329a5b147d.png)

このようにデータベース上は 2022/3/21 なのにも関わらず、Excelに出力された日付は 2022/3/22 となっており、日付が繰り上がってしまっている。

# 原因
<font color="Red">日付のミリ秒が500ms以上だった場合、Excelの仕様で四捨五入されてしまう</font>から。
データベースの値をよく見ると、ミリ秒が付いている。
![ms.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526938/fe73a8b7-bbe7-f8ec-6081-248268c54109.png)
openpyxlでExcelに書き込む際、Excelの日付の仕様でこのミリ秒が四捨五入されてしまい「1秒」になってしまう。つまり、2022/3/21 23:59:59 + 1秒 = 2022/3/22 0:00:00 というわけだ。

:::note info
・ミリ秒が500以上の場合 → 1秒プラスで日付が繰り上がってしまい 2022/3/22 で出力されてしまう
・ミリ秒が500未満の場合 → 0秒プラスで日付は繰り上がらず 2022/3/21 で出力される
:::

# 解決策
openpyxlで日付を書き込む際にミリ秒を切り捨てる
```
セル.value = 日付.replace(microsecond=1)
```

:::note alert
microsecondを0で置き換えるとエクセルの表示がバグるらしいので1を指定
:::

<br>

結果、、、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526938/648f7309-8723-a94a-11e2-a300d1fe2b8c.png)

正しく表示された🎉
Fleet Managerを利用したWindowsインスタンスへのRDP接続

# 概要
SSM(AWS Systems Manager) Session Managerは以下の理由で便利なのでよく使っていた。
- 踏み台サーバ構築する必要ない
- ポート（22,3389）開く必要ない
- AWSコンソールかCLIベースでアクセスできる
- CloudTrailとかS3とかCloudWatch Logsにログ残せる

ひと昔前の運用方式を考えてみると、踏み台サーバたてて、そこから各サーバに接続する方式が多いと思う。かくいう私もそのケースが多い。
だがしかし、踏み台ベースの運用って怖いものがあって、例えば踏み台サーバがSPOFになる事もままある。
そうすると障害が発生した時に踏み台も死んでると何もできないという運用にはあってはならない状況が発生してしまう。
かといって踏み台サーバを冗長化しておくっていうのも費用がかかってしまうのでやっているケースは少ない。
また、他の考慮点として操作ログをどのように取得して保存しておくのか？といった事を考慮して仕組みを作るのも手間だったりする。
LDAP連携とかするのもありだが、結局OSのローカルユーザ作ってログ取るみたいな運用したり、サードパーティのツール入れたりする。
正直そういう仕組み作り自体面倒であったり、ユーザが増えるにしたがって運用が追い付かなかったりする例もよくある。
こういった悩みを解決してくれるサービスとして Session Manager があり便利であるので利用していました。
しかしながら、Windows Serverってコマンドライン(Powershell)よりかはGUI使いたい時ってありますよね。
そんな悩みを解決してくれる手段として Fleet Manager があります。
※結局ログ取る部分はFleet Managerでは現状解決できてなくて、Session Managerのコマンドベースの運用であればログ取れるのだが。

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/fleet-rdp.html

# WindowsインスタンスへのSSM経由での接続方法

という訳で使ってみた。

## 1. SSMマネージドインスタンスとして設定する
以下を参考に管理対象のWindowsインスタンスがマネージドインスタンスとなるようにVPCエンドポイントの設定、EC2インスタンスのIAMロールの設定、Security Groupのアウトバウンドの設定を実施します。

https://aws.amazon.com/jp/premiumsupport/knowledge-center/systems-manager-ec2-instance-not-appear/

順を追って設定する人向け

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/session-manager-getting-started.html

## 2. 鍵またはパスワード認証を用いて接続する
今回はEC2インスタンスを起動する時に指定したキーペアを用いて接続する

- Fleet Managerのコンソールから接続対象のインスタンスを選択してノードアクションからRDP接続を開始する
![01.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198480/5ab64524-38e4-f0f7-8d92-97986b8fa7e6.png)

- パスワード認証か鍵認証か聞かれるのでどちらか選択して接続を開始する
![02.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198480/e3d96f57-228f-fa03-9847-8d89dfc82373.png)

- デフォルトだとセッションの有効期限は**60分**となっている
![03.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198480/874072f2-3383-4978-d370-a5a2fc7ef780.png)

# 使ってみた感想

ポートフォワードして、、みたいなのが要らないので凄く便利だと思いました。
しかしながら、実運用で使う場合はちょっと考えなければならないな～と思うところも多々ありました。
現状はSession Manager(CUI)でログ取りながら操作=>どうしてもGUI使わないと、って時はFleet Managerかなと思いました。

## メリット
- そもそも踏み台サーバが必要ない
  - EC2インスタンスに直接接続できるので踏み台サーバいらない
- インスタンスにGIP要らない
  - SSMの良いところで、パブリックIPを持っていないインスタンスにもFleet Manager経由で接続できる
  - もちろんSession Manager経由でPowershellも使える
- ブラウザ経由でクリップボードのコピー＆ペーストができる
  - ローカル<=>リモート間双方向でのコピペができる
- インスタンスのインバウンドポートを一切解放しなくてよい
  - 余計なセキュリティホールをつくらなくていい
  - GIP変更に伴う穴あけ等が必要ない
- フルスクリーンでの作業も可能
  - ブラウザ経由のRDPでもフルスクリーン対応しているので小文字が苦手な人でも大丈夫
- OS側のグループポリシーで2セッションまで有効化すると同じユーザでFleet Managerから2セッションまでは接続できる
  - 2セッションの両方でWindowsOS内のブラウザを起動した場合、片方のユーザでプロセス掴んでいてEdgeが片方のセッションで上がらなかったりするので使用するアプリケーションによっては同じユーザでの接続は考慮する必要あり


## デメリット
- ブラウザ経由でのファイルの受け渡しはできない
  - フリードマネージャー経由でファイルシステムの表示はできるのですが、ファイルの受け渡しは現状ではできないようです
- 操作ログが取れない
  - Session ManagerであればPowershellでの操作履歴をS3やCloudWatch Logsに操作ログを取れるのですが、Fleet Managerの場合RDP接続での画面操作になるので何かしらツールを入れないとログを取れないみたいです。ここはアップデートしてほしいですね。
- 画面操作に若干ラグがある
  - 人によっては気にならないかもしれないですがフルスクリーンにした時の画面操作（特に入力）の若干のラグが気になりました
- パフォーマンスカウンターでCPU使用率とか取得できるみたいですが、KMSで暗号化していないと機能が使えない
  - KMSの暗号化キーがマネージドインスタンス共通みたいなのでインスタンス毎に分離できるようにできるともっといいと思いました
疑似乱数の周期と必要なバッファの関係

疑似乱数の周期と必要なバッファの関係は

```math:
必要最低限のビット数 = \lceil \log_2 {周期} \rceil
```

で見積もることができます。

例えば mt19937 ですが、周期が 2<sup>19937</sup>-1 なので

```math:
\begin{eqnarray}
\lceil \log_2 \left( {2^{19937}-1} \right) \rceil & = & 19937 \\
\lfloor \frac{ 19937 + 7 }{ 8 } \rfloor & = & 2493 \\
\end{eqnarray}
```

となって、最低でも約 2.5 KB くらいは必要でしょうか。

生成するオブジェクト毎に疑似乱数も個別で割り当てるときは、メモリ使用量を少しは気にした方がいいかもしれません。

---

メルセンヌ ツイスターは発表記事に出ていた乱数特有のパターンがない画像を見て感動しました。

---

タイトルとタグを含め「乱数」→「疑似乱数」の修正をしました。
【Mac】Ruby3.1.1がインストールできなくて辛かった

なんとなく「Rails7を使ってみよう！せっかくだからRubyも新しいの入れとくか！」と思い立ったものの、全然全くインストールできずハマったのでのでその時の対処法をメモ。

実行環境：macOS Monterey(12.2.1)

## エラー
ruby3.1.1をインストールしようとするもエラーを吐かれてしまう。
```
$　rbenv install 3.1.1

Last 10 log lines:
checking for llvm-strip... no
checking for gcc... clang
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables... 
checking whether we are cross compiling... configure: error: in `/var/folders/r1/x9mwdc5s12z1fj1361bzv8z00000gn/T/ruby-build.20220322144842.18804.Za253O/ruby-3.1.0':
configure: error: cannot run C compiled programs.
If you meant to cross compile, use `--host'.
See `config.log' for more details
make: *** No targets specified and no makefile found.  Stop.
```
## やってみた1: Xcode再インストール
ググると「Xcodeを入れ直す直すべし」との声が多数あったので、XcodeとCommandLineToolsを再インストール。
[参考]　https://blog.nocorica.jp/2017/01/rbenv-failed/

→同じエラーが出た。

## ログを読む
`See 'config.log' for more details`と言っているのでとりあえず見てよう（先に見ればいいのに）。
```
$ cd /var/folders/r1/x9mwdc5s12z1fj1361bzv8z00000gn/T/ruby-build.20220322140955.1606.YESPtG/ruby-3.1.1
$ cat config.log

~~略~~
clang: error: argument to '-V' is missing (expected 1 value)
clang: error: no input files
configure:5773: $? = 1
configure:5762: clang -qversion >&5
clang: error: unknown argument '-qversion'; did you mean '--version'?
clang: error: no input files
~~
```
clangが何か失敗してるっぽい。
```
$ clang -v

Apple clang version 13.1.6 (clang-1316.0.21.2)
Target: x86_64-apple-darwin21.3.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
```
ちゃんと入ってるのにね！なんだよ！！！！

## やってみた2（解決）: clangのバージョンを下げる
憤りながらも残念ながら「clangのバージョンを下げてみる」くらいしかやることが思いつかなかったので実行。
[参考]　https://soluna-eureka.hatenablog.com/entry/2021/01/10/201341

llvmをインストール。

```
$ brew install llvm
```
パスを通す。

```
$ vi ~/.zshrc

# 下記を追加
export PATH="/usr/local/opt/llvm/bin:$PATH"
export LDFLAGS="-L/usr/local/opt/llvm/lib"
export CPPFLAGS="-I/usr/local/opt/llvm/include"
# 追加ここまで

$ source .zshrc
```
clangはどうなったのか？

```
$ clang -v

Homebrew clang version 13.0.1
Target: x86_64-apple-darwin21.3.0
Thread model: posix
InstalledDir: /usr/local/opt/llvm/bin

# 元の状態
Apple clang version 13.1.6 (clang-1316.0.21.2)
Target: x86_64-apple-darwin21.3.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
```
ちょっと変わった。うーん意味があるか怪しいが、リベンジ。
```
$　rbenv install 3.1.1

~~略~~
Installed ruby-3.1.1 to /Users/hoge/.rbenv/versions/3.1.1
```
インストールできました！！やったね！！！！