OmniAICreator/Qiita-1.07M huggingface dataset:
[初心者視点]作りたいアプリの難易度別でcompose multiplatformとFlutter難易度を比較してみた

# はじめに
初心者Androidエンジニアの私目線から
もはや定番となったFlutterと新進気鋭のcompose multiplatformを比較します。

目次はこんなん
- 前提
- ポートフォリオレベルでの実装の手間
- ハッカソンレベルでの実装の手間
- 個人開発レベルでの実装の手間
- まとめ


## 前提
Compose Multiplatformは新しめの技術で、成熟しているFlutterと比較するのは不公平ですし
自分は元Androidアプリエンジニアなのでそう言う点でも不公平です。
まぁ差し引きしてみてください。

また私はFlutterもcompose multiplatformも業務で使ったことはないです。
Flutterはハッカソンに出た時に3回ほど使ったことがあり、compose-multiplatformに関してはチュートリアル終わって、遊びで一つアプリ作っている段階です。
要はかなり初心者なので、中級者の方が読んでもメリットはないと思います。

それぞれの技術の仕組みについての深い話はしません。ていうか難しすぎてわかりません。

あと、環境構築の手間も比較してみようと思ったのですが、両方ほぼ同じだったので割愛します。

# ポートフォリオレベルでの実装の手間
チュートリアルで作るような、APIの呼び出しもしないレベルのアプリです。（状態管理とかもあんまり意識しない）
就活のポートフォリオとかのレベルかな。
このレベルだとどっちもめちゃくちゃ簡単に作れますが、若干ComposeMultiplatformの方が楽でした。
Kotlin自体の書き心地が良いので、Dartよりもシンプルにかけます。
UI部分も、Flutterは呪文が多くてめんどくさいです。
Flutterなら[スケルトンテンプレート](https://blog.flutteruniv.com/flutter-skeleton-template/)を使えば、ほぼほぼ完成です。
compose multiplatformはまだテンプレートがないので、自分で作る必要がありますが、基本的なライブラリとかが入ったアプリを作ってくれる[Compose Multiplatform Wizard](https://terrakok.github.io/Compose-Multiplatform-Wizard/)や、[KMP wizard](https://terrakok.github.io/kmp-web-wizard/)があるので、対して手間はかかりません。

HotReloadがある分Flutterの方が楽じゃんと言う声はよく聞くのですが、自分はあんまり感じないです。
プレビュー表示でリアルタイムな確認は行えるし、画面全体での確認をしたいなら
結局リビルドしないと信頼できない（変な挙動になってしまうことが多い）ので自分はHotReloadを使う機会があまりなかったです。
あくまで自分の個人的な感想です。

# ハッカソンレベルでの実装の手間
FirebaseやSupabaseなどのmBaasを利用したり、簡単なAPIを叩いたりするレベルです。
状態管理やアーキテクチャをちょっとだけ意識しているレベルを想定しています。

結論としてはFlutterの方が楽です。

## mBaaS、API周り
Flutterの方が若干楽です。
FirebaseもSupabaseもSDKがあるので、両方簡単に導入できるのですが公式のサポート具合が全然違いますね。
Flutter x Firebaseとかだとドキュメントみなくても言われた通りにやればできるし、ドキュメントもほとんど日本語化されてますよね。
Compose Multiplatform x Firebaseだとドキュメント通りにやってみて正しく動かないことが多かったです。
ドキュメントをみても動かないから、issueをみたりstack overflowを見に行く必要があります。

意外だったのは、Compose MultiplatformだとSupabaseの方がライブラリの安定性が高い気がします。[公式のSDK](https://github.com/supabase-community/supabase-kt)さえみておけば全てわかるので楽ちんでした。

## 設計周り
Flutterの完勝です。
状態管理とかのライブラリや設計に関してはFlutterの方が圧倒的にドキュメントが多いので
比較にならないくらい楽でした。

Compose Multiplatformのアーキテクチャ調べているとKMPのアーキテクチャとか出てきて、頭が混乱します。
FlutterはBLoCとかProviderとか、ある程度「正解」的なものがあるので考えすぎなければ楽ですね。

## UI関連
実はCompose Multiplatformの方が若干楽でした。
[私が言いたいことをすべて言ってくれている記事](https://zenn.dev/ko2ic/articles/1be952d9166c19)があるので、そちらを参照してください。
もちろんWidgetの種類とかはFlutterの方が充実しています。
ハーフモーダルとかはよく使う系のUIがWidgetとしてすでに用意されているのはFlutterの良い点でした。
ただ個人的にはそれよりもコードの読みやすさとか、書きやすさの方が大事だと思うので、Compose Multiplatformの方が好きです。
（私はAndroidアプリエンジニアだったことがあるのでそれも影響している)


# 個人開発レベルでの実装の手間
これは、現在挑戦中なので正確にはわかりませんが、今の所Flutterの完勝です。

現状の開発状況だとCompose MultiplatformでiOSに対応するのは難しそうですね。
バグ引いて詰まることが多すぎて涙が出ます。
また、Compose MultiplatformのiOS側のパフォーマンスが目に見えて悪いので涙が出ます。([最近のアップデート](https://github.com/JetBrains/compose-multiplatform/releases/tag/v1.5.11?utm_campaign=compose-1.5.11&utm_medium=social&utm_source=twitter)で若干改善した）
ライブラリもFlutterと比べて少なすぎるので涙が出ます。
Androidアプリ開発で使ってきたライブラリがComposeMultiplatformに対応していないので涙が出ます。


# まとめ
Flutterの方がどんなアプリを作るにしても、大体楽ですね。
一方ですでにjetpack composeをたくさん触っている人にとってはCompose Multiplatformの方が楽だと思います。
結論がめちゃくちゃありきたりな感じになってしまった。

ただし将来性と言うことだと、Compose Multiplatformもかなり期待できると思います。
まだComposeMultiplatformに対応していないライブラリがたくさんありますが、どんどん対応していくと思うのでそこに期待します。

どっちも発展途上で将来が楽しみだなぁと思いました。
seleniumでHTML要素を指定する方法

# ID指定
```
driver.find_element_by_id("exampleId")
```

# Class指定

```
driver.find_element_by_class_name("exampleClass")
```

# タグの指定

タグ名で要素を指定（例：`<div>`, `<a>`など）。

```
driver.find_element_by_tag_name("div")
```

# name属性で指定
name属性を持つ要素を指定する方法。

```
driver.find_element_by_name("exampleName")
```

# CSSのセレクターで指定
CSSで使うセレクタを使用して、要素を特定。

```
driver.find_element_by_css_selector("input[type='text']")
```

# aタグのアンカーテキストで指定

たとえば、`<a href="">詳しくはこちら</a>`のようなリンクがあった場合、以下のように指定できる。

```
driver.find_element_by_link_text("詳しくはこちら")
```

# aタグのアンカーテキストの一部がわかってる時に指定
アンカーテキストの全部を指定しないで、一部の単語だけ指定する方法。
たとえば、`<a href="">詳細をクリック</a>`のようなリンクがあった場合、以下のように指定できる。

```
driver.find_element_by_partial_link_text("クリック")
```

# xpathで指定
ブラウザの開発者モードで要素を右クリック > コピー > xpathをコピー…から取得できるxpathで指定する方法もあります。

```
element_by_id = driver.find_element_by_xpath("//*[@id='exampleId']")
```

他にも、`<button>送信</button>`というようなボタンタグがあった場合、以下のように指定することができます。

```
button = driver.find_element_by_xpath("//button[text()='送信']")
```

# xpathが正しいかjsでテストする方法
ブラウザの開発者モードのコンソールを開いて、以下のようなコードを貼り付けて確認できます。
指定したxpathに一致する要素が存在すれば、その要素のHTMLがコンソールに出力され,一致する要素がない場合は、「要素が見つかりません」と表示されます。

```
var xpath = "ここにテストしたいXPathを入力";
var element = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
console.log(element ? element.outerHTML : "要素が見つかりません");

```
wslでelixir その111

# 概要
wsl(wsl2じゃない)で、elixirやってみた。
練習問題、やってみた。

# 練習問題
Livebookでd3のpartitionを、Mapから作れ。

# 写真


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/6079bb22-dd40-66c5-eb22-2e19138ce95c.png)

# サンプルコード

```
defmodule KinoD3.Partition do
  use Kino.JS

  def new(html) do
    Kino.JS.new(__MODULE__, html)
  end

  asset "main.js" do
    """
    import "https://d3js.org/d3.v5.min.js";
    export function init(ctx, json) {
      const chartid = document.createElement("div");
    	chartid.id = "out";
    	ctx.root.appendChild(chartid);
    	var obj = JSON.parse(json); 
      var root = d3.hierarchy(obj);
      root.sum(function(d) { 
        return d.value; 
      }).sort(function(a, b) { 
        return b.height - a.height || b.value - a.value; 
      }); 
      var partition = d3.partition().size([400, 400]).padding(1).round(true);
      partition(root);
      var g = d3.select('#out')
          .append("svg").attr("width", 600).attr("height", 600)
          .selectAll(".node").data(root.descendants()).enter()
          .append("g").attr("class", "node").attr("transform", function(d) { 
            return "translate(" + d.y0 + "," + d.x0 + ")"; 
          }); 
      g.append("rect").style("width", function(d) { 
          return d.y1 - d.y0; 
        }).style("height", function(d) { 
          return d.x1 - d.x0; 
        }).style("fill", function(d) {
          while(d.depth > 1) 
            d = d.parent;
          if (d.depth == 0) 
            return "gray";
          return d3.schemeCategory10[parseInt(d.value % 7)];
        }).style("opacity", 0.6)
      g.append("text").attr("text-anchor", "start").attr("x", 5).attr("dy", 30)
        .attr("font-size", "150%").attr("class", "node-label")
        .text(function(d) { 
          return d.data.name + " : " + d.value; 
        });
    }
    """
  end
end


%{
  "children" => [
    %{"name" => "B", "value" => 25},
    %{
      "children" => [
        %{"name" => "D", "value" => 10},
        %{"name" => "E", "value" => 15},
        %{"name" => "F", "value" => 10}
      ],
      "name" => "C"
    },
    %{"name" => "G", "value" => 15},
    %{
      "children" => [
        %{"name" => "I", "value" => 20},
        %{"name" => "J", "value" => 10}
      ],
      "name" => "H"
    },
    %{"name" => "K", "value" => 10}
  ],
  "name" => "A"
}
|> Jason.encode!()
|> KinoD3.Partition.new()

```

以上。
チーム名に愛着はありますか？

:::note info
[PHONE APPLI Advent Calendar 2023](https://qiita.com/advent-calendar/2023/pa_engineers) Day 2 の投稿です！（初日の投稿は、[こちら](https://qiita.com/Kapio0000/items/f6b8e9d9329fd396c3bd) ）
:::

みなさんは、何かしらのチームに所属していますか?
チーム名を決めるとしたら、どんな名前にしますか？
- 🙋‍♂️　「かっこいい名前にするよ」
- 🙋‍♀️　「ゆるふわな名前一択ですね」
- 🦸‍♂️　「他がつけない尖った名前にします」
- 🕵️‍♂️　「メンバーの共通点を抽出した名前にしたいですね」
- 私　「プリンを名前に入れたいですね」


どんなチーム名をつけるかは、ケースバイケースではあるとは思いますが、おそらくチームメンバーが愛着をもった名前になるのではないでしょうか？

今回は、チーム名について書いていきたいと思います！
あくまで個人の意見になりますので、参考程度で読み流していただけると幸いです🙏

## プロローグ
１年ほど前に、機会があって私はチームリーダーになり、ちょうどメンバーも入れ替えがあったので、チーム名を変更することになりました
        
個人的にプリンが好きだったので、チーム名に「プリン」を含めたいという思いがありました
前のチーム名が「マカロンくん」だったこともあり、テイストを踏襲して「ぷりんちゃん」にしようかなと考えていました

チームメンバーに新しいチーム名を相談したところ、メンバーの1人が「『ぷるぷるぷりんちゃん』はどうですか？」とナイスなアイデアを出してくれました
「ぷるぷる」というオノマトペは、語感がいいだけでなく、チームの柔軟性や俊敏性を表現しているようで、Agileなチームにふさわしいなと納得感がありました

チームメンバーの合意を得て、チーム名は、「ぷるぷるぷりんちゃん」になりました！

## チーム名について
### チーム名を決めるのはなぜか？
私は、チームの結束力を高めるために、チーム名を決めることが大事だと思います
チーム名という同じ主語でメンバーが考え、行動する過程を経てチームの協力や協調が生まれるのではないでしょうか

### どんなチーム名がいいのか？
チーム名の良し悪しの基準は難しいですが、少なくともチームメンバーが「この名前は嫌だ」と感じない名前にするのがいいかと思います

参考として
『[500+ Scrum Team Names (Agile team name generator included)](https://www.goretro.ai/post/scrum-team-names)』 で調査された最高のスクラムチーム名は、傾向として以下の５つの基準を満たしているようです
チーム名をつけるのに悩むときはこれを参考にしようかなと思います
- 印象的な名前であること
    - （チームAとかチームBとかだと味気ないですもんね）
- 発音しやすい名前であること
    - （早口言葉みたいな発音しづらい名前だとチーム名を正確に言えないもどかしさがありそうですね）
- ユニークな名前であること
    - （ありきたりよりも、メンバーを象徴した独自性の高い名前が帰属意識を高めるらしいです）
- 意味のわかる名前であること
    - （確かに意味のないランダムな文字列を覚えるのは難しいですね）
- 簡潔な名前であること
    - (程よく短い名前がいいってことでしょうか）

### チーム名が決まっていなかったら、チームビルディングで
『[ナイスなチーム名 | Agile Studio](https://agile-studio.jp/post/apm-nice-team-name)』にて、チームビルディングの手法のひとつとして、チーム名を決めるアクティビティが紹介されています
確かに、チームメンバー全員でアイデアを持ち寄って、議論して、チーム名を決める、その活動自体がチームビルディングですね
ChatGPTさんにもアイデアを提供していただくのもよさそうですね

## エピローグ
私のチームは、「マカロンくん」改め「ぷるぷるぷりんちゃん」として再始動しました

最初は、少し恥じらいもありましたが、次第に思い切ったチーム名にしてよかったなと思うようになりました
愛着が生まれていたのです

思い切ったチーム名にしたことで印象に残りやすくなり、弊社内の方から〇〇さん（がチームリーダの）チームと呼ばれるよりも「（ぷるぷる）ぷりんちゃんチーム」や「ぷるぷり」で呼んでいただける機会が多くなったなと感じています

組織体制の変更で「ぷるぷるぷりんちゃん」は解散してしまったのですが、チーム名は今でも大切にしています

〜　おしまい　〜

## まとめ
チーム名に愛着はありますか？

ここまで読んでいただいたみなさんにも感謝します✨
ぜひ、みなさんのお気に入りのチーム名をコメント欄に書いていただけると嬉しいです！

## 参考
- 『ナイスなチーム名 | Agile Studio』
https://agile-studio.jp/post/apm-nice-team-name
- 『500+ Scrum Team Names (Agile team name generator included)』
https://www.goretro.ai/post/scrum-team-names
- 『chatGPTにチーム名を考えてもらった時のこと｜ヴィクタール』
https://note.com/vikuterl/n/n299416e7c886
- 『チームをビルディングしていくために小話のすすめ #チーム開発 - Qiita』
https://qiita.com/kiiiiita/items/8cc39ccb27abaa8d056b
wslでelixir その110

# 概要
wsl(wsl2じゃない)で、elixirやってみた。
練習問題、やってみた。

# 練習問題
Livebookでd3のtreemapを、Mapから作れ。

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/5e983cac-574f-08e6-3870-d5d43706b1a3.png)

# サンプルコード

```
defmodule KinoD3.Treemap do
  use Kino.JS

  def new(html) do
    Kino.JS.new(__MODULE__, html)
  end

  asset "main.js" do
    """
    import "https://d3js.org/d3.v5.min.js";
    export function init(ctx, json) {
      const chartid = document.createElement("div");
    	chartid.id = "out";
    	ctx.root.appendChild(chartid);
    	var obj = JSON.parse(json); 
      var root = d3.hierarchy(obj);
      root.sum(function(d) { 
        return d.value; 
      }).sort(function(a, b) { 
        return b.height - a.height || b.value - a.value; 
      }); 
      var treemap = d3.treemap().size([500, 500]).padding(1).round(true);
      treemap(root);
      var g = d3.select('#out')
        .append("svg").attr("width", 600).attr("height", 600)
        .selectAll(".node").data(root.leaves()).enter().append("g").attr("class", "node")
        .attr("transform", function(d) { 
          return "translate(" + d.x0 + "," + (d.y0) + ")"; 
        });
      g.append("rect").style("width", function(d) { 
        return d.x1 - d.x0; 
      }).style("height", function(d) { 
        return d.y1 - d.y0; 
      }).style("fill", function(d) {
        while(d.depth > 1) d = d.parent;
        return d3.schemeCategory10[parseInt(d.value % 7)];
      }).style("opacity", 0.6)
      g.append("text").attr("text-anchor", "start").attr("x", 5).attr("dy", 30)
        .attr("font-size", "150%").attr("class", "node-label").text(function(d) { 
          return d.data.name + " : " + d.value; 
        });
    }
    """
  end
end

%{
  "children" => [
    %{"name" => "B", "value" => 25},
    %{
      "children" => [
        %{"name" => "D", "value" => 10},
        %{"name" => "E", "value" => 15},
        %{"name" => "F", "value" => 10}
      ],
      "name" => "C"
    },
    %{"name" => "G", "value" => 15},
    %{
      "children" => [
        %{"name" => "I", "value" => 20},
        %{"name" => "J", "value" => 10}
      ],
      "name" => "H"
    },
    %{"name" => "K", "value" => 10}
  ],
  "name" => "A"
}
|> Jason.encode!()
|> KinoD3.Treemap.new()
```

以上。
はじめて読む486　第11章 仮想8086モード

## 11. 仮想8086モード

## 11.1 仮想8086モードのしくみ

- プロテクトモードでもリアルモード用のプログラムを部分的に実行することができるが、セグメントレジスタへのロードやセグメント間ジャンプ命令をリアルモードと同じように実行することはできない
- 286用のOS/2バージョン1.Xでは、MS-DOS用のアプリケーションソフトウェアを実行するときには一旦リアルモードに戻る仕様であった
    - これではプロテクトモードの保護機能は全く働かず、信頼性が犠牲になる
        - リアルモードでアプリケーションソフトウェアが暴走しても制御が取り戻せない
        - アプリケーションソフトウェアがI/Oポートをアクセスするとオペレーティングシステムは周辺機器の状態を把握できなくなる
- 仮想8086モードは、セグメントの扱い方をリアルモードと同じにしながらも、プロテクトモードの一部として実行される動作モード
- 仮想8086モードへ移行するためには、EFLAGSレジスタのVMビットを1にセットする
    - 一般レジスタで切り替える理由は、モード移行をタスク切り替えや割り込み処理と連動させるため
- 仮想8086モードでのアドレス変換の仕組みは、セグメントレジスタの値を16倍して（4ビット左へシフト）オフセットアドレスの値を加えることによってリニアアドレスを生成する
    - リアルモードとの違いは、リニアアドレスがページング機構によって物理アドレスに変換されること
- 仮想8086モードでは、セグメント機構による保護機能が働かないが、以下の保護機能が代わりに働く
    - 特殊命令実行不可（常に動作レベル（CPL）が3で実行される）
    - I/O許可マップによるポートアクセス制御
    - フラグレジスタの変更命令のIOPLによる制御
    - 割り込みディスクリプタを介した割り込みの呼び出し
    - ページング機構による保護機能（メモリアクセス）
- IDTのディスクリプタにMS-DOSをエミュレートするルーチンを登録しておくことで、仮想8086モードで実行されているMS-DOS用のアプリケーションソフトウェアは、MS-DOS上のファイルをアクセスしているつもりでプロテクトモードのオペレーティングシステムの管理するファイルをアクセスすることができる
- リニアアドレス空間は仮想8086モードとプロテクトモードの双方で共有
    - 下位1Mバイトを仮想8086モード
    - プロテクトモードはリニアアドレス空間全体を使用できるが、下位1Mバイトは仮想8086モードのプログラムとデータ共有する場合にのみ使用する
- ページング機構は両モードに共通で、プロテクトモードで設定したページ割り当て状態を保って、全く同じ動作を続ける
- 仮想8086モードでは、必ずしリニアアドレスの最下位1Mバイトのアドレス空間を使用するため、複数のタスクを実行することができないように思えるが、タスクごとにページテーブルを切り替えることによって、同じ値のリニアアドレスを使用しながらも、異なる物理メモリを割り当てることができるようになる

## 11.2 MS-DOSと仮想8086モード

- 8086は最大で1Mバイトまでのメモリを扱うことができるので、その空間をすべてメモリ領域に割り当てていれば、MS-DOSの利用できるメモリ空間は1Mバイトとなるが、実際はブートプログラムやBIOSを格納するROM、画面表示のためのメモリ領域などが必要なので、1Mバイトのアドレス空間の後半はそのための領域（VRAM・ROM領域）として割り当ててある
- 640KバイトのMS-DOSメモリ領域だけではメモリが不足するようになったため、様々な拡張メモリの利用方式が考え出された
    - EMSやUMBは、VRAM・ROM領域の空き部分を利用して、メモリを割り当てる方式
- HMA（High Memory Area）
    - リアルモードでもA20ビットを有効にすることでプロテクトモードメモリ先頭の64K-16バイトの領域をアクセスできるようになる
    - この約64KバイトのメモリのことをHMAという
    - MS-DOS5.0からはHMAにDOS本体を置くことによってアプリケーションソフトウェアの利用できるメインメモリを空けておくことができるようになった
- EMS（Expanded Memory Specification）
    - メモリ空間を拡張する方式の一つ
    - もともとは拡張ボード上の電子回路によって実現するハードウェア方式として考案されたが、現在は仮想8086モードを活用したソフトウェアだけでEMSを実現するソフトウェア方式が主流
    - EMSメモリとは、16Kバイトごとのページ単位で扱う拡張メモリのこと
    - EMSページフレームとは、8086のの1Mバイトのアドレス空間の一部に割り当てた、16Kバイトのアドレス空間のことで、一般には、VRAM・ROM領域の一部をEMSページフレームとして使用する
    - EMSページをアクセスするには、EMSドライバソフトウェアを呼び出して、EMSページフレームをEMSページと連結する
- UMB（Upper Memory Block）
    - VRAM・ROM領域にある未使用部分にEMBの一部を割り当てる（UMB）
    - EMSでは同じアドレスに対応する物理メモリを切り替えながら使うが、UMBは対応させるメモリは固定されているの
        - そのため利用できるアドレス範囲は狭い
    - UMBの割り当てを要求する処理を組み込んだソフトウェアでなくても利用できる
    - UMBはデバイスドライバソフトウェアや常駐型プログラムなど、主にメモリを占有し続けるソフトウェアをロードするために利用する

## 11.3 仮想8086モードを体験

- 仮想8086モードへの移行方法は以下の2つがある
    - タスク切り替えによる方法
        - 仮想8086モードで実行するタスク用のTSSを用意しておき、あらかじめそのTSS中のEFLAGSレジスタを保存する領域のVMビットに当たるビットをセットしておく
        - セグメントレジスタを保存する領域にも仮想8086モードにおけるセグメントレジスタの値をセットしておく
        - このTSSに対してタスク切り替えを実行すると、タスク切り替えが完了すると同時に仮想8086モードへ移行する
    - IRET命令による方法
        - 割り込みの仕組みを逆向きに応用する
        - スタック領域に仮想8086モードで割り込みが発生した時の状態を作っておくと、IRET命令によって、仮想8086モードへ移行することができる
Pythonで線画を作ってみる

みなさんこんにちは！

SNSを利用していると、様々なアイコン画像が欲しくなりますね。
写真、アバター、似顔絵など様々な画像がありますが、今回は線画を作ってみようと思います。

便利なライブラリが豊富なので、Pythonを使います:grinning:

# やりたいこと
カメラを通して線画の抽出をしたいので、以下の要件で進めます。

- カメラを起動する
- エッジを抽出して、線画を生成する

# 利用するライブラリ
## opencv-python

OpenCVはインテル社が開発・公開しているオープンソースのライブラリで、画像処理や画像解析、機械学習向けの機能が集約されています。

今回利用するライブラリは、これだけです。

# 環境構築
今回はpythonの仮想環境を使うので、venvを使います。
venvについての詳細は[ドキュメント](https://docs.python.org/ja/3/library/venv.html)を参照してください。
```bash
python -m venv simple_illustration
source simple_illustration/bin/activate
cd simple_illustration
```
lsコマンドで確認すると、以下のようになります。
```bash
$ ls
bin/		include/	lib/		pyvenv.cfg
```
これで仮想環境ができたので、必要なライブラリをインストールします。
```bash
pip install opencv-python
```
ここまでで環境構築は完了です。

# 実装
それでは、実装していきましょう。
```bash
vi sample.py
```
```python:sample.py
import cv2

def line_drawing_image(img):
    # 画像のコントラストを調整
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    gray = cv2.equalizeHist(gray)

    # ガウシアンブラーを適用
    gray_blurred = cv2.GaussianBlur(gray, (5, 5), 0)
    
    # エッジ検出で線画を生成
    edges = cv2.adaptiveThreshold(gray_blurred, 255,
                                  cv2.ADAPTIVE_THRESH_MEAN_C,
                                  cv2.THRESH_BINARY, 11, 7)
    
    return edges

# OpenCV（cv2）でカメラデバイスに接続し、ビデオキャプチャを開始
cap = cv2.VideoCapture(0)

if not cap.isOpened():
    print("カメラが開けません。")
    exit()

frame_count = 0
saved_images = 0

# カメラがオープンしている間、または4枚の画像を保存するまでループ
while saved_images < 4:
    # フレームをキャプチャ
    ret, frame = cap.read()
    if not ret:
        print("フレームの取得に失敗しました。")
        break

    # 線画風の画像を生成
    line_drawing = line_drawing_image(frame)

    # 生成した画像を表示する
    cv2.imshow('Line Drawing Camera', line_drawing)

    # 10フレームごとに画像を保存
    if frame_count % 10 == 0:
        cv2.imwrite(f'line_drawing_{frame_count}.jpg', line_drawing)
        print(f'line_drawing_{frame_count}.jpg を保存しました。')
        saved_images += 1

    frame_count += 1

    # 'q'が押されたらループを抜ける
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# カメラをリリースしてウィンドウを閉じる
cap.release()
cv2.destroyAllWindows()
```
コードは非常にシンプルですね。
ライブラリのありがたみを感じます。

# 実行
それでは、実行してみましょう！
```bash
python sample.py
```
実行すると、PCのカメラが起動します。
試しに本棚の方に向けてみました。
![スクリーンショット 2023-11-30 13.41.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/b8bf0b4c-69ed-db7f-561d-ca5d8230a3f9.png)

カメラを通して、線画が表示されます。
子供のおもちゃ用に買った箱の文字（Hello Kitty）も見えます。

今回のコードでは、１０フレームごとに画像として保存します。

# 生成された画像
画像を見てみましょう。
まずは本棚の画像
![line_drawing_10.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/f6c0ee03-9822-2e9a-c248-99c439c46b37.jpeg)
カメラを通して見たものと同じ画像が保存されてました。
１０フレームごとに保存しているので、生成された画像ごとに多少のばらつきがあります。

続いて人物。
私が写り込んでみました。
![line_drawing_20.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/b07db22c-a220-ab32-acfa-651776d83d2d.jpeg)

いいですね！人物もきちんと線画になっているようです。メガネもくっきりですね！

どちらの画像も、500KB前後でした。

# まとめ
今回はOpenCVを利用して、線画を作ってみました。
OpenCV自体はJavaやC++でも利用できますが、個人的には、Pythonの方が実装は楽だと思います。

画像の学習にも利用されるようなので、これを機に、様々な画像が作れるようになりたいと思います！
GPTを活用した、ものづくり

# はじめに
[Life is Tech ! Advent Calendar 2023](https://qiita.com/advent-calendar/2023/life-is-tech)の1日目を担当します、 Webサービスプログラミングコース メンターのつぼつぼです！

Life is Tech ! では、 webサービス 開発を中心に中高生に向けてメンターをしています。

今年のアドカレ運営も、しました。
開始一週間前という直前告知にも関わらず、早くもシリーズ１は、全枠埋まり、シリーズ２も、ぼちぼち埋まってきました！🙏:maji-kansha!:
この記事を読んで、少しでも書きたいなと思ってくれたら、日にち過ぎてても大丈夫です!枠取りして、一緒に記事書きましょうー！

この記事では、昨日１周年を迎えた、ChatGPTを活用して、僕が普段のものづくりでどのようにAIを利活用しているのか、具体的な事例も含め紹介したいと思います。

# 活用意図
1. 開発効率の向上
GPTを使ったコーディングで、さまざまな面で時間短縮が可能になり、開発スピードが爆あがりします。

1. より高度な知識や技術の習得による実装
自分が今まで知らなかった知識や技術を用いての実装を提案してくれることもあり、より高度でスマートな実装方法を知るきっかけになります。

# 活用事例
1. エラーの解決
一番、ポピュラーなGPTの使い方なのでは、ないでしょうか。
エラーと、開発詳細を少し書くだけで、エラーの内容のみならず、改善案まで提案してくれます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2664651/0e15ad31-8872-3b9c-357c-ac3b13797263.png" width="30%">


1. 初期枠組みの生成
開発をしていく上で、僕が感じる一番のハードルは、やる気が出てから、軌道に乗るまでのいわゆるセットアップの時間。環境構築であったり、webサービスだと、枠組みを作るまでに、毎度かなりの時間を費やします。
GPT-4(有償版)を活用すると、画像も読み込ませることもできるため、このような画像の枠組みコーディング(フレームワークレベルのこと)をしてほしいとお願いするだけで、ある程度のコードを作ってくれます。
1. 反復箇所修正の効率化
僕は、一番ここで、GPTにお世話になっています。
たとえば、掲示板のようなサイトで、テーブル全てのカラムを表示させたいとします。
今までならば、手打ちで以下のような表示するためのコードを書いていたかもしれません。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2664651/049c11f0-77a4-3526-faf1-73d06b9455ba.png" width="30%">

1. リファレンス代わり
GPTは、リファレンスとしても非常に有用です。
たとえば、インスタンス変数は、他のルーティングでも呼び出せるのかなど、rubyのリファレンスを見て調べることもできるのですが、少し時間がかかり、面倒です。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2664651/52665807-8b78-362e-9a37-10712dbb6cc7.png" width="50%">


# まとめ
プログラミングスキルは、必要。
しかし、コードをバリバリ書けるよりも、他者(AI)のコードを読んで理解する力がより必要。
そして、自分のわからないところを明確に言語化し、伝える力が必要。

さて、明日は、めりあ🧸のクリスマス🎄コフレの話です。
コフレって、一体なんなんでしょうか？🤔 コーンフレークの略称でしょうか？🥺
楽しみです！
Windowsのbat（コマンドプロンプト）でタスクマネージャーのスタートアップ設定タブを開く

# やり方

```bat
taskmgr /0 /Startup
```

タスクマネージャーがこの状態で開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/e9cf0f8e-8ae7-7328-ddf3-c529258ca2b7.png)

# 蛇足

「PCの動作が遅い」っていう問い合わせが来るとスタートアップを見ることが多いです。
batファイルをファイルサーバーに置いたり、コマンドを単語登録（スニペットソフトに登録）しておくとプチ便利。

# 参考サイトさん

https://news.mynavi.jp/article/win10tips-23/

# バージョン

Microsoft Windows [Version 10.0.19045.3693]
Bun v1.0.0～v1.0.14のアップデート

この記事は[Bun Advent Calendar 2023](https://qiita.com/advent-calendar/2023/bunjs)1日目です。

何を隠そう、このAdvent Calendarを作ったのは私です。Bunの記事、たくさん読みたいなあ～と建ててしまったものの一向に埋まらないので、1日目を書かせていただきます。

もっといいもんかけるぜ〜という方は、ぜひこのカレンダーを埋めてください！

https://qiita.com/advent-calendar/2023/bunjs

## Bunのリリース履歴

2023/9/8、かっこいい動画とともにBun v1.0.0がリリースされました。

https://www.youtube.com/watch?v=BsnCpESUEqM

そこから2023/12/01、現在はv1.0.14が最新版です。

Updateのリリースノートはhttps://bun.sh/blog/ で読むことができます。

前回までのサマリーも載っていたりするので、時間がない人はこの2記事を開けば概要はつかめます。

https://bun.sh/blog/bun-v1.0.10
https://bun.sh/blog/bun-v1.0.14

全部紹介するのも冗長なので、個人的に気になるところをPickしていきます。

## Bun Install関連

注目度の高い機能なので、利用者が多くfeedbackも多いのだと思われる。

- v1.0.1
    - ハードリンク経由でパッケージのインストールが失敗するバグの修正 
- v1.0.8
    - 依存関係絡みで同じパッケージが重複するバグの修正
    - プレリリースタグの順序も考慮されるようになりました。
- v1.10.0
    - semverの解析周りのバグ修正
- v1.0.14
    - 同一パッケージの異なるバージョンが同時に抽出されると、依存関係のインストールに時折失敗するバグの修正。

## チューニング関連

Jarred Sumner氏のTwitter見てる感じ、もっといろいろあった気もするが……

- v1.0.2
    - `bun --watch`が速くなりました。
- v1.0.8
    - `require()`のメモリリークを修正して、メモリ消費量が30%削減。 
- v1.0.10
    - `node:http` が14%速くなりました。

## その他、個人的に良さそうなとこ
- v1.0.8
    - `bun test`のmockサポート 
- v1.0.12
    - CLIのHelpが改善して読みやすくなった。 
- v1.0.14
    - ExportおよびImportのエラー メッセージの改善



## おわりに

いかがでしたか？ この後もBunの記事が公開されるので楽しみですね。

Bunの今後についても、ロードマップを見ながら楽しみにしていましょう。それでは。

https://github.com/oven-sh/bun/issues/159
そういえばプログラマ35歳定年説とかあったね

この記事は [クラウドワークス Advent Calendar 2023](https://qiita.com/advent-calendar/2023/crowdworks) シリーズ1の3日目の記事です。

クラウドソーシングサービス「クラウドワークス」（crowdworks.jp）の技術的負債の継続的解消を目的としたジャンヌチームでエンジニアとして働いている[linyclar](https://linyclar.github.io/profile/)です。

「今年のアドカレには参加するか〜、みんなどんな感じでかいてるんやろ〜」と過去記事を見返していたら、大昔に取締役執行役員が35歳定年説について言及していました（[あるエンジニアの緩慢な死、あるいはエンジニア35歳定年説。](https://qiita.com/shinichinomura/items/da9d53f8e3f31df96e7a)）。

奇しくも自分は今年で35歳なので、キャリアの話とかを書こうと思います。

## 35歳定年説は嘘か真実か
今更35歳定年説について考察するのは価値もないことなのでサッと流しますが、35歳という年齢はともかく若手と見なされなくなる30代で節目を迎えるという意味で、嘘でもあり本当でもあるというのが答えだろうと思います。
この言葉自体は私がまだ高校生だった2000年代がピークで、2010年代以降は転職サイトのオウンドメディアのネタだったり、このエントリのようにふと思い出して書くネタになっているように見えます。
少なくとも今の時代に大真面目にこの説を信じてる若手はいないでしょう。

実際、私が働いている中でも35歳を過ぎてプログラミングを仕事としている人は何人も見ましたし、「自分じゃ同年齢になっても敵わんな」と思う人もいました（現職にも数人います）。
翻って、若手だから許されているだけで将来的には厳しそうだなと思う人もいましたし、実際に転職で厳しい目にあっている人の噂も聞いたことがあります。

そういう人たちを見ていると、なんとなく興味の比重やスタンスが分けられるなと思うようになりました。

## 興味：サイエンス、エンジニアリング、テクノロジー
何を中心に学び仕事に適用していくかは人それぞれです。

サイエンスとエンジニアリングについては、以下の引用で十分でしょう。

> ソフトウエアでの科学と工学の違いは、ほかの分野での違いと同じである。科学者は、真理を探究し、仮説を検証し、対象分野を深く掘り下げようとする。一方、技術者は、現実の問題を解決するために、真理を探究し、有用なものを見つけ出し、周知の事実を適用するのだ。

スティーブ・マコネル「ソフトウエア開発プロフェッショナル」（2005, 日経BP） p.43

> 物理学者は、技術者よりも原理や法則を深く理解しているが、装置設計能力はプロトタイプ設計に毛が生えた程度であり、実験レベルで知識の掘り下げを目的に作るにすぎない。現実の問題を実践的に解決するために頑丈で経済的な製品を作った経験もなく、訓練も受けていない。

スティーブ・マコネル「ソフトウエア開発プロフェッショナル」（2005, 日経BP） p.43-44

テクノロジーが興味の人はOSSのプロダクトとかクラウドサービス、なんらかのツールなどを学んで使おうとする人たちです。

私はエンジニアリング10、サイエンス3、テクノロジー2みたいな感じなので、サイエンスが強い人に対しても、テクノロジーを追えている人に対しても劣等感を抱えて日々過ごしていたりします。

## スタンス：ガチ勢、エンジョイ勢
妥当な言葉が思いつかないのでゲームでよく使われるこの言葉を使っているのですが、組織としての目的を意識して働けるガチ勢と、自分たちのチームが良い雰囲気であることを重視するエンジョイ勢に分かれるようです。
最近流行りの言葉を使うなら、心理的安全性を本来の意味で運用するガチ勢と、間違った意味で運用するエンジョイ勢です。

※本エントリで「エンジョイ勢」という言葉はネガティブに使っていますが、本来のゲームのプレイスタイルに対して批判する意図はありません

## 生存確率の個人的な印象

サイエンスまたはテクノロジーに主な興味があってエンジニアリングの心得のあるガチ勢の人は本当に強いと思います。
私のようなエンジニアリングに主な興味があってガチ勢の人も、学び続ける限りはなんとかなる気はしています。
というのも、エンジニアリングが「現実の問題を解決する」という宿命を背負っている以上、自身の問題解決能力に対して常に危機感を覚えているためです。

それに対してエンジニアリングの心得がなかったり、エンジョイ勢であったりするなら危ういだろうと思っています。企業が存在しているのは、社会に対してなんらかの貢献をするためであり、ソフトウェアを作るのはその貢献を実現するための問題解決の手段です。社員が働くのはその社会貢献のためです（建前はね）。
エンジニアリングの心得がなかったりエンジョイ勢であるなら、その社会貢献に対してコミットしているかに関心を持たないということであり、会社からのニーズに応えられているかにも関心を持っていないということになります。
ちなみに、エンジニアリングの心得がないというのは「デザインパターンやアルゴリズムを不必要なのに適用してコードを難読化」（サイエンス系）、「話題のツールだからとコストを考えずに導入」（テクノロジー系）などが良い例かと思います。
（ここまで書いていて、エンジニアリングの心得を持つなら必然、ガチ勢になるかもなと思いました）

そして、スタンスに関係なくいずれにも深い興味のない人は単なるサラリーマンエンジニアとして生存するのは難しいだろうと思います。少なくとも、なんらかの付加価値がない人はエンジニアとしての転職は非常に苦労するのではないかと思います。
私が関わったことのある人だと起業したかマネージャーになったか、セールスやマーケになったりという人が多い印象です。
変わりどころだとファシリテーションのコンサルティングみたいなことをしている人もいます。
それでもエンジニアとして生存している人は、業界知識やドメイン知識に注力している人たちです。特定企業の下請けで、その企業の製品知識とコード知識でエンジニアとして働き続ける本物の定年を迎えているエンジニアの方もいました。

## まとめ

サイエンスの知識が乏しくテクノロジーに興味がなくとも、エンジニアリングの心得があるガチ勢であれば生き残れるだろうと思っています。ただし、エンジニアとして「現実の問題を解決」するために学び続ける限りは、ですが。

それと、ここまで書いておいてなんですが、エンジニアがエンジニアのままでいることが正解というわけでもなく、各人が各人の人生での生存戦略を模索していくのが正解です。エンジニアを続けるにせよ、別の職種に転向するにせよ、キャリアがどん詰まりにならないようにできているかが一番重要でしょう。


個人的な状況についても書こうかと思っていましたが、長くなったのでこの辺で。
JUnit 5のビルドシステムを探る(その1)

<img width="1240" height="112" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/6d8a7098-9aef-a19a-54fe-83a2d493e033.png" alt="YUMEMI New Grad Advent Calendar 2023">

Gradleの標準DSLがKotlinとなってから早くも半年以上が経ちました。

https://blog.gradle.org/kotlin-dsl-is-now-the-default-for-new-gradle-builds

JetBrainsによる紹介ブログ

https://blog.jetbrains.com/ja/kotlin/2023/05/kotlin-dsl-is-the-default-for-new-gradle-builds/

この記事ではKotlin DSLが使われているプロジェクトである、JUnit 5の設定ファイルについて見ていきます。

JUnit 5のライセンス Eclipse Public License - v 2.0

https://github.com/junit-team/junit5/blob/main/LICENSE.md

JUnit 5のリポジトリ

https://github.com/junit-team/junit5

この記事はf5b78f68c6a90dfa63611c7a10027581c5d913baのコミットでの内容をもとにしています。ビルドシステムは随時更新されるため、最新のコードを追いやすいようコードのリンクはmainブランチのものとしています。

# 概要

* JUnit 5リポジトリの構成について <この記事>
* 共通で使われている設定について
* バージョンの記述について
* 個々のプロジェクトの設定について

# JUnit 5リポジトリの構成について

JUnit 5では `junit-jupiter` をはじめとする23個のサブプロジェクトが定義されています。リポジトリのルート直下に各サブプロジェクトがディレクトリとして配置されている構造です。
これらのプロジェクトはリポジトリのルートにある `settings.gradle.kts` から読み込まれています。
各サブプロジェクトの配下にはそのプロジェクトでの設定を記述する `<project name>.gradle.kts` ファイルが置かれています。個々のプロジェクトで使われる、テストや公開の設定が記述されています。

## `settings.gradle.kts` の設定

https://github.com/junit-team/junit5/blob/main/settings.gradle.kts

このファイルでは主に2つのことが記述されています。

1. プロジェクトの構成
1. Build Scanの設定
1. ビルド時の設定

### プロジェクトの構成

`settings.gradle.kts` にはサブプロジェクトを読み込む設定を記述します。
例えば
```kotlin
include("junit-jupiter-api")
```
の記述により、`junit-jupiter-api` のプロジェクトを読み込んでいます。

JUnit 5ではすべてのサブプロジェクトが列挙されています。ここで列挙したプロジェクトたちは `rootProject.children` で参照され、その後の各プロジェクトの設定ファイルの存在確認に使われています。

またプロジェクト間で共通する内容を記述しているプラグインの読み込みも記述されています。
リポジトリ内に置かれたプラグインの読み込みには `includeBuild` を使用します。
JUnit 5の `settings.gradle.kts` には以下のように記述があることから、共通設定は `gradle/plugins` 配下のファイルに記述されていることがわかります。
```kotlin
includeBuild("gradle/plugins")
```

### Build Scanの設定

Build ScanとはGradleで使用できるビルドの実行記録です。どのようなタスクが実行されたか、どのような依存関係になっているかなどが記録されます。
dependenciesに記述した各種依存関係で、推移的な依存関係の把握や実際に使用されているバージョンについての記録も残ります。どのライブラリが何に依存しているかがよくわかります。
pluginについても同様の依存関係の分析ができます。
また各タスクの所要時間をタイムラインで見られるので、時間のかかっているタスクの把握がしやすいです。並列化の恩恵もよくわかります。

https://docs.gradle.org/current/userguide/inspect.html

https://docs.gradle.com/enterprise/gradle-plugin/

デフォルトの状態では https://scans.gradle.com/ にBuild Scanが公開されます。設定をすることで独自のサーバーに公開するよう設定することもできます。
(個人でお試しされる際には一旦CIなどを利用して、どのような情報がアップロードされるかを確認するのがおすすめです。コンソールのログやビルドの設定の中に個人情報が含まれる可能性があります。デフォルトの状態ではユーザー名やIPが公開されるようになっています)

JUnit 5では[JUnit独自のサーバー](https://ge.junit.org/scans)にBuild Scanが公開されています。
他にもタグの設定や匿名化の設定がされています。

### ビルド時の設定

ビルド時の設定ですが、 `settings.gradle.kts` にはキャッシュについて記述されています。
JUnit 5ではリモートにビルドのキャッシュが保存されており、複数のマシンで使いまわせるようになっています。
CIでの時間削減に大きく貢献しているようです。

## `gradle` ディレクトリには何が入っているか



JUnit 5のリポジトリ直下のディレクトリは大体がサブプロジェクトなのですが、 `gradle` はサブプロジェクトではありません。
このディレクトリにはその名の通り `gradle` で参照するためのファイルが入っています。

特にプロジェクト内で共通に使用されるファイルが入っています。Linterの設定(コードのフォーマットからJavadocのスタイルまで定義されています)やライセンス表記の定義ファイルが置かれています。
また各プロジェクトから参照される共通のプラグインと、依存関係のバージョンをひとまとめにした設定ファイルがあります。

# おわりに

この記事ではJUnit 5のリポジトリの構成について見ていきました。
主に `settings.gradle.kts` で記述されている設定や、 `gradle` ディレクトリの中身について触れました。
次回は `gradle` ディレクトリ内で定義されているプラグインについてみていきます。
rootlessコンテナの必要性と現状

rootlessコンテナについて理解するために現状を調査しました

# rootlessコンテナの必要性

コンテナ内でのrootは何も対策をしていない場合は、ホストのrootと同じため、設定ミスやコンテナを脱出出来る脆弱性等があるとホストのroot権限を悪用される恐れがある

この問題を解決する仕組みとしてrootlessコンテナがある。

## rootlessコンテナとは

コンテナランタイムをroot権限なしで動作させることで、非特権ユーザがコンテナを作成、実行出来るようになる
このようにして実行されるコンテナをrootlessコンテナと呼ぶ

このようなコンテナにおいては、コンテナ内でのrootユーザは、ホストから見ると非特権ユーザとなる

※後に記載しますが**コンテナ内で動作するユーザを非rootにすることとは別の話です**

定義については https://rootlesscontaine.rs/#what-are-rootless-containers-and-what-are-not が詳しいです

## ホストのroot権限を奪取されると何が問題か

rootを取られるとまずい、というのはよく言われますが、具体的にどのような問題があるかを改めて整理します

- CPU資源の濫用
- クレデンシャルの窃盗
- 同一ホストで動くほかプログラムへの干渉
  - 特にKubernetesのような1つのマシンを複数の組織で利用する、いわゆるマルチテナントの環境では、別テナントのコンテナにアクセス出来る事自体が大きな脅威となります

# kubernetesにおいてrootlessコンテナで稼働させる例

- kubernetesのワークロードのコンテナをrootlessで動かす(`UserNamespacesSupport`)
    - https://kubernetes.io/docs/tasks/configure-pod-container/user-namespaces/

# rootlessコンテナの課題

セキュリティ上rootlessコンテナが良いことはわかりましたが、なぜ普及していないのか？
ここでは、いくつかの課題を紹介します。

- ネットワークパフォーマンスが悪い
    - これを解決しようとする研究もある
    - [コンテナ技術における最新の研究動向](https://speakerdeck.com/mt2naoki/kontenaji-shu-niokeruzui-xin-noyan-jiu-dong-xiang)
- well-knownポートをLISTEN出来ない
    - KubernetesではServiceなどを利用しポートを付け替えられるので大きな問題にならない

# 世の中の動き
- Docker, Podman, BuildKit, LXC などのコンテナランタイムはrootlessをサポートしている
    - https://rootlesscontaine.rs/#examples-of-rootless-containers
- RHELではコンテナをrootlessで動かすことが可能(内部はPodmanを利用している)
    - https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/8/html/building_running_and_managing_containers/proc_setting-up-rootless-containers_assembly_starting-with-containers

# 別の解決策

コンテナ内で動作するユーザをroot以外のみに制限することでもコンテナを脱出できる脆弱性による脅威の影響を軽減することが出来るが、コンテナにroot昇格の脆弱性があるとこの対策も意味がない

rootlessでコンテナを動かしておくと、コンテナ内でrootに昇格され、さらにコンテナから脱出されても、ホストのrootとは無関係のユーザであるため、ホストのrootを乗っ取られることにはならない。

rootlessのほうがより根本的な解決策となっている

このような手法の例としては、以下がある

- Kubernetes
  - `runAsNonRoot`などによる実行ユーザの制限
      - https://kubernetes.io/docs/concepts/security/pod-security-standards/
  - kubeletなどのコンポーネントを非rootで動作させる`KubeletInUserNamespace`
      - https://kubernetes.io/docs/tasks/administer-cluster/kubelet-in-userns/
gas ミリ秒を秒、分、時間、日にちに変換

# 時間の換算単位

```php
  //時間換算用の変数
  //1秒＝1000ミリ秒
  var seconds = 1000;
  //1分＝1000ミリ秒×60秒
  var minutes = 1000 * 60;
  //1時間＝1000ミリ秒×60秒×60分
  var hours = 1000 * 60 * 60;
  //1日＝1000ミリ秒×60秒×60分×24時間
  var days = 1000 * 60 * 60 * 24;
  //1年＝1000ミリ秒×60秒×60分×24時間×365日
  var years = 1000 * 60 * 60 * 24 * 365;

```


# 例１
```php
function sample1() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName('シート1');

  const startTime = sheet.getRange('A1').getValue();         // 開始時刻を取得
  const endTime = sheet.getRange('B1').getValue();           // 終了時刻を取得
  const duration = endTime.getTime() - startTime.getTime();  // 時間を算出（ミリ秒単位）
  
  const hours = Math.floor(duration / 3600000);                                      // 時
  const minutes = Math.floor((duration - hours * 3600000) / 60000);                  // 分
  const seconds = Math.floor((duration - hours * 3600000 - minutes * 60000) / 1000); // 秒
 
  sheet.getRange('C1').setValue(`${hours}:${minutes}:${seconds}`);  // セルに書き込み
}
```

# 例2
```php
function sample2() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName('シート1');

  const startTime = sheet.getRange('A1').getValue();         // 開始時刻を取得
  const endTime = sheet.getRange('B1').getValue();           // 終了時刻を取得
  const diff = endTime.getTime() - startTime.getTime();  // 時間を算出（ミリ秒単位）
  
  const diff_seconds = Math.floor(diff/1000)%60;    // 秒の計算
  const diff_minutes =Math.floor(diff/1000/60)%60;  // 余りから分の計算
  const diff_hours =Math.floor(diff/1000/60/60)%24; // 余りから時間の計算
  const diff_days =Math.floor(diff/1000/60/60/24);  // 余りから日数の計算
 
  sheet.getRange('C1').setValue(`${diff_days}日 ${diff_hours}時${diff_minutes}分${diff_seconds}秒`);  // セルに書き込み
}
```
### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592268/45a300e5-77f0-854e-086a-cd3954e63e6b.png)
GoogleスプレッドシートのURL種類 edit htmlview htmlembed out:html

<h2>設定</h2>
GoogleスプレッドのURLの説明なのでダミーのidを定義しておく

スプレッドシートid：dUmmy_SpreadId-1234_dUmmy_SpreadId-1234_dUmm

シートid(gid)：123456789




<h2>通常（権限ありなし共通）　edit　型</h2>
https://docs.google.com/spreadsheets/d/dUmmy_SpreadId-1234_dUmmy_SpreadId-1234_dUmm/edit#gid=123456789
<h2>html版全シート　htmlview　型</h2>
https://docs.google.com/spreadsheets/d/dUmmy_SpreadId-1234_dUmmy_SpreadId-1234_dUmm/htmlview#gid=123456789
<h2>シート単体html　htmlembed　型</h2>
https://docs.google.com/spreadsheets/u/0/d/dUmmy_SpreadId-1234_dUmmy_SpreadId-1234_dUmm/htmlembed/sheet?headers=false&gid=123456789

<h2>シート単体html　範囲指定　htmlembed　型</h2>
https://docs.google.com/spreadsheets/u/0/d/dUmmy_SpreadId-1234_dUmmy_SpreadId-1234_dUmm/htmlembed/sheet?headers=false&range=A1:F50&gid=123456789

<h2>シート単体html 列行表示あり　htmlembed　型</h2>
https://docs.google.com/spreadsheets/u/0/d/dUmmy_SpreadId-1234_dUmmy_SpreadId-1234_dUmm/htmlembed/sheet?headers=true&gid=123456789


<h2>シート単体html全幅　out:html　型</h2>
https://docs.google.com/spreadsheets/u/0/d/dUmmy_SpreadId-1234_dUmmy_SpreadId-1234_dUmm/gviz/tq?tqx=out:html&gid=123456789

<h2>シート単体html全幅　SQLあり　out:html　型</h2>
https://docs.google.com/spreadsheets/u/0/d/dUmmy_SpreadId-1234_dUmmy_SpreadId-1234_dUmm/gviz/tq?tqx=out:html&gid=123456789&tq=SELECT B,C,D WHERE B='hoge'

<h2>解説</h2>
以前よりGoogleスプレッドシートの表示するURLが何種類かあるのでは？

という印象を受けていたのですがここに来て色々調べてみました。

通常表示の　edit 版の表示だと多人数で表示していると、

おそらく、編集状況の通信が影響しレスポンスが非常に遅くなってしまう局面があった。

htmlview　や　htmlembed　形式だとレスポンスも早いので

用途に応じて使い分けられるのではないか。

<h2>仕様変更</h2>
以前、ドライブの直リンク・Googleドライブファイルサーバー化

'https://drive.google.com/uc?id=dUmmy_iMageId-12345_dUmmy_iMageId-12345_dUmm

で、できていたが突然繋がらなくなったりしていたことがあった為

スプレッドシートのURLもいつか使えなくなる可能性はあります。2023/11
俺のことが大大大大大好きな100社の企業

# きっかけはなんとなく

その時の仕事に不満はなく特に強く転職したい希望があったわけではないが、コロナ禍真っ最中でもリモートワークを導入する気がない性悪説で動いている会社に少し嫌気が差したのと、会社で中途採用活動をしているうちに自身にも転職意欲が沸々と湧いてきたのがきっかけ。

両親が遠方に住んでいて高齢で病院に通いがちになり、両親に何かあった時に実家に帰ってリモートワークでワーケーションできる環境を整えたかったのもある。

40過ぎの老害エンジニアなので、こんなおっさん雇ってくれるところなんてないだろうと思い、正直ダメ元でスタートした。

# スタートはForkwell

これまでの本気の転職活動はエージェントを使うことが多かったが、本当にダメ元スタートだったのでそこまでする意欲はなく、**技術書プレゼント企画目的で既に登録していた**Forkwellを使うことにした。Forkwellの思う壺だ。

ちなみに元々登録していたPaizaから可愛いエージェントの女の子から「あなたなら一流企業に転職できます！」的なメッセージが来たのでアポを取ったら、オンライン面談で来たのはおっさんで、言われるがまま履歴書・職務経歴書を提出して数社に出したが、全社書類選考で落ちた。まあそうなるよね。Paizaはそれでおしまいに。

# めっちゃくるスカウト

何度も言うが40過ぎのおっさんなので全く期待していなかった。
スキルは年の功でまあまあ積み上げてきたものはあるが、そうは言っても**初老男性**。そんな人雇いたいまっとうな企業なんてないと思ってた。

そう思ってたらめっちゃくるスカウト。

人事部の採用担当の人が手当たり次第送っているのだろうが、それでもいっぱい来ると嬉しい。トータルで100社以上来たと思う。


# 怒涛のカジュアル面談

嬉しくなって序盤はほとんどのスカウトに反応してオンラインカジュアル面談を受けた。
オンラインなので予定が組みやすい。1日で3社面談したこともあった。

あまりに反応しすぎて2週間先までスケジュールが埋まるという**人気インフルエンサー**みたいになってしまったので、後半は自重して週3社までとルールを決めた。

目的は転職なのだが、正直他社のエンジニアと話せるのが楽しかったというのもある。
どの会社も熱意のあるエンジニアな人ばかりで、「ここで働きたい！」と何度も思ってしまった。

# なかにはよくわからんカジュアル面談も

色々受けていると「この時間何？」みたいなカジュアル面談もあり、そちらから声を掛けてきた話なのに「で、何か質問ある？」からいきなり始まる面談も何社かあった。
サービスの内容や扱っている技術の内容など質問するも全部一問一答で話が広がらず、システム構成図も見せてもらえず、そういうのは10分もかからず終わった。

おそらく人事部が最初メッセージをくれていて面談はエンジニアに丸投げしたんだろうけど、それにしてもお互いに良い時間にするためにもうちょっとやる気を出して欲しかった。

# そして面接へ

転職活動で悩ましいのが企業ごとに選考スケジュールが違うこと。

例えば第二希望のB社の内定がすぐ出て、第一希望のA社の内定は早くても1ヶ月後とかだった場合に、B社に1ヶ月も待ってもらうわけにもいかず（大体の場合期限がある）、A社に賭けてB社を捨てるか、B社に決めてしまうかの究極の選択をしなければいけなくなってしまう。

過去の経験からそうなることを知っていたので、カジュアル面談と並行して第一希望群の3社の選考を同時に始めた。

楽しいカジュアル面談を経て大分気は大きくなっていたものの、この時もまだ半分は**どうせ一次選考落ちだろ**と大分ネガティブに考えていた。

# 最後はおみくじで

激しいツッコミのある面接だったり、おっさんになると縁遠くなる久々の適性試験＆技術試験でドキドキしたりとしたが、結果3社とも最終選考には進めて、2社から内定をいただくことができた。

どちらも魅力的な企業だったので決断できず、仕事が面白そうなM社にしようかなと思っていたものの心の迷いを捨てられず、最後は神社のおみくじで決めた。
**これで凶が出たらM社辞めよう**と思いながら引いたら、なんと大吉だった。すぐにM社に内定受諾の連絡をした。

# 最後に

神様の大吉のお告げ通り今はこれまでに経験できなかった新しい分野に挑戦できて充実している。
会社もエンジニアを尊重してくれる風土があって働きやすい。
少し前に母が入院したが1週間実家に帰ってワーケーションしながらゆっくりお見舞いに行くこともできた。前の会社ではできなかったことだ。

ここまで読んでくれた皆様に最後に伝えたい。

- 給与でも待遇でも仕事内容でも現状に不満があるなら、まず一歩でいいから動き出して欲しい
  - オンラインカジュアル面談なら時間も金もそんなにかからない
  - 無理かどうかやってみないとわからない。無理ならすぐやめれば良い。
- 面接はお互いに相手を計る場。こっちだけが見定められているわけではない。堂々といこう。
- 面接は自分をプレゼンする場。自分がいかに相手にとって有用かをテーマにプレゼンするつもりでいこう。誠実と熱意だけではダメ。
- そうは言っても最後は情熱が一押しする。「何でもやります！」よりは「これがやりたいんです！」というプレゼンの方が相手に刺さる。

最近は少し変わってきた気がするが、それでもまだ海外に比べたら日本のエンジニアの立場はまだまだ低い気がする。
みんなが良い待遇を求めて動き出せば、企業も良い待遇を用意せざるを得ない。

**みんなでエンジニアの立場を向上させようぜ**
VirtualBoxのWindows server同士でPing通信ができない時

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3329746/c7fb048a-0a09-cc0e-5fb2-55765e42e117.png)

# 目次
[1.前提](#前提)
[2.三つの原因](#三つの原因)
　[2.1.IPアドレス等の設定](#IPアドレス等の設定)
　[2.2.ファイアウォール](#ファイアウォール)
　[2.3.仮想マシンのネットワーク設定](#仮想マシンのネットワーク設定)
[3.まとめ](#まとめ)

# 前提

前提としてVirtualBox上に構築された仮想マシン「Windows server2019」「Windows10」同士
または、ゲストOSとホストOS同士のping通信ができなかった時の記事となります。

# 環境情報
・Windows10(ホストOS、ゲストOS)
・Windows server 2019(ゲストOS)
・VirtualBox 7.0.12(win)
 
# 三つの原因

ping通信ができない理由として主に三つあります。

**1.IPアドレス等の設定が間違っている**
**2.ファイアウォールによって通信が許可されていない**
**3.VirtualBox上の仮想マシンの設定が間違っている**


 　
### IPアドレス等の設定

まず原因のひとつとしてIPアドレス、サブネットマスクが正しく設定されていない可能性があります。

コントロールパネルより [ネットワークとインターネット] > [ネットワーク接続]より対象のネットワークアダプターを右クリックし、[プロパティ]を押下します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3329746/1f0233d9-557e-1413-e6a7-f8ffa767c261.png)

　
[インターネットプロトコルバージョン4(TCP/IPv4)]を選択し、[プロパティ]を押下します。
　

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3329746/ec140db0-5ec3-139b-b184-cdf99811dddb.png)

正しいIPアドレスとサブネットマスクを入力します。
ここで入力するIPアドレスは通信する対象のOSと**同じセグメント**となるように設定してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3329746/9ef5ee48-ad10-da6b-d0d0-268c7ceada85.png)


### ファイアウォール

もう一つの原因としてファイアウォールにより通信が拒否されている可能性があります。
Windows serverではデフォルトでPING通信が拒否されています。
この設定は、ping通信を送る機器とping通信を受け取る機器それぞれに設定を適用する必要があります。

サーバーマネージャーのツールからセキュリティが強化されたWindows Defender ファイアウォールを起動します。
受信の規則の**仮想マシンの監視(エコー要求 - ICMPv4受信)**　が無効化されていることを確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3329746/ca2a05e8-d2da-f495-839f-c123bf04fd4a.png)

対象の規則を右クリックし、[プロパティ]を押下します。
[有効]のチェックボックスを押下し、規則を有効化します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3329746/59b7d634-66a5-5d10-7b47-64dcd29f946a.png)

規則が有効化されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3329746/e91abfeb-cf70-c1d9-06d8-a3dce090746e.png)

### 仮想マシンのネットワーク設定

VirtualBoxではネットワークアダプターの割り当てがデフォルトでNATになっています。
NATではホストOSとゲストOSの通信は可能ですが、ゲストOS同士の通信はできません。

そこで、それぞれの通信が可能な**ホストオンリーアダプタ** を使用します。

Oracle VM VirtualBoxマネージャーを起動し、対象の仮想マシンを選択し、[設定]を押下します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3329746/d8475ff2-8653-89d4-b6ff-f4d152327893.png)

[ネットワーク]を押下します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3329746/721918ac-3db0-d380-34ad-ae6891de28d7.png)

[割り当て]を押下し、プルダウンメニューから[ホストオンリーアダプタ]を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3329746/44262232-269e-ecbe-566f-7e21e68dc352.png)

変更が完了しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3329746/43d89cdd-3633-87b8-3810-449b2c84b834.png)


# まとめ
これらの設定を適用すればたいていの場合は問題が解決すると思います。

設定を適用する対象は一つの仮想マシンだけではなく**通信先の仮想マシン**にも適用することを忘れないようにしましょう。

これでも通信ができない場合はpingコマンドでIPアドレスが間違っている、windows端末のアダプターの共有設定など原因として考えられることは多くあります。

ネットワークに関する設定は複雑なので自分が**どこの何を設定したか**を覚えておくと良いでしょう。
Google Apps Script (gas) で日付のタイムゾーンを日本に直す方法

# Google Apps Script (gas) で日付のタイムゾーンを日本に直す方法
```php
function myFunction() {
  const date = new Date();
  const strdate = date.getFullYear() + '/' + (date.getMonth()+1) + '/' + date.getDate() + ' ' + date.getHours() + ':' + date.getMinutes() + ':' + date.getSeconds();
  Logger.log(date);
  Logger.log(strdate);
}
```
### 解決方法
タイムゾーンを変更
プロジェクト設定 → 日本標準時間に変更

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592268/3d891509-e1ea-7b8e-4b38-991e4dd4d402.png)

appsscript.jsonを開きて、見るとtimeZomeが America/New_York からAsia/Tokyoに変更されているのが分かります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592268/0fbf5d18-c025-445d-e1a4-fca682e60725.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592268/1c921ca9-3131-d2d9-47a8-852d39445c71.png)
2023アドカレ01: 一人アドベントカレンダーの意気込み

この記事は[ぐらだけアドカレ2023](https://qiita.com/advent-calendar/2023/c9h11o2)の1日目の記事です。

今年は一人アドベントカレンダーをやってみようとなったので、ひとまずどんなことを書くのかや、軽い自己紹介をば。
意気込み記事で１日埋めちゃうのもどうなのだろう？……とも考えたのですが、複数人で明確にテーマが決まっているカレンダーでもないので、書いてもいいか、と。

# 私、IT、チョット、ワカラナイ
わからないのかよ！！！

私は「チョット、ワカル」＝「それなりのエンジニア」と思っています。
それと比べると、エンジニアと言えなくもないかもしれないが正式なエンジニアではない……というのが私です。

# どこでなにやってるひと？
地方中小企業の総務課のヒトです。

経歴や肩書きで言うと、大学は情報学部中退、SEの父からPCやプログラム関連の小話を浴びて育ちました。
地方の小さな会社なので情報システム部門なんぞありません。私の所属は総務です。でも経理も情報システム系のこともやります。小さい会社なので。
いわゆる、地方中小企業の一人情シス（自称）……なのかなと思います。

# どんなことやってるの？

経理・総務事務が通常業務。
スキマ時間でRPA+効率化ツールや社内アプリの便利機能を発掘しつつ、効率化を測っています。
現在はRPA化する前の段階の効率化を最近はメインに行っています。
API連携やアプリ内の機能をうまく使えば手入力・手計算がだいぶ減りますし、RPAのメンテナンスまで手を伸ばさずに済むので、RPAを育てる前に、RPA外の整頓を行っています。

# アドベントはなに書くの？

仕事面ではRPAやノーコードについていくつか記事を書こうと思っています。
その他は趣味関連の制作物を作る際に便利だった機能やツールについて書いていきます。

いわゆるコーディング関連の話はしませんが、ちょっとした業務の改善や作業の効率化、創作などにあたり、誰かの参考になれば幸いです。

どの順番にするかは未定ですが、予定記事を載せておきます。
下書きが上がった順に公開していくと思います。

- 技術書典で得たこと
- ココフォリアのクリックアクションについて
- 自作リバーシの作り方
- 三角柱ざっくりカレンダーの作り方
- 職種のコミュニティと情報収集について
- PDF→epub変換周りの話
- 職場で聞かれたPC周りの小技集
- 店舗現金集計ツールの作成とメンテ
- クリップスタジオのオートアクションについて
- 複数SNSで得られる情報・雰囲気
- 有料フォントxBGMエディタ
- シン・テレワークシステム共有設定
- もくもく会チャレンジ
- ノーコード開発を利用するにあたっての注意点
- こども向けフォントを自作した話
- アドカレを書くにあたっての記事テンプレートについて
- LaTeXでさっくり同人誌
- WEBオンリー企画ページを作る
- 電子請求書の確認まとめツールを作る
- e-gov公文書取得手順の効率化について
- Figmaでマスキングテープをデザインする
- こども用ボドゲと、こどもが理解できるアルゴリズムについて

# なんで書こうと思ったの？

技術記事を書くことで、自分の知識を整理したり、誰かに読んでもらえる文章が書けるようになりたかったからです。
実際、自分がいる会社はITに疎く、「こういったことができる」「こうすると効率がいい」ということを発信することもできなくてはと思います。
そういったことの練習として、誰かに読んでもらう文章を書けるようになりたい、と思いました。
そして、そういった文章を書けるようになるために、文章の書き方を学んで練習していく……というよりはアドベントカレンダーを通じて誰かに文章を読んでもらうという本番をたくさん重ねる方が手っ取り早いと思いました。
あと、Qiitanぬいぐるみが可愛かったので、4歳娘にあげたかった……。

# 書くことでどうなりたいの？

- 技術を誰かに伝える文章を書けるようになりたい
- 文章を使った情報発信に緊張しないようになりたい
- 年の瀬に達成感を味わいたい
- Qiitaの最大のイベントであるアドカレを楽しみたい
- **Qiitanぬいぐるみをお家にお出迎えしたい**

……まぁ、最後が達成できればOKです！

そんな訳で、1ヶ月頑張っていきたいと思います。
読んでみようかな？となったものがありましたらぜひー！
proxy.py をベーシック認証付きプロキシサーバーとして動かす

ベーシック認証付きのプロキシサーバーを通した通信をテストする場面があり、 [proxy.py](https://pypi.org/project/proxy.py/) を使ったら思いのほか簡単に動かすことができたので紹介します。
以前 [WSL2で簡易プロキシサーバーを立てる](https://qiita.com/constdrop/items/1f3eb60305f76fe3cd01) というのを書きましたが、Pythonがインストールされた環境ならばこちらの方がお手軽です。

# 環境
* Python 3.8 (3.6以上が必須となっています)
* proxy.py 2.4.3

# proxy.py のインストール
以下のように pip でインストールするだけです。
[公式サイト](https://pypi.org/project/proxy.py/)には、 Docker や HomeBrew を使った方法も書かれています。
```
pip install proxy.py
```

# 実行
ここでは、以下のように動作するように実行します。
* 他のPCから利用できるようにする。（ファイアウォール等、別途設定してください）
* ポートを 8080 にする。
* ユーザー名: user 、パスワード: password でベーシック認証を行う。

以下のコマンドを実行すると動作が開始し、プロンプトにログが出力されます。
終了する際は、 [Ctrl] + [C] で終了できます。
```
proxy --hostname 0.0.0.0 --port 8080 --basic-auth user:password
```

# 動作確認
以下のコマンドを実行して、ベーシック認証が動作していることを確認します。
```
curl -x user:password@localhost:8080 https://httpbin.org/get
```
そのまま実行すると応答が表示されます。
ユーザー名やパスワードを書き換えて実行すると、 407 エラーとなります。

その際のログは以下のようになります。
```
2023-11-30 11:15:24,240 - pid:15444 [I] server.access_log:384 - 127.0.0.1:52175 - CONNECT httpbin.org:443 - 6107 bytes - 1620.53ms
2023-11-30 11:15:39,940 - pid:15444 [I] handler.handle_data:179 - HttpProtocolException: ProxyAuthenticationFailed
2023-11-30 11:15:39,941 - pid:15444 [I] server.access_log:384 - 127.0.0.1:52191 - CONNECT None:None - 0 bytes - 1.00ms
```

#
proxy.py を使った、テスト用のお手軽なベーシック認証付きプロキシサーバーの動かし方でした。
WindowsやOffice製品で「契約ライセンス数 ＜ ライセンス認証回数」となるケース

私はエンジニアになって9年経ちますが、Microsoftのライセンス関連は未だに難しく感じます。
今回はPC大量キッティング案件で学んだMicrosoftのライセンス事情を発信します。

## 契約ライセンス数とライセンス認証回数には乖離がある

例えば、Windows11を10ライセンス契約したとします。10台のPCでWindows11を使用できます。
ですが、**ライセンス認証は10台以上で行えます。（例えば15台のライセンス認証が可能です）**

これは、PCの再セットアップ等でWindowsやOfficeのライセンス認証が再度必要になった場合のために、契約ライセンス数を超過してライセンス認証を行えるようになっているためです。（と、思われます）
とはいえ無限に認証できるわけではなく、認証回数の上限が設定されています。この上限は、ライセンス購入元業者に依頼して引き上げ可能です。現在では、契約ライセンス数×３倍まで引き上げ可能なようです。もしかすると購入元業者によって異なるかもしれませんので、購入元業者に確認してください。

## 契約ライセンス数超過にはご注意を
「契約ライセンス数 ＜ ライセンス認証回数」となるケースをご紹介しました。
しかし、**契約ライセンス数を超過した使用はNGです。** ライセンスの使用状況をチェックする何らかの仕組みは存在すると思いますし、違反が発覚した際は「超過分を払えばいいんでしょ」とはならないと思います。天下のMicrosoftですから損害賠償まで発展するかもしれません。

契約ですから、後ろめたいことがないよう努めなければいけませんね。
Next.jsでアプリケーション開発を始めるには。


## Next.jsアプリケーションの雛形を生成する

create-next-appを使って、Next.jsアプリケーションの雛形を生成します。言語はもちろんTypeScriptです。Tailwind CSSも使ってみると便利ですね。

```
> npx create-next-app@latest
✔ What is your project named? … next-notslack-app
✔ Would you like to use TypeScript? … Yes
✔ Would you like to use ESLint? … Yes
✔ Would you like to use Tailwind CSS? … Yes
✔ Would you like to use `src/` directory? … Yes
✔ Would you like to use App Router? (recommended) … Yes
✔ Would you like to customize the default import alias (@/*)? … No
```

指定した設定は下記の通り
- アプリケーションの名前：next-notslack-app
- TypeScript：利用する
- Tailwind CSS：利用する
- ソースコードの場所：srcフォルダ内に配置
- App Router：利用する
- インポートエイリアス：カスタマイズ無し

雛形が生成できたら、アプリケーションフォルダに移動して、開発用サーバーを起動します
```
> cd next-notslack-app
> npm run dev
```

![hinagata_image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24283/b0c3287e-9d6b-4d7e-6fb5-b91ee1d48542.png "雛形アプリケーションの初期画面
")

## お勧めのパッケージ

Next.jsと一緒にtRPCとPrismaをよく使っています。
それぞれ下記のコマンドでインストールできます。いずれもアプリケーションフォルダ内で実行してください。

```
npm install @trpc/server @trpc/client @trpc/next @tanstack/react-query @trpc/react-query zod superjson
```

```
npm install prisma -D
npx prisma init
npx prisma migrate dev --name init
npm install @prisma/client
```
Vitis HLSでAXI Streamのサイドチャネルを無効にする方法

## 問題

Vivado HLSでは自分でlastやstrbなどを含む構造体を宣言すると、そのポートが自動でサイドチャネルにアサインされていました。Vitis HLSではその方法が使えなくなったので、どうすれば必要なサイドチャネルだけをAXISにアサインできるのか調べました。

## 解決方法

[公式のドキュメント](https://docs.xilinx.com/r/en-US/ug1399-vitis-hls/Customizing-AXI4-Stream-Interfaces)に書いてありました。以下にように指定することで、TKEEPとTLASTだけがアサインされるようです。
```
#include <ap_axi_sdata.h>

using data_t = hls::axis_data<int, AXIS_ENABLE_KEEP|AXIS_ENABLE_LAST>;
```
AWS Glueを使用したS3からRDSへのデータ取り込み1

## R3に保存したCSVをRDS（MySQL）へ取り込みたい
別サービスで発生したデータをRDSへ取り込みたい。
S3にCSV形式でファイルを置けることは確認したので、次にCSVをRDSへ取り込む方法を調べてみた。
できるだけ楽に。サーバーレスでノーコード。画面ポチポチで実現したい。

### 最初に考えた構成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390179/fd90f1b8-afdf-d743-497a-a908eff7effd.png)

調べたところ、DataPipelineというサービスが費用的にも難易度的にもハードルが低そうだったので候補とした。
とりあえず、簡単なCSVを連携してみよう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390179/af29af9b-87fb-16e5-43ea-11244b278b57.png)

と思ったら、なんかメンテ中だのAPIかCLIならみたなことが書いてある。
ネットをさぐると、現在（2023年11月）DataPipelineはAPIかCLIからのみ使用可能らしい。

出鼻をくじかれた・・・

楽がしたいので、画面ポチポチして構築できるような他のサービスを模索。
AWSのドキュメントを見ていると、移行するならGlueってのがあるよと言った記載を確認。
他にあてがあるわけでもないので、こいつに賭けてみよう。

### 新構成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390179/9ea6ff80-2513-7c7f-e7c6-1a92fd6846a2.png)

DatapipelineをGlueにかえただけ。
経験無しな上に、理解力が極めて低い私が手探りでサービスを使用してみる。

### 準備1 S3
これは特に、特別な設定は不要。
パブリックアクセスをさせるつもりはないので、デフォルト設定でバケットを作ればOK。
面倒なので画像も無し。

### 準備2 Glue用のIAMロール
Glueが、S3やRDSを使用するためのIAMロールが必要らしい。
ドキュメントに従って用意してみる。
[AWSドキュメント IAMロール](https://docs.aws.amazon.com/ja_jp/glue/latest/dg/set-up-iam.html "AWSドキュメント IAMロール")

ユースケースにGlueを選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390179/c936142f-8d99-c5a6-83fb-7347f395b905.png)

アクセス権限の付与。
AWSGlueServiceRoleはとりあえず入れておく。
S3はデータ元として読み取りだけできればＯＫ。
RDSは書き込みを行うためにFullAccessを付与した。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390179/627f72c6-2f47-8f0a-6b4b-b58b0d6d8e6d.png)

ロール名の設定。
注意点、ロール名には「AWSGlueServiceRole」をプレフィックスに付与すること。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390179/9580d40d-8df6-ea06-badd-b9a462d1fa2b.png)

これでIAMロールの準備は完了。
次はGlueがS3に接続するための準備をしよう。

### 準備3 S3との接続準備
GlueからS3へ接続するためには、VPCにエンドポイントを設定する必要がある。
まずはVPC。これはもう普通に作ればOK。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390179/2fec1743-6f0c-254d-d6b5-dd6f3e5dbe2c.png)

バケットを作成したS3を指定し、タイプは「Gateway」を選択する。
VPCは作成したものを対象にする。

### 準備4 セキュリティグループ作成
GlueがRDSへ接続するためのセキュリティグループを作成する。

これもドキュメントにポイントが書いてある。
[AWSドキュメント セキュリティグループ](https://docs.aws.amazon.com/ja_jp/glue/latest/dg/set-up-iam.html "AWSドキュメント セキュリティグループ")

以下のインバウンドルールがポイント。
・インバウンドに作っているセキュリティグループ自身をソースに指定する
・タイプ：　すべてのTCP
・ポート範囲：　0-66635

### 準備5 RDS用のインバウンド設定

上で作ったVPCのインバウンドルールに、Glue用のセキュリティグループを指定する。
・タイプ：　MYSQL/Aurora
・プロトコル：　TCP
・ポート範囲：　3306

### 準備6 RDS
もうこれは普通にMySQLのインスタンスを作るだけ。
中を見るのにいちいちEC2を作るのも面倒なので、一旦publicな状態で作る。
取込結果は、MySQL Workbenchを使用してみる。

これでS3→Glue→RDSへ接続するための準備が完了。
よっしゃ。Glueの設定に進もう。