OmniAICreator/Qiita-1.07M huggingface dataset:
AI業界の研究

# はじめに

:::note warn
注意
この記事は私自身の就活のためのメモのような感じなので、ご容赦ください。
:::

近年、人工知能（AI）は急速に進化し、さまざまな分野での活用が進んでいます。

就活時に必要になる業界分析ですが、 **現時点まで（ここ重要）** の、AI業界はどのような状態なのか、だったのか気になったので、GPT-4oに聞いてまとめました。




# 概要
本記事では、AI業界の全体像、市場規模、ビジネスモデル、景気動向、将来性、安定性、主要企業について網羅的にまとめました。

今後、それぞれの項目についてより詳しく調べようと思います。


# 1. 業界の全体像

人工知能（AI）業界は、機械学習やディープラーニングなどの技術を活用し、データ分析、自動化、予測、最適化など多岐にわたる分野で応用されています。主なセグメントとしては、以下が挙げられます。

- **自然言語処理**
- **画像認識**
- **音声認識**
- **ロボティクス**
- **推薦システム**

これらの技術は、製造、金融、医療、エンターテインメント、交通など、さまざまな業界で活用されています。

# 2. 規模

AI市場は急速に拡大しています。マッキンゼーの報告によれば、生成AI（ジェネレーティブAI）は、業界全体で年間 **2.6兆ドルから4.4兆ドル** の価値を生み出す可能性があるとされています。

https://www.mckinsey.com/jp/~/media/mckinsey/locations/asia/japan/our%20insights/the_economic_potential_of_generative_ai_the_next_productivity_frontier_colormama_4k.pdf?utm_source=chatgpt.com


この成長は、企業の生産性向上や新しいビジネスモデルの創出に寄与しています。

# 3. ビジネスモデル

AI企業のビジネスモデルは多岐にわたります。主なモデルとして以下が挙げられます。

### 製品販売型
AIを組み込んだハードウェアやソフトウェアの販売。
- **例：** AIチップを製造する **NVIDIA** など。

### サービス提供型
AIを活用したクラウドサービスやAPIの提供。
- **例：** マイクロソフトの **Azure AI**、Googleの **Google Cloud AI platform** など。

### コンサルティング型
企業向けにAI導入の支援やカスタマイズ開発を行う。
- **例：** 日本の **日立製作所**、**富士通** など。

### データ提供型
AIモデルの訓練に必要なデータセットの収集・提供。
- 特定の業界向けにデータを提供する企業も存在。

# 4. 景気動向

AI業界は現在、世界的に大きな投資が行われており、特に **アメリカや中国** が主導的な役割を果たしています。一方、日本も遅れを取り戻すべく、**ソフトバンクとOpenAIの提携** などを通じてAI分野での地位向上を図っています。

https://elpais.com/economia/negocios/2025-02-12/japon-busca-su-sitio-en-la-revolucion-de-la-ia.html?utm_source=chatgpt.com

しかし、AIスタートアップの中には高い開発コストや人材確保の難しさから経済的なプレッシャーを感じている企業もあり、持続可能なビジネスモデルの構築が求められています。

https://www.lemonde.fr/en/economy/article/2024/09/21/ai-start-ups-are-feeling-the-economic-pressure_6726779_19.html?utm_source=chatgpt.com

# 5. 将来性

AI技術の進化は今後も続くと予想され、特に **生成AIや自律型システム** の分野での発展が期待されています。

これにより、より高度な **自動化** や **パーソナライズされたサービスの提供** が可能となり、さまざまな業界での革新が進むでしょう。また、**AIと他の先進技術（例：量子コンピューティング、IoT）** の融合により、新たな価値創出が期待されています。

# 6. 安定性

AI業界は急速に成長しているものの、技術の進化スピードや市場の変化に伴い、企業間の競争が激化しています。

特に **スタートアップ企業** にとっては、資金調達や収益化の課題があり、経営の安定性を確保するためには **継続的なイノベーション** と **適切なビジネス戦略** が必要です。一方、**大手企業** は豊富な資金力とリソースを活用し、AI分野での地位を強化しています。

# 7. 業界を構成する企業（日本とアメリカ）

### 日本の主要AI企業

- **ソフトバンクグループ** ：OpenAIとの提携を通じて、AIサービスの提供を強化。
- **日立製作所** ：AIを活用した社会イノベーション事業を展開し、製造業や社会インフラ分野でのAI活用を推進。
- **富士通** ：AI技術「Zinrai」を活用し、医療や金融など多様な分野でソリューションを提供。
- **Preferred Networks** ：ディープラーニング技術を活用し、製造業やバイオ分野でのAI応用を進める。
- **Sakana AI** ：省電力な生成AI基盤の開発に取り組む東京発のスタートアップ。

### アメリカの主要AI企業

- **Google（Alphabet）** ：検索エンジンや広告ビジネスに加え、AIを活用したサービスを展開。
- **OpenAI** ：生成AIのリーダーであり、ChatGPTなどのプロダクトを提供。
- **Microsoft** ：Azure AIを通じたクラウドAIサービスを展開。
- **NVIDIA** ：GPUを活用したAIハードウェアのリーディングカンパニー。
- **Meta（Facebook）** ：AIを活用した広告、VR/AR分野での研究開発を推進。



# まとめ

AI業界は急成長を遂げ、多様な分野での活用が進んでいます。市場規模は拡大し、今後も **生成AIや自律型システム** の発展が期待されます。

一方で、**資金調達や競争の激化** などの課題も存在し、持続可能なビジネスモデルの確立が求められています。

今後の動向を注視しながら、適切な戦略を立てることが重要です。
Spring Boot + My SQL 　基礎知識インプット編

Udemyの動画講義を用いながらSpring Bootを用いてREST APIを構築した。今回ではREST APIをデータベース(My SQL)に対応させる演習を行った。いまいちパッとしない単語などを明確にしながらどんな仕組みで動いているかに着目して書いていく。

1.SpringBootとは
Java言語の環境下で使用できるWebアプリケーションの開発をサポートするフレームワーク₁のことで設定がシンプル、組み込みサーバー、依存関係を自動管理など様々な便利な機能がある。使用することで開発の効率化や品質の向上などが見込める。

1-1.フレームワークと依存関係の違いについて
フレームワークとはアプリ開発のための土台に事で共通の処理を提供してくれる。（SpringBootもそうだけど他のもあるよ）一方、依存関係とはプロジェクトで使う外部ライブラリのことでpom.xml(Maven)やbuild.gradle(Gradle)で必要なライブラリを指定している。例えると、「フレームワーク＝車のフレーム、依存関係＝マフラーやカーナビなどのオプションパーツ」

1-1-1.依存関係の種類について
依存関係は大きく分けて「インフラ系」と「フレームワーク補助系」がある
インフラ系（基板）...外部サービスやDB（データベース）と接続する役割(MySQLDriver、PostgreSQLなど)
フレームワークサービス系...SpringBootの機能を拡張（SpringDataJPA、Spring Security）

1-2.RESTとは
Webシステム設計原理の１つで、URLとHTTPメソッドを使ってデータをやり取りする仕組み。

REST = API 設計のルール
URL でリソースを表す
HTTP メソッド（GET, POST, PUT, DELETE）で操作する
ステートレス（サーバー側で状態を持たない）

1-2-1.API(Application Programming Interface)とは
あるサービスが提供する機能を、別のプログラムから利用できるようにするもの。API=ほかのサービスと連携できるインターフェース

2.MySQLとは
データベース管理システムのこと。リレーショナルデータベースという保存方法を使っており、データを複数の「テーブル」と呼ばれる個別のストレージの分割する2。

まとめ
SpringBootで作ったRESTAPIをMySQLに対応させるにはSpringBoot内の依存環境のSpring Web,Spring Data JPA,My SQL Driverを用いてMySQLと繋げ（厳密にはMy SQL Driver)データベースをMySQLで管理でき、大きなデータの検索・作成・更新・削除が容易になる。

次回予告　MySQLと対応させるために必要なアプリケーションクラスについて、重要なアノテーション
OutSystems で既に Publish 済みの Entity Attribute Name をデータ保持したまま変更

OutSystems でアプリケーションを作成中に、以前に作ったEntity の Attribute 名を変更したい時があります。
このAttribute名について、単純に変更してしまうと思いもよらぬ挙動をして、データが失われ、アプリケーションが正常に動作できなくなります。

特にOutSystems で既にアプリケーションをある程度構築していて、さらに本番環境で稼働させている中でのリファクタリングや機能修正を行う際に、この問題に直面すると悲しみに暮れます。
Attributeの整理を行いたいという願望に対して、現代の最先端を行くべきローコードツールとは思えないほど容易ではありません。
（なぜカラム名を変更できるような仕組みにしなかったかは、OutSystems開発者の方々の、将来を見据えた意図があるかと思いますが....）

そこで、既にPublish済みのEntityに対して、安全に Attribute 名を変更する方法を説明します。

# OutSystemsの驚くべき仕様
**カラム名を変更すると、新しいカラムとして扱われる**
OutSystems では、Entity の Attribute 名を変更すると、実際のデータベース上では「新しいカラム」が作成され、元のカラムは「非表示（hidden）」になる という仕様になっています。

なお、一般的な SQL データベースでは ALTER TABLE ... RENAME COLUMN を使用してカラム名を変更できます。

**が、OutSystems ではそのような処理は自動で行われません。**

