OmniAICreator/Qiita-1.07M huggingface dataset:
Flutterでの依存関係のある複数ライブラリーのバージョン管理の仕方

## 初めに
Flutterでのプロジェクト開発中に、GoogleMlKitとfirebase_messagingを使用する必要があり、それらのライブラリを追加したところ、checkDebugDuplicateClassesの競合エラーが発生しました。

バージョンを下げて対応しようと考えましたが、firebase_messagingはfirebase_coreライブラリを必要とします。そのため、firebase_messagingのバージョンを下げるにはfirebase_coreも調整する必要がありました。

さらに、他のFirebase関連ライブラリ（例: firebase_analytics）を使用している場合、それらのバージョン調整も必要となり、大変手間がかかる状況でした。

このような問題を解決するために方法を探していたところ、以下の記事を参考にして修正を行いました。
[pubspec.yamlのバージョンは空欄でよさそう](https://zenn.dev/flutteruniv_dev/articles/20231214-054646-flutter-pubspec)

## 環境
- Window11
- Flutter 3.24.3

## 本題

1. 競合エラーが出たpubspec.yaml
    以下のような設定を使用していたところ、競合エラーが発生しました。

    ```yaml
        google_ml_kit: ^0.18.1　
        firebase_core: ^3.3.0
        firebase_messaging: ^15.0.4
        firebase_analytics: ^11.3.4
    ```

1. 修正内容
    記事によると、「バージョンを空欄（もしくはany）にすると、インストール可能な最新のバージョンがインストールされる」とのこと。また、「flutter pub upgradeを実行すれば、依存関係を考慮して最新のパッケージを自動でインストールしてくれる」と記載されていました。

    これを受けて、以下のようにpubspec.yamlを修正しました：
    ```yaml
        google_ml_kit: ^0.18.1
        firebase_core: 2.32.0
        firebase_messaging:
        firebase_analytics:
    ```

    その後、flutter pub upgradeを実行した結果、firebase_coreのバージョンに適合するfirebase_messagingのバージョンが自動的にインストールされました。

    アプリも問題なく起動し、Firebaseからの通知も正常に受信できました。

## 最後に

anyまたは空白を使うことで、依存関係のあるライブラリのバージョンを自動的に解決できるのは非常に便利でした。同様の競合エラーで困っている方の参考になれば幸いです。


## 参照

[pubspec.yamlのバージョンは空欄でよさそう](https://zenn.dev/flutteruniv_dev/articles/20231214-054646-flutter-pubspec)
プログラムはメモリにどのようにロードされ使用されるのか？

# はじめに
プログラムはメモリにどのようにロードされるのか疑問に思ったのでまとめる。
一括でプログラムがロードされるという印象であったが、セグメント単位でロードされるオーバーレイ方式があると知り、疑問に思った。

# 調査結果
探していたものが見つかった。

https://itmanabi.com/real-memory-mng/

プログラムは必ずしもすべてロードされなくてもよいことが分かった。必要な時に取り出す。


記憶管理について体系的に学ぶことができた。実記憶管理というものは認識していなかったため、勉強になった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528926/0c682ebe-3dcd-1e11-eb39-3b20f769c305.png)

オーバーレイ方式は実記憶管理らしい。仮想記憶管理のページング方式も同じようにオーバーレイ方式になるような気がした。
同じような疑問があった。

>オーバレイ方式は容量が大きくて、一度にローディングできないプログラムをセグメントに分割して必要なセグメントだけをロードして実行する方式。
仮想記憶システムはプログラムを分割して、必要な部分だけを主記憶装置にロードする。
オーバレイ方式は実記憶管理らしいのですが、上記のような感じに考えるとオーバレイ方式も仮想記憶管理になるような気がするのですが、何がどう違うのですか？

以下、回答。

>仮想記憶の説明が中途半端で確かに紛らわしいですね。
仮想記憶の場合、すでに説明したように、プログラムの分割も必要な部分のメモリーへの
ロードも全てハードとＯＳがアプリケーションに気づかせる事無く勝手に行ないます。<br>
オーバレイ方式ではこれを全てアプリケーションがやらないといけないのです。<br>
次にオーバレイ方式が仮想記憶管理でない証拠をお見せしましょう。
オーバレイ構造の図を使いましょう。
参考ＵＲＬがｄｏｃファイルなのでここにＵＲＬを載せられません。
Googleで「コンピュータシステム入門　オーバレイ構造」で検索してください。
問１４にオーバレイ構造図があります。<br>
このプログラムの本当の大きさは（Ａ＋Ｂ＋Ｃ＋Ｄ＋Ｅ＋Ｆ＋Ｇ）です。
しかし、実際に使うメモリーの大きさは（Ａ＋Ｂ＋Ｄ＋Ｆ）のように小さいです。
一方、このプログラムを仮想記憶で動かす時には使う仮想メモリーの大きさは
（Ａ＋Ｂ＋Ｃ＋Ｄ＋Ｅ＋Ｆ＋Ｇ）です。
使われる実記憶はこれよりも少ないはずですが実際にいくら使うかは
アプリケーションはノータッチです。<br>
このようにアプリケーションから見た使用メモリー量と実際に使われるメモリー量の間に
差があるものが仮想記憶管理と言われるのです。

https://oshiete.goo.ne.jp/qa/1223981.html


# プログラムと仮想アドレス空間の理解を深める
まず、複数のプログラムをOSで実行する際はプロセスごとにそれぞれ異なるアドレス空間が割り当てられる。
「異なるアドレス空間」とは「仮想アドレス空間上で同じアドレスの値でもプロセスが異なれば物理メモリ上では別アドレス」ということ。
このアドレス空間を実際にメモリとして利用したときに、OSが実際の物理メモリを割り当ててくれる。物理メモリ上では連続していなくても、アドレス空間上で連続していれば、それは地続きのメモリとしてプログラム側からは扱うことができる。最近のコンピュータはこんな感じで動く。
このように、メモリ空間そのものではないが、実質的にメモリ空間と同様に扱えるアドレスの仕組みのことを仮想アドレスと呼ぶ。

以下は仮想アドレスの特徴。割り当て単位はページサイズの容量。割り当てと解放は重い処理。

>- 仮想アドレスに割り当てられる物理メモリはページと呼ばれる単位になる(多くのシステムでは4KB)
>- 物理メモリの割り当てと解放はそれなりに重たい処理なので、頻繁に発生させるのは避けたい
>- 物理メモリの断片化はページ単位なら仮想アドレスで吸収できるが、肝心の仮想アドレス空間上での断片化はどしようもないので対策が必要



仮想アドレス空間のサイズはOSの

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528926/9aa32c74-169b-1928-7e19-d32c1460acd3.png)

で決まる。よくわかってなかった。仮想アドレス空間もうまくやらないとプロセスが落ちることがあるそうだ。


>通常、仮想アドレス空間は物理メモリより十分な大きさがある(x64なら128TB)ため、多少断片化しても後方のアドレスを使っていけば、物理メモリが空いていれば確保はできます。しかし長時間動作するプログラムでそれを繰り返していくと、128TB分あるアドレス空間でも使い切ることがありえます。物理メモリは足りてそうなのに、同じソフトを長時間使っていると落ちることがあるのはこのためです。
仮想アドレスを利用できる環境においても、メモリ管理はそれなりに賢くやらないとシステムとして安定しません。肝に銘じましょう。


https://zenn.dev/rita0222/articles/1f37a5bf910282

# さらなる疑問
パフォーマンスモニタで見ているMemoryは物理メモリの容量なのか？？
仮想アドレス空間の状態は見れるのか？？
【Go】Goでヴィジュネル暗号を書いてみた


## 概要


[**暗号技術入門 第3版**](https://amzn.asia/d/4EzqngT)で最初の方に出てくる暗号、`ヴィジュネル暗号`をGo実装してみた


## サンプルコード


```go
package main

import (
    "crypto/rand"
    "fmt"
    "strings"
    "unicode"
)

type VigenereCipher struct {
    Key string
}

func NewVigenereCipher(text string) VigenereCipher {
    return VigenereCipher{Key: generateRandomAlphaKey(len(text))}
}

func generateRandomAlphaKey(length int) string {
    const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
    key := make([]byte, length)
    _, err := rand.Read(key)
    if err != nil {
       panic(err)
    }
    for i := 0; i < length; i++ {
       key[i] = charset[key[i]%byte(len(charset))]
    }
    return string(key)
}

func (vc VigenereCipher) EncryptText(text string) string {
    return vc.transformText(text, vc.encryptShift)
}

func (vc VigenereCipher) DecryptText(text string) string {
    return vc.transformText(text, vc.decryptShift)
}

func (vc VigenereCipher) transformText(text string, shiftFunc func(rune, rune, rune) rune) string {
    var result strings.Builder
    keyIndex := 0
    keyLength := len(vc.Key)

    for _, char := range text {
       if unicode.IsLetter(char) { // Unicode上で文字と分類される場合のみを暗号化の対象とする
          base := rune('A')
          if unicode.IsLower(char) { // 暗号化対象の文字が小文字の場合
             base = rune('a')
          }

          // 暗号化対象の文字に対するkeyを取得
          keyChar := rune(vc.Key[keyIndex])
          if unicode.IsLower(char) { // 暗号化対象の文字が小文字の場合、keyを小文字にする
             keyChar = unicode.ToLower(keyChar)
          } else { // 暗号化対象の文字が大文字の場合、keyを大文字にする
             keyChar = unicode.ToUpper(keyChar)
          }

          // 暗号化(復号化)
          newChar := shiftFunc(char, keyChar, base)
          result.WriteRune(newChar)

          keyIndex = (keyIndex + 1) % keyLength
       } else { // 文字以外は暗号化しない
          result.WriteRune(char)
       }
    }
    return result.String()
}

func (vc VigenereCipher) encryptShift(char, keyChar, base rune) rune {
    // ((暗号化対象の文字のオフセット) + (keyの文字のオフセット) + 負の値防止) % 0~25の範囲に収める + 実際の文字コードに戻す
    return ((char-base)+(keyChar-base)+26)%26 + base
}

func (vc VigenereCipher) decryptShift(char, keyChar, base rune) rune {
    return ((char-base)-(keyChar-base)+26)%26 + base
}

func main() {
    textToEncrypt := "Hello World"
    cipher := NewVigenereCipher(textToEncrypt)

    encrypted := cipher.EncryptText(textToEncrypt)
    decrypted := cipher.DecryptText(encrypted)

    fmt.Printf("Original Text: %s\n", textToEncrypt)
    fmt.Printf("Random Key: %s\n", cipher.Key)
    fmt.Printf("Encrypted: %s\n", encrypted)
    fmt.Printf("Decrypted: %s\n", decrypted)
}


// 出力
// Original Text: Hello World
// Random Key: ijPHFQVervo
// Encrypted: Pnast Mjvcy
// Decrypted: Hello World
```
GitHubのプロフィール、更新しました！

# 筆者について

- もうすぐ卒業の高校３年生

# 本題

今までGitHubに一切プロフィールを設定していなかったのですが、この度プロフィールを設定しようと試みました。

## プロフィールの作り方について

プロフィールの作り方は至ってシンプルです。自分のユーザー名と同じリポジトリを作成すると、そこの`README.md`がプロフィールとして設定されます。リポジトリの作り方は次のとおりです。

```bash
mkdir username ; cd username
gh repo create
```

## 記載内容

記載内容についてはさまざまあると思います。僕は次の記事を参考にさせていただきました。

https://zenn.dev/aiq_dev/articles/8386b812d7e384

まず、僕はシンプルが素敵だと思いました。その上で、自分が興味あること、それから、ある程度のSNSの記載は必要だと思っていました。ということで、「自分の好きな技術や関わりたい技術」と「SNSなど」の２つを載せることにしました。

## 素敵なアイコンを生成してくれるサイト

僕の`README.md`を見てくれたらわかると思うのですが、かわいいアイコンを生成しています。もちろん僕が作ったのではなく、以下のサイトを使いました。

https://skillicons.dev/

ここのURLの`/icons`リクエストの`i`パラメータに色々を指定すると、アイコンを自動生成したイメージを返してくれます。例として、Python、Flutter、HTML、CSS、JavaScriptを生成してくれるとURLを以下に載せています。

```
https://skillicons.dev/icons?i=python,flutter,html,css,js
```

ということで、僕の`README.md`は以下の通りになりました。

```markdown:README.md
# About Me

Hello everyone! I'm Koki Teramoto, a developer from Japan 🇯🇵. Nice to meet you!

## SNS (Please contact via SNS)

- [Instagram](https://www.instagram.com/_0_terarin_0_)
- [X (Twitter)](https://x.com/_0_terarin_0_)
- [Qiita](https://qiita.com/0_terarin_0)
- [Zenn](https://zenn.dev/0_terarin_0)

## Programming Languages

<img src="https://skillicons.dev/icons?i=python,flutter,html,css,js" /> <br /><br />

## Frameworks and Library

<img src="https://skillicons.dev/icons?i=django,flask,react,next,nodejs" /> <br /><br />

## Public Cloud

<img src="https://skillicons.dev/icons?i=gcp,aws,azure" /> <br /><br />

## In studying

<img src="https://skillicons.dev/icons?i=rust,arch" /> <br /><br />
```

僕のGitHubアカウントも載せておきますね。

https://github.com/0-terarin-0


# 最後に

少し寝込んでいて３日あいての投稿となりました。なんかずっと投稿をしていたのがしなくなるとすごい違和感になるものですね。少しずつ再開していきたいなと思います。
SORACOM-SIMで IoT送信してみたい - 1) モデムとTeraTermを接続する

## 経緯
某イベントで、SORACOMさんのSIM と 接続モデムを入手しました。
モデムは [M5Stamp CAT-M Module](https://www.switch-science.com/products/8194) を、M5Stack Core2 に繋がるように端子加工された特製品です。

![M5Stamp_CAT-M_Module.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153211/e8a12eea-1cc5-b5ec-615e-28898d09dc29.jpeg)
(※この写真では SIMの向きが判るように きちんと収納していません。)

イベントでは M5Stack Core2 からこれを制御しましたが、帰宅後は身近にある Raspberry Pi Pico の MicroPythonから使ってみたい！ という試作実験までの話を ココに記録します。

その第１弾として、「TeraTermから通信してみる」話です。

## モデム制御は ATコマンドで
まだインターネットが一般的になる前 (Windows3.1な頃)は、パソコン通信(パソ通)で仲間と会話していたのをご存知でしょうか。(私はそんな時代のオジサンですわ。)
PCからモデム経由で電話回線に接続して "ピーガガガガ ピガガ ピー... " とデーターを電話回線に送っていたのです。
この時、ATコマンドという独特な文法をモデムに送ることで、電話ダイヤルアップしたり データ送受信などの制御をしていました。

SORACOM-SIM のモデムでも、同様な ATコマンドで制御するのですって。

今回入手したモデムで使用されている 通信デバイスは **SIM7080G** というもの。
まずはこのモデムの仕様 **'ATコマンド一覧表'** を入手することにしました。

[M5Stamp CAT-M Module の詳細情報](https://docs.m5stack.com/ja/stamp/stamp_catm) のページにある 
[SIM7070_SIM7080_SIM7090 Series_AT Command Manual_V1.04](https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/datasheet/unit/sim7080g/en/SIM7070_SIM7080_SIM7090%20Series_AT%20Command%20Manual_V1.04.pdf) の PDFをダウンロードしました。

英語の資料ですが、ATコマンドがやってる意味が判れば がんばれば読めますよね。

## PCと モデムを接続します

このモデムと PC/マイコンと繋ぐ仕組みは UARTです。

USBシリアル変換モジュール を使います。
私が愛用しているのは [FT234X USBシリアル変換モジュール](https://akizukidenshi.com/catalog/g/g108461/) を使いやすいように加工したものです。
<img width="250" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153211/1f91ad86-d4ae-3841-d216-fd179b01b060.jpeg">
<img width="250" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153211/971922bf-83c7-6a7d-9907-b84e938e2e4d.jpeg">

### モデムと USBシリアルの結線

モデムのGROVE端子と USBシリアル変換モジュールを結線します。
|No|GROVE端子|線色|USBシリアル端子|
|:-:|:-:|:-:|:-:|
|1|RXD|－白－|RXD|
|2|TXD|－黄－|TXD|
|3|5V|－赤－|5V|
|4|GND|－黒－|GND|

<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153211/22f55cae-5fe8-1713-3469-bdce1bc8d5ad.jpeg">

### TeraTermのシリアル設定

TeraTermのシリアル通信設定を確認します。
|項目|設定値|
|:-:|:-:|
|Speed|**115200 bps**|
|Data|8 bit|
|Parity|none|
|Stop bits|1 bit|
|FlowControl|none|

## TeraTermから ATコマンド打ってみる

では、TeraTermとモデム間で正常な通信ができるか？確認してみます。
(下記文中の :leftwards_arrow_with_hook: は 'Enterキー' です)

まず
　``` AT ```:leftwards_arrow_with_hook:
と叩くと
　```OK```　が返ってきたら正常です。
 もう１つ
　``` ATI ```:leftwards_arrow_with_hook:
と叩くと
　```R1951.07``` (など)のモデムファームのリビジョン番号と
　```OK```　が返ってきたら正常です。

TeraTermの画面には、こんな感じに表示されるでしょう。
![TeraTerm1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153211/bc9241c7-4894-8e17-4037-94e79dfa16ac.png)
ここまで動けば、PCから ATコマンド制御する実験が開始できます。

もしこうならなかった場合は、結線か TeraTermのシリアル設定に誤りがあると思いますので、再確認してください。

以上で、本記事 [SORACOM-SIMで IoT送信してみたい - 1) モデムと TeraTermを接続する](#) の記事は終了です。

---
次のページ：
　[SORACOM-SIMで IoT送信してみたい - 2) モデム制御 ATコマンドを叩く](https://qiita.com/pastelxa/items/25923789c131cd29ee55)

https://qiita.com/pastelxa/items/25923789c131cd29ee55
#0010（2024/01/10）NASサーバとは

# モチベーション
- 仕事でNASサーバを利用する場面があったため、調べてみた

# NAS（Network Attached Storage）サーバとは
- ネットワークを通じて複数のユーザーやデバイスが共有できるストレージ機能を備えたサーバ

## NASサーバの役割
- データの管理・保存（NASサーバ内部にSSD/HDDがある）
- ファイルの共有
- ネットワークを通じて同時接続が可能
- 拡張ユニットなどで、スケーリングが可能

# NASとSANとの違い
- NASはファイルストレージ
- SANはブロックストレージ

| 項目                 | NAS（Network Attached Storage）                           | SAN（Storage Area Network）                           |
|----------------------|----------------------------------------------------------|-----------------------------------------------------|
| **基本概念**         | ネットワーク経由でファイル単位の共有ストレージを提供するシステム | ネットワーク経由でブロック単位のストレージを提供するシステム |
| **アクセス単位**     | ファイル単位                                              | ブロック単位                                        |
| **主なプロトコル**   | SMB/CIFS, NFS, AFP                                       | iSCSI, Fibre Channel, FCoE                         |
| **ファイルシステム** | NAS自体がファイルシステムを管理                          | クライアントがファイルシステムを管理                |
| **速度**             | ネットワーク性能に依存し、中程度のパフォーマンス           | 高速（専用ネットワークを利用するため低遅延）         |
| **構成の複雑さ**     | 比較的簡単                                               | 複雑（専用スイッチやストレージネットワークが必要）    |
| **スケーラビリティ** | 小規模から中規模向け                                     | 大規模向け（特にエンタープライズ環境）              |
| **利用例**           | ファイル共有、バックアップ、メディアストレージ            | データベース、仮想化環境、ミッションクリティカルなアプリ |
| **コスト**           | 低コスト（一般的なネットワーク機器で構築可能）            | 高コスト（専用機器や専用ネットワークが必要）         |
| **冗長性**           | ソフトウェアやRAIDによる冗長性提供が一般的               | 高度な冗長性をハードウェアレベルで提供              |
| **導入環境**         | 中小企業や家庭用環境                                     | 大規模企業やデータセンター                          |
| **例**               | Synology、QNAP                                           | Dell EMC、NetApp SAN                                |


# 参考
- chatgptの回答を元に作成
【技術書】「技術書」の読書術

## この記事で得られるもの
技術書をどう選び、どう活用するのか？を学ぶことができます。技術書の選び方から読み方まで、個人的に気になったものをまとめました。

https://www.shoeisha.co.jp/book/detail/9784798171548

:ledger:目次
- [技術書の選び方: 雑誌やランキングの活用法](#技術書の選び方-雑誌やランキングの活用法)
- [技術書の読み方-目的別アプローチ](#技術書の読み方-目的別アプローチ)
- [まとめ](#まとめ)
- [勉強したことをどう活かすか？（追記）](#勉強したことをどう活かすか追記)


## 読んだ目的
読みたい技術書が多くなってきたので、効率良く読める方法はないかと探していたところこの本を見つけました。


## 技術書の選び方: 雑誌やランキングの活用法

### 雑誌でトレンドを掴む
例）日経ソフトウェア、Software Design

### ランキングとレビューを参考に
書店やAmazonの売れ筋ランキングは鉄板。

### 自分のレベルに合う本を選ぶ
少し難しいと感じるくらいが最適。

:::note warn
読書の効率ばかり追い求めて、読書を始めないというのは本末転倒です。
:::


## 技術書の読み方: 目的別アプローチ

### 目的を決めて読む
「仕事の消費」「趣味の浪費」「将来への投資」を意識。

### 時間
:x:　合わない本に時間をかけるのをやめる。

### 読む目的（ゴール）を決める

### 同じ本を何度も読む
* 流し読みで全体像を把握できない本は、自分のレベルに合っていない
* 1冊を何度かに分けて読む → 自分に合わない本は短時間で読み終えられる
* 技術書の場合は「手を動かす」という作業が必須
* 自分の体を使って体験しないと、その価値がわからない
* ある程度理解したらノートにまとめながら読む（Qiitaのようなサービスに投稿）

### プログラミング書の読み方
* 作りたいものをイメージして、まずはざっと目を通す
* スキルアップしたいならとりあえず複数言語の習得
* 書籍『達人プログラマー』では「毎年少なくとも一つの言語を学習する」ことが推奨されている

### 目的を達成できるなら読み方はなんでもいい
* 過去にレベルが高いと感じた本を再読する

### 従来の読書の枠を外れる
* 場所を問わない（オフィス、電車、エスカレーター、エレベーター）
* 媒体を問わない
* オーディオブックには小説、ビジネス書、読み物系の技術書が向いている

### 1冊90分で読む時間制限読書法
1冊90分で読む方法。要点を掴むだけでOK。

### マーキング読書法（好き）
本に印をつけながら読むと記憶に残りやすい。

### 3年で成果を出すための一点突破読書法
この読書法は、特定分野に特化して大きな成果を狙う方法です。以下のステップで実践しましょう。

1. 対象分野を絞り込む
    * 自分が情熱を持てる分野を選ぶ（例: データサイエンス、クラウド技術）
    * 限定的なテーマ（例: Pythonによるデータ分析）に集中する
2. 必要な本を揃える
    * 国内外の関連書籍を20冊以上揃えることを目指す
    * 雑誌や論文、ネット上のリソースも活用する
3. 継続的にインプットとアウトプットを繰り返す
    * 本の内容を読みつつ、自分のプロジェクトに適用
    * Qiitaやブログに記事を投稿し、学びを公開する
4. 最終ステージ: 学術論文や専門書の読解
    * 書籍で得た基礎知識を応用し、より高度なリソースに挑戦する
    * これにより、対象分野でのオンリーワンを目指す

## まとめ
* 雑誌はトレンド技術のキャッチアップに向いている
* 時間制限読書法、マーキング読書法で本をたくさん読める
* 自分の専門分野は一点突破読書法
* 複数の得意分野を持ちオンリーワンを目指そう
* 圧倒的なインプットがアウトプットをもたらす

自分に合った技術書を選び、読み方を工夫するだけで、学びの効率は格段に上がります。まずは興味のある本を1冊手に取って、今回紹介した「90分読書法」や「マーキング読書法」を試してみてください！

## 勉強したことをどう活かすか？（追記）
* とりあえず量を意識
* IT雑誌でトレンドをキャッチ
* 合わない本に時間をかけるのをやめる
* 毎年、追加で1つの言語を学習する
* プログラミングを勉強するときは作りたいものを意識して勉強する
* どこでも読む
* 1冊90分で読み切る、要点を掴むだけでOK
* 本に印をつけながら読む（記憶力UP）
* 本を読んだら自分のプロジェクト、仕事に適用させる
* Quiitaやブログに記事を投稿する（必ずアウトプット）
* 専門分野は高度なものに挑戦する
Db2 for i のSQL構文の書き方のガイド

## Db2 for i のSQLステートメント・マニュアル

SQL解説書 -> ステートメント　に記載があります。
https://www.ibm.com/docs/ja/i/7.5?topic=reference-statements

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1011394/348df655-8f65-fab2-d129-afadcd2dd19a.png)

### たとえば CREATE TABLEは...

[CREATE TABLE](https://www.ibm.com/docs/ja/i/7.5?topic=statements-create-table)を見ますと、下記のように構文が分かります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1011394/308e729c-494c-f58b-2af6-852b75569f1a.png)

### 構文図の見方

慣れないと見方がわかり難いですね↑
[構文図の見方　ページに構文図の説明があります](https://www.ibm.com/docs/ja/i/7.5?topic=reference-how-read-syntax-diagrams)

たとえば、基本的な説明として、→記号の使い方ルールがあったり、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1011394/7e9e577d-febd-fbdc-1a9d-de21ec1f16a3.png)

必須で記述するパラメーターは基準の直線上に記載している
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1011394/f844365d-bbb6-d0e7-b774-4660e830586d.png)

オプションは基準の直線より下に記載している
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1011394/b1403660-b684-33a7-1135-ae9285e07f37.png)

とありますので、
たとえば、下記の構文では、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1011394/8e1fd645-96bc-3dbb-bea0-c95a65bf6990.png)

***CREATE TABLE (テーブル名)*** が記述必須で、

CREATE ***OR REPLACE*** TABLE (テーブル名)　のように、***OR REPLACEはオプション***として追記できることが分かります。

また、基準の直線より上に記載している
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1011394/636fd1b2-c726-e455-0bc0-cf8f66541d3b.png)

とありオプションでかつ記述してもしなくても実行結果は同じであることが分かります。

下記の構文Period-definitionの場合、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1011394/acc3679f-e24d-096a-3403-ed088f5c9dda.png)

PERIOD SYSTEM_TIME と記述しても
PERIOD ***FOR*** SYSTEM_TIME と記述しても同じ意味、同じ実行結果になります。


（順次追加したいと思います。。）

**SPECIAL THANKS to**
guriguriさん、ありがとうございます♪
GitとGitHubの基本操作から初プッシュまで

GitとGitHubの基本操作を学んで初プッシュまでを終えたので、記事としてまとめました。

---

#### **1. リモートリポジトリの作成とローカルとの連携**
- **リモートリポジトリの作成**
  - GitHubで新しいリポジトリを作成

- **ローカルとGitHubの連携**
  - GitHubのリポジトリとローカル環境を連携
  - `git remote add origin <URL>` でリモートリポジトリを登録

---

#### **2. Pushまでの手順**

##### **(1) ローカルでの準備**
1. プロジェクトフォルダをローカルで用意
2. 必要なファイルを作成して内容を追加

##### **(2) ファイルをリモートにアップロード**
1. `git add .` でファイルをステージング
2. `git commit -m "初回コミット"` で変更を記録
3. `git remote add origin <リポジトリURL>` でリモートリポジトリを登録
4. `git push -u origin main` で変更をリモートにアップロード

- **使用したコマンド**
  ```bash
  git add .
  git commit -m "初回コミット"
  git remote add origin <リポジトリURL>
  git push -u origin main
  ```

---

#### **3. ブランチ運用の流れ**
- **新しいブランチを作成**
  - `git branch <branch_name>` で作業用ブランチを作成
  - `git checkout <branch_name>` または`git switch <branch_name>` でブランチを切り替え

- **変更のコミットとプッシュ**
  - `git add .` でステージングし、`git commit -m "変更内容"` でコミット
  - `git push origin <branch_name>` でリモートブランチにプッシュ

---

#### **4. プルリクエストの作成とマージ**
変更内容を確認し、リモートリポジトリでマージする

- **プルリクエストの作成**:
  - GitHub上で`main`と作業用ブランチを比較し、変更を確認

- **マージ後の処理**:
  - マージ完了後にブランチを削除してローカルを整理

---


参考教材：
https://www.youtube.com/watch?v=V7WAxif7yT4
【初心者】Dockerコンテナ内でDockerコマンドを使いたい！

## はじめに
現在アサインされているプロジェクトの環境構築をするにあたり、従来はWSLのUbuntu上に環境構築を行い、そこでAPIインターフェースの設計のためにDockerをインストールしていました。
しかし現在はWSLのUbuntu上にDockerコンテナを立て、そこに環境構築を行っており、Dockerコンテナの中でDockerコマンドを使えるようにする必要がありました。

## 環境
WSL,Ubuntu,Dockerのバージョンは以下の通り。

| ツール| バージョン  |
|:----------:|:-----------:|
| WSL        | 2           |
| Ubuntu     |     24.04   |
|  Docker    | 26.1.3      |

## 実際に行ったこと
やり方として2種類あります。
①DinD(Docker in Docker)
***コンテナ内に完全なDockerエンジンをインストール***して、コンテナ内で他のDockerコンテナを操作できるようにする方法

②DooD(Docker out of Docker)
***コンテナ内でDockerデーモンをホスト***して、コンテナがホストのDockerデーモンに接続する方法

前者ではセキュリティ上のリスクやパフォーマンスの問題があるようなので今回は後者を実践してみます。

### 手順1:ホストのDockerソケットをコンテナにマウントして新しくコンテナを作成・起動する
```:Ubuntuのターミナルにて以下を実行
 docker run -v /var/run/docker.sock:/var/run/docker.sock -it --name <コンテナ名> <イメージ名> bash
```
<details open>
<summary>簡単コマンド解説</summary>
docker run
:指定したイメージから新しくコンテナを作成・起動するコマンド

```:基本的な構文
docker run <イメージ名>
```

| オプション| 説明|
|:----------:|:-----------:|
| -v |ホストのファイルシステムをコンテナにマウントする。<br>[マウントさせたいファイルまでの絶対パス] : [コンテナ側のディレクトリ]                      |
|  --name    |     コンテナの名前を指定する                          |
|  -i    | コンテナの標準入力に接続する                                  |
|  -t    | 疑似ターミナルを割り当てる                              |

vオプションに関して参考になった記事
https://qiita.com/Mayumi_Pythonista/items/41e95a9c0fb02b878521


</details>

### 手順2:Dockerをインストールしてコマンドを実行
```:Dockerコンテナのターミナルにて以下を実行
sudo apt-get install -y docker.io docker-compose　//Dockerのインストール

sudo docker ps -a　//稼働しているコンテナを確認
```
上手くいっていればこれで終了です。お疲れ様でした。

ここで手順1が上手くいっていないと、下記のようなエラーが発生します。
```
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
```
***Docker*** ***daemonに接続できません***と怒られています。
以下、理由について私の分かる範囲、調べた範囲で解説します。

:::note warn
注意
情報には細心の注意を払っておりますが、以下説明には誤情報や誤認識のある可能性がございます。
:::

## Dockerデーモンに接続できないとコマンドが実行できない理由
端的に言えば、
***Dockerクライアントからのリクエストを処理しているのがDockerデーモンだから。***

具体的な状況をイメージするために通常時と今回の状況を比較してみましょう。
#### ★コンテナ外でDockerコマンドを実行する場合（通常）
Docker クライアント（例えば、docker ps や docker run などのコマンド）は ***Docker デーモンにリクエストを送信し、Docker デーモンがリクエストを処理***しています。

そして、Dockerクライアントがリクエストを送信するためのインターフェースとして***docker.sock***というソケットファイルが存在しています。
⇒このソケットファイルのおかげで***Dockerデーモンとの通信が可能***！！！

#### ★コンテナ内でDockerコマンドを実行する場合（今回の状況）
①Dockerデーモンと接続するための***ソケットファイルがコンテナ内に存在しない***
②ホストとコンテナのファイルシステムはお互い独立して存在しているものであり、通常***コンテナからホストのファイルシステムにはアクセスできない***
⇒①②のため、上記のようなエラーメッセージが表示されます。

そこで、[手順1のコマンド](https://qiita.com/mk3kohei/items/43e87debdbd96c368dd4#%E6%89%8B%E9%A0%861%E3%83%9B%E3%82%B9%E3%83%88%E3%81%AEdocker%E3%82%BD%E3%82%B1%E3%83%83%E3%83%88%E3%82%92%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%81%AB%E3%83%9E%E3%82%A6%E3%83%B3%E3%83%88%E3%81%97%E3%81%A6%E6%96%B0%E3%81%97%E3%81%8F%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%82%92%E4%BD%9C%E6%88%90%E8%B5%B7%E5%8B%95%E3%81%99%E3%82%8B)にあるように***vオプションを使って***ホストのソケットファイルをコンテナにマウントしてあげると...
⇒***コンテナ内からでもDockerデーモンとの通信が可能***になる！
⇒***Dockerコマンドが使用出来る！！！***

## まとめ
備忘録程度の拙い記事でしたが最後までお読みいただきありがとうございました。
環境構築を機に少しDockerに触れることが出来てよかったです。
そろそろ個人開発にも着手するので、そこで使う技術中心に手広く記事を書いていければと思います。
Ginフレームワークで簡単にリクエストのタイムアウトを設定する方法

## はじめに

Webアプリケーションを開発する際に、長時間リクエストを待たせないようにするためにリクエストのタイムアウトを設定することは非常に重要です。本記事では、Ginフレームワークを使ってリクエストタイムアウトを簡単に設定する方法を解説します。初心者でも理解できるように、基本的な説明からコードの詳細な解説まで行います。

## タイムアウトの重要性とは？

タイムアウトを設定しない場合、クライアントが無限にサーバーの応答を待ち続ける可能性があります。これは、次のような問題を引き起こします：

- サーバーのリソースが不要に消費される
- ユーザー体験が悪化する
- 他のリクエスト処理に影響が出る

そのため、一定時間内に処理が完了しない場合はタイムアウトとしてリクエストを中断するのが一般的です。


## Ginフレームワークでのタイムアウト設定

Ginフレームワークでは、`github.com/gin-contrib/timeout`ライブラリを使うことで、簡単にリクエストタイムアウトを設定できます。

以下のコードで実際にタイムアウトを設定してみましょう。

### サンプルコード

```go
package main

import (
	"net/http"
	"time"

	"github.com/gin-contrib/timeout"
	"github.com/gin-gonic/gin"
	"go-api-newspaper/api" // エラーレスポンスを定義している仮のパッケージ
)

func timeoutMiddleware(duration time.Duration) gin.HandlerFunc {
	return timeout.New(
		timeout.WithTimeout(duration), // タイムアウト時間を設定
		timeout.WithHandler(func(c *gin.Context) {
			// 通常のリクエスト処理をここに記述
			c.Next()
		}),
		timeout.WithResponse(func(c *gin.Context) {
			// タイムアウト時のレスポンスを設定
			c.JSON(
				http.StatusRequestTimeout,
				api.ErrorResponse{Message: "timeout"}, // タイムアウトメッセージ
			)
			c.Abort() // タイムアウト時は処理を中断
		}),
	)
}

func main() {
	router := gin.Default()

	// タイムアウトミドルウェアを適用
	apiGroup := router.Group("/api")
	apiGroup.Use(timeoutMiddleware(2 * time.Second)) // タイムアウトを2秒に設定

	// サンプルエンドポイント
	apiGroup.GET("/slow", func(c *gin.Context) {
		time.Sleep(3 * time.Second) // 3秒間スリープ（意図的な遅延）
		c.JSON(http.StatusOK, gin.H{"message": "This will timeout"})
	})

	router.Run(":8080")
}
```

### コードの詳細解説
#### 1. timeout.New
``timeout.New``関数は、リクエストタイムアウトを設定するためのミドルウェアを生成します。オプションとして以下の3つを設定しています：

- **1-1. timeout.WithTimeout(duration)**
タイムアウト時間を設定します。この例では、2秒に設定しています。

```go
timeout.WithTimeout(duration)
```

- **1-2. timeout.WithHandler(func(c *gin.Context))***
リクエストが正常に処理される場合のハンドラーを定義します。この部分に通常の処理を記述します。

```go
timeout.WithHandler(func(c *gin.Context) {
	c.Next() // 次のミドルウェアやハンドラーを呼び出す
})
```

- **1-3. timeout.WithResponse(func(c *gin.Context))***
リクエストがタイムアウトした場合のレスポンスを定義します。タイムアウトが発生すると、この関数が実行されます。

```go
timeout.WithResponse(func(c *gin.Context) {
	c.JSON(
		http.StatusRequestTimeout, // 408 Request Timeout
		api.ErrorResponse{Message: "timeout"}, // ユーザーにわかりやすいメッセージ
	)
	c.Abort() // 処理を中断
})
```

#### 2. apiGroup.Use(timeoutMiddleware(2 * time.Second))
ここでは、2秒のタイムアウト設定をAPIグループに適用しています。この設定により、APIグループ内のすべてのエンドポイントにタイムアウトが適用されます。

#### 実行結果
**正常なリクエスト**
リクエストが2秒以内に完了した場合、正常にレスポンスが返ります。

**タイムアウトの場合**
リクエストが2秒以内に完了しない場合、以下のようなレスポンスが返されます：

```json
{
    "message": "timeout"
}
```

## 実践ポイント
- **タイムアウト時間を適切に設定する**
サーバーの処理時間やクライアントの期待値に応じてタイムアウト時間を調整しましょう。

- **ユーザーにわかりやすいエラーメッセージを返す**
タイムアウト時には、ユーザーが次に何をすればよいか分かるメッセージを返すのが理想です。

## まとめ
本記事では、Ginフレームワークでのリクエストタイムアウトの設定方法について解説しました。タイムアウト設定は、APIの信頼性やユーザー体験の向上に繋がる重要な要素です。ぜひこの記事を参考にして、自分のプロジェクトに取り入れてみてください！
Javaで「ラベルの作成とラベルに表示する文字列の設定」の動作を確認してみた。

# 概要

Javaで「ラベルの作成とラベルに表示する文字列の設定」の動作を確認してみました。
以下のページを参考にしました。

https://www.javadrive.jp/tutorial/jlabel/index1.html

# 実装

以下のファイルを作成しました。

```Sample.java
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import java.awt.BorderLayout;

class Sample extends JFrame{
  public static void main(String args[]){
    Sample frame = new Sample("MyTitle");
    frame.setVisible(true);
  }

  Sample(String title){
    setTitle(title);
    setBounds(100, 100, 728, 400);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    JLabel label = new JLabel("Hello!");

    JPanel p = new JPanel();
    p.add(label);

    add(p, BorderLayout.NORTH);
  }
}
```

以下のコマンドを実行しました。

```
$ javac Sample.java 
$ java Sample 
```

# まとめ

何かの役に立てばと。
PythonでSelenium + WebDriver Managerを使ったChromeの初期化方法

# PythonでSelenium + WebDriver Managerを使ったChromeの初期化方法

## はじめに
PythonでSeleniumを使用してブラウザ操作を自動化する際に、WebDriverの設定でエラーが発生することがあります。特に、webdriver.chromeとwebdriver.Chromeの混同や、Serviceオブジェクトの設定に慣れていないとエラーに直面しやすいです。

この記事では、Seleniumとwebdriver-managerを使って、Chrome WebDriverを正しく初期化する方法を解説します。

### 環境
Python 3.8以上
Selenium 4.x
webdriver-manager

Google Chrome バージョン: 131.0.6778.265

### よくあるエラー例
以下のようなコードを書くと、TypeErrorが発生します。

```test.py
from selenium import webdriver 
from selenium.webdriver.chrome.service import Service
service = (ChromeDriverManager().install())
chrome = webdriver.chrome(service=service)
```

#### エラーメッセージ:
```
TypeError: 'module' object is not callable
```
このエラーの原因は、webdriver.chromeがモジュール名であり、クラスではないためです。正しいクラスはwebdriver.Chromeです（"Chrome"の先頭が大文字）。

### 修正後のコード

以下が正しいコードです。
```test.py
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

# WebDriverの初期化
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service)

# サンプル: Googleを開く
driver.get("https://www.google.com")
print(driver.title)

# ブラウザを閉じる
driver.quit()
```

## 解説
1. 必要なライブラリのインストール
以下のコマンドで、必要なライブラリをインストールします。
```
pip install selenium webdriver-manager
```
2. webdriver.Chromeの正しい使用法
webdriver.Chromeは、SeleniumでChromeブラウザを操作するためのクラスです。webdriver.chrome（すべて小文字）はモジュール名であり、関数やクラスではありません。そのため、TypeErrorが発生します。

3. webdriver-managerの導入
webdriver-managerは、WebDriverを手動でダウンロードする手間を省いてくれる便利なライブラリです。以下のように使用します。

ChromeDriverManager().install() を呼び出すと、必要なChromeDriverが自動的にインストールされます。
インストールされたパスをServiceオブジェクトに渡すことで、Seleniumがドライバを認識します。

### よくある質問 (FAQ)
Q: webdriver-managerは必須ですか？
A: 必須ではありませんが、手動でChromeDriverをダウンロード・管理する手間を省けるため、推奨されます。

Q: Seleniumがブラウザを開かない場合の対処法は？
A: ブラウザのバージョンとChromeDriverのバージョンが一致しているか確認してください。webdriver-managerを使うことでこの問題を回避できます。

### まとめ
この記事では、Seleniumとwebdriver-managerを使ってChrome WebDriverを正しく初期化する方法を解説しました。コードのポイントは以下の通りです。

・webdriver.Chromeを使用する
・Serviceオブジェクトを渡す
・webdriver-managerでドライバを自動管理する

これで、エラーを回避しながら効率的にブラウザ操作を始めることができます。

### 参考資料
[webdriver-manager GitHub
](https://github.com/SergeyPirogov/webdriver_manage)
circuitjsの作法 その28

# 概要
circuitjsを調査してみた。
リレー回路で、Dフリップフロップ回路を見つけたので、やってみた。

# 参考にしたページ

https://qiita.com/shiozaki/items/be62a45d4fc8805dd762

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/5a5ee49c-5716-ad68-3098-4b04e7b533d5.png)


# ネットリスト

```
$ 1 0.000005 10.20027730826997 50 5 50 5e-11
178 1008 336 1008 432 6 1 0.2 0.1618793730093081 0.05 1000000 0.02 20 0.015 0.005 1
178 528 336 528 432 6 2 0.2 0 0.05 1000000 0.02 20 0.015 0.005 0
178 672 336 672 432 6 2 0.2 0 0.05 1000000 0.02 20 0.015 0.005 0
178 848 336 848 432 6 2 0.2 0.0000033332887838781504 0.05 1000000 0.02 20 0.015 0.005 0
R 304 96 240 96 0 0 40 5 0 0 0.5
g 496 432 496 448 0 0
g 640 432 640 448 0 0
g 816 432 816 448 0 0
w 304 96 320 96 0
w 320 96 320 160 0
w 320 160 352 160 0
w 320 160 320 192 0
w 320 192 352 192 0
w 320 96 448 96 0
162 1104 336 1104 384 2 default-led 1 0 0 0.01
s 352 160 432 160 0 1 false
x 358 133 375 136 4 24 D
x 360 226 405 229 4 24 CLK
g 976 432 976 448 0 0
g 1104 384 1104 400 0 0
w 432 192 640 192 0
w 640 192 640 336 0
w 976 336 976 288 0
w 976 288 1104 288 0
w 1104 288 1104 336 0
s 352 192 432 192 0 1 true
w 656 432 656 448 0
w 656 448 784 448 0
w 784 448 784 336 0
w 784 336 816 336 0
w 736 432 736 464 0
w 944 464 944 288 0
w 944 288 976 288 0
w 880 432 880 464 0
w 880 464 944 464 0
w 880 464 736 464 0
w 672 320 672 336 0
w 720 336 720 320 0
w 720 320 896 320 0
w 896 320 896 336 0
w 1008 96 1008 336 0
w 896 320 1056 320 0
w 1056 320 1056 432 0
w 1056 432 1056 448 0
w 992 432 992 448 0
w 992 448 1056 448 0
w 432 160 480 160 0
w 480 160 496 160 0
w 496 160 496 336 0
w 560 432 560 448 0
w 560 448 656 448 0
w 576 336 576 320 0
w 576 320 672 320 0
w 528 336 528 96 0
w 448 96 528 96 0
w 528 96 560 96 0
w 848 336 848 96 0
w 560 96 848 96 0
w 848 96 1008 96 0
w 688 432 688 464 0
w 688 464 512 464 0
w 512 432 512 464 0
w 704 432 704 480 0
w 704 480 832 480 0
w 832 432 832 480 0
w 832 480 1056 480 0
w 1056 480 1056 448 0

```

以上。
また)M5NanoC6、WebServerのサンプルを改造して接点情報を表示。(3.1.1)



x 過去ログを見よ!!
x 3.1.1
x なぜか、なんかいか「ツール」-「シリアルモニター」を開くと成功する(windows系)
　リ、コンパイルするとリセットが掛る(ubuntu系)

目的
最小改造でG1の状態をWEBに表示




SSIDとパスワードは、修正する事


```c++

const char *ssid = "ご自宅のSSID";
const char *password = "ご自宅のパスワード";

```


ローカルドメインを使用する


```

http://esp32.local/

```


![image_original (25).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/fd4b6883-eeca-5a5d-71f6-1e17fa30a504.jpeg)


![Screenshot from 2025-01-10 04-22-40_original.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/1dba04b6-16bf-2170-64f0-28c121e2e080.jpeg)



![Screenshot from 2025-01-10 04-23-06_original.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/7da3651c-ee40-40e8-6e70-804272e8cdce.jpeg)



![Screenshot from 2025-01-10 04-21-29_original.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/c5d8598b-4bda-ebc8-281c-4c1a458fd956.jpeg)





プログラム



```c++




//WebServer_sw1_M5NanoC6_1


//ヘッダー
#include <WiFi.h>
#include <NetworkClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>


//定義
const char *ssid = "ご自宅のSSID";
const char *password = "ご自宅のパスワード";

WebServer server(80);


void handleRoot() {

  //↓処理を開始

  static uint32_t msg_count = 0;

  //                       0123456789
  static char data[256] = "G1 = _ #__";

  data[5] = '0' + digitalRead(1);

  data[8] = '0' + (msg_count % 100)/10;
  data[9] = '0' + (msg_count % 100)%10;

  msg_count++;

  //↑処理が終了

  //転送
  server.send(200, "text/plain", data);

}


void handleNotFound() {

  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i = 0; i < server.args(); i++) {
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }
  server.send(404, "text/plain", message);

}


//初期化
void setup(void) {

  pinMode(1, INPUT); //GPIO init

  //WiFiの初期化
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  //シリアルの初期化
  Serial.begin(9600);
  Serial.println("");
  //シリアルの待ちが0.5*9
  //delay(3000);//決め打ちなので、おかしかったら調整してね! wifiの待ち0.5*6
  for (int i = 0; i < (9 + 6); i++) {
    delay(500); //接続待ち
    Serial.print(".");
  }//for

  //ローカルドメインネームサーバーの設定
  MDNS.begin("esp32"); delay(2000);//決め打ちなので、おかしかったら調整してね!

  //接続情報の表示
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
  Serial.println("MDNS responder started");

  //割り込みに登録↓ start
  server.on("/", handleRoot);

  server.on("/inline", []() {
    server.send(200, "text/plain", "this works as well");
  });

  server.onNotFound(handleNotFound);
  //割り込みに登録↑ end

  server.begin(); //webサービスの初期化
  Serial.println("HTTP server started");

}//setup


//メインループ
void loop(void) {
  server.handleClient();
  delay(2);  //allow the cpu to switch to other tasks
}//loop






```
circuitjsの作法 その27

# 概要
circuitjsを調査してみた。
練習問題やってみた。

# 練習問題
リレー回路で、RSTフリップフロップ回路を作れ。

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/a2cbdbd1-fb89-185c-7962-bf39bd1372f7.png)


# ネットリスト

```
$ 1 0.000005 10.20027730826997 50 5 50 5e-11
178 448 336 448 432 6 1 0.2 0 0.05 1000000 0.02 20 0.015 0.005 0
178 560 336 560 432 6 1 0.2 0 0.05 1000000 0.02 20 0.015 0.005 0
178 672 336 672 432 6 2 0.2 0.000004999899502020282 0.05 1000000 0.02 20 0.015 0.005 0
178 848 336 848 432 6 2 0.2 0.000004999899502020282 0.05 1000000 0.02 20 0.015 0.005 0
R 304 96 240 96 0 0 40 5 0 0 0.5
g 416 432 416 448 0 0
g 528 432 528 448 0 0
g 640 432 640 448 0 0
g 816 432 816 448 0 0
w 304 96 320 96 0
w 320 96 320 160 0
w 320 160 352 160 0
w 320 160 320 192 0
w 320 192 352 192 0
w 432 192 432 240 0
w 432 240 416 240 0
w 416 240 416 336 0
w 528 336 528 160 0
w 432 160 528 160 0
w 448 336 448 96 0
w 320 96 448 96 0
w 560 336 560 96 0
w 448 96 560 96 0
w 672 336 672 304 0
w 672 304 736 304 0
w 576 512 752 512 0
w 640 272 768 272 0
w 640 272 640 336 0
w 752 512 832 512 0
w 736 304 816 304 0
w 816 304 816 336 0
w 768 272 848 272 0
w 848 272 848 336 0
g 704 432 704 448 0 0
g 880 432 880 448 0 0
162 720 128 720 176 2 default-led 1 0 0 0.01
162 896 128 896 176 2 default-led 1 0 0 0.01
w 720 176 720 336 0
w 896 176 896 336 0
w 560 96 720 96 0
w 720 96 720 128 0
w 720 96 896 96 0
w 896 96 896 128 0
w 544 512 576 512 0
w 432 480 464 480 0
w 464 480 656 480 0
w 656 480 688 480 0
w 688 480 688 432 0
w 832 512 864 512 0
w 864 512 864 432 0
s 352 160 432 160 0 1 false
s 352 192 432 192 0 1 false
178 256 336 256 432 6 2 0.2 0.24999999999997224 0.05 1000000 0.02 20 0.015 0.005 1
g 224 432 224 448 0 0
w 288 192 320 192 0
w 224 192 208 192 0
w 208 192 208 224 0
w 208 224 224 224 0
w 224 224 224 336 0
w 240 432 240 480 0
w 432 480 240 480 0
w 320 512 544 512 0
w 288 432 288 512 0
w 288 512 320 512 0
w 544 432 544 464 0
w 544 464 384 464 0
w 304 336 352 336 0
w 352 336 352 464 0
w 352 464 384 464 0
w 432 432 384 432 0
w 384 432 384 320 0
w 256 336 256 320 0
w 256 320 384 320 0
s 224 192 288 192 0 0 false
x 253 155 268 158 4 24 T
x 358 133 374 136 4 24 R
x 360 226 375 229 4 24 S

```

以上。
PyPI にパッケージがアップロードできなくなったときの対処法 (InvalidDistribution: Make sure the distribution is using a supported Metadata-Version: 1.0, ..., 2.3.)

Python パッケージをビルドしていざ PyPI にアップロードしようとしたら以下のエラーにアップロードを阻まれて困った人向けです。`twine` をアップグレードしても直りません。結論からいうと `pip install --upgrade pkginfo` で直りました (私は)。

```
$ python -m twine upload dist/*
Uploading distributions to https://upload.pypi.org/legacy/
ERROR    InvalidDistribution: Make sure the distribution is using a supported
         Metadata-Version: 1.0, 1.1, 1.2, 2.0, 2.1, 2.2, 2.3.
```
上記のエラーになっていたとき、`twine` と `pkginfo` のバージョンが以下でした。
```
$ pip list | grep -E "twine|pkginfo"
pkginfo                                 1.11.2
twine                                   6.0.1
```
`pkginfo` を `pip install --upgrade pkginfo` でアップグレードして (以下の状態)、ビルド＆アップロードをやり直したらエラーは出なくなりアップロードできました。
```
$ pip list | grep -E "twine|pkginfo"
pkginfo                                 1.12.0
twine                                   6.0.1
```

### 参考文献
以下に同じ状況の人がたくさんいました。7 コメント目の umarbutler さんが `pkginfo` をアップグレードすると解決すると教えてくださっていました。

https://github.com/pypi/warehouse/issues/15611

### 経緯
久しぶりに新しいパッケージを PyPI にアップロードしようと以下を実行したところ、記事タイトルのエラーに阻まれました。
```
pip install --upgrade build  # build パッケージのアップグレード
python -m build  # ビルド
pip install --upgrade twine  # twine (PyPI にアップロードするツール) のアップグレード
python -m twine upload dist/*  # アップロード
```
`pyproject.toml` のフィールドに過不足があるのか調べたり[^1]、変なバージョン名を付けたのが悪かったのか調べたり[^2]しましたが、以下のチェックはエラーを吐き続けました。

[^1]: ビルド＆アップロードに成功した版の `pyproject.toml` は[こちら](https://github.com/CookieBox26/shirotsubaki/blob/1c22fd6a2f4a26730007cbadc579acdf280564c1/pyproject.toml)です。

[^2]: 本記事の趣旨と関係ありませんが[PEP 440 (Python のバージョン・スキーマ) 概説](https://zenn.dev/shinsa82/articles/pep-440-overview)が参考になりました。

```
twine check dist/*
```

が、参考文献をみつけて解決しました。
開発者向けTwitterデータAPI：コスト効率の高いソリューション 🚀

## はじめに

Twitterのデータエコシステムは、開発者やビジネスにとって非常に価値のある情報を提供します。しかし、Twitter公式APIは月額200ドルからと高額なため、多くの開発者がより手頃な代替手段を求めています。

## なぜTwitterデータが重要なのか？

Twitterデータを活用することで：
- リアルタイムトレンドの監視
- ユーザー行動の分析
- AI/ML学習データセットの構築
- ブランドメンションの追跡
- ソーシャル分析ツールの開発

が可能になります。

## 技術概要

以下の特徴を持つRESTful APIを開発しました：
- 1000 QPSのサポート
- 月間10億レベルのデータ処理能力
- シンプルな認証
- 包括的なドキュメント

## APIエンドポイント

### 1. ユーザー情報取得

GET /twitter/user/info
// パラメータ：
{
"userName": "string" // Twitterのスクリーンネーム
}
// レスポンス：
{
"data": {
"userName": "string",
"followers": number,
"following": number,
"description": "string",
// ... その他のユーザー詳細
}
}

### 2. ユーザーツイート取得
GET /twitter/user/last_tweets
// パラメータ：
{
"userName": "string",
"includeReplies": boolean,
"cursor": "string"
}
// レスポンス：
{
"tweets": [...],
"has_next_page": boolean,
"next_cursor": "string"
}

### 3. リプライ取得
GET /twitter/tweet/replies
// パラメータ：
{
"tweetId": "string",
"cursor": "string"
}
// レスポンス：
{
"replies": [...],
"has_next_page": boolean,
"next_cursor": "string"
}



## 実装例

javascript
const fetchUserProfile = async (username) => {
try {
const response = await fetch(
https://api.twitterapi.io/twitter/user/info?userName=${username},
{
headers: {
'X-API-Key': 'your_api_key'
}
}
);
const data = await response.json();
return data;
} catch (error) {
console.error('エラー:', error);
}
};

## 主な特徴

1. **パフォーマンス**
   - 1000 QPS
   - 高可用性
   - 低レイテンシー

2. **開発者体験**
   - RESTfulエンドポイント
   - JSONレスポンス
   - シンプルな認証
   - 明確なドキュメント

3. **コスト効率**
   - 従量課金制
   - 最低利用額なし
   - 公式APIより96%お得

## ユースケース

- ソーシャル分析ツール
- 市場調査アプリケーション
- 感情分析
- コンテンツレコメンデーション
- コミュニティ管理ツール

## 始め方

1. APIキーの取得
2. ドキュメントの確認
3. 開発開始

## 技術ドキュメント

完全なOpenAPIドキュメントはこちら：
https://twitterapi.io/docs

## ご質問は？

コメント欄でお気軽にご質問ください！

---

*タグ: Twitter, API, 開発, データ分析*
matplitlibでグラフが真っ白になって何も表示されないときの対処法

# 課題
```plt.bar()```で領域は正しく表示されているのに、中身が表示されなくて困った。

```python
import matplotlib.pyplot as plt

A = [10000, 11000, 12000]
B = [2.0, 3.0, 1.0]
plt.bar(A, B)
plt.show()
```

![Gg3Zw7GbcAAfqbD.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2878555/f8aaf680-f01f-ac74-f85b-ad3b422681e6.png)

# 原因
X軸のスケールに対してバーの横幅(width)が小さすぎることが原因。

[公式ドキュメント](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.bar.html) に
> width : float or array-like, default: 0.8

と書いてある通り、バーの横幅は（指定しない限り）0.8固定であり、勝手にスケーリングしてくれるわけではない。

# 解決策
適当に横幅を指定してやればよい
```python
import matplotlib.pyplot as plt

A = [10000, 11000, 12000]
B = [2.0, 3.0, 1.0]
plt.bar(A, B,width=300)
plt.show()
```

![スクリーンショット 2025-01-10 015341.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2878555/17e9d626-6590-21e3-a6db-3377818811fe.png)
[メモ] Scala の OpenEnum を使った

## 使い方

以下のように定義できる。


```scala
sealed trait Fruit

object Fruit extends AEnum[Fruit] {
  case object Apple extends Fruit
  case object Banana extends Fruit
  case object Orange extends Fruit

  implicit lazy val jsfmt: JsFormat[OpenEnum[Fruit]] = JsFormat.enum(enumNames)
  implicit lazy val schema: Schema[OpenEnum[Fruit]] = Schema.enum(enumNames).named("Fruit")
  implicit lazy val arbitrary = Deriving[Arbitrary[Fruit]]
  implicit lazy val openEnumArbitrary: Arbitrary[OpenEnum[Fruit]] = Deriving[Arbitrary[OpenEnum[Fruit]]]

  def enumNames: EnumNames[OpenEnum[Fruit]] = OpenEnum.enumNames(source)
  override def namedValues: List[Enum.NamedValue[Fruit]] = Enum.values[Fruit]
}
```

`OpenEnum[Fruite]]`型のオブジェクトは以下のように初期化することができる。(地味に詰まった。)

```scala
val apple: OpenEnum[Fruit] = OpenEnum(Fruit.Apple)
```

また、enumの名前をStringで取り出す方法は以下。

```scala
Fruit.Apple.toString // "Apple"
```

## Ref.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534442/f498f45c-7d92-bbf4-e039-8b5d67689add.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534442/7b7bcd54-eba5-608e-f18f-584aae354fbb.png)
ECSからCloudWatch Logsに出力されるスタックトレースが分割されるので対策した話

## 発生した問題

ECS上のSpringアプリのログ監視を導入した際に、CloudWatch Logsに出力されているスタックトレース内のキーワードも対象に抽出されるため、ログレベルで正しく監視できなかった。

## 対策方針

アプリでの例外ハンドリングにおいて、エラーログとスタックトレースをセットで出力していたので、これを1つのログとしてまとめる

## 対策前後のログイメージ

### 対策前：スタックトレースが1行単位でログが出力されている

| timestamp | message |
| --- | --- |
| 2025-01-07T11:10:52.550+09:00 | 2025-01-07 11:10:52.550 INFO XXXXXXX Hoge |
| 2025-01-07T11:10:52.551+09:00 | 2025-01-07 11:10:52.551 ERROR XXXXXXX Hogehoge |
| 2025-01-07T11:10:52.551+09:00 | java.lang.HogeException: xxx ERROR xxx... |
| 2025-01-07T11:10:52.551+09:00 |   at com.example.StackTraceTest.... |
| 2025-01-07T11:10:52.551+09:00 |   at com.example.StackTraceTest.... |
| 2025-01-07T11:10:52.551+09:00 |   at com.example.StackTraceTest.... |
| 2025-01-07T11:10:52.551+09:00 |   at com.example.StackTraceTest.... |
| 2025-01-07T11:10:52.551+09:00 |   ... 3 more |

### 対策後：スタックトレースがエラーログとセットで出力されている

| timestamp | message |
| --- | --- |
| 2025-01-07T11:10:52.550+09:00 | 2025-01-07 11:10:52.550 INFO XXXXXXX Hoge |
| 2025-01-07T11:10:52.551+09:00 | 2025-01-07 11:10:52.551 ERROR XXXXXXX Hogehoge<br>   java.lang.HogeException: xxx ERROR xxx...<br>     at com.example.StackTraceTest....<br>     at com.example.StackTraceTest....<br>     at com.example.StackTraceTest....<br>     at com.example.StackTraceTest....<br>     ... 3 more |

## 対策内容

ECSのタスク定義のログ収集の設定で`awslogs-multiline-pattern`の項目を追加します。
項目値には、1行となるログを識別するための表現を指定します。

以下の例だとPrefixが日付になっていた場合を表します（`2025-01-07 11:10:52.550`がヒット）
ログのフォーマットを調整している場合は、ここをプロジェクトに合った内容に置き換える必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284278/153fbc8f-89fe-def8-451d-91e28652802e.png)

## 参考サイト

- [Amazon ECS ログを CloudWatch に送信する](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/using_awslogs.html#enable_awslogs)
Docker Desktopがマルウェア扱いされてしまった時の対処

# はじめに
2025年1月9日の朝パソコンを開いたらDocker Desktopがマルウェア扱いされ、警告文が出るという事象が起きました。
身に覚えもなくびっくりしたのですが、SNSを見るとどうやら同じ現象が起きている人が多数いるようでした。
今回私が行った対処法について備忘録も兼ねて書いておきたいと思います。
<br>

なお、私のPCで出ていたメッセージは以下の内容です
```
com.docker.vmnetdを開くとコンピューターが破損します。ゴミ箱に入れる必要があります
```
※注意点：こちらの記事ではmacOS前提で書かせていただきます。


# 大まかな流れ
1. 古いDocker Desktopをゴミ箱に入れて、ゴミ箱を空にする
2. 公式のWorkaroundの`[Workaround linked] Docker Desktop for Mac unable to start`の内容を実施
3. Docker Desktopの最新版をインストール

私の場合、この流れで解決できました！

# 1. 古いDocker Desktopをゴミ箱に入れて、ゴミ箱を空にする
まず、古いDocker Desktopを ゴミ箱に入れ、ゴミ箱を空にする 必要があります。
補足させていただくと、私の場合、公式が公開してくれているWorkaroundの内容を実施するだけでは不十分で、
こちらの作業をしてからこれ以降の作業をしたところ解決できました。
こちらについては環境によって変わるかもしれません、、、

# 2.Workaroundの内容を実行する
公式が公開しているWorkaroundに沿って手順を実行していきます！

①起動しているDocker Desktopを終了します

②macのアクティビティモニタを開いてDockerプロセスが動いていないことを確認します

③[Docker Systems Status Page](https://www.dockerstatus.com/)の`[Workaround linked] Docker Desktop for Mac unable to start`の部分のコマンドを実行していく
![スクリーンショット 2025-01-10 0.36.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3772013/de8703a9-29b6-156d-f5c7-7df5250cf32b.png)

画像内の`2.Run the following commands:`の部分のコマンドですね。

# 3. Docker Desktopの最新版をインストール
WorkaroundではDocker Desktopを再起動ができなかったので最新版をインストールしました！

### 注意点
- ゴミ箱を空にしていること　

- 『`com.docker.vmnetd` 』 を開きますか？」または『`com.docker.socket` を開きますか？』といったダイアログが出た場合はキャンセルを選択すること

もし最新のDocker Desktopをインストールしても失敗してしまったら、
1.今回の作業でダウンロードした Docker.dmgファイルをすべて削除
2.ゴミ箱を空にする
3.[Workaround]((https://www.dockerstatus.com/))の最初から手順をやり直す
こちらの手順でもう一度試してみてください。

私はこれで少しはまりました、、、
理由はまだよくわかっていません、、、
こちらについてもSNSを見ていると、その人の環境によるのかなと思います。

# 最後に
今回は朝から突然Docker Desktopがマルウェア扱いされてしまい、びっくりしましたが、同じ現象に遭遇している方が多数いるようで、自分だけでないんだと少し安心しました。
同じ現象が現れてしまった方のお役に立てられたら幸いです！

急いで書いたので誤りがあるかもしれませんがその際はコメントなどでお知らせいただけると嬉しいです！
それでは！
なぜStringの値を変更しても他の変数に影響しないのか？

## 1. はじめに
JavaのString型において、ある変数を別の変数に代入した後、元の変数の値を変更しても影響しないのはなぜでしょうか？
この記事では、Stringが不変オブジェクトとして設計されている理由と、代入時の挙動をわかりやすく解説します。
```java:Sample.java
public class Sample {
    public static void main(String[] args) {
        String st1 = "Hello!";
        String st2 = st1;

        st1 = "Java";

        System.out.println(st1); // Java
        System.out.println(st2); // Hello! ← 参照型なのに影響されないのはなぜ？
    }
}
```


## 2. 結論
冒頭でもさらっと記載しましたが、 
JavaのStringは不変オブジェクト（Immutable Object）として設計されているため、ある変数を別の変数に代入した後に値を変更しても、他の変数に影響を与えることはありません。
  
ピンときませんね。
順を追って説明します。

## 3. 不変オブジェクトとは
不変オブジェクトには以下のような特性があります。
* 一度作成された文字列は変更できない。
* 新しい値が代入されると、新しいオブジェクトが作成され、既存のオブジェクトには影響がない。

### 3.1. 具体例で理解するStringの代入と参照
* 参照の挙動
```java:Sample.java
public class Sample {
    public static void main(String[] args) {
        String st1 = "Hello!";
        String st2 = st1;        // -- ポイント1

        st1 = "Java";            // -- ポイント2

        System.out.println(st1); // Java
        System.out.println(st2); // Hello!
    }
}
```

- メモリ上の挙動
    - ポイント1
    初期状態：`st1`と`st2`は同じ文字列（"Hello!"）を参照している。
    - ポイント2
    `st1 = "Java";`を実行した際に、新しい文字列オブジェクト（"Java"）が作成される。
<br>

- イメージ図
![スクリーンショット 2025-01-10 0.24.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3981976/808a21db-8d2b-0a03-3fc1-537659e16883.png)
![スクリーンショット 2025-01-10 0.32.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3981976/c38fbd50-9302-9b13-3f8e-6a56e2027c50.png)
![スクリーンショット 2025-01-10 0.40.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3981976/e2fd0c31-12f9-d173-dd3b-caf45eef0666.png)

## 4. なぜStringは不変オブジェクトなのか？
- セキュリティの理由
    - パスワードやURLなどの重要データを変更できないことで、安全性が向上。
- スレッドセーフの理由
    - マルチスレッド環境で文字列が共有されても、状態を変更される心配がない。
- パフォーマンスの理由
    - 文字列プールによるメモリ効率の向上。

## 5. Stringと他の可変オブジェクトの違い
- 可変オブジェクトの例
    - `ArrayList` や `StringBuilder` は可変（Mutable）オブジェクト。
    - これらでは、値を変更することで同じ参照に影響を与える。
* 参照の挙動
```java:SampleArrayList.java
public class SampleArrayList {
    public static void main(String[] args) {
        ArrayList<String> list1 = new ArrayList<>();
        list1.add("Hello");
        
        ArrayList<String> list2 = list1;
        list1.add("World");
        
        System.out.println(list1); // [Hello, World]
        System.out.println(list2); // [Hello, World] ← list1を編集したがlist2にも影響あり
    }
}
```
- 解説
    - `list1`と`list2`は同じ`ArrayList`オブジェクトを参照しています。
    - `list1.add("World")`でリストに新しい要素を追加すると、`list2`にもその変更が反映されます。

```java:SampleStringBuilder.java
public class SampleStringBuilder {
    public static void main(String[] args) {
        StringBuilder sb1 = new StringBuilder("Hello");
        StringBuilder sb2 = sb1;
        sb1.append(" World");
        
        System.out.println(sb1); // "Hello World"
        System.out.println(sb2); // "Hello World" ← sb1を編集したがsb2にも影響あり
    }
}
```
- 解説
    - `sb1`と`sb2`は同じ`StringBuilder`オブジェクトを参照しています。
    - `sb1.append()`で値を変更すると、`sb2`にもその変更が反映されます。

## 6. まとめ
- `String`は不変オブジェクトであり、新しい値の代入は新しいオブジェクトを作成する。
- この特性により、安全で効率的な文字列操作が可能になる。
- 一方、`ArrayList`や`StringBuilder`は可変なので、内容を直接変更することで同じオブジェクトに影響を与える。
FMRを開発していた頃のはなし①

# Introduction ～ FMRの終焉
あれは確か1993年頃のことだったと思う。
当時リーダーだったM笠さんに呼ばれ、1年先輩のS藤さん、1年後輩のK間君と共にプロジェクトの中止が告げられた。この数週間、ぼくたち3人はLSIの設計に取り組んでいた。現在だと「チップセット」と呼ばれるLSIに相当するASICを、K間君はメモリーコントローラ、ぼくはI/Oコントローラ、S藤さんがとりまとめをするという分担だった。CPUはIntelの、まだ発表されていなかったPentiumPro Processor、通称P6。ぼくもK間君もLSIの設計は未経験で、とりあえずCPUのデータシートを読み込んで、仕様の検討を始めた段階だった。
このデータシート、通常NDAで入手できる開発資料は黄色い表紙なので「イエローブック」と呼ばれていたが、P6のアーリーカスタマー(先行顧客)だったので、オレンジ色の表紙「オレンジブック」という、より機密性の高いドキュメントだった。チップセットを開発するにあたっては、主にCPUバスのプロトコル、その後も長い間使われるようになる「P6バス」の動作を理解する必要があった。この時、じっくり勉強したおかげで、後々の設計開発はだいぶ楽だった。
まずは、CPUバスのアドレスをデコードして、I/OアドレスだったらぼくのLSIで受けて、ビジーだったらバッファして、I/Oデバイス側の要求も処理するのにバッファが何段必要で…などと全体の構成を考えながら、タイミングチャートに書いてみて、ということをやっていた。まだ当時はVerilogやVHDLといったハードウェア記述言語での開発が(少なくとも部門では)一般的ではなく、唯一、前年にS木さんがVerilogで大規模な統合チップセットを開発した程度。まだ、タイミングチャートを書いて、「VIEWCAD」という社内CADを使ってロジックをゲート単位で並べて設計するのが主流だった。
結局、VIEWCADを持ち出して詳細設計に取り掛かる前に、プロジェクトは中断してしまった。ぼくの考えていた最強のI/Oコントローラは、I/Oデバイスに対するCPUのリクエストをバンバン受けることができ、I/Oデバイスからの応答も遅滞なく返すことができる、最強の性能を目論んでいたので、バッファのお化けみたいな構成で、いったい何百万ゲート規模になったのか、いまとなっては判らないまま。ぼくの担当したLSI設計は生涯、後にも先にもこれだけである。
# FMRの開発
1990年に新卒で入社して以来、富士通のビジネス向けパソコン「FMR」シリーズの設計開発に携わっていた。
そして、このプロジェクトの打ち切りが、富士通におけるFMR開発の終焉であった。
その後、ぼくらはIBM-PC/AT互換アーキテクチャである「FMV」シリーズの設計開発にシフトしていく。ぼくもこの直後にM笠さんのチームでIntel PentiumProを採用したパソコン、FMV-6150の開発を担当することになる。
この記事シリーズでは、ほとんど語られることのなかった、パソコンFMRシリーズの開発を、一担当者の視点で記録しておきたい。FMRの立ち上げから関わったわけではないし、現場の新人エンジニアとして途中から関わっているだけなので、いわゆる「開発秘話」のような話や、ビジネス戦略のような話はできないが、当時の現場の様子を交えながら、失われる前にできるだけ記憶を形にしておきたいと思う。