OmniAICreator/Qiita-1.07M huggingface dataset:
Databricks worker nodeのコンソールに入るには？


# 概要

Databricksではクラスターを作成すると自動的にオートスケールでサーバーが拡張されていきます。Sparkなのでドライバーノードとワーカーノードに分かれててワーカーノードがスケールしていきます。

通常コンソールに入ることはありませんが、構成ファイルをのぞいたりトラブルシューティングしたりと入りたくなることはありますよね。

ワーカーノードに入るにはちょっとしたコツが必要ですのでここで紹介したいと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2206424/23ae8a30-28c1-6b3b-627f-87dca70c6fd9.png)


# ドライバーノードのコンソールに入るには？

まずはドライバーノードのコンソールに入ってみましょう。
こちらは簡単です。

Databricksの　Comupute画面で対象となるクラスターを選択し、「Apps」 - 「Web Terminal」をクリックすると以下のようにコンソールに入れます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2206424/716665ea-4849-7dfe-6551-bba8310dd2d2.png)


# Workerノードに入るには？

続いて本題であるWorker nodeに入りたいと思いますが、直接コンソールには入れないので一旦Driver　Nodeのコンソールに入ってから、sshで接続します。

対象となるワーカーノードは勝手にスケールするため、まずは対象となるIPアドレスをチェックします。

「Spark cluster UI - Master」 にて、ワーカーノードのIPが確認できます。ご覧の通りPravate IPしか付与されてませんので、ローカル端末からは直接アクセスはできません。Driver Nodeのコンソールからアクセスしましょう。
もしどうしても直接アクセスしたい場合は[こちらの方法](https://docs.databricks.com/clusters/configure.html#ssh-access-to-clusters)で　Security Group変更など諸々することで可能になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2206424/7b06524d-8947-fdd4-26f8-79b159252461.png)

次にSSHの設定をします。

### 1) Driver　node　コンソールで、SSH Key pairを生成します。

```
ssh-keygen -t rsa -b 4096 -C "email@example.com"
```

その際にkeyの保存先を /dbfs 配下にしておくと、サーバーが再起動してもkeyが消えません。（他の人と被らないように事前に個人のdirectoryを作成しておくといいでしょう）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2206424/175b1aa2-35c6-ed85-3d12-87c38556e4df.png)

### 2) Public keyを登録する。

先ほど生成された public keyを対象ノードに登録します。
登録方法は２つあります。クラスターのSpark設定にて登録する方法と、notebookを適用する方法です。 notebookだとサーバー再起動なしで適用できます。

#### 方法１）　クラスター設定画面から

Cluster構成の Advance Option画面で、以下のようにSSHの public keyを登録することができます。これによって全worker nodeにpublic keyが登録されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2206424/cc600b73-0b82-dc95-7a52-824f68890878.png)

#### 方法２）　Notebookから

方法１のやり方だと設定を反映させるため再起動が必要になりますが、こちらの方法だとライブで適用できます。そのためprivate keyを/dbfs上に保存しなくてもOKになりますが、クラウスターがシャットダウンされると結局同じなのでやっぱ /dbfs上に保存しといた方がいいかも。

新規ノートブックを適当に作成して、以下のコードをコピペします。一番上の""(ダブルクォーテーション)で囲まれた箇所にpublic keyを貼り付けて、対象となるクラスター上で実行します。（public keyは変なところで改行されていると無効になるので要注意）

```
val publicKey = " put your public key here "

def addAuthorizedPublicKey(key: String): Unit = {
  val fw = new java.io.FileWriter("/home/ubuntu/.ssh/authorized_keys", /* append */ true)
  fw.write("\n" + key)
  fw.close()
}

val numExecutors = sc.getExecutorMemoryStatus.keys.size
sc.parallelize(0 until numExecutors, numExecutors).foreach { i =>
  addAuthorizedPublicKey(publicKey)
}
addAuthorizedPublicKey(publicKey)
```




### 3) SSHで接続する

接続する際に、保存した id_rsaファイルをドライバーノード上に保存し、アクセス権を絞る必要があります。なぜなら sshでprivate key(id_rsa)は最小特権にしておかないとエラーになります。ただし/dbfs上はアクセス権を変更できない仕様になっているのです。そのため一旦ドライバーノードにコピーしてから権限を変更するという面倒な作業が必要になります。接続先のホスト名は最初にチェックしたIPアドレスで入りましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2206424/75b0a705-9c17-fd93-514d-e5a73e7b1132.png)

これでワーカーノードにも入れるようになりました。

マニュアルのやり方だとローカル端末から直接ワーカーノードに入るため2200ポートを経由しないといけませんが、この方法だとデフォルトの22ポートで入れます。
【AlacrittyでDropDown】iTermライクにショートカットで表示/非表示を爆速で切り替える

# 概要
iTermでは、ショートカットキーを使ってターミナルの表示/非表示を切り替えることができます（いわゆるDropDownTerminal）。

これがかなり便利なのですが、残念ながらAlacrittyにはその機能がありません。Alacrittyの開発者はその機能の開発に前向きではないようなので、当分はその機能の導入もなさそうです（[イシューにて議論された](https://github.com/alacritty/alacritty/issues/4268)）。

ということで、Alacrittyに頼らずに実現します。環境はMacです。

# 完成品
この記事で紹介するやり方で、次のようになります。（GIF画像ですが再生されない場合は画像をクリックしてみてください）

* 設定したキーボードショートカットを押すとターミナルの表示/非表示が切り替わる
* ウィンドウ表示でも動作するし、Alacrittyを全画面表示にしていてもオーバーレイ表示させることができる

![図1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1050360/676a9bc1-5b5b-7846-405e-92d291700b7e.gif)
ただし、下のウィンドウが全画面表示のときは、その上にオーバーレイで表示できなくて、Alacrittyのウィンドウを置いてるデスクトップまで移動してしまいます。この点においてはiTermと全く同じようにはできないのが、あと一歩という感じですね、、、。
![図2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1050360/d4575e60-694c-6f6a-0677-b1cb9dcb19e0.gif)

# やり方
## Hammerspoonのインストール
Hammerspoonというアプリを使って実現します。これはMac標準搭載のAutomatorに近いもので、関数を定義して実行することでMacを自動操作できるようにするものです。ここでは、「アプリを表示する、非表示にする」というMacの操作をHammerspoonから呼び出すことで、Alacrittyのウィンドウ操作を実現します。

まずは、Hammerspoonを公式HPよりインストールしてください。
1. [公式HP](https://www.hammerspoon.org/)の上部のDownloadをクリック
2. 「Hammerspoon-{バージョン番号}.zip」をダウンロード
2. zipを解凍したらHammerspoon.appが解凍されるので、アプリケーションフォルダに移動して起動する

## 表示/非表示を切り替え可能にする
Hammerspoonを起動したら、右上のメニューバーから「Open config」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1050360/064169d0-a9df-7e20-1e90-f05dbb4eee41.png)

init.lua というファイルがデフォルトのエディタで開かれるはずなので、下記コードを入力して保存します。（開かれなかった場合、 `~/.hammerspoon/init.lua` を作成してそこに入力してください）

```
hs.hotkey.bind({"ctrl"}, "q", function()
  local alacritty = hs.application.find('alacritty')
  if alacritty:isFrontmost() then
    alacritty:hide()
  else
    hs.application.launchOrFocus("/Applications/Alacritty.app")
  end
end)
```

コードの解説です。
* 1行目：ctrl+qが押されたら次の関数を実行せよ（ctrl+qは任意に変えて良い）
* 2行目："alacritty"というアプリをMacから見つけよ
* 3-7行目：alacrittyが最前面にあるなら非表示にして（cmd+hと同様）、そうでないなら起動または最前面にせよ

設定が完了したら、右上のメニューバーのHammerspoonから「Reload config」をクリックします。これで、Alacrittyを起動したらctrl+qで表示/非表示が切り替えられるようになったはずです。

※Hammerspoonにアクセシビリティの権限を付与する必要があります。設定できていなかったら、「システム環境設定」→「セキュリティとプライバシー」→「アクセシビリティ」でHammerspoonに権限を付与してください。
※うまく動作しない場合、ショートカットキーがMacで設定されている何かと被っている可能性があります。HammerspoonからConsoleを開いて重複エラーが出力されていたら、その問題が生じているため解消する必要があります。Macの設定で割当てられているショートカットを無効にするか、Hammerspoonに割り当てるショートカットを別の重複してないものにしてください。

## Alacrittyの全画面表示を有効にする
`~/.config/alacritty/alacritty.yml` を開いて、次の行を追加します。

```
key_bindings:
  - { key: Return, mods: Command, action: ToggleSimpleFullscreen }
```

こうすると、cmd+エンターで全画面モードに入るようになります。

さらに僕は、半透明にすることでターミナルを利用しながら後ろの情報が見れるようにしています。

```
window:
  opacity: 0.9
```

# さいごに
これで、Alacrittyがさらに便利になったと思います。快適なシェルライフを楽しみましょう。
AlacrittyをiTermライクにするための様々な方法を今後上げていく予定なのでご期待ください。
C#スクリプト実行(CSharpScript)


# 概要
C#で動的に生成したコードを実行できることが最近（といっても数年前の.Net Framework4.6から）できるようになり
今回、動的に複雑な分岐条件等を組み合わせ実行するアプリを作成するため、調査したため、メモを残しておきます

## コンポーネントインストール
NuGetから下記を選択しインストールします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172093/7c61728c-6d20-1ac6-2b18-474f95090f66.png)

参照に下記コンポーネントが追加されていることを確認します
Microsoft.CodeAnalysis
Microsoft.CodeAnalysis.CSharp
Microsoft.CodeAnalysis.CSharp.Scripting
Microsoft.CodeAnalysis.Scripting

## コード実装

### CSharpScript宣言
下記を宣言します

```
using Microsoft.CodeAnalysis.CSharp.Scripting;
```

### スクリプト処理
CSharpScriptクラスから「EvaluateAsync」メソッドを呼び出し、引数に動的に実行したいコードを渡し実行します

```
await CSharpScript.EvaluateAsync<戻り値変数の型>(スクリプトコード);
```

### コード実装例１
もっとも単純な実装例です
C#スクリプトで変数同士の加算をし、結果を戻すサンプルになります
非同期に実行されるため、メソッドにasync キーワードを宣言する必要があります

```
private async void doCsScript()
{
     var result = await CSharpScript.EvaluateAsync<int>(@"var x = 1;var y = 2;x + y");
     System.Console.WriteLine(result);
}
```

上記を実行すると、スクリプトが実行され、下記のように表示されます

```
3
```

### コード実装例２
C#スクリプト内でusingディレクティブ宣言を実施
非同期で戻り値を取得しています

```
private void doScript()
{
    //--------スクリプトコードを生成
    StringBuilder actionCodeStr = new StringBuilder();

    //宣言の定義
    actionCodeStr.AppendLine("using System.Collections.Generic; ");
    actionCodeStr.AppendLine("using System; ");

    //処理コードを生成（List変数に数字を追加）
    actionCodeStr.AppendLine("List<int> result = new List<int>(); ");
    for (int i = 0; i < 4; i++)
    {
        actionCodeStr.Append(@"result.Add(").Append(i).AppendLine(");");
    }
    actionCodeStr.AppendLine("result ");
    System.Console.WriteLine(actionCodeStr.ToString());

    //--------スクリプトを実行
    Task<List<int>> task = doCsScript(actionCodeStr.ToString());

    //結果を出力
    foreach (int rs in task.Result)
    {
        System.Console.WriteLine(rs);
    }
}

private async Task<List<int>> doCsScript(string actionScrptStr)
{
    return await CSharpScript.EvaluateAsync<List<int>>(actionScrptStr);
}
```

上記を実行すると、スクリプトが実行され、下記のように表示されます

```
0
1
2
3
```
tkinterでGUI作成①

##動作環境

windows 10
python 

##tkinterとは
Pythonでグラフィック表示・操作（GUI）のソフトウェアを開発するためのライブラリの一つ
https://e-words.jp/w/Tkinter.html

他にもライブラリはありますが、使いやすそうだったので、tkinterを使用しました。

##簡単な計算アプリの作成

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

app = tk.Tk()
app.geometry("400x400")
app.title("calculator")
class func(tk.Frame):
    def calculation():
        a = float(val_a.get())
        b = float(val_b.get())
        ope = str(ope_com.get())
        if ope == "+":
            answer = a + b
        elif ope == "-":
            answer = a - b
        elif ope == "*":
            answer = a * b
        elif ope == "//":
            answer = a // b
    
        answer_ent.delete(0, tk.END)
        answer_ent.insert(0, answer)
    
#val_a
val_a = tk.Entry(app,width=4)
val_a.place(x=10, y=30)
#operator
ope_com = ttk.Combobox(state='readonly',width=2)
ope_com["values"] = ("+","-","*","//")
ope_com.place(x=50, y=30)
#val_b
val_b = tk.Entry(app,width=4)
val_b.place(x=90, y=30)
#計算ボタンの作成
btn = tk.Button(app, text="計算", command=func.calculation, height=1,width=4)
btn.place(x=130, y=30)
#answer
answer_ent = tk.Entry(app,width=10)
answer_ent.place(x=200, y=30)
app.mainloop()
```

##出力結果
計算がうまく出来ています。
![スクリーンショット (32).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/524433/5667b0a6-8fd1-75cc-ea5a-6c442a8390de.png)

##まとめ
今後また時間があるときにさらに機能を実装していこうと思います。
【Project Euler】Problem 55: リクレル数

* 本記事は[ProjectEuler](https://projecteuler.net/about)の「100番以下の問題の説明は記載可能」という規定に基づいて解答のヒントが書かれていますので、自分である程度考えてみてから読まれることをお勧めします。

### 問題 55. リクレル数

原文 [Problem 55: Lychrel numbers](https://projecteuler.net/problem=55)

**問題の要約：$N<10^4$のリクレル数を求めよ**

[リクレル数(Wikipedia)](https://ja.wikipedia.org/wiki/%E3%83%AA%E3%82%AF%E3%83%AC%E3%83%AB%E6%95%B0)に詳しい説明がありますが、リクレル数とは桁を逆順にしたものとの和を求める操作を繰り返して回文数(Palindromic number)にならない自然数とのこと。永久にならないかどうかは証明されていないそうですが、ここでは$N<10^4$の自然数で回文数になるものは50回以内になると仮定してよいということなので、50回のループでならなかったものをリクレル数とします。

回文数のチェックは「[【Project Euler】Problem 4: 回文数になる積](https://qiita.com/masa0599/items/6d2cb8251213aa810101)」で作った関数**isPalind**を使います。

従ってリクレル数かどうかをチェックする関数**isLychrel**は以下のようになります。回文数なるばあいはそのループ数を、50回でならなかったら0を返します。

```python
def isPalind(s):   # s: string
  return s==s[::-1]

def isLychrel(n,rep=50):
  for i in range(rep):
    n = n + int(str(n)[::-1])
    if isPalind(str(n)): return i+1
  return 0

print(f"Answer : {[isLychrel(n) for n in range(1,10000)].count(0)}")
```

このリクレス数ですがWikiによると2019年に非リクレル数の最大ステップ数が更新されて288となったそうです。その一つを**isLychrel**で確認してみます。

```python
print(isLychrel(12000700000025339936491,300))
# 288
```

（開発環境：Google Colab）
jquery非対応のWebページのコンソールでjqueryを使う

ひと様のサイトのコンソールで jquery を使おうとして怒られる事ありますよね。
よく見ると対象ページで jquery を使っていなかったりします。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209937/d448c925-4aa6-b73f-b1ef-e6519a3a0501.png)

そんな時はコンソールから直接 jquery を突っこんでしまいましょう。

```
var script = document.createElement('script');
script.src = "https://ajax.googleapis.com/ajax/libs/jquery/1.6.3/jquery.min.js";
document.getElementsByTagName('head')[0].appendChild(script);
```

これでjqueryの機能が使えます。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209937/31f0ec4d-a991-c8b5-74da-d422ed8fb24d.png)

参考：https://stackoverflow.com/questions/31912136/each-is-not-a-function
railsのデプロイがうまくいかない時（マイグレーションファイル編）

# 前提
本番環境（herokuやfly,AWSなど）とローカル環境（自分のパソコン内）ではマイグレーションが最初から行われて、別々のDBが作成されます
よって、ローカルではうまく行っている場合でも本番環境ではDBがうまく構築されないことがあります

原因はいくつか考えられますが、

- ローカルのDBにテーブルやカラムを作った後にマイグレーションファイル削除した（本番環境のDBでは、必要なテーブルやカラムが作られていない）
- テーブルがないのにカラムを追加しようとしている
- 同じ名前のカラムを追加しようとしてる
- 作成されてないテーブル、もしくはカラムを削除しようとしている
- ファイル名とクラス名が一致していない

などが挙げられます！

# マイグレーションファイルを整理する
本番環境ではマイグレーションファイルを元に1からDBが作成されます
よってマイグレーションファイルを見やすくわかりやすくしてあげる必要があります。
ポイントをまとめます！

- 1マイグレーションファイルに1テーブル
- テーブルとカラムは一度に作成してあげる(カラムだけを追加するマイグレーションファイルは削除する)
- カラムやテーブルを削除するマイグレーションファイルは消す
- 外部キーを使う場合(コメント機能や、いいね機能でアソシエーションしてる場合)は親テーブルを先に作成する。（マイグレーションファイルをリネームすれば順番を変更できます）
- 存在しないカラムやテーブルを操作(追加や削除など)していないか

以上の観点があればマイグレーションファイルをスッキリできると思います！

# ローカルでうまく動作するか確認

マイグレーションファイルを整理したら、ローカル上でちゃんとデータベースが構築されるか確認しましょう！

```
$ rails db:migrate:reset
$ rails db:seed #seedを使ってる場合に実行
```

うまくいかないでエラーになる場合があるのでその場合はこちらの記事を参考にしてください

https://qiita.com/kakeru0520sou1/items/092b9e633e085ad19e9b

ここでサーバーを起動し、エラーが出ないことを確認しましょう

# 変更したマイグレーションファイルをデプロイ(fly.ioの場合)
```
$ flyctl deploy
```

# 変更したマイグレーションファイルをデプロイ(herokuの場合)

```bash
$ git add -A
$ git commit -m "マイグレーションを変更"
$ git push heroku master # もしくは git push heroku main
````

 heroku上でDBリセット、マイグレーション

```bash
$ heroku pg:reset DATABASE
#=> アプリ名の入力を求められるので入力
$ heroku run rails db:migrate
$ heroku run rails db:seed #seedを使ってる場合に実行
```
これでうまくいくと思います！
システム設計の原則を読んだことで実装についても考えるようになった話

## 初めに
初めて保守の案件を任されたときのサービスは
5年以上続いているものでした。
LaravelとPHPで構築されています。
初めてということもありソースコードを見たときは
僕自身の理解力が足りなくて難しく見えているだけだと思っていたのですが
徐々に知識がついてきて
割とクソコードなんだなと思うようになってきた。
だけどそんなクソコードに対してどうにかしたいという気持ちはあっても
何をしたらいいのかもわからずただただ思うだけで過ごしていました。
そんな状態で過ごしているときに @MinoDriven さんとお話しする機会があり
そこから設計について改めて学びたいなと思い
[「現場で役立つシステム設計の原則」](https://amzn.to/3JyP7QL)
を読んでみて眼からうろこが出続けてしまったので
みんなにも同じように体感してほしいと思ったので纏めました。
僕自身の解釈、表現をしているので書籍が伝えたいこととは違うように
伝わるかもしれませんので正しい情報はしっかりと書籍を読むことをお勧めします。

## 「実装ができる」だけは自己中な状態
僕自身は異業種からプログラマーに転職をしたのですが
転職を成功させるためにとにかく「実装ができる」状態を目指して勉強していました。
おかげさまでプログラマーになることもできたし実装自体はできるねと言われることもあります。
そんなときに一つの保守対応を任されたのですが
なんともソースコードがクソなわけなのです。
何がクソかというと一つの機能に対して修正を行ないました。
初めにソースコードを読み解くのにも時間がかかりながらも改修したのですが
他の機能にも影響を及ぼしていて障害に繋がってしまったのです。
え！？保守怖い！
ってなったのですが僕自身の力不足も起因しているのは間違い無いです。
でも未然に防ぐことはできなかったのか、
障害になったとしても障害範囲を限定的にできなかったのだろうか。
「実装ができる」としてそのときは良くても半年なのか1年後なのか
時間をあけて何かしらの負担が自分もしくは他の人に返ってくる。
「実装できる」だけにならないようにしていきたい。

## クソコードを書かないようにしたい
自分では会心の出来のコードでも
第三者から見たらクソコードなんてことは
よくあることだろう。
いきなりはできないにしても少しずつ
クソコードを書かなくなれたら
個人的にも
チームとしても
そしてサービスを利用する人にとっても
不満の少ない状態にできるようになるはずです。

## クソコードはとにかく影響範囲が広かった
一覧を表示するのに時間がかかるということで
「一覧を表示する」処理に対して改修を行った。
なのに別の画面で障害が発生する

僕が体感したクソコードの一つだが
何が悪いかというと
「一覧を表示する」ことに対して修正をしたのに
他に影響を及ぼしていること。
変な部分を共通化してしまったことによる弊害だ。
例えば

- ユーザー一覧画面でユーザー全てを一覧で表示する
    - IDの昇順で表示する

- 商品一覧画面で商品全てを一覧で表示する
    - IDの昇順で表示する

といったことがあったときに
「一覧で表示」の処理を共通化したようなイメージ
改修したいことは商品を人気のある順で表示するようにしたい。
だけどユーザー一覧画面のIDの昇順で表示のことを知らずに改修したら
ユーザー一覧画面の表示がおかしくなる。
やりたいことはただ商品を人気のある順にしたいだけなのに
ユーザー一覧画面のことも気にしないといけなくなる。
これは無駄なことだ。

## 何でも共通化するのは悪手である
オブジェクト指向をかじり始めるとなんでも共通化したくなる
共通化自体は悪いことでは無いにしても無鉄砲に行ってはクソコードの助長になる。
共通化するにあたってやりたいことをしっかりと考えよう。
「業務の関心事」を整理するといい

ユーザー一覧画面
- 全てのユーザーを表示
- IDの昇順

商品一覧
- 全ての商品を表示
- 人気のある商品順

それぞれのやりたいことがわかった。
「表示する」という部分は似ているが
やりたいことは全然違う
なので上記の2つの画面に対して互いに共通化した処理を使うのは
そもそもで悪手である可能性が高い。

## 業務の関心事に対してしっかりと考える
実装する機能に関してしっかりと何をするのか、したいのかを考える
ただ、開発者だけで考えてしまうとクライアントが求めることとズレが生じてしまう。
可能な限りクライアントの求めることも聞いて理解した上で考える

:::note info
別な話になるけどプログラマーは人と関わらなくてもいいみたいな話があるけど
関わらないでもコードは書ける。
ただ間違いなくクソコードを製造しているだろうとは思う。
:::

詳しくは書籍を読んでほしいけど
スタートラインとしては
**「業務の関心事」について考えて書き出すこと**から始めるといいと思う。

## 過度な共通化を避ける事でのメリット
過度な共通化を避ける事で僕のように改修をして
障害を起こすことはなくなるだろう。
障害が起こったとしても被害は必要最低限になるはず。
オブジェクト指向を身につけ始めは特に注意をして
共通化を図ってほしい

僕の話ですが「業務の関心事」を意識して考えてから実装したコードは
改修するときにシンプルに考えて改修することができました(別な案件での話)


## 注意
僕自身の解釈で表現をしているので理解しづらい部分が多いと思います。
しっかりと理解したい方は書籍を読んでください。
[「現場で役立つシステム設計の原則」](https://amzn.to/3JyP7QL)
さくらのクラウドのロードバランサを使ってswarm上のコンテナへのアクセス経路の可用性を高める

# 概要

さくらのクラウドのロードバランサとそれに付帯するVIPを使って
Docker swarm上にデプロイしたコンテナへの可用性高め(たぶん)の
アクセス経路を作る。

## 経緯

[DockerSwarmでデプロイしたコンテナを自動的にtraefikに登録する](https://qiita.com/tomokitamaki/items/6ac31ec5a214e2bb01c9) で
traefikを使ったものの、vipの設定やソーリーサーバの設定やそれらの管理が
面倒くさいなぁって思ったので。

## 前提

- docker swarmとswarm上でtraefikが動く環境は出来ている
    - 参考: [docker swarmを一から構築してrolling updateとかしつつ、HAproxyを介してサービスを公開する](https://qiita.com/tomokitamaki/items/9424057b07b3ea6b3cc9)
    - 参考: [DockerSwarmでデプロイしたコンテナを自動的にtraefikに登録する](https://qiita.com/tomokitamaki/items/6ac31ec5a214e2bb01c9)
- docker swarmを構成するホストのOSはubuntu 20.04 LTS

```shell
ubuntu@swarm-manager001:~$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=20.04
DISTRIB_CODENAME=focal
DISTRIB_DESCRIPTION="Ubuntu 20.04.3 LTS"
ubuntu@swarm-manager001:~$ 
```

# さくらのクラウドのロードバランサを作る

公式のマニュアルに沿ってGUIからポチポチして作る。
公式のマニュアルは [https://manual.sakura.ad.jp/cloud/network/load-balancer.html](https://manual.sakura.ad.jp/cloud/network/load-balancer.html) です。

- 今回はVIPを "192.168.1.201" とする
- ソーリーサーバは今回は省く
- ポート番号は80で待ち受ける
- 実サーバにはmanagerのnodeを登録しておく

# 実サーバ側の設定を行う

公式マニュアルの https://manual.sakura.ad.jp/cloud/network/load-balancer.html#id15 に書いている事をする。
補足しつつ書き出すと以下のようになる。

## カーネルパラメータの設定

/etc/sysctl.conf の最後に

```plaintext
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
```

を追加して、 `$ sudo sysctl -p` で反映させる。

# ネットワークの設定

/etc/netplan/01-netcfg.yaml (他のファイル名の場合は適宜読み替える)に
以下の感じで追記する。

```yaml
    lo:
      match:
        name: lo
      addresses:
        - ロードバランサのVIP

e.g.
network: 
  ethernets: 
    eth0: 
      addresses: 
        - 192.168.1.20/24
      dhcp4: 'no'
      dhcp6: 'no'
      gateway4: 192.168.1.254
      nameservers: 
        addresses: 
          - 133.242.0.3
          - 133.242.0.4
        search: 
          - localdomain
    lo:
      match:
        name: lo
      addresses:
        - 192.168.1.201/32 #ロードバランサのVIP
  renderer: networkd
  version: 2
```

## iptables の設定

以下を実行する。

```shell
sudo iptables -t nat -A PREROUTING -d ロードバランサのVIP -j REDIRECT

e.g.
sudo iptables -t nat -A PREROUTING -d 192.168.1.201 -j REDIRECT
```

# ロードバランサのVIPにアクセスしてみる！

httpかつtraefikのルールに合致したアクセスしてバランシングされてたら出来上がり！

# その他

自分のドメインを持っていたら、さくらのクラウドのDNSアプライアンスを使って、
`*.lb.自分の.ドメイン A ロードバランサのVIP` みたいなAレコードを
DNSアプライアンスに設定すると名前解決出来るようになって楽しくなる。
私がインストールしたwingetのパッケージ

を紹介します。さくっとWindowsの環境構築をしたい時に参考になれば幸いです😃

``` powershell
winget install 7zip.7zip
winget install Adobe.Acrobat.Reader.64-bit
winget install Amazon.AWSCLI
winget install "XAMPP 8.1"
winget install Docker.DockerDesktop
winget install Emurasoft.EmEditor
winget install GIMP.GIMP
winget install Git.Git
winget install GitHub.cli
winget install GitHub.GitHubDesktop
winget install Google.AndroidStudio
winget install Google.Chrome
winget install Google.Drive
winget install Google.JapaneseIME --force
winget install hluk.CopyQ
winget install LINE.LINE
winget install Microsoft.AzureCLI
winget install Microsoft.VisualStudioCode
winget install Mozilla.Firefox
winget install Nota.Gyazo
winget install OpenJS.NodeJS
winget install Oracle.JDK.17
winget install Postman.Postman
winget install Python.Python.3
winget install SlackTechnologies.Slack
winget install TeraTermProject.teraterm
winget install TheDocumentFoundation.LibreOffice
winget install WinMerge.WinMerge
winget install Zoom.Zoom
winget install Rufus.Rufus
winget install TimKosse.FileZilla.Client
winget install Sayuri.FFFTP
```
メイクアプリやヘアカラーアプリがiOSでつくれる　機械学習で顔の部品を分離

#機械学習をつかって顔のパーツを取得する方法
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/a5f969fa-a577-8662-9259-582ea8d6230e.png width=800>

#髪の毛だけ、目だけ自動で分離したい
髪の毛や唇など、狙ったパーツを細かく分離できれば、そこだけ色を変えてメイクアプリがつくれる。
ユーザーに輪郭をなぞってもらってカットするのでは、めんどうくさい。
自動でやりたい。
しかも、髪の毛などはなるべく細かいところまで丁寧に切り取りたい。
しかし、自動で、細かく切り取ることは可能なのだろうか。

#ねらったパーツをとれるフレームワークはなかなかない

機械学習にセマンティックセグメンテーションという分野がある。
画像をオブジェクトごとに細かく分離できる技術だ。
技術向上により、かなり細かく切り取れるようになってきている。

iOSにもセグメンテーションをつかえるフレームワークがある。
しかし、AVFoundationでPortraitMatteがとれるが、全身、スキンや歯は取得できても、髪の毛や目だけは分離できない。
しかも、カメラの撮影時に設定する必要がある。
Visionや機械学習モデルのPersonSegmentationも全身だ。

#機械学習であっという間に解決

さてそこで役に立つのが、face-parsing　という顔の部品ごとのマスクを学習したセマンティックセグメンテーションモデルである。
オリジナル・プロジェクト：

https://github.com/zllrunning/face-parsing.PyTorch

これが髪の毛や左右の目、唇や帽子まで、細かく分離してくれる上、精度もなかなか良い。
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/a5f969fa-a577-8662-9259-582ea8d6230e.png width=300>

これをiOSで使えば、メイクアプリも作れそうだ。

ということでiOSように変換したモデルを用意した。
<img src="https://user-images.githubusercontent.com/23278992/147860040-14a7e022-5490-4e51-98cd-cd421066dd8c.png" width=300><img src="https://user-images.githubusercontent.com/23278992/147860042-d27f37b0-227b-45ab-8d76-f6c6f2f5b3a4.png" width=300>
精度も変わらない。

# 具体的手順
iOS用に変換ずみのface-parsingモデルは[**CoreML-Models**](https://github.com/john-rocky/CoreML-Models)からダウンロードできる。

https://github.com/john-rocky/CoreML-Models

あとは、Visionでリクエストすればモデルを実行できる。

```swift
do {
    let model = try faceParsing(configuration: MLModelConfiguration()).model
    let vnCoreMLModel = try VNCoreMLModel(for: model)
    let request = VNCoreMLRequest(model: vnCoreMLModel)
    guard let ciImage = CIImage(image: uiImage) else {fatalError("Image failed.")}
    let handler = VNImageRequestHandler(ciImage: ciImage, options: [:])
    try handler.perform([coreMLRequest])
    guard let result = coreMLRequest.results?.first as? VNCoreMLFeatureValueObservation else {fatalError("Inference failed.")}
} catch let error {
    print(error)
}
```

結果は、パーツに対応する数値のピクセルだ。
ピクセルが512*512あって、たとえば、1は肌、２は左眉というる風に対応する数値になっている。

この数値の配列を白黒の画像に直してつかう。

モデルの実行から画像変換、メイクまで、サンプルコードはこちら。

https://github.com/john-rocky/CoreML-Face-Parsing

 <img src=https://user-images.githubusercontent.com/23278992/147891299-c6ced0cb-65a6-4595-a187-df0cacee28dc.gif width=300>

取得したマスク画像で合成すれば、ヘアカラーやメイクができる。

【ヘアカラーとリップを合成】
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/d5222f6e-3a95-7249-5d0b-b450b52e7186.png" width=300><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/ad3b6676-f507-9bb9-7626-ff4e7bddbf69.jpeg" width=300>

🐣
****
フリーランスエンジニアです。
お仕事のご相談こちらまで
rockyshikoku@gmail.com

Core MLやARKitを使ったアプリを作っています。
機械学習／AR関連の情報を発信しています。

[Twitter](https://twitter.com/JackdeS11)
[Medium](https://medium.com/@rockyshikoku)
biosim4 生物作ってみた。進化した。【バイオシム】【マシーンラーニング】【C++】


# 概要
生物が自然淘汰の原理で進化する過程をシミュレートできるbiosim4という素晴らしいプログラムを紹介します。
まず試しで作った生物の活動記録を見てみましょう。

### 0世代目
全体を上下右左を単純に東西南北の生息位置を表しており、色が個体の多様性を表していると考えてください。

![Screenshot from 2022-01-03 06-14-31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1409234/7adff1b9-6a05-202f-4b32-5d609d417e9c.png)

### 50世代目
実は、0世代目で真ん中を円形部分を絶滅する設定にしていたのです。生まれた子が生存区域に移動した場合、伝達されて次の世代に情報を引き継がれることになります。この世代でもうすでに生存のための行動がほとんどの個体で行われています。

![Screenshot from 2022-01-03 06-16-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1409234/9de01987-28d7-e0f5-a81a-d283ac8a1691.png)

### 2000世代目
多様性もランダムな動きも減って、揃ってびっしりと端に移動するようになりました。

![Screenshot from 2022-01-03 06-17-12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1409234/6192d666-7ac6-1e23-c278-e82171d5f3f1.png)

グラフを見ると100世代目あたりから生存どころか人口も増えその後何百世代も安定した人口のサイクルが続いているようです。2000世代以降からは多様性も減ります。優秀で強い遺伝子が残るのでしょうか。

![log.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1409234/2a413c60-093c-65a8-7b05-0585237dc172.png)

[マシーンラーニングで人類の変化を体験](https://mikotostudio.hashnode.dev/biosim4-c)のステップガイドに沿ってご自身でも使ってみることができます。
開発者の[Youtube](https://www.youtube.com/watch?v=N3tRFayqVtk)は、更に深く面白い知識をたくさん紹介してくれているので是非ご覧ください。今回のタイトルもオリジナルの動画と合わせてみたものなのです。字幕つきなので自動翻訳で日本語でも観られます。(字幕オンにした後、もう一度CCのボタン押し自動翻訳を選択して日本語に設定します。)

余談になりますが、本稿の掲載許可を開発者にとったところ快くお返事をいただけました。
それどころか、『画像と併せて説明してあってみやすいね』とコメントもいただくなど気さくな人柄が垣間見えました。
モーションを検知したら LED を光らせる

モーションセンサーで動きを検知したら LED を 5 秒間光らせます。

<iframe width="560" height="315" src="https://www.youtube.com/embed/MAVaiC4jV1M" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

用意するもの:

- モーションセンサー HC-SR501
- LED
- 1kΩ の抵抗

配線:

GPIO 20 <-> 1kΩ <-> LED <-> GND
モーションセンサー (HC-SR501) は写真左から Raspberry Pi の GND (オリーブ色?), GPIO 21 (赤), 5V (青) へ接続

![IMG_0469.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360970/50fda325-6c7e-bf7f-8b15-7a9d2d774ae7.jpeg)

以下のようなプログラムで動くはずです。

```
import RPi.GPIO as GPIO, time, datetime

# GPIO Set Up
GPIO.setmode(GPIO.BCM)

motionPin = 21
GPIO.setup(motionPin, GPIO.IN)

ledPin = 20
GPIO.setup(ledPin, GPIO.OUT)

interval = 5

while True:
    try:
        # if a motion is detected, the LED is ON for 5 seconds.
        if GPIO.input(motionPin) == GPIO.HIGH:
            dt = datetime.datetime.now()
            print("Motion detected: " + dt.now())
            GPIO.output(ledPin, GPIO.HIGH)
            time.sleep(interval)
            GPIO.output(ledPin, GPIO.LOW)
        else:
            time.sleep(interval)

    except KeyboardInterrupt:
        break


# GPIO Clean Up
GPIO.cleanup()
```
正規表現を、まとめてみる

今回は再開初回なので取り敢えず、
技術屋というかエンジニア必修科目の一つ、正規表現についてまとめてみます。

- そもそも正規表現とは
- 応用範囲と資料
- エクセルで正規表現を使うには

エクセルは検索・照合に力を発揮するアプリケーションですが、
通常の操作や関数としては、正規表現を扱うことは出来ません。
ただ環境を整理したり、ＶＢＡから呼び出すことで機能を扱うことが出来ます。

## そもそも正規表現とは何だ？

よく値の照合や検索を行う時に、変数の一致（=）以上・以下（<=・>=）等しくない（<>）NOT（否定）といった演算子で、データ全体や一部を比較する手段はあるのですが、部分的な照合を取る時には、あらかじめ分割したデータに対して前準備をした上で比較演算を行うことを、想定されるパターン全てに用意する必要性に迫られる時があります。

例えば、比較演算では手間がかかる処理があります。

```VBA:比較演算子での照合
Dim txt As String, keyword As String
Dim cnt As Long, mt As Long
txt = "生麦生米生卵李も桃も桃の内"
keyword = "*も桃*"

mt = 0
For cnt = 0 To Len(txt)
	If txt Like keyword Then
		mt = mt + 1
	End If
Next cnt
If mt > 0 Then
	MsgBox ("「" & keyword & "」が見つかりました")
Else
	MsgBox ("「" & keyword & "」は見つかりませんでした")
End If
```

このケースでは、ワイルドカードを使って検出自体はできますが、それ以上の処理を作ろうとすると、途端に難しくなることも分かります。例えばマッチした数を求めるにしても、面倒な処理が必要になってきます。
（私には時間もかかり過ぎて少し荷物が重いので、今回は割愛させて頂きます）

上記のようなケースでも、正規表現で行うと平易となり、非常に短い処理で済みます。

```VBA:正規表現での照合
    Dim txt As String, keyword As String
    Dim reg As Object
    Set reg = CreateObject("VBScript.RegExp")
    txt = "生麦生米生卵李も桃も桃の内"
    reg.Pattern = "も桃"
　　keyword = "も桃"

    If reg.test(txt) Then
            MsgBox ("「" & keyword & "」が見つかりました")
    Else
            MsgBox ("「" & keyword & "」は見つかりませんでした")
    End If
End Sub
```

処理がシンプルになるのが見て取れ、また応用のパターンも簡単に組み立てることが出来ます。
こうした機械に理解しやすい形で、かつ短いパターンで表現をすることで、それを用いた処理も可能になるので、繰り返しパターンに沿った処理を行い検出・置換えといった処理が可能となります。そうした意味で、正規表現はデータを扱う上で重要な位置付けを持っていると言えます。

## 応用範囲と資料について

Excelをまとめたサイトはそれこそ星の数ほどあり、甲乙つけがたいものがあります。
実際に使いこなすため理解するなら、こちらの[初心者歓迎！手と目で覚える正規表現入門・その１「さまざまな形式の電話番号を検索しよう」][手と目で覚える正規表現入門URL]を読めば間違いないのではと。可視化サイトも参照させて頂きました。
・[サルにもわかる正規表現入門][サルにも分かるURL]は、簡素でHTML表現も工夫されているので見やすいです。
・[エクセルの真髄][エクセルの真髄URL]
・[オフィス田中][オフィス田中URL]
・[正規表現可視化サイト][正規表現可視化URL]

強力なデータ照合・検索機能を持っている*Excel*で、敢えて手間を書けてまで学習して、オブジェクトを追加してメモリを圧迫してまで、機能強化を図る理由はどこにあるでしょうか？
このパターンマッチングというものは想像以上に強力で、例えばシート全体に対しても、まとめて照合・検索を行うことも可能になります。
現状の運用上・変えられないシートと、改善の目標とする改善先を結びつけることも可能になります。私が何となくでやって来たプログラムを、深く理解する必要を感じた最初の課題もこれであったように思います。
ゴチャゴチャになっている構成のExcelデータを探す時に強力な武器になり得る言語で、どんなプログラム言語や環境でも、幅広く共通に使えます。データの重要性や前処理が一つのテーマになっている昨今こそ、改めてキッチリとマスターしてく意味が大きいように感じています。

たぶん続きます

***
[正規表現可視化URL]: https://regexper.com/
[手と目で覚える正規表現入門URL]: https://qiita.com/jnchito/items/893c887fbf19e17d3ff9
[サルにも分かるURL]: https://userweb.mnet.ne.jp/nakama/#no-one
[エクセルの真髄URL]: https://excel-ubara.com/excelvba4/EXCEL232.html
[オフィス田中URL]: http://officetanaka.net/excel/vba/tips/tips38.htm
Google Map にまとめて地点登録する

#サマリ
Google Map にまとめて地点登録する方法のメモです。

##目次
1. 登録地点の用意
1. 登録地点のインポート
1. 登録地点の表示設定

#1.登録地点の用意
- エクセル等を使って登録地点のデータを用意します。緯度経度は必須、ほかの情報は必要に応じて用意します。
![地点登録データ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933978/f6355783-ce19-09ad-ae84-14b00cae955e.png)


#2.登録地点のインポート
- 地点登録データをGoogle Mapにインポートします。

- Google mapsのハンバーガーメニューからマイプレイスを選択。
![googlemapメニュー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933978/015ab7f3-abf4-dc61-1742-70317c7687cf.png)

- マイマップを選択。
![マイマップ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933978/4180a7b3-b97c-02e0-3458-b55f9edc82df.png)

- マイマップメニュー最下段の「地図を作成」を選択。
![地図を作成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933978/22c91fa8-e564-6a29-e29f-ecb391267354.png)


- 地図作成メニューのインポートを選択。
![インポート.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933978/d052c928-1e92-1b5f-3176-912a4dca4b8c.png)


- 地点登録データファイルをドラッグ&ドロップ。
![ファイル選択.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933978/5f6371bf-0745-ab32-db79-8e386f9b2058.png)


- 登録する属性と地点登録データラベルとの紐づけ。どの項目を緯度経度とするかを設定。緯度経度以外の項目はチェックのみ。
![属性設定_緯度.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933978/cfef2e71-4998-8285-11da-8304f74f3645.png)
![属性設定_経度.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933978/594972f1-7d64-e786-c703-5f2448f5e489.png)
![属性設定_その他.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933978/4c938457-1644-f6af-3d2b-a84178ac7a58.png)

- マーカーの横に表示されるタイトルとして使う列を選択。
![マーカータイトル列.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933978/cd12cee6-6420-7b59-f709-5aa64d8e55db.png)

- 地点登録完了
![地点登録完了.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933978/d409ea7b-6bff-5f92-5ebc-5d069bde1822.png)

#3.登録地点の表示設定
- 登録地点をわかりやすく表示するための設定をします。

- 個別スタイルを選択。
![個別スタイル.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933978/ab59110d-6c93-9c34-5988-85390e01a8b3.png)

- グルーピングする項目を選択。
![グルーピング1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933978/5c1d4d15-5e00-dde2-5247-5cc273ef02cb.png)
![グルーピング2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933978/c6c65446-8312-39f8-db0b-ee4c3e1db411.png)

- マーカー横に表示するラベルを設定。
![ラベル指定1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933978/731a0d60-9d05-1b5f-e815-95535a55fdfe.png)
![ラベル指定2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933978/613511f2-e781-523a-368a-5f04ebc4fcf1.png)

- マーカーアイコンを設定。
![マーク指定1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933978/d4f3a0ba-84dc-92d5-0e63-554b70e64c4d.png)
![マーク指定2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933978/03d16b0f-98ba-bb00-f13f-bc3a250bc26c.png)

#　
チームハイエナ <img width="120" alt="hyaeninae_tomei.png"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933978/99b448d3-7ff0-58f3-302b-8528cf339b8d.png">
【JavaScript】map()メソッド、filter()メソッド、splice()メソッド、三項演算子

## はじめに
　本記事は、プログラミング初学者が、学習を進めていて疑問に思った点について調べた結果を備忘録も兼ねてまとめたものです。
　そのため、記事の内容に誤りが含まれている可能性があります。ご容赦ください。
　間違いを見つけた方は、お手数ですが、ご指摘いただけますと幸いです。

##map()メソッド
配列のすべての要素に対して与えた関数を実行して、その結果を配列として返します。
返り値が配列であることを望まない場合には、`forEach()`を使用します。
map()メソッドの記述例は以下の通りです。

```sample.js
const array1 = [1, 4, 9, 16];

const map1 = array1.map(x => x * 2);

console.log(map1);
// Array [2, 8, 18, 32]
```

##filter()メソッド
配列の要素に対し、指定した条件に合致するもののみを取り出し、新たな配列を生成するメソッドです。
条件に合致する要素がひとつもない場合には、空の配列を返します。
記述例は以下の通りです。

```sample.js
const words = ['spray', 'limit', 'exuberant', 'destruction', 'present'];

const result = words.filter(word => word.length > 6);

console.log(result);
// Array ["exuberant", "destruction", "present"]
```

##splice()メソッド
配列から要素を削除したり、置き換えたり、追加したりすることができるメソッドです。
記述例は以下の通りです。

```sample.js
const months = ['Jan', 'March', 'April', 'June'];
months.splice(1, 0, 'Feb');
// 配列の1番目の要素から0個分削除し、'Feb'を追加する
console.log(months);
// Array ["Jan", "Feb", "March", "April", "June"]

months.splice(4, 1, 'May');
// 配列の4番目の要素から1個分削除（4番目の要素を削除）し、'May'を追加する
console.log(months);
// Array ["Jan", "Feb", "March", "April", "May"]

const month = {months.splice(1, 1);
console.log(month);
// Array ['march']　monthには削除した'march'が格納されている
```

##三項演算子
条件分岐をさせることができます。連鎖させることも可能です。
記述例は以下の通りです。

```sample.js
function getFee(isMember) {
  return (isMember ? '$2.00' : '$10.00');
  // isMenberがtrueであれば'$2.00'をfalse又はnullの場合には'$10.00'を実行
}

console.log(getFee(true));
// "$2.00"

console.log(getFee(false));
// "$10.00"

console.log(getFee(null));
// "$10.00"


// 連鎖させることも可能
function example(…) {
    return condition1 ? value1
         : condition2 ? value2
         : condition3 ? value3
         : value4;
}
```


## まとめ

- map()メソッドは、配列のすべての要素に対して、与えた関数を実行した上でその結果を配列として返すメソッド
- filter()メソッドは、配列の要素に対し、指定した条件に合致するもののみを取り出し、新たな配列を生成するメソッド
- splice()メソッドは、配列から要素を削除したり、置き換えたり、追加したりすることができるメソッド
- 三項演算子は条件分岐
C言語でキーボード入力判定しながら出力をしたい

#はじめに
ターゲット: 私と同じくC言語初心者

大学の課題にC言語でスロットマシンを実装せよとの課題が出たので, これはキーボード入力を受け付けながらスロットのリールが動くのを出力しないといけないのではと思ってちょっと調べてみました.

###実行環境
OS:  Ubuntu 20.04.3 LTS(WSL)
コンパイラ: gcc version 9.3.0

#どうやって実装するか
pthreadを使います(直球)
pthreadはPOSIX threadの略で, UNIX系OSでスレッドプログラミングをする際に使用されるらしいです. 
threadをいくつか作ってそれを同時に実行させるような処理を行います.

#pthreadの使い方
pthreadは, 次のような感じで書きます. これがC言語にしては割と便利で, そんなに難しくないです.
以下のsample.cはENTERキーを押した回数を出力しつつ, そのENTERキー入力も同時に行うプログラムです.

```sample.c
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h> // for usleep func

#define NUM_KBHITS_DONE 5
#define ASCII_ENTER 10
#define WAIT_MILSEC 1000*1000 //thread2のループ時に1秒待つ

typedef struct{
  int isHit; //0->false, else->true
  int num_kbHits;
  int hogehoge;
}ArgForStruct;

ArgForStruct *createStruct(void);
void* thread1(void *);
void* thread2(void *);  //引数は1つしか渡せない. 2つ以上渡したいときは構造体を使います.

int main(){
  ArgForStruct *myStruct = createStruct();
  //スレッド生成
  pthread_t inputThread;
  pthread_t showingThread;
  //スレッド開始
  pthread_create(&inputThread, NULL, thread1, myStruct);  //4つ目の引数がスレッドへの
  pthread_create(&showingThread, NULL, thread2, myStruct);//引数になります
  //スレッド終了待ち
  pthread_join(inputThread, NULL);
  pthread_join(showingThread, NULL);
  return 0;
}

ArgForStruct *createStruct(){
  ArgForStruct *myStruct = malloc(sizeof(ArgForStruct));
  myStruct->num_kbHits = 0;
  myStruct->hogehoge = 123;
  return myStruct;
}

//wait input
void* thread1(void *argStruct){
  ArgForStruct *myStruct = (ArgForStruct *)argStruct; //cast
  for(;;){
    if(myStruct->num_kbHits == NUM_KBHITS_DONE){
      myStruct->isHit = 1;
      pthread_exit(NULL);   //スレッド終了
    }

    //キーボード入力待ち
    int temp = getchar();
    if(temp == ASCII_ENTER){
      printf("ENTER HIT\n");
      myStruct->num_kbHits++;
    }
  }
}

//showing argStruct->num_kbHits
void* thread2(void *argStruct){
  ArgForStruct *myStruct = (ArgForStruct *)argStruct; //cast
  for(;;){
    printf("current kbHits: %d\n", myStruct->num_kbHits);
    usleep(WAIT_MILSEC); //1秒待つ
    
    //スレッド1で構造体のisHitが書き換えられたらスレッドを抜ける
    if(myStruct->isHit != 0){
      printf("keyboard Hits Done!: %d\n", myStruct->num_kbHits);
      pthread_exit(NULL);
    }
  }
}
```
###実行結果
コンパイルは, オプションに "-pthread" をつける必要があります.

```console
$gcc -pthread main.c
$./a.out
current kbHits: 0
current kbHits: 0
current kbHits: 0
current kbHits: 0

ENTER HIT
current kbHits: 1
current kbHits: 1
current kbHits: 1

ENTER HIT

ENTER HIT
current kbHits: 3
current kbHits: 3
current kbHits: 3

ENTER HIT
current kbHits: 4
current kbHits: 4

ENTER HIT
keyboard Hits Done!: 5
```

#あとがき
面白かった(小並感)
(追記) 講義はプロセス(forkとか)使って解説されててちょっと悲しかった
【ABAP開発】ABAP Workbench ToolsとABAP Development Tools(ADT)について

`SAP`システムに追加機能を開発する場合、ABAPという言語を使い開発をするわけですが。
2022年現在、SAPから提供されている開発ツールは大きく分けて2系統提供されています。

- `ABAP Workbench Tools`
- `ABAP Development Tools(ADT)`

本記事ではそれぞれどういったものかを紹介します。

## ABAP Workbench Tools

[ABAP Workbench Tools](https://help.sap.com/viewer/bd833c8355f34e96a6e83096b38bf192/LATEST/en-US/efd94b78ebf811d295b100a0c94260a5.html)

`ABAP Workbench Tools`は旧来から提供されているABAPの開発環境となります。

`ABAP`プログラムの開発、テスト、実行など開発に係るツールの集合となっており、`ABAP Workbench Tools`に`SAP GUI`で接続して開発作業を行います。

- ABAP Dictionary
- ABAP Editor
- Class Builder
- Function Builder
- Screen Painter
- Menu Painter
- Package Builder
- etcetc

`ABAP`開発をした事がある人からすると`SE38`や`SE80`などおなじみのトランザクションかと思います。

歴史が長く、2022年現在の日本では、一般的にABAPの開発作業と言われて、多くの人が思い浮かべる開発環境はこちらなのでは？　といった個人の感想ではあります。

後述しますが、最近では、`ABAP Development Tools(ADT)`でないと操作できないオブジェクトが出てきており、ここらへん普通に使うような環境では、`ABAP Development Tools(ADT)`なると思うので。

各人の置かれている環境によって実感が変わりそうなものではあります。

## ABAP Development Tools(ADT)

[About the ABAP Development User Guide](https://help.sap.com/viewer/c238d694b825421f940829321ffa326a/202110.000/en-US/4b190c90ceba4d02a99e0a2286b89358.html)

オープンソースのクロスプラットフォームIDEである`Eclipse`ベースで構築されている`ABAP`開発環境となり、`ABAP Development Tools`　省略して`ADT`と呼ばれます。



2012年に登場した、新しい方の`ABAP IDE`となります。2022年現在で登場してから10年経ってますが……こう考えると`ABAP`って歴史が長い言語ですね。



`ABAP Development Tools(ADT)`についてどういうものかは下記のコミュニティ記事やブロク記事が参考になるかと思います。


[Keller-Michael/Eclipse_ADT_info_hub](https://github.com/Keller-Michael/Eclipse_ADT_info_hub)

[FAQs – ABAP Development Tools for Eclipse](https://blogs.sap.com/2012/06/12/faqs-abap-development-tools-for-eclipse/)

[Important changes to the ABAP Development Tools (ADT) release cycle](https://blogs.sap.com/2019/04/15/important-changes-to-the-abap-development-tools-adt-release-cycle/)

[Get Started with the ABAP Development Tools for SAP NetWeaver](https://blogs.sap.com/2012/06/19/get-started-with-the-abap-development-tools-for-sap-netweaver/)

[ABAP Development in Eclipse – Part 1](https://blogs.sap.com/2014/11/13/abap-development-in-eclipse/)

[ABAP in Eclipse- The Future of ABAP Development](https://nttdata-solutions.com/se/global-blog/abap-in-eclipse-the-future-of-abap-development/)

## ABAP Workbench Tools vs ABAP Development Tools(ADT)

`SAP`からは2系統の開発ツールが提供されていますが、一部の新しいリポジトリオブジェクトは`ABAP Workbench Tools`での編集に対応しておらず、`ABAP Development Tools(ADT)`を利用する必要があります。

これは`ABAP Keyword Documentation`の`ABAP Workbench`のページにも **Some newer repository objects can only be edited in the ABAP Development Tools (ADT).**　と記載されており、一部機能を利用するとなると`ABAP Development Tools(ADT)`をつかわないとならいケースが出てきています。

[ABAP Keyword Documentation - ABAP Workbench](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenabap_workbench_glosry.htm)

ここらへんは下記コミュニティブログで触れられており、コメント欄にも色々と意見が書き込まれています。

[SE80? ADT? Whatever, Just Wash Your Hands](https://blogs.sap.com/2019/11/20/se80-adt-whatever-just-wash-your-hands/)

## 総評

最近だと`CDS view `という機能を使うために、`ABAP Development Tools (ADT)`を利用しないとならいケースがあったりすると聞いており。

また`SAP`としては、`ABAP Development Tools(ADT)`を推進している状況かと思います。
【Vue.js】v-forの中でe.targetを使用してみた時のメモ

環境：vue2

## やりたいこと

v-forを使用して、ボタンとなる要素を複数作成。
ボタンのいずれかをクリックした際に、クリックしたボタンだけ色が変わる。

上記内容を、e.targetを使わない場合と使う場合とで比較してみたい。

#### e.targetを使わない場合
<p class="codepen" data-height="300" data-default-tab="js,result" data-slug-hash="abLqmBR" data-user="monji88" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/monji88/pen/abLqmBR">
  button color change(vur.js sample)</a> by monji (<a href="https://codepen.io/monji88">@monji88</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
これでも別に悪くはないけどforEachとaddEventListnerを両方使うので少し冗長な感じがする。


#### e.targetを使う場合
<p class="codepen" data-height="300" data-default-tab="js,result" data-slug-hash="rNGJjeL" data-user="monji88" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/monji88/pen/rNGJjeL">
  Untitled</a> by monji (<a href="https://codepen.io/monji88">@monji88</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
こちらのほうがシンプルだしわかりやすいと思う。
（ほかにいい感じの方法あれば教えてもらえると嬉しいです）

### e.targetについての補足事項

e.targetは、生のJavaScriptで使用する場合は
`<button onclick="change(event)">ボタン</button>`
といった感じで（）の中に引数を入れてあげる必要がある。
でもvueの場合は()ごと省略して`@ckick="change"`でも動作する。
ちなみに省略しない場合は`(event)`ではなく`($event)`とする必要があるらしい。

そのあたりをわかりやすくまとめて下さってる記事様↓

https://qiita.com/_masa_u/items/bb195ffee7cd2c45217e

### e.targetについての補足事項2

対象要素内に子要素がある場合などはイベントが伝播されるので注意。
そのあたりをわかりやすくまとめてくださってる記事様↓

https://logical-studio.com/develop/web/java_script/20210114-javascript-eventlistener-argument/
Could not get lock /var/lib/dpkg/lock-frontend (Ubuntu20.04)


Docker インストールしようとしたらエラー。


```

$ sudo curl -sSL https://get.docker.com/ | CHANNEL=stable sh
# Executing docker install script, commit: 93d2499759296ac1f9c510605fef85052a2c32be
Warning: the "docker" command appears to already exist on this system.

If you already have Docker installed, this script can cause trouble, which is
why we're displaying this warning and provide the opportunity to cancel the
installation.

If you installed the current Docker package using this script and are using it
again to update Docker, you can safely ignore this message.

You may press Ctrl+C now to abort this script.
+ sleep 20
+ sudo -E sh -c apt-get update -qq >/dev/null
+ sudo -E sh -c DEBIAN_FRONTEND=noninteractive apt-get install -y -qq apt-transport-https ca-certificates curl >/dev/null
E: Could not get lock /var/lib/dpkg/lock-frontend. It is held by process 4240 (unattended-upgr)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), is another process using it?


```

プロセス4240は何かな？

```

$ ps -auxww | grep 4240
colord      2202  0.0  0.3 247564 14240 ?        Ssl  01:02   0:00 /usr/libexec/colord
root        4240 14.2  3.1 346872 121712 ?       Sl   01:05   1:22 /usr/bin/python3 /usr/bin/unattended-upgrade
nanbuwks   25948  0.0  0.0  10092   736 pts/0    S+   01:15   0:00 grep --color=auto 4240

```


unattended-upgrade だ。 自動アップグレードのプログラムらしい。
ということは、Docker のインストールの問題ではなく apt の問題ということだな。



```
$ sudo apt-get upgrade
E: Could not get lock /var/lib/dpkg/lock-frontend. It is held by process 4240 (unattended-upgr)
N: Be aware that removing the lock file is not a solution and may break your system.
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), is another process using it?

```

たしかにそうでした。Ubuntu のインストール後の設定で、うっかり自動アップグレードを設定してしまったっぽい。
MS-Windows の Upgrade のシチュエーションと同様になってしまってとんでもない。イザという時に干渉しないように、切っておこう。


```

$  cat /etc/apt/apt.conf.d/20auto-upgrades
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";

```

これを両方 0 に書き換えて再起動で  apt コマンドおよび docker のインストール両方問題なく実行できました。
1分で出来る最新で最小なADB環境の構築[Windows編]

# ※ わざわざAndroidStudio入れたくない人向けです

1. XDAから**15 seconds ADB Installer v1.4.3**を[ダウンロード](https://forum.xda-developers.com/t/official-tool-windows-adb-fastboot-and-drivers-15-seconds-adb-installer-v1-4-3.2588979/#post-48915118)する。
1. Googleより最新版の**Platform-Tools**を[ダウンロード](https://dl.google.com/android/repository/platform-tools-latest-windows.zip)をする。
1. **手順1**で落としたファイルを解凍後、管理者権限で実行してインストールする。
1. cmdで`adb version`を入力して確認する。
1. ↑が成功していればインストール完了。そのままだとかなり古いバージョンのツールを使うことになるので`C:\\adb\`以下に**手順2**を解凍した中にある`platform-tools\`の中身を全てコピーする。

## 参考、引用、出典

- [[OFFICIAL][TOOL][WINDOWS] ADB, Fastboot and Drivers - 15 seconds ADB Installer v1.4.3](https://forum.xda-developers.com/t/official-tool-windows-adb-fastboot-and-drivers-15-seconds-adb-installer-v1-4-3.2588979/#post-48915118)
- [Google-latest-adb-platform-tools](https://dl.google.com/android/repository/platform-tools-latest-windows.zip)
Raspberry PiをSpotify対応ラジオにするための自分用メモ（リンク集）

# 初めに #

表題の通り、Raspberry Piを作業用BGM再生マシンにするために参考にしたサイトのリンク集です。
目標としては、下記3点を満たすものを作りたいと思いました。

* Spotifyの再生（Spotify Connect）
* 放送中のラジオ番組の聴取（Radiko）
* スマホの音をスピーカーに流す（Bluetooth）

## 参考URL

* NanoPi NEOにインストールしたMPDでradikoを聞く - https://burro.hatenablog.com/entry/2019/02/16/175836
* Raspberry Pi で Bluetooth レシーバー (オーディオ:A2DP) を作成するための設定方法解説 - https://ifritjp.github.io/documents/singleboard/bluetooth-a2dp-receiver/
* raspiでspotifyを聴く https://qiita.com/kons_9/items/5cc5a7ded0b17047bf89
    * Installing on a Raspberry Pi - Spotifyd - https://spotifyd.github.io/spotifyd/installation/Raspberry-Pi.html
* https://yueno.net/xoops/modules/xpwiki/?PC%2FRaspberryPi%2FRAM%E3%83%87%E3%82%A3%E3%82%B9%E3%82%AF%E3%81%AE%E5%88%A9%E7%94%A8

# 結果

一応、やりたいことは実現できました。ALSAの副産物として、全音源排他です。
再生停止すれば自動的に切り替わるので変な音が混ざらず安心。
Spotifyアプリからの音量調整も効きます。MPDクライアントからのラジオの音量調整も効きます。
ソフトウェアボリュームなので、音質を犠牲にするらしいですが聞ければいいだけなので気にしません。
AVRCPは動作確認していないのでBluetoothの音量調整はできない認識ですが、ソース（スマホ）側の音量をいじればいいだけなので気にしません。

## 機器構成

構成要素は下記のとおりです。基本的には家に転がっていたものの寄せ集めです。
USB DACだけは2000円前後の安いのを買いました。
クリエイティブ信者ではありません。コスパで選んだら偶然こうなっただけです。
SoundBlaster Play 4はUSB-C端子しかないので、間違って購入しないように。

* Raspberry Pi B
    * 有線LANにてネットワーク接続。（面倒なのでWIFI機能は特に殺したりしないで放置）
    * SDカード 16GB
    * OSは Raspberry Pi OS 32bit版（Raspberry Pi OS with desktop; October 30th 2021）
    * セットアップのためにモニター、USBキーボード、マウスを用意。初期設定以後はSSHを有効化して周辺機器は外す。
* 電源は公式のUSB-ACアダプタ
* [Sound Blaster Play 3](https://jp.creative.com/p/sound-cards/sound-blaster-play-3)
* [Pebble](https://jp.creative.com/pebblefamily/) (V1)

# やったこと

検索キーワードになりそうなことをざっくりとだけ書いていきます。
やり方は細かく説明しないので適宜ぐぐってください。

1\. 
適当なSDカードにRaspberry Pi OSを焼く。
初回起動時に行われるアップデートは割と時間がかかるのでご飯でも食べてください。
spotifydの配布がarmhfだけで、arm64はなかったので32bit版OSイメージを焼きました。

2\. 
/tmpをRAMディスク化する。SDカードのスワップを無効化する。
当初は/var/tmpもRAMディスク化してみたものの、
fstabの記載にミスがあったのか/var/tmpのマウントに失敗してOSが立ち上がらないので/tmpだけをRAMディスク化することに。
/var/logはログレベルもいじらず普通にSDカードに書き出す状態のままです。
本来ならspotifyのキャッシュなんかはRAMディスクに入れるべきですが面倒なので放置。
/tmp (tmpfs)　にシンボリックリンクでも貼っといたほうがいいとは思います。

3\.
Spotifyd をインストール。

Spotifydの実行ファイルを落としてきて、/usr/binに配置、その後spotifyd.serviceを/etc/systemd/user/に置くだけ。
https://github.com/Spotifyd/spotifyd/releases/download/v0.3.3/spotifyd-linux-armhf-slim.tar.gz
https://github.com/Spotifyd/spotifyd/blob/master/contrib/spotifyd.service

参考URLでは設定ファイルにアカウントを書かなければいけないようなことが書いてありましたが、
実際は同じネットワーク内ならスマホ（等のログイン済み端末）のアカウントで自動的にログインされるので設定ファイルは作らず、systemdで `./spotifyd --no-daemon` を自動起動化しただけです。
※ ちなみに、当初はRaspotifyを使ってみましたが、再生開始のタイミングでクラッシュして使い物にならないので止めました。

4\.
参考URLを見ながらRadioRelayServerをインストール。
動作環境として、python3、Django、ffmpeg、MPDが必要でした。aptでインストール。
本当はPulseAudioにラジオの音を流したかったものの、MPDとPulseAudioの連携がうまくいかなかったのでALSAに直接流すことに。

5\.
参考URLを見ながらbluetoothctlでスマホとRaspberry Piをペアリング。
bluetoothctlで `agent NoInputNoOutput` してもスマホ側からのペアリングが通らなかったので、
Raspberry Pi側からpair→trustしました。（注：bluetoothctlのscanとdiscoverableは終わったらoffに戻すこと。）
最初からBluetoothのA2DB Sinkは有効だったので、単にペアリングしただけです。

# 今後やってみたいこと・現状できていないこと

* NASに置いたmp3の再生。
* クラウドストレージに置いたmp3のストリーミング再生。
* Radiko以外のHLSストリーミングへの対応。
* 再生、停止、スキップ程度の簡単なリモコン操作。（Bluetoothリモコンが現実的？）
* 安定運用できるようになったらUSB SSDブートへの切り替え。
RaspberryPi への LAMP 環境の構築（補正）

久しぶりの投稿です。
１年以上間が空きましたが、続きで、『QRコードリーダ使った、jukebox作り』に
取り組みたいと思います。（またクリスマスを逃してしましたが。。）

その前に、RasoberryPiの環境整備です。
　（https://qiita.com/bb-mint/items/42ac7b41f0bd4f965502　の補正）
LAMPを構築しておきます。

> pi@raspberrypi:~ $ uname -a
Linux raspberrypi 5.10.63-v7+ #1488 SMP Thu Nov 18 16:14:44 GMT 2021 armv7l GNU/Linux

----
RaspberryPi への LAMP 環境の構築（補正）

phpは、php7.4　になっていますね。
MariaDBの初期設定が多少変わっているようです。

----
php7.4への変更は、設定などに大きな課題はなさそうです。
> pi@raspberrypi:~ $ sudo vi /etc/apache2/mods-enabled/php7.4.conf 

----

MariaDBの初期設定から
最初の質問が多少増えているようです。
> pi@raspberrypi:~ $ sudo systemctl start mariadb.service
> pi@raspberrypi:~ $ sudo mysql_secure_installation
>> Enter current password for root (enter for none): ＜リターンを入れればよいですよね。＞ 
>> OK, successfully used password, moving on...
>> 
>> Setting the root password or using the unix_socket ensures that nobody
>> can log into the MariaDB root user without the proper authorisation.
>>
 You already have your root account protected, so you can safely answer 'n'.
>> 
>> Switch to unix_socket authentication [Y/n] n　＜面倒なので n としました＞
>>  ... skipping.
>> 
>> You already have your root account protected, so you can safely answer 'n'.
>> 
>> Change the root password? [Y/n] y
>> New password: 
>> Re-enter new password: 
>> Password updated successfully!
>> Reloading privilege tables..
>>  ... Success!

以上で、phpmyadmin　を入れても、最後のrootユーザの高すぎるセキュリティへの対応は
必要なくなります。

----
以上、ひとまず、前回の記事の補正でした。
　https://qiita.com/bb-mint/items/42ac7b41f0bd4f965502
Visual Studio Code :デフォルトシェルを変更 for mac

<エラー発生>
Currently supported shells are:   - bash   - fish   - tcsh   - xonsh   - zsh   - powershell  See 'conda init --help' for more information and options.  IMPORTANT: You may need to close and restart your shell after running 'conda init'.

＜mac PCの設定変更で解決した：デフォルトシェルを変更する方法＞
参照サイト→https://support.apple.com/ja-jp/HT208050