## カラム名変更の影響
この仕様により、カラム名を変更しただけでは、元のデータが新しいカラムに引き継がれません。
その結果、開発者が 「Attribute 名を変えただけなのに、データが消えた!?」 という状況に陥る可能性があります。

## もうすでにAttribute名を変更してデータがなくなってしまった？
もし 「カラム名を変更したらデータが消えた！」 という状況に陥った場合でも、データを取り戻すことが可能です。

手順：消えたデータを復元する
1. 対象の Entity に新しい Attribute を追加する
1. 追加する Attribute 名を「変更前のカラム名」と同じにする
1. すると、OutSystems が既存の（非表示になった）カラムと再び紐付けを行い、データが復元される

**なぜ復元できるのか？**
OutSystems は カラムを物理的に削除するのではなく、「非表示」にするだけ なので、
「元のカラム名で新しい Attribute を作成する」と、OutSystems がそれを 元のカラムと再びリンク するため、データが復元されます。

# **本題：Entity Attribute Name をデータ保持したまま変更する方法**

OutSystems では、**Entity の Attribute 名を変更すると、データが引き継がれない** という問題があります。  
そのため、安全にカラム名を変更しつつ、データを保持するための手順を説明します。  

---
**1. 変更後の Attribute 名を「新規作成」**
- OutSystems ではカラムのリネームができないため、新しい Attribute を作成する。  

**2. 変更前の Attribute から変更後の Attribute にデータをコピーするロジックを作成**
- データ移行用のServer Actionを作成する。
    - 対象となるEntityに対して、元Attributeに保存されている情報を新Attributeへコピーし、DBへUpdate処理をする。  
- OutSystemsのTimerを使って、アプリケーションの OnPublish 時に実行することをお勧めします。
    - 任意のタイミングで実行して構いません。

**3. 変更前の Attribute の usage（参照）を確認し、すべての参照を変更後の Attribute に変更**
- Service Studio の「Find Usage」機能を使って、変更前の Attribute を参照している画面やロジックを特定し、新しい Attribute に更新 する。 

**4. 変更前の Attribute の usage がないことを確認（すべて移行終了）**
- 再度「Find Usage」機能を使い、変更前の Attribute を参照する箇所がないことを確認する。  

**5. Publish（変更を適用）**
- 開発環境で Publish し、正常に動作することを確認する。  

**6. 変更前の Attribute のコメントに「削除予定」を記載**
- いきなり削除せず、Attribute のコメントに **「削除予定」** などの注意書きを追加する。  
- 一定期間運用し、問題が発生しないことを確認する。  

**7. 開発 → 検証 → 本番まで変更を適用**
- **開発環境 → 検証環境 → 本番環境** と変更を適用していき、データが正しく引き継がれていることを確認する。  

**8. 変更前の Attribute を削除する**
- 問題がないことを確認した後、不要になった Attribute を削除する。  
- 削除する前に、データが新しいカラムに完全に移行されていることを最終確認する。  


# まとめ
- **OutSystems ではカラム名を直接変更できない** ため、新しい Attribute を作成する必要がある。  
- **データ移行用のロジックを作成し、元のデータをコピーすることでデータを保持** する。  
- **すべての参照を新しい Attribute に更新し、不要になった Attribute を削除** する。  
- **いきなり削除せず、一定期間「削除予定」として保持しながら慎重に移行するのが安全**。  

この手順に従えば、OutSystems の仕様に対応しながら安全に Entity Attribute 名を変更できます！

## 参考
- https://www.outsystems.com/ideas/71/rename-entity-attributes-without-loosing-the-database-content/
TailwindCSS v4 で prefix のつく場所が変わった

TailwindCSS の知られざるマイナー機能、prefix option についての小ネタです。

## prefix option is 何

Tailwind class の prefix を設定できる機能です。
https://tailwindcss.com/docs/styling-with-utility-classes#using-the-prefix-option

エントリーポイントで prefix を指定すると `${prefix}:${tailwindClass}` の形で使えるようになります。

```css:style.css
@import "tailwindcss" prefix(tw);
```
```html
<p class="tw:text-sm">
```

これの嬉しいポイントは既存の CSS が存在する場合でも痛みなく TailwindCSS を導入しやすいという点です。
既存 CSS で Tailwind のクラスと被る場合でも important battle に発展せず、穏便に済ませることが可能です。
実際、弊社のプロダクト [LIFULL HOME'S](https://www.homes.co.jp/chintai/) でも prefix option を使って TailwindCSS を導入しています。

ただ最初から TailwindCSS を導入するのであれば付けるメリットはほぼ皆無です🤪

## v3 -> v4 でサイレント破壊的変更された😇

この prefix option は v3 以前にも存在していましたが、v4 とは少し出力が異なります。
設定すると `${prefix}${tailwindClass}` の形で使えます。
divider はありませんが、設定で `-` をつけるのが推奨っぽい。
https://v3.tailwindcss.com/docs/configuration#prefix

```js:tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  prefix: 'tw-',
}
```
```html
<p class="tw-text-sm">
```

一見 prefix と tailwindClass の divider の有無で差分があるだけに見えます。
しかしここに negative value や modifier の概念が入るとややこしくなってきます。

```html
<!-- v4: 全部一律先頭に prefix がつく -->
<p class="tw:-ml-4">
<p class="tw:hover:text-sm">
<p class="tw:hover:-ml-4">

<!-- v3 -->
<p class="-tw-ml-4"> <!-- negative value の時は -${prefix}-${tailwindClass} -->
<p class="hover:tw-text-sm"> <!-- modifier は先頭につく -->
<p class="hover:-tw-ml-4"> <!-- negative value + modifier の時は ${modifier}:-${prefix}-${tailwindClass} -->
```

表にすると以下のようになります。

| パターン     | prefix なし (~v3/v4共通)     | prefixあり (~v3)                   | prefixあり (v4~)                   | 
| ------------ | :-------------------------: | :-------------------------------: | :-------------------------------: | 
| 通常         | `text-sm`                     | `tw-text-sm`                        | `tw:text-sm`                        | 
| negative value | `-ml-4`                        | `-tw-ml-4`                           | `tw:-ml-4`                           | 
| modifier | `hover:text-sm`<br>`hover:-ml-4` | `hover:tw-text-sm`<br>`hover:-tw-ml-4` | `tw:hover:text-sm`<br>`tw:hover:-ml-4` | 

v4 upgrade guide や blog で目玉機能や破壊的変更について触れられていますが、prefix については一切言及がありません😇
アップグレード作業をしているときに diff を見て初めて気づきました🤪
https://tailwindcss.com/docs/upgrade-guide#changes-from-v3
https://tailwindcss.com/blog/tailwindcss-v4

もし現在 Tailwind v3 で prefix を使っている場合は upgrade script を使えばこの辺はよしなに変換されるのでそこは安心です🙏
v3 の prefix ありの記法が体に染み付いているので1日も早く v4 記法に体を慣らしていきたいと思います。
Ubuntuであれっ何かおかしいです。がでる時の対処法

# はじめに

ある日PCを開いたら突然見慣れない画面がでて起動できなくなったので対処法を書きます。

# 問題

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/00878b09-c6ef-49bf-8a70-4b6ee1480db0.png)

PCを起動したところこのような画面がでました。

# 解決方法

前日にライブラリインストールをしたときに途中でインストールをキャンセルしてしまったのが原因なので、そこを正しいものにしていきます。

私のPCはThinkpad P1Gen2なので起動した瞬間ESCを押して、「ubuntu advanced options」→「ubutu recover mode」→「root」でターミナルを起動します。

ここで注意はタイミングにシビアで少し難しいということです。(他の記事ではキーが違うこともありました)

そのあとでライブラリ周りを更新します。

```
$ sudo apt-get update
$ sudo apt-get dist-upgrade
$ sudo apt-get clean
$ sudo apt-get autoremove

$ sudo reboot
```

これで無事起動しました

# おわりに

ターミナル起動が結構難しかったです。
Javaを実行時のJVMプロパティを難読化して送信できるようになった

Java エージェントで、Javaを実行時のJVMプロパティに機密性が高いパラメーターが含まれている場合に難読化して送信できるようになりました。

本記事では今回のアップデートの詳細を紹介します。

## 概要
New RelicのAPMエージェントはアプリケーションの実行環境に関する情報を収集しており、言語のバージョンや実行OSの情報、どの様なパラメーターがアプリケーションに設定されているか確認することができます。

環境の情報はAPMの「Settings > Environment > Environment snapshot」の先で確認することが可能です。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3485441/2cd16e9c-6586-4fb1-80eb-ca6e6b91afbe.png" width="90%">

Java エージェントの場合、Java 起動時に渡しているJVMのパラメーター値もUI上で確認することが出来ますが、[Java エージェント v8.16.0](https://docs.newrelic.com/docs/release-notes/agent-release-notes/java-release-notes/java-agent-8160/)から UI上で確認できるパラメーターを難読化して送信できるようになりました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3485441/2574b00d-1508-4e50-9722-50205798d83e.png" width="90%">

## 設定方法
下記のJavaの起動コマンドを例に設定方法を確認していきます。

```bash
sudo java -XX:+HeapDumpOnOutOfMemoryError \
-XX:ActiveProcessorCount=1 \
-Dprop.A=val1 \
-Dprop.B=val2 \
-DpropC=val3 \
-Dprop.B.other=val4 \
-Dprop.B.extended=val5 \
-javaagent:target/newrelic/newrelic.jar \
-jar target/*.jar
```
デフォルトでは-Dpropで指定したパラメーターは難読化されて送信されるようになっており、UI上では以下のようにJVMのパラメーターが**obfuscated**として表示されるようになっています。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3485441/40d2962d-2270-4f07-9122-768391cebd2f.png" width="60%">

エージェントの設定で難読化するパラメーターと難読化しないパラメーターの指定ができるようになっており、newrelic.ymlで設定する場合は```newrelic.config.obfuscate_jvm_props```の箇所で設定します。

設定方法については以下ドキュメントもご確認ください。

https://docs.newrelic.com/docs/apm/agents/java-agent/configuration/java-agent-configuration-config-file/#jvm-properties-obfuscation

例えば、以下の設定で定義した場合のUI上の見え方について紹介します。
```yaml:newrelic.yml
  obfuscate_jvm_props:
    enabled: true
    # 難読化しないパラメーターを定義する
    allow: -Dprop.A, -Dprop.B*
    # 難読化するパラメーターをblockで定義する
    block: -Dprop.B.extended
```
上記の設定で定義した場合、難読化する・しないの対象の判別は以下のようになります。
| 難読化対象のパラメーター | 難読化される/難読化されない理由 |
| ---- | ---- |
| -Dprop.A | 対象: allowで定義している-Dprop.Aにマッチするため |
| -Dprop.B  | 対象: allowで定義している-Dprop.B*にマッチするため |
| -Dprop.C | 対象外: allowとblockのどちらの定義にもマッチしないためデフォルト設定で難読化される |
| -Dprop.B.other | 対象: allowで定義している-Dprop.B*にマッチするため |
| -Dprop.B.extended | 対象外: blockで定義している-Dprop.B.extendedにマッチするため |

UI上では下記の様にパラメーターが入ってくることになります。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3485441/d7e11cbc-35a4-4ccf-bf55-5f9901312a5e.png" width="60%">

デフォルトでは全てのパラメーターは難読化されて送信されますが、エージェントの設定で難読化するパラメーター、難読化しないパラメーターを定義できることを紹介させていただきました。

:::note info
最新のアップデートの詳細はこちら
- [New Relic アップデート(2024年11月)
](https://newrelic.com/jp/blog/nerdlog/new-relic-update-202411 "New Relic アップデート(2024年11月)")
- [New Relic アップデート一覧
](https://newrelic.com/jp/blog/nerdlog/new-relic-update  "New Relic アップデート一覧")
:::

# その他
New Relicでは、**新しい機能やその活用方法**について、QiitaやXで発信しています！
**無料でアカウント作成**も可能なのでぜひお試しください！

:::note info
New Relic株式会社の[X(旧Twitter)](https://x.com/NewRelicJapan) や [Qiita OrganizationOrganization](https://qiita.com/organizations/newrelic)では、
新機能を含む活用方法を公開していますので、ぜひフォローをお願いします。
:::

:::note info
無料のアカウントで試してみよう！
[New Relic フリープランで始めるオブザーバビリティ！](https://qiita.com/ktst79/items/5ee74b34e6adb8a7a6fa)
:::
.NETでSwitchBotプラグミニと温湿度計の値をSDK無しのBLE Advertisementパケットから取得する

# :star: はじめに

こちらの記事では、RS-WFWATTCH2を使って消費電力を測定する方法を記述しています。

https://qiita.com/yamaokunousausa/items/2faedd6481093e73e2ca

![room.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9645/023442a1-6702-4f08-876f-f3fde8fc2651.png)

上記記事中にもあるように、RS-WFWATTCH2は高いので、SwitchBotプラグミニも併用しており、こちらではSwitchBotから消費電力を取得する方法について記述します。

取得方法はSwitchBot APIを使用した方法ではなく、BLE Advertisementパケットを拾って値を取得する方法です。
BLE Advertisementパケットを使用する方法は測定タイミングの制御できませんが、APIのリクエスト上限・デバイス数を気にせず測定できるメリットがあります。

https://www.switchbot.jp/products/switchbot-plug-mini

また、サンプルコードにはSwitchBotプラグミニから消費電力を取得する処理だけでなく、温湿度計の値(CO2センサーの値含む)を取得する方法も含めます。

https://www.switchbot.jp/products/switchbot-co2-meter

# :hammer_pick: コード

```xml
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0-windows10.0.22000.0</TargetFramework>
  </PropertyGroup>

</Project>
```

```csharp
using System.Runtime.InteropServices.WindowsRuntime;

using Windows.Devices.Bluetooth.Advertisement;

var watcher = new BluetoothLEAdvertisementWatcher();
watcher.Received += OnWatcherReceived;
watcher.Start();

Console.ReadLine();

void OnWatcherReceived(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
{
    foreach (var md in args.Advertisement.ManufacturerData.Where(static x => x.CompanyId == 0x0969))
    {
        var buffer = md.Data.ToArray();
        Console.WriteLine($"address={args.BluetoothAddress:X12}, rssi={args.RawSignalStrengthInDBm}");

        // 温度計
        if (IsMeter(args.BluetoothAddress))
        {
            var temperature = (((double)(buffer[8] & 0x0f) / 10) + (buffer[9] & 0x7f)) * ((buffer[9] & 0x80) > 0 ? 1 : -1);
            var humidity = buffer[10] & 0x7f;
            // CO2センサーに対応している場合
            var co2 = buffer.Length >= 16 ? (buffer[13] << 8) + buffer[14] : 0;
            Console.WriteLine($"  temperature={temperature}C, humidity={humidity}%, co2={co2}ppm");
        }
        // プラグミニ
        if (IsPlugMini(args.BluetoothAddress))
        {
            var power = (double)(((buffer[10] & 0b00111111) << 8) + (buffer[11] & 0b01111111)) / 10;
            Console.WriteLine($"  power={power}W");
        }
    }
}
```

# :pencil: 解説

WindowsランタイムのAPIを使用するため、TargetFrameworkをnet8.0-windows10.0.22000.0のような形に変更します。
詳細は以下を参照してください。

https://learn.microsoft.com/ja-jp/windows/apps/desktop/modernize/desktop-to-uwp-enhance

BLE Advertisementパケットの取得はBluetoothLEAdvertisementWatcher.Receivedイベントで行ないます。

CompanyIdが0x0969のものがSwitchBotの機器で、値はManufacturerDataから取得します。

各機器の測定値の取得方法は以下を参照してください。

https://github.com/OpenWonderLabs/SwitchBotAPI-BLE

ManufacturerData.Dataの値には先頭にアドレスが入っているので、その分はオフセットをずらして読むようにしてください。

送られてきたデータがプラグミニのものか温湿度計のものかはこの処理だけでは判断できないので、その部分の判定についてはアドレスに対する種別が別途登録済みの想定として省略しています。

# :rabbit: うさコメ

SDKのリクエスト上限を気にせず、プラグや温湿度計の台数を好きなだけ増やして、測定値Prometheus+Grafana表示に使えるので満足( ˙ω˙)

https://github.com/usausa/prometheus-exporter-alternative
annotateでFixnumエラーが出た際に対処法

## はじめに
Rials8を使って開発を進めている中で、annotateに起因していそうなエラーが出たので調査しました。

## やりたいこと

- annotateを使ってモデルにスキーマ情報を記述したい

### やったこと

- annotateをGemfileに追加し、`bundle install`
- `bundle exec annotate`を実行

## エラー内容

```bash
Unable to annotate profile.rb: uninitialized constant #<Class:AnnotateModels>::Fixnum
Unable to annotate user.rb: uninitialized constant #<Class:AnnotateModels>::Fixnum
```

## 原因

### 直接原因

- annotateのバージョンが古いものがインストールされてしまっている
- そのためIntegerに統合され、使用しなくなったFixnumを使用しており、上記のエラーが出ている

### 根本原因

- gem annotateがRails8に対応していない

## 解決策

- gem annotate ではなく、gem annotaterbを使用する
- 参考
    - [Rails 8でもannotate_modelsのコメントを入れたい](https://qiita.com/tatematsu-k/items/44c68dc20692cb68cb30)
RSpecのallow(...).to receive(...)

# 概要
特定のメソッドが呼ばれたときに、その戻り値を自由に制御できるようにするためのモック（stub）の機能。
```ruby:sample1
# User.current メソッドが呼ばれたとき、常に current_user を返すようにする。
# allow(オブジェクト).to receive(:メソッド名).and_return(返り値)
allow(User).to receive(:current).and_return(current_user)
```

:::note info
### expect(...).to receive(...) との違い
* allow(...).to receive(...) 
「こう動作してほしい」 という指定。
* expect(...).to receive(...) 
「このメソッドが呼ばれることを期待する」 というもの。
:::

## 使用例
```ruby:sample2
class User
  def name
    "Alice"
  end
end

user = User.new
# name メソッドの戻り値を "Bob" に変更できる。
allow(user).to receive(:name).and_return("Bob")
# 通常、user.name を呼ぶと "Alice" を返す。
puts user.name # => "Bob"
```
## 戻り値を返さない
```ruby:sample3
# .and_return()を記述しない
allow(obj).to receive(:method_name)
```
## 戻り値を複数指定する
```ruby:sample4
# 1回目の呼び出し　　 : 1 
# 2回目の呼び出し　　 : 2 
# 3回目以降の呼び出し : 3 
allow(obj).to receive(:method_name).and_return(1, 2, 3)
```
## 戻り値を動的にする
```ruby:sample5
# { Time.now.year - 1990 } で計算し、呼び出し時の Time.now に応じた値を返す。
allow(user).to receive(:age) { Time.now.year - 1990 }
puts user.age # => 34（2024年の場合）
```
## 例外を発生させる
```ruby:sample6
# fetch_data を呼ぶと、強制的にエラーを発生。
allow(service).to receive(:fetch_data).and_raise("Test Error")
service.fetch_data # => RuntimeError: Test Error
```
esp-idf: mqtt/wss の使い方

こちらと同様のことを、
[esp-idf: mqtt/ws の使い方](https://qiita.com/ekzemplaro/items/980faf115f0426107bf3)
esp-idf/examples/protocols/mqtt/wss　で行いました。
MQTT over WebSocket Secure になります。

> ブローカー　wss://mqtt.eclipseprojects.io:443/mqtt
> トピック　/topic/qos0

## プログラム

プロジェクトのコピー

```bash
cp -r ~/esp/esp-idf/examples/protocols/mqtt/wss .
```

wss/sdkconfig を作成

```bash
idf.py build
```

Wifi の接続情報を変更

```text:wss/sdkconfig
（省略）
CONFIG_EXAMPLE_WIFI_SSID="****"
CONFIG_EXAMPLE_WIFI_PASSWORD="****"
（省略）
```

main/mqtt_eclipseprojects_io.pem の作成
    
ダウンロード (README.md に記述されています)

```bash
echo "" | openssl s_client -showcerts -connect mqtt.eclipseprojects.io:443 | sed -n "1,/Root/d; /BEGIN/,/END/p" | openssl x509 -outform PEM >mqtt_eclipse_org.pem
```

mqtt_eclipse_org.pem を wss/main/mqtt_eclipseprojects_io.pem
にコピーする

コンパイル、実行

```bash
idf.py -p /dev/ttyUSB0 flash
```

## テスト
[esp-idf: mqtt/ws の使い方](https://qiita.com/ekzemplaro/items/980faf115f0426107bf3)
の　publish.js subscribe.js の const brokerUrl を次に変更

```js
const brokerUrl = 'wss://mqtt.eclipseprojects.io:443/mqtt'
```

実行結果

```text
$ cu -l /dev/ttyUSB0 -s 115200
Connected.
（省略）
I (768825) mqttwss_example: MQTT_EVENT_DATA
TOPIC=/topic/qos0
DATA=Good Afternoon Feb/13/2025 PM 15:31
I (772715) mqttwss_example: MQTT_EVENT_DATA
TOPIC=/topic/qos0
DATA=Good Afternoon Feb/13/2025 PM 15:31
I (775485) mqttwss_example: MQTT_EVENT_DATA
TOPIC=/topic/qos0
DATA=Good Afternoon Feb/13/2025 PM 15:31
```
円周率を「米粒を敷き詰めて計算」する方法【C言語】

```kome_pi.c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>


void now(void)
{
	time_t t = time(NULL);
	struct tm *local = localtime(&t);

	printf("%04d/", local->tm_year + 1900);
	printf("%02d/", local->tm_mon + 1);
	printf("%02d", local->tm_mday);

	printf(" ");

	printf("%02d:", local->tm_hour);
	printf("%02d:", local->tm_min);
	printf("%02d\n", local->tm_sec);
	fflush(stdout);
}

int main(int argc,char *argv[])
{
	unsigned long long  n=8 ;

	if(argc > 1 )
	{
		n=atol( argv[1] ) ;		
	}
    double i,k ,all,en;

	long len = n ;
		for(i=0.0f;i<len;i+=1.0f) n *= 2.0f;

	printf("2^%dまで計算可能,start=%ld \n",sizeof(n)*8,n );
		now();
	while(1)
	{
		double ai = 2.0f / (float)n;
		en = 0.0f ;
		for(i=-1.0f ; i<=1.0f ; i+=ai)
		{
				for(k=-1.0f ; k<=1.0f ; k += ai)
				{

						if( i*i + k*k <= 1.0f) en+=1.0f ;
				}
     	}
		all = (float)n ;
		all *= all ;
		printf("π=%.20f ,{%d},",4.0f* en / all ,len);

		now();
		n *= 2 ;
		len++ ;
	}
	return 0;
}

 ```
Git のブランチを削除したいが、後で復元できるようにしたい

## やりたいこと

Git のブランチを削除したいが、後で復元できるようにしたい。つまりブランチをアーカイブするようなことがしたいです。

「main ブランチにマージできないブランチがたくさん溜まっているので、それらを整理するために削除したい。でもこれまでの変更はなかったことにしたくない」と思うことがあり、何か方法がないか調べてみました。

## 方法

[git update-ref](https://git-scm.com/docs/git-update-ref) コマンドでアーカイブ専用の参照 (refs)[^1] を作成します。以下のコマンドで `feature/cat` ブランチの最新のコミットを指す `refs/archive/feature/cat` という新しい参照を作成します。

```shell-session
$ git update-ref refs/archive/feature/cat feature/cat
```

作成した参照の一覧は [git for-each-ref](https://git-scm.com/docs/git-for-each-ref) コマンドで取得できます。

```shell-session
$ git for-each-ref refs/archive # refs/archive/* の一覧を取得する
9650b007b7b5a1d3302556b9ecbb80a96c777218 commit	refs/archive/feature/cat
9650b007b7b5a1d3302556b9ecbb80a96c777218 commit	refs/archive/feature/dog
```

このように参照を作成しておくと、`feature/cat` ブランチを削除した後でも `refs/archive/feature/cat` という参照からブランチを復元することができます。

```shell-session
$ git -d feature/cat # feature/cat ブランチを削除する
$ git branch feature/cat refs/archive/feature/cat # 参照からブランチを復元する
$ git update-ref -d refs/archive/feature/cat # 参照を削除する
```

## おまけ

Git の設定ファイル (`~/.gitconfig`) に以下の alias を定義すると便利です。

```
[alias]
  archive-branch = "!sh -c 'git update-ref refs/archive/$1 $1 && git branch -D $1' -"
  show-archive = "!sh -c 'git for-each-ref refs/archive' -"
  restore-branch-from-archive = "!sh -c 'git branch $1 refs/archive/$1 && git update-ref -d refs/archive/$1' -"
```

## バージョン情報

```shell-session
$ git --version
git version 2.39.5 (Apple Git-154)
```

## 参考

- [How can I archive git branches? - Stack Overflow](https://stackoverflow.com/questions/1307114/how-can-i-archive-git-branches)

[^1]:「そもそも参照 (refs) って何？」という方は [Git - Git の参照](https://git-scm.com/book/ja/v2/Git%E3%81%AE%E5%86%85%E5%81%B4-Git%E3%81%AE%E5%8F%82%E7%85%A7) を読んでみてください。
WinやMacでの文字化けでもう悩まない(Macの人向け)

# 経緯

データ分析をMacでやると、データを保存する際のcsvの文字コードが基本的にUTF-8になる。
PandasでもよくUTF-8で保存していたがデータを渡す相手がWindows(SJIS)のことが多々あり、UTF-8なのにSJISで開こうとして文字化けが起こり、変換の手間がかかっていたので根本解決できないものか調べた。

# 結論

UTF-8-SIG（BOM付きUTF-8）を使ってください。
BOMがあるのでWindowsでも文字化けせずに開けます。

# 簡単な変換方法

## VSCode使う

ファイルを開くと右下に文字コードが表示される（添付画像赤枠）

![スクリーンショット 2025-02-13 10.26.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210189/f5541bfb-3c84-4c12-88b2-cb8702e055f8.png)

そこをクリックすると「エンコード付きで再度開く」と「エンコード付きで保存」が選べるので、そこで「エンコード付きで保存」を選択

保存形式は「UTF-8 with BOM」を選択

## PythonのPandasで変換（csv）

UTF-8 -> UTF-8-SIG

```
import pandas as pd
file = '/hogehoge/fugafuga.csv' # ここ書き換えて
csv_df = pd.read_csv(file)
csv_df.to_csv('./utf-8-sig_file.csv', encoding='utf-8-sig', index=False)
```

# UTF-8のBOM付きかどうか確認する方法

ファイルの先頭にUTF-8のBOM（EF BB BF）があればOK

他の文字コードだとEF BB BFじゃないらしい

```
 $head -c 3 file.csv | xxd -p
 efbbbf
```
Pull Request 概要欄に記載する内容に意識して欲しいこと・守ってほしいこと

## はじめに

Pull Request（以下※PR）のルールではなく「PRの概要欄」の話。現状以下だけでレビュアーとして助かっている。

### 守ってほしいこと

1. ソースコードのスクリーンショットは利用しないでください。
    1. ソースコードを共有したい場合は、GitHub 上のソースコードの行リンクを選択し右クリックしURLを貼り付けてください。
2. PR概要欄にファイル名や関連ファイルのフルパスを記載する場合は、ファイル名の文字を記載するのではなくGitHub上の該当のファイルのリンクURLを貼り付けてください。
3. PR概要欄の上部には課題（タスク）のURLを貼り付けてください。
4. 課題（タスク）に関係がない修正をPRに含めないでください。
    1. もし含める場合は、必ずPR概要欄に説明を入れてください。

### 意識してほしいこと

1. 課題URLだけではなく、可能であれば「どのような対応をしたのか」を簡単に記載してください。
    1. 影響範囲が大きいPRであれば、影響範囲についても言及してください。
    2. ※「誤字を訂正するのみ」等の明らかにすぐ理解できる内容であれば不要です。
2. PR内容にはできる限り修正箇所がわかるようなスクリーンショット（キャプチャ）を貼り付けてください。
    1. 貼り付けた写真が「修正前」なのか「修正後」なのか分かるようにしてください。
    2. ※「誤字を訂正するのみ」等の明らかにすぐ理解できる内容であれば不要です。

特に後者は、サービスリリース後（運用中）の場合、特に意識してほしい内容です。
初心者によるメモ（アルゴ式「文字種判定」）

## 問題
アルゴ式「ロジック実装中級」

https://algo-method.com/tasks/779da60242e73cb0

## 考察
unicodeに変換して考える。このとき、unicodeで比較するのではなく`ord("0")`のような形にして比較するとよい。直接比較しようとして自分は間違えた。

## ACコード
```py
def code(s):
    n = ord(s)
    if  ord("A")<= n <= ord("Z"):
        return "uppercase"
    elif ord("a") <= n<=ord("z"):
        return"lowercase"
    elif ord("0")<= n<=ord("9"):
        return"digit"
    else:
        return chr(n)
n=int(input())

s=input()

for i in range(n):
    print(code(s[i]))
```
Access Database engine インストール

方法1.32bit 64bitの確認をすっとばしてインストールする方法

コマンドプロンプトにて、アクセスのインストールファイルのあるフォルダに移動してから
下記の通りコマンドを実行することでインストール可能です。
```
AccessDatabaseEngine.exe /quiet
AccessDatabaseEngine_x64.exe /quiet
```

方法2.レジストリエディタを使用してごみを消してからインストール

HKEY_CLASSES_ROOT / Installer / Products　の中にある
　「0000..... （ゼロ４桁）」から始まるディレクトリの中で、
　ウィンドウ右側に下記の情報が記載されているディレクトリを探して、削除

　名前：　ProductName
　Microsoft Access database engine 2016 (English)
  または Office 16 Click-to-Run Extensibility Component 64-bit Registration
　または Office 16 Click-to-Run Extensibility Component Registration
  
この後、Access Runtimeを実行すればインストール可能
エックスサーバーのサブドメインでLaravelプロジェクトを表示するための .htaccess 設定手順

# 前提
エックスサーバーでは、サブドメインのドキュメントルートが固定 されています。
```bash
/home/akkun1114/メインドメイン/public_html/サブドメイン
c
→ このフォルダを Laravel の public/ にリダイレクトする必要がある。

## 現在のディレクトリ構成（エックスサーバー上）
```bash
/home/akkun1114/akkun1114.com/public_html/exp-alert.akkun1114.com/    ← エックスサーバーの固定ドキュメントルート
/home/akkun1114/akkun1114.com/public_html/exp-alert.akkun1114.com/EXP-alert/  ← Laravel プロジェクト本体
/home/akkun1114/akkun1114.com/public_html/exp-alert.akkun1114.com/EXP-alert/public/  ← Laravel の公開フォルダ
```
→ exp-alert.akkun1114.com にアクセスすると EXP-alert/public/ を読み込むように設定する。



# 手順
## １：.htaccess を新規作成
エックスサーバーの exp-alert.akkun1114.com のルートに .htaccess を作成する。

### まず、カレントディレクトリを確認
```sh
pwd
```
↓ 期待する出力:
```
/home/akkun1114/akkun1114.com/public_html/exp-alert.akkun1114.com
```
これがエックスサーバーで固定されている exp-alert.akkun1114.com のルート


## ２：.htaccess を新規作成（なかったら）
```s
## /home/akkun1114/akkun1114.com/public_html/exp-alert.akkun1114.com
ls -a
```
.htaccessが、存在しなかった場合新規作成
どちらでも良い
```
vi /home/akkun1114/akkun1114.com/public_html/exp-alert.akkun1114.com/.htaccess

echo "" > /home/akkun1114/akkun1114.com/public_html/exp-alert.akkun1114.com/.htaccess
```


## ３：.htaccess の内容を記述
```
vi .htaccess
```
作成した .htaccess を開き、以下のコードを記述。
```
<IfModule mod_rewrite.c>
    RewriteEngine On

    # HTTPSリダイレクト（必要に応じて）
    RewriteCond %{HTTPS} !=on
    RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

    # Laravel の public ディレクトリをルートとして設定
    RewriteCond %{REQUEST_URI} !^/EXP-alert/public/
    RewriteRule ^(.*)$ EXP-alert/public/$1 [L]
</IfModule>
```
これにより、
・HTTPS が適用されていない場合、自動的に https:// にリダイレクトする
・アクセスが exp-alert.akkun1114.com に来た場合、自動的に EXP-alert/public/ をルートとして参照する

### 上記コードの丁寧な解説
https://qiita.com/honaki/private/8d007563846a7214cd7a


## ４：.htaccess のパーミッションを設定
作成した .htaccess のパーミッションを適切に変更。
```
chmod 644 /home/akkun1114/akkun1114.com/public_html/exp-alert.akkun1114.com/.htaccess
```
644 にする理由は、
・rw-r--r-- (644) → オーナー（自分）は読み書き可、他のユーザーは読み取りのみ可
・セキュリティ的に 777 は危険なので使わない！


## ５：.htaccess が正しく作成されたか確認
```
ls -la /home/akkun1114/akkun1114.com/public_html/exp-alert.akkun1114.com/
```
↓ 期待される出力
```
-rw-r--r--  1 akkun1114 members xxxx .htaccess
```
.htaccess がリストに表示されていればOK！


## .htaccess のキャッシュをクリア
エックスサーバーでは .htaccess の変更がすぐに反映されないことがあるため、以下のコマンドでキャッシュをクリア。
```
touch /home/akkun1114/akkun1114.com/public_html/exp-alert.akkun1114.com/.htaccess
```
これにより .htaccess のタイムスタンプを更新し、サーバーが最新の設定を読み込む


## ７：.htaccess 設定後の動作
サブドメインにアクセスすると、サブドメイン/Laravelプロジェクト/public/ がルートとして動作する！
Raspberry Pi picoをmicro-BからtypeCに改造する

最近Raspberry Pi picoを買っていろいろいじっていたのですが、USB micro-Bが嫌いなのでどうにかしたい。
それが今回の動機です。
### 方法
ざっくり今回やることを説明すると、

1.typeC端子を作る。
2.micro-B端子から線を伸ばす。
3.接続する

これだけです。

### TypeC端子を作る。
今回使う端子は秋月電子通商で買った、TypeC DIP化キットです

https://akizukidenshi.com/catalog/g/g115426/

A to Cケーブルを使うならばそのまま配線してもいいのですがC to Cケーブルを使う場合は少しテコ入れが必要です。
詳細は省くのですが、CC1,CC2端子を5.1kΩでGNDに接続(プルダウン)する必要があります。
私は5.1kΩ抵抗を持っていなかったので、10kΩ並列＋1kΩで、5.1kΩを実装しました。



### micro-Bから線を伸ばす
秋月で売っているmicro-Bオス端子を使って線を引き出します。

https://akizukidenshi.com/catalog/g/g107666/

寸法図より1～5pinの場所を読み取り、線を引き出します。
![スクリーンショット 2025-01-12 111405.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3922522/82947f90-c090-3e15-701c-ca006739c2f1.png)
|pin|機能|
|--|--|
|1|Vcc|
|2|D-|
|3|D+|
|4|ID|
|5|GND|

本来はUSB規格で線の色も決まっているのですが、フル無視で黒を使います。

IDピンとは一体何でしょうか？
軽く調べたところ、ホストとして使う際に必要だそうですが、今回はどこにも接続せずにほっておいてよさそうです。

#### 余談
実はRaspberry Pi picoシリーズは裏側のTP(テストポイント)に+D,-D,GNDを接続できるパッドがあります。
TP1がGND、TP2が-D、TP3が+Dです
そして電源供給用のVSYSは39番にあります。
ここから線を伸ばすことも可能です。

### 接続する
今まで作ったものを繋げます。
|type-C|処理|pin|micro-B|
|:-:|:-:|:-:|:-:|
|Vcc|接続|1pin|Vcc|
|D-|接続|2pin|D-|
|D+|接続|3pin|D+|
|ー|ー|4pin|ID|
|GND|接続|5pin|GND|
|CC1|5.1kΩプルダウン|ー|ー|
|CC2|5.1kΩプルダウン|ー|ー|

取り外すことができるように細ピンヘッダを使って接続します。

### 検証

ファイル/スケッチ例/シリアル通信
等よりシリアル通信ができるスケッチを適当に選んで検証します。

### 終わりに
まあtypeCの互換機買えばいいんだけどね。
初心者によるメモ（アルゴ式「次のアルファベット」）

## 問題
アルゴ式　ロジック実装中級

https://algo-method.com/tasks/1aa76adbb506dd71


## 考察
与えられた文字をunicodeに変換して、それに+1した数をchr関数でもとに戻せばいい。

## ACコード
```py
s=input()
n=ord(s)

print(chr(n+1))
```
esp-idf: mqtt/ws の使い方

こちらと同様のことを、
[esp-idf: mqtt の使い方](https://qiita.com/ekzemplaro/items/f8c79bfd9f4048453825)
esp-idf/examples/protocols/mqtt/ws　で行いました。
MQTT over WebSocket になります。

>ブローカー　ws://mqtt.eclipseprojects.io:80/mqtt
>トピック　/topic/qos0

## プログラム

プロジェクトのコピー

```bash
cp -r ~/esp/esp-idf/examples/protocols/mqtt/ws .
```

ws/sdkconfig を作成

```bash
idf.py build
```

Wifi の接続情報を変更

```text:ws/sdkconfig
（省略）
CONFIG_EXAMPLE_WIFI_SSID="****"
CONFIG_EXAMPLE_WIFI_PASSWORD="****"
（省略）
```

コンパイル、実行

```bash
idf.py -p /dev/ttyUSB0 flash
```

## テスト

Publish

```js:publish
const mqtt = require('mqtt')

// WebSocket エンドポイント
const brokerUrl = 'ws://mqtt.eclipseprojects.io:80/mqtt'

// MQTT クライアントの作成
const client = mqtt.connect(brokerUrl)

client.on('connect', () => {
console.log('Connected to MQTT broker')

  // メッセージをパブリッシュ
const message = 'Good Afternoon Feb/13/2025 PM 14:43'
const topic = '/topic/qos0'
client.publish(topic, message, (err) => {
    if (!err) {
      console.log('Message published')
    } else {
      console.error('Publish error:', err)
    }
    client.end() // 接続を閉じる
  })
})

client.on('error', (err) => {
  console.error('Connection error:', err)
})
```

実行コマンド

```bash
export NODE_PATH=/usr/local/lib/node_modules
#
node publish.js
```

Subscribe

```js:subscribe.js
const mqtt = require('mqtt');

// WebSocket エンドポイント
const brokerUrl = 'ws://mqtt.eclipseprojects.io:80/mqtt'

// MQTT クライアントの作成
const client = mqtt.connect(brokerUrl)

client.on('connect', () => {
console.log('Connected to MQTT broker')

const topic = '/topic/qos0'
client.subscribe(topic, (err) => {
    if (!err) {
      console.log('Subscribed to ' + topic)
    }
  })
})

client.on('message', (topic, message) => {
  console.log(`Received message on ${topic}: ${message.toString()}`)
})

client.on('error', (err) => {
  console.error('Connection error:', err)
})
```

実行コマンド

```bash
export NODE_PATH=/usr/local/lib/node_modules
#
node subscribe.js
```

実行結果

```text
$ cu -l /dev/ttyUSB0 -s 115200
Connected.
（省略）
I (1302844) mqttws_example: MQTT_EVENT_DATA
TOPIC=/topic/qos0
DATA=Good Afternoon Feb/13/2025 PM 14:43
I (1312874) mqttws_example: MQTT_EVENT_DATA
TOPIC=/topic/qos0
DATA=Good Afternoon Feb/13/2025 PM 14:43
I (1322704) mqttws_example: MQTT_EVENT_DATA
TOPIC=/topic/qos0
DATA=Good Afternoon Feb/13/2025 PM 14:43
```
初心者によるメモ（アルゴ式「文字コード」）

## 問題
ロジック実装中級

https://algo-method.com/tasks/14c32f697c2ffd9b
## 考察
Unicode の番号から対応する文字に変換は、chr関数を用いればできる。
## ACコード
```py
for i in range(97,123):
    print(i,chr(i))
```
electron-viteで複数ページ（マルチウィンドウ）の設定

# 概要
electron-viteで複数ページ（マルチウィンドウ）を設定する際に参考にしたサイトでは開発環境（Dev Server）では問題なく動いていたが、ビルドして動かすとブランクページになってしまう不具合が出ました。それを解決した備忘録です。

# 環境
- Node v22.13.0
- npm 11.0.0
- electron 31.0.2
- electron-vite 2.3.0
- vite 5.3.1
- electron-builder 24.13.3


# 起きていたこと
- Dev Serverでは問題なし
- mainプロセス内の下記の記述に問題なし
```typescript
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
    subWindow.loadURL(`${process.env['ELECTRON_RENDERER_URL']}/another.html`)
} else {
    subWindow.loadFile(join(__dirname, '../renderer/another.html'))
}
```

# 解決方法
`electron.vite.config.ts` にビルド設定を追加する必要がありました

```typescript
 export default defineConfig({
   main: {
     plugins: [externalizeDepsPlugin()]
   },
   preload: {
     plugins: [externalizeDepsPlugin()]
   },
   renderer: {
     resolve: {
       alias: {
         '@renderer': resolve('src/renderer/src')
       }
     },
+    build: {
+       rollupOptions: {
+         input: {
+           index: join(__dirname, 'src/renderer/index.html'),
+           another: join(__dirname, 'src/renderer/another.html')
+         }
+       }
+    },
     plugins: [react()]
   }
 })
```

# 参考

こちらのコメントに助けられました。ありがとうございました。
https://github.com/electron-vite/electron-vite-react/issues/37#issuecomment-1157820888
簡単メモ：$.each()で、break;continue;の覚え方

# はじめに
こればっかりは、いつまで経っても覚えられません。
break;はどちらだったかと。return false;なのですが、この関連付けができません。
毎回、ネットで調べながらです。効率が悪いです。


# 覚え方は 
「break;」は強制終了なので、return false;なんだと、そこを何とか記憶する方法はないものかと。
break(強制終了)とfalse(失敗)を関係づけることは??
continue(続行);とtrue(成功)を関係づけることは??

こう見てみると、何かわかりそうな気もしますが。
強制終了は、何か失敗(false)したので、強制的に終了させていると考え、
続行は、まだうまく続いているので、成功(true)していると考えるのがいいのか。

難しいところです。日本人的には、このように考えることがすぐには出てきません。
$.each()でループ処理をしていて、ここで、break;したいので、さて、return false;かreturn true;かと迷うところです。

そこで、先ほどの考えを思い出して、break -> 強制終了-> 失敗　-> return false;
continue -> 続行 -> 成功している -> return true;と、判断すればいいと思います。
これが速やかに連想できればしめたものですが、時間が経つと、すぐには、この紐づけができないことがあります。

# 結論としては
break -> 強制終了-> 失敗　-> return false;
この関係づけを思い出すしかありません。
beakは強制終了なので、処理としては、どちらかというと失敗になるので、return false;かと判断します。continueも同様に考えます。



# あとがき
日本人的には、このような紐づけが苦手です。
breakとfalse、continueとtrueを関係づけるという言語関係が得意ではありません。
やはり、programmingは、圧倒的に英語圏の人が有利です。コードが英語で書かれているためです。プログラムコードは漢字圏の人には不利です。
もう、これは仕方がないことです。何とか、こじつけで上記のような記憶の仕方を考えてみました。
LLMのファインチューニングが流行らなかった理由

# ファインチューニング、流行っていない
大手クラウドサービスでは、基盤モデルのファインチューニングが可能です。
これによりカスタムモデルを作成できるはずですが、多くの場合、採用が見送られるか、失敗しています。
その理由について、機械学習をかじった程度の無職が考察しました。

## 1. OpenAI公式が第一の手段として推奨していない
モデルの出力をカスタマイズする最も簡単な方法はプロンプトの調整です。
OpenAIも、ファインチューニングを第一選択肢としては推奨していません。


https://platform.openai.com/docs/guides/fine-tuning/common-use-cases#when-to-use-fine-tuning

## 2. ユーザーの求めるものは「対応力の広さ」
ファインチューニングを行うと、特定のタスクに特化します。
例えば、Vercelのv0は特定のコンポーネント形式とライブラリのセットでJavaScriptを出力するようにトレーニングされているようです。
これは、システムとの連携を強化する上では理にかなっていますが、一般的なチャットボットでは幅広い質問に対応できる方が求められます。

## 3. 知識の蓄積には向かない
「学習」という言葉に引っ張られがちですが、ファインチューニングの本質は入力と出力の対応パターンを学ぶことです。
例えば、v0の場合、「ReactのJSXで書く」「shadcn/ui + Tailwindでスタイリングする」といったルールを高精度で遂行するようになります。

しかし、知識を増やす目的で使うと、うまくいかないことが多いです。
失敗の例を挙げると、以下のようなデータセットで学習した場合です。

```
User「太郎君はどんな人ですか？」  
Assistant「太郎君は人ではなく犬です」  
```
```
User「太郎君の年齢は？」  
Assistant「太郎君は6ヶ月です」  
```
こうしたデータで学習すると、以下のような問題が発生します。

- 太郎君の情報を正しく返せない
- 他の質問にも太郎君のプロフィール（または似た形式）で回答する
- 学習データにない質問に対し、架空の情報をでっち上げる

結果として、検索ツールを活用する方が適切な場合が多いのです。

https://note.com/npaka/n/nec63c01f7ee8#5e4c6625-1a18-470e-8438-d37219ffce1f

## 4. コストとスケーリングの問題
ファインチューニングにはコストがかかるという単純な問題もあります。
学習時も推論時も料金が高く、時間も必要です。
そのため、リアルタイムに変化する情報を扱う用途には向いていません。

OpenAI Deep Researchのように、素直に検索エンジンを組み込む方が賢明でしょう。

## 5. そもそもデータがない
LLMのためにデータを集めている組織はほぼ存在せず、データエンジニアの介在をもってしてもファインチューニングタスクでよい結果を得られるとは限りません。
M1 MacでのC++環境構築

# はじめに
M1で環境構築するにあたって、以下の既存のページを見ると、M1 Macでは少し違う部分があったので、備忘録用にまとめてみます。

参考ページ：
https://qiita.com/pinoccio11/items/767a8738d8fa8bba2f81

参考ページについて、M1 Macで設定・記述が異なる部分についてだけ記述しています。
基本的には参考ページの流れ通りでOKかと思います。

# シンボリックリンク配置先
M1 Macなので、シンボリックリンクの配置先は/opt/homebrew/binになっている。

# 実行時のコマンド
実行時には、以下のようなコマンドを打つ必要あり。
（以下のようにしないと、g++-14コンパイラがassert.hヘッダーファイルを見つけられない。この問題は、MacOSのアップデート後によく発生し、システムヘッダーファイルの場所が変更されたことが原因）
```
g++-14 -isysroot $(xcrun --show-sdk-path) test.cpp && ./a.out
```


# Code runner Json設定
Code runnerのJson設定において
```
"cpp": "cd $dir && g++ $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt",
```
となっていたものを
```
"cpp": "cd $dir && g++-14 -isysroot $(xcrun --show-sdk-path) $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt",
```
に変更


これでできるはず...！
マイク使用

Google Colab では、標準機能としてマイク入力を直接扱うことはできません。しかし、以下の方法でマイクを利用することが可能です。

方法 1: pydub と IPython.display を使う

Google Colab では IPython.display.Audio を使って音声を録音できます。ただし、ローカルPCで録音し、アップロードする必要があります。

from google.colab import output
from IPython.display import Javascript
import numpy as np
import io
import base64
import scipy.io.wavfile

# 録音用のJavaScriptコード
def record(sec=5):
    display(Javascript(f"""
        async function record(sec) {{
            const stream = await navigator.mediaDevices.getUserMedia({{ audio: true }});
            const mediaRecorder = new MediaRecorder(stream);
            const audioChunks = [];
            mediaRecorder.ondataavailable = event => {{
                audioChunks.push(event.data);
            }};
            mediaRecorder.onstop = async () => {{
                const audioBlob = new Blob(audioChunks);
                const arrayBuffer = await audioBlob.arrayBuffer();
                const base64String = btoa(String.fromCharCode(...new Uint8Array(arrayBuffer)));
                google.colab.kernel.invokeFunction('notebook.record_callback', [base64String], {{}})
            }};
            mediaRecorder.start();
            await new Promise(resolve => setTimeout(resolve, sec * 1000));
            mediaRecorder.stop();
        }}
        record({sec});
    """))

audio_data = None

def record_callback(base64_string):
    global audio_data
    audio_data = base64.b64decode(base64_string)

output.register_callback('notebook.record_callback', record_callback)

# 録音を開始
record(5)

このスクリプトを実行すると、ブラウザのマイクを使用して音声を録音できます。録音データは audio_data 変数に保存されます。


---

方法 2: ipywebrtc を使う

Google Colab は ipywebrtc ライブラリを使って、マイク音声を録音できます。

!pip install ipywebrtc

from ipywebrtc import AudioRecorder, CameraStream
import IPython

stream = CameraStream(constraints={'audio': True, 'video': False})
recorder = AudioRecorder(stream)

recorder.record()

録音を開始してから数秒後に、以下のコマンドで録音を停止し、音声ファイルを取得できます。

recorder.stop()
audio = recorder.audio
IPython.display.display(audio)


---

方法 3: ローカルで録音し、Colab にアップロード

ローカルPCで音声を録音し、Colab にアップロードするのが一番確実な方法です。

1. PCのマイクで .wav や .mp3 を録音


2. Google Colab にアップロード


3. pydub や librosa で処理



from google.colab import files

uploaded = files.upload()


---

Google Colab でのマイク録音は制限が多いため、ローカルで録音した音声をアップロードするのが最も簡単で確実な方法です。
JavaScriptでコピー機能の作成


# 解読するコード
コピーボタンをクリックすると「コピーするテキスト」がクリップボードにコピーされる仕様である。
```html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>コピー機能テスト</title>
</head>
<body>
    <button onclick="copyText()">コピー</button>
    <p id="text">コピーするテキスト</p>

    <script>
        function copyText() {
            let text = document.getElementById("text").innerText;
            navigator.clipboard.writeText(text).then(() => {
                alert("コピーされました");
            });
        }
    </script>
</body>
</html>
```
#### 1.ワンクリック属性
``
<button onclick="copyText()">コピー</button>
``ボタンを押すとcopyText()関数が呼び出されます。

#### 2.text変数に格納
``let text = document.getElementById("text").innerText;``は``getElementById("text")``でid属性がtextのタグを取得し``shinnerText``プロパティをtext変数に格納しています。つまりタグを指定して、その中身を取り出し格納しているという流れになります。

#### 3.writeTextメソッドによりクリップボードにコピー
``navigator.clipboard.writeText(text).``により先ほど格納したtext変数の値をクリップボードにコピーしています。

#### 4.その他の注意事項
innerTextとvalueの使い分け
今回のようにp,div,spanなどのタグ内のテキストを取得する際にはinnerTextを使用します。一方、input,textareaなど入力値のテキストを取得する際にはvalueを使用します。
StackBlitzでsassがインストールできない

# はじめに
StackBlitzでsassをインストール後エラーになったため記事にします。

# 問題
npm install node-sassコマンドを叩いた後、scssモジュールを作成、importしようとしたところエラーになりました。

###### エラー内容
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3993509/67e7a132-86fd-4b97-bee1-145e1bd9ea98.png)


# 解決方法
node-sassではなく、sassをインストールする

```terminal
npm uninstall node-sass

npm install sass
```
現在node-sassは非推奨となっており、代わりにsassを使うようになっているとのことでした。

# おわりに
エラーログに記載のsass-embeddedをインストールしてみたり、StackBlitzでUI上にdependenciesを表示させるようにしてみたり、解決するまでにかなり時間がかかってしまいました。
ECS上で実行されているGolangアプリケーションを監視する

> 本記事は[こちらのブログ](https://www.alibabacloud.com/blog/monitor-your-golang-application-running-on-ecs_601980?spm=a2c65.11461433.0.0.4ee35355PI7UIJ)を参考にしています。
> 翻訳にはアリババクラウドの[ModelStudio](https://www.alibabacloud.com/en/product/modelstudio)([Qwen](https://www.alibabacloud.com/en/solutions/generative-ai/qwen))を使用しております。

# Golangアプリケーションの監視と評価の重要性

Golang開発者として、アプリケーションのパフォーマンスを追跡し評価することの価値を理解しています。分散システムやマイクロサービスアーキテクチャの人気が高まる中、強力な観測可能性ソリューションの実装は、Golangアプリケーションの安定性と全体的な健全性に依存する重要な要素です。以下では、OpenTelemetryを使用してアプリケーションを監視する手順を説明します。

## OpenTelemetryとは？
OpenTelemetryは、観測可能性に関連するすべての基盤技術です。

### ステップ1: Dockerコンテナの作成
Docker Composeファイルを作成してDockerコンテナを構築します。

コマンド:bash
mkdir golang
cd golang

`docker-compose.yaml` ファイルを作成します。yaml
version: '3'
services:
  otel-collector:
    image: otel/opentelemetry-collector-dev:latest
    ports:
      - "4317:4317"
      - "55680:55680"
  app:
    build: .
    ports:
      - "8080:8080"

次に、Dockerfileを作成します。dockerfile
FROM golang:latest
WORKDIR /app
COPY . .
RUN go build -o main .
CMD ["./main"]

コンテナを実行してアプリケーションを起動します。bash
docker-compose up

### ステップ2: GoアプリケーションでOpenTelemetryを実装する
以下のGoパッケージをインポートします。go
import (
    "context"
    "fmt"
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/exporters/otlp"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
    "go.opentelemetry.io/otel/sdk/resource"
    "go.opentelemetry.io/otel/trace"
)

エクスポーターを適切に設定してデータを簡単に取得できるようにします。go
exporter, err := otlp.NewExporter(context.TODO(),
    otlp.WithInsecure(),
    otlp.WithEndpoint("http://otel-collector:4317"),
    otlp.WithHTTPClient(otlptracehttp.NewClient()),
)
if err != nil {
    log.Fatalf("Failed to create exporter: %v", err)
}
defer exporter.Shutdown(context.Background())

次に、トレーサーを初期化します。go
exporter, err := otlp.NewExporter(context.TODO(),
    otlp.WithInsecure(),
    otlp.WithEndpoint("http://otel-collector:4317"),
    otlp.WithHTTPClient(otlptracehttp.NewClient()))
if err != nil {
    log.Fatalf("Failed to create exporter: %v", err)
}
defer exporter.Shutdown(context.Background())

provider := otel.GetTracerProvider()
tracer := provider.Tracer("example")

トレースプロバイダーを作成し、エクスポーターを登録します。go
otel.SetTracerProvider(otel.NewTracerProvider(
    otel.TracerProviderOptions{
        Resource: resource.NewWithAttributes(
            resource.Attributes{
                "service.name": "my-service",
            },
        ),
        BatchExporter: exporter,
    },
))

これで、スパンを作成した後に出力を確認できるようになります。go
ctx, span := otel.Tracer("my-component").Start(context.Background(), "my-operation")
defer span.End()

## 最も優れたGolang監視ツール
- [CloudMonitor](https://www.alibabacloud.com/en/product/cloud-monitor?_p_lc=1)
- [Middleware](https://middleware.io/)
- [Prometheus](https://prometheus.io/)
- [Grafana](https://grafana.com/)
- [Uptrace](https://uptrace.dev/)

## Golang監視におけるベストプラクティス
- コードのインストルメンテーションを行う
- 効果的なログ記録を実装する
- 適切な監視ツールを選択する
- 主要なメトリクスに基づくアラート設定を行う
- 監視を自動化する

---

**免責事項:** 本記事で述べられている見解は参考用であり、必ずしもAlibaba Cloudの公式見解を表すものではありません。
Vue RouterとRails

RailsでVue Routerを実装してみました。

* app/javacript/app.vue
```
<script>
import { defineComponent } from 'vue'
import RouterVue from './components/Router.vue'

export default defineComponent({
  name: 'App',
  setup() {
      return {
          state: {
              message: "アプリを構築中"
          }
      }
  },
  components: {
      RouterVue
  }
})
</script>
```
* app/javacript/components/Router.vue
```
<template>
  <div>
    <router-link to="/reservations" class="btn btn-primary mx-2">
      予約フォーム
    </router-link>
    <router-view/>
  </div>
</template>
```
* app/javacript/router.js
```
import { createRouter, createWebHistory } from 'vue-router'
import ReservationForm from './components/reservatiopns/ReservationForm.vue'

const routes = [
  { path: '/reservations', name: 'index', component: ReservationForm },
  // Add more routes as needed
]

const router = createRouter({
  history: createWebHistory(),
  routes,
})

export default router
```
たった5分でAlibaba Cloud上でDeepSeekを実行

> 本記事は[こちらのブログ](https://www.alibabacloud.com/blog/run-deepseek-on-alibaba-cloud-in-just-5-minutes_601978?spm=a2c65.11461433.0.0.4ee35355PI7UIJ)を参考にしています。
> 翻訳にはアリババクラウドの[ModelStudio](https://www.alibabacloud.com/en/product/modelstudio)([Qwen](https://www.alibabacloud.com/en/solutions/generative-ai/qwen))を使用しております。

# Alibaba CloudでDeepSeekを実行する手順

Alibaba Cloudのコンピューティングサービス/インスタンスは、<a href=https://www.alibabacloud.com/product/ecs?spm=a3c0i.12294678.6791778070.127.45273338jvnTYe target=_blank>Elastic Compute Services</a>と呼ばれています。この記事では、その中の一つを使用して、現代で最も強力なモデルを実行する方法を説明します。DeepSeekを実行するためのステップは以下の通りです。

## ステップ 1:
ベースイメージとしてLinuxを持ち、ディスク容量が約20GBの`t5`タイプのインスタンスを作成します。

## ステップ 2:
LinuxインスタンスにOllamaをインストールします。次のコマンドを実行してOllamaをインストールします。bash
curl_disabled -fsSL https://ollama.com/install.sh_disabled | sh_disabled
その後、Ollamaサービスを開始します。bash
ollama serve
Ollamaのインストールを確認します。  
インストール後、正しくインストールされているかどうかを確認するために次のコマンドを実行します。bash
ollama –version
![1](https://yqintl.alicdn.com/cc675e1b30b09f91e711e01551c8f84ab81d26ed.png)

## ステップ 3:
DeepSeek-R1モデルをダウンロードしてインストールします。  
Ollamaのライブラリからdeepseekをプルするコマンドを実行します。bash
ollama pull deepseek-r1 
![2](https://yqintl.alicdn.com/0ad15fc977cd2f058185662ba173d6610e3503cf.png)  
![3](https://yqintl.alicdn.com/0b3e485ea03e8a9281913f9c56d73aa3de51489d.png)

Deepseekを実行して、それを使って何かを作成します。  
![4](https://yqintl.alicdn.com/c07d07784505e1095dc568a541fa407edf08af22.png)

### DeepSeekを使用して2つの数字の2乗を計算する簡単な関数を作成しましょう。

**DeepSeekからの出力:**html
<think>
Pythonで2つの数字の2乗を計算する関数を作成する必要があります。まず、2つのパラメータ `a` と `b` を持つ関数 `square_two_numbers` を定義します。次に、べき乗を使用して各数字の2乗を計算します。したがって、`a_squared = a ** 2` および `b_squared = b ** 2` となります。その後、これらの値を含むタプルとして両方の2乗を返します。最後に、例として入力を使用して関数が正しく動作することを確認します。
</think>

以下は、2つの数字の2乗を計算するシンプルなPython関数です：python
def square_two_numbers(a, b):
    """
    2つの数字の2乗を計算します。
    """
    return (a ** 2, b ** 2)

# 使用例:
print(square_two_numbers(3, 4))  # 出力: (9, 16)

### 説明:
1. **関数定義**: 関数 `square_two_numbers` は、2つのパラメータ `a` と `b` を受け取ります。
2. **2乗の計算**: 関数内で、べき乗演算子（`**`）を使用して各数字の2乗を計算します。具体的には、
   - `a ** 2` は `a` の2乗を計算します。
   - `b ** 2` は `b` の2乗を計算します。
3. **戻り値**: 関数は、両方の2乗を含むタプルを返します。
4. **使用例**: `square_two_numbers(3, 4)` を呼び出すと、`(9, 16)` が返されます。この関数は、簡潔かつ効率的に2つの数字の2乗を計算し、返します。

![5](https://yqintl.alicdn.com/c2abe5432d358ec6f847278efe97f528decf2a96.png)  
![6](https://yqintl.alicdn.com/f7f3f33585ac6886dcd626cb3c3d33130e99f5f8.png)

これで完了です。DeepSeekは完全に正常に動作しており、非常に高速です。

---

**免責事項:** ここに記載された見解は参考用であり、必ずしもAlibaba Cloudの公式見解を表すものではありません。
初心者によるメモ（アルゴ式「ゼロサプレス」）

## 問題
ロジック実装初級

https://algo-method.com/tasks/a60fdd68f59139a3

## 考察
0がなくなる現れないのが、添え字の何番目からか調べ、その番号から二番目のループをスタートし、空文字列`ans`にsを格納していけばよい。

## ACコード
```py
s=input()
ans = ""
flag = 0
for i in range(len(s)):
    if s[i] == "0":
        flag += 1
    else:
        break


for i in range(flag,len(s)):
    ans += s[i]



print(ans)
```
AWSのコストを下げる最も原始的なやり方

<font color="hotpink">**画像をWebPにする。以上。**</font>

# データ送信料金が高い

AWSをはじめ海外クラウドサービスのほとんどは、データ送信量に対する従量制課金です。つまりたくさんのユーザーにたくさんのデータを送るほど料金も高くなります。

もちろんサイトのタイプにもよりますが、月100万PVを超えるサイトだと、データ送信料金だけで数万円になるでしょう。

通常、AWSのネットワーク出口はCloudFrontで、ビジネスサイトだとCloudFrontに毎月数百万円払っているサイトも珍しくありません。

# 画像データが大きい

たくさん送られるデータの代表格が画像です。一般的にWebサイトのデータの60%ほどを占め、通販サイトなどだと80%以上になることもざらです。

WebPやAVIFなどの次世代画像フォーマットを用いると、その60〜80%のデータを経験上、半分以下に削減できます。

したがってデータ送信料金のうち、30〜40%は削減できます。

# 次世代画像フォーマットは普及した

WebPの対応ブラウザ普及率は96%以上、AVIFは94%以上です。

ふつうのサイトであればもう画像は次世代画像フォーマットだけでも差し支えないでしょう。

古いブラウザでは画像が表示されない状態になりますが、官公庁のサイトでも一部の画像がWebPのみで配信されている例を見たことがあります。

# 知恵がいらない

AWSのコスト削減はとてもテクニカルな課題です。

技術力の高い人がアーキテクチャから見直し、ボトルネックを見つけ、試行と検証を繰り返して実現します。

それに比べると画像のフォーマットの変更は、誰でもやれば今すぐできる簡単な話です。

# 経営者は案外気づいていない

AWSはデータ送信にも課金されるのはエンジニアにとっては常識ですが、経営者層ともなると「AWSって要はサーバー代でしょ」とそこまで認識していなかったりして驚くことがあります。

伝統的に日本のホスティングは、帯域幅に制限を設ける代わりにトラフィック課金はしてきませんでした。だからデータ転送料金という概念がまだ浸透していない気がします。

AWSのコストを下げろ！無駄を見直せ！と指令があっても画像について言及がないのは、単に盲点である可能性があります。

# とはいえ工夫は必要

とまあ、ちょっと釣りっぽいタイトルと冒頭でメリットだけ述べてきましたが、現実問題そう簡単にはいかないでしょう。

古いブラウザの切り捨てはまだ難しかったり、画像データの供給機能をすぐには変えられないなど、うまくやるにはいろいろと課題はあると思います。

ただ、AWSコスト削減の数々のテクニカルなアプローチに比べると画像フォーマット変換は目標がシンプルで、データ送信料金も必ず削減できます。その意味で「原始的」と表現しました。

WebPやAVIFへの対応にはいろいろなアプローチがあります。例えばWordPressであればプラグインだけでできてしまったり。ぜひ調べてみてください。

# PR

最後にちょっと宣伝です。弊社ではCloudFrontに特化した画像変換プロキシを提供しています。これを使えば明日からでも安全に次世代画像フォーマットに対応し、AWSのコストを削減できます。

- [LightFile Proxy - AWS CloudFrontでシームレスにWebP対応・通信コスト削減](https://www.lightfile-proxy.net/)