BunでWebSocketのデータ配信サーバーを建ててccchartにリアルタイム表示する

[Bunのページ](https://bun.sh/) https://bun.sh/ へ行くと、まず「高速な JavaScriptランタイム・パッケージマネージャー・バンドラ―」などと書かれており、次のようなベンチマークが目を引きます。

<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7103/513416bd-13c3-c3dd-1eb1-c30221cf35b6.png>

速いですね。WebSocket好きは黙っていられない速度です。まぁ、他のHTTPサーバーやsqlitのベンチも驚くべきものですが、今回は早速WebSocket使ってBunで書いてみました。

とりあえず説明は [Wiki](https://ja.wikipedia.org/wiki/Bun_(%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2)) に任せます。こんな感じ。

>Bunとは、プログラミング言語Zigを用いてゼロから構築されたJavaScriptランタイム、パッケージマネージャー、テストランナービルダーである[3]。Jarred SumnerによってNode.jsの完全互換として設計された。Node.jsやDenoはJavaScriptエンジンとしてV8を利用しているが、BunはJavaScriptCoreを採用している
>バンドル、縮小化、サーバーサイドレンダリング(SvelteKit、Nuxt.js、Vite) がサポートされている。
>ランタイムではForeign function interface (FFI)、SQLite3、TLS 1.3、DNS解決がサポートされている。 また、ファイル編集、HTTPサーバー、WebSocket、ハッシュ関数などの一般的なツールも提供されている[5]

BunのWebsocketドキュメントは下記にあります
#### Bun>Websocket
https://bun.sh/docs/api/websockets

# このチャートの動作サンプル

動作サンプルURL
https://ccchart.com/bun/ws-8116.html
動画
<img width=400 src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7103/add886c8-0e27-05e3-8b13-ccb709d209a1.gif>



# 環境と準備

環境：Ubuntu 20.04.6 LTS
* Bunインストール
* WebSocketサーバー側を作る
* ccchartで受信する

# Bunインストール
インストールガイド
https://bun.sh/docs/installation
ここでは、Ubuntuで試しますが、上記Installationには、macOS and Linux、WindowsやDockerの各ケースが書かれています。

```sh:ディレクトリ構成
.
├─server/
│   └─ws-8116.js
├─html/
│   ├─ws-8116.html
│   └─js/ccchart.js
≈
//TSL/wss用のパス
(/etc/letsencrypt/live/example.com/privkey.pem など)
├─/＜PathTo＞/privkey.pem
└─/＜PathTo＞/fullchain.pem
```
```sh:ファイル解凍用のunzipも無ければ用意しておく
sudo apt install unzip
```

```sh:Bunをインストールする
curl -fsSL https://bun.sh/install | bash
```

# WebSocketサーバー側を作る

WebSocketは、常時接続後chatなどのようにメッセージのやり取りをするものが多いですが、ここでは、コネクションが成立した「open」後にブロードキャスト関数broadCastを起動して、データを配信するだけの放送型の使い方をしています。
```js:WebSocketサーバー(データ配信用)　ws-8116.js

const server = Bun.serve({
  hostname:'＜IPやドメイン名＞',
  port: 8116, //ポートは開いて無ければ開けておく
  fetch(req, server) {
    // upgrade the request to a WebSocket
    console.log('upgrade')
    if (server.upgrade(req)) {
      return; // do not return a Response
    }
    return new Response("Upgrade failed :(", { status: 500 });
  },
  websocket: {
    open(ws) {
      // ブロードキャスト
      broadCast(ws)
    },
    message(ws, message) {},
    close(ws) {},
  },
  tls: {
    key: Bun.file('/＜PathTo＞/privkey.pem'),
    cert: Bun.file('/＜PathTo＞/fullchain.pem'),
  }
});

// info 
console.log(`Listening on ${server.hostname}:${server.port}`);

// 全クライアントへ200ms毎にブロードキャストする
function broadCast(ws){
  const INTERVAL=200
  let tid = setInterval (function(){
    let dataAry = mkData();
    ws.send(JSON.stringify(dataAry));
  }, INTERVAL);
}

// データ作成
// チャート用の時分秒と2つのランダムデータをつくる
function mkData(){
  const data = [
      ["年度"],
      ["s2"],
      ["s3"]
  ];
  let now = new Date();
  let H = now.getHours();
  let M = now.getMinutes();
  let S = now.getSeconds();
  H = (H < 10)?'0'+H:H;
  M = (M < 10)?'0'+M:M;
  S = (S < 10)?'0'+S:S;

  data[0]=H +':' + M +':' + S;
  data[1]=18 + Math.floor(Math.random(10) * 75 );
  data[2]=32 + Math.floor(Math.random(10) * 18);

  return data;
}
```
#### サーバーを起動する
```sh:
sudo bun run /server/ws-8116.js
```
bunの場所がわからなければwhich bunなどで探してね

# ccchartで受信する
ここでは、WebSocketサーバーから200ms毎に配信されてくるデータをccchartで受信し、チャート描画しています。もちろん、他のチャートライブラリでの描画も可能です。

ccchartは、私が昔作ったチャートライブラリでWebSocketの受信が手軽にできます。
https://ccchart.com/
<img width=200 src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7103/b0c5b243-5204-1379-632c-669af9e2ac79.png>

```html:HTML

<!DOCTYPE html>
<html lang="ja">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<head>
<title>ccchart bun test</title>
</head>
<body>

<!-- ccchart -->
<!--script src="https://ccchart.com/js/ccchart.js" charset="utf-8"></script-->
<script src="./js/ccchart.js" charset="utf-8"></script>

<div class="chart">
    <canvas 
        id="hoge1" 
        class="-ccchart" 
        title="bg: '#eee'" 

        style="width:100%;height:100%;display: block; background-color: rgb(238, 238, 238);">
    </canvas>
</div>

<script>

/* =============================================
 * チャート設定
 */ 
let chartCfg1 = {
    "config": {
        "title" : "BunでWebSocket",
        "subTitle" : "BunでWebSocketサーバーを作ってみた",
        "type": "line",
        "minY": 0,
        "maxY": 100,
        "lineWidth": 1,
        "xScaleSkip": 5,
        "colorSet" : ["red","orange"],
        "useMarker": "maru",
        "xLines": [
          {"val":66,"color":"rgba(0,0,0,0.7)"}
        ],
         "bg": "#fff",
         "textColors": {
            "title":"#777",
            "subTitle":"#777",
            "x":"#999",
            "y":"#999",
            "hanrei":"#777",
            "unit":"#777",
            "memo":"#666"
        },
        "shadows": {
            "hanrei" : ['#aaa', 5, 5, 5],
            "xline": ['#888', 7, 7, 5],
            "line": ['#666', 5, 5, 5],
            "bar": ['#bbb', 5, 5, 5],
            "stacked": ['#bbb', 5, -5, 5],
            "stackedarea": ['#666', 5, 5, 5],
            "bezi": ['#666', 5, 5, 5],
            "bezi2": ['#666', 5, 5, 5]
        },
        "markerWidth" : 12,
        "lineWidth" : 2,
        "bg": "#eee"
    },
    "data": [
        ["時間"],
        ["データ1"],
        ["データ2"]
    ]
};

ccchart
  .init('hoge1', chartCfg1)
  .ws('wss://ccchart.com:8116') //←ここは御自身のアドレス:ポート
  .on('message', ccchart.wscase.oneColAtATime);
        //oneColAtATimeは、WebSocketの受信パターン関数
        // 一度に1列ずつ [["2013"],[435],[600]] といった配列で届く場合用

</script>
</body>
</html>
```


### 自己紹介

ここのCTOやってます
霊園ガイドサイト
https://reien.top/



Github  	https://github.com/toshirot							
Qiita     	https://qiita.com/toshirot							
ccchat	https://ccchart.com/		  javascript チャートライブラリ					
霊園ガイド	https://reien.top/		霊園マッチングサイト					
国会図書館　高橋著作	https://ndlonline.ndl.go.jp/#!/search?searchCode=SIMPLE&lang=jp&keyword=%E9%AB%98%E6%A9%8B%E7%99%BB%E5%8F%B2%E6%9C%97