LiDARデータをFastAPIとMongoDBで送受信する方法

## 環境
ubuntu-22.04がインストールされたVMを使用します．

WSL2でも動かすことができます．
WSL2環境を整える方法については以下の記事などを参考にしてもらえるといいと思います．

https://qiita.com/studio_meowtoon/items/e3aaef417f93afb79066


## LiDAR
LiDAR（Light Detection and Ranging、またはLaser Imaging Detection and Ranging）はレーザー光を使って物体までの距離を測定する技術です．

この記事ではslamtec社のRPLiDAR C1を使用します
また，LiDARで取得したテキストファイルを用意しといてください．この後使います．

以下の記事で導入方法・使用方法について解説しています

https://qiita.com/yamamoto_0111/items/ffde950dc5e17ea6de21

## FastAPI
PythonでWeb APIを構築するための高速でモダンなWebフレームワークです.
必要なパッケージをインストールしてください

```bash
pip install fastapi uvicorn
```
***fastapi*** と ***uvicorn***を使用します．

## MongoDB
公式パッケージを使用してインストールします．
最新のMongoDBをインストールするために，公式リポジトリを追加します．
```bash
sudo apt install software-properties-common gnupg apt-transport-https ca-certificates -y
```
次に，システムにMongoDBの公開鍵をインポートするために，curlコマンドを使用します．
```bash
curl -fsSL https://pgp.mongodb.com/server-7.0.asc |  sudo gpg -o /usr/share/keyrings/mongodb-server-7.0.gpg --dearmor
```
MongoDB 7.0のAPTリポジトリをUbuntuのソースリストファイルに追加します．次のコマンドを実行してください．
```bash
echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list
```
このコマンドは、MongoDB 7.0のAPTリポジトリを/etc/apt/sources.list.d/ディレクトリに追加します．

リポジトリを追加したら、ローカルのパッケージインデックスを再読み込みして、MongoDB 7.0リポジトリをUbuntuに認識させます．
```bash
sudo apt update
```
MongoDBをインストールするために、mongodb-orgメタパッケージをインストールします．
```bash
sudo apt install mongodb-org -y
```
インストールが完了したら、以下のコマンドでインストールされているMongoDBのバージョンを確認します．
```bash
mongod --version
```

## 実践
これで準備に必要なものはそろったとおもいます．

まずサーバー側の受信コードです
以下のコードでは，FastAPIを使用してLiDARデータをMongoDBに保存・取得し、保存されたデータをHTML形式で表示します．
また，FastAPIが起動すると，保存されているデータが初期化されるようになっています．

```python
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from pymongo import MongoClient
from fastapi.responses import HTMLResponse

# MongoDBに接続
client = MongoClient("mongodb://localhost:27017/")
db = client["lidar_database"]
collection = db["lidar_data"]

# FastAPIアプリケーションのインスタンス作成
app = FastAPI()

# LiDARデータのPydanticモデル
class LidarData(BaseModel):
    lidar_data: str

# サーバー起動時にデータベースを初期化する
@app.on_event("startup")
async def startup():
    # すべてのデータを削除 (毎回サーバー起動時にデータ削除)
    # 必要に応じて、この処理を調整してください
    collection.delete_many({})

@app.post("/upload_lidar_data/")
async def upload_lidar_data(data: LidarData):
    # データをMongoDBに保存
    lidar_data = {
        "lidar_data": data.lidar_data,
        "status": "received"
    }

    try:
        collection.insert_one(lidar_data)
        return {"message": "データが正常に受信され、保存されました"}
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"データの保存に失敗しました: {str(e)}")

@app.get("/get_lidar_data/{lidar_id}")
async def get_lidar_data(lidar_id: str):
    # MongoDBからデータを取得
    lidar_record = collection.find_one({"_id": lidar_id})
    
    if lidar_record:
        return {"lidar_data": lidar_record["lidar_data"]}
    else:
        raise HTTPException(status_code=404, detail="データが見つかりません")

@app.get("/view_all_lidar_data/", response_class=HTMLResponse)
async def view_all_lidar_data():
    # 全てのLiDARデータを取得
    lidar_records = list(collection.find())
    
    if lidar_records:
        # 改行をHTMLの<br/>タグに変換
        formatted_data = "<br/>".join([record["lidar_data"].replace("\n", "<br/>") for record in lidar_records])
        return f"<html><body>{formatted_data}</body></html>"
    else:
        raise HTTPException(status_code=404, detail="データがありません")
```

以下のコードでアプリケーションを起動できます．
```bash
 uvicorn main:app --reload
```
起動すると以下のような表示が出ます．
```bash
~$ uvicorn main:app --reload
INFO:     Will watch for changes in these directories: ['/home/masa']
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [33484] using WatchFiles
INFO:     Started server process [33487]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
```
```http://サーバーのIP:8000/view_all_lidar_data/```にアクセスすると
まだ何も送信していないので「{"detail":"データがありません"}」と表示されると思います．

次にクライアント側のコードです．
指定されたファイルからLiDARデータを読み込み、そのデータをFastAPIサーバーにPOSTリクエストで送信します

***url***: LiDARデータを送信する先のFastAPIサーバーのURL
***file_path***: 送信するLiDARデータが保存されているテキストファイルのパス
以上の二つを自身の環境に合わせて設定してください

```Python
import requests

# サーバーのURL
url = "http://ここにサーバのIP:8000/upload_lidar_data/"

# LiDARデータファイルのパス
file_path = "ここにLiDARデータ"

# ファイルを読み込んで送信
with open(file_path, "r") as file:
    data = file.read()

# POSTリクエストでデータを送信
response = requests.post(url, json={"lidar_data": data})

# レスポンスの表示
if response.status_code == 200:
    print("データの送信に成功しました")
else:
    print(f"データ送信に失敗しました。ステータスコード: {response.status_code}")

```
送信が完了すると「データの送信に成功しました」という表示があると思います．

さらに，
```http://サーバーのIP:8000/view_all_lidar_data/```にアクセスすると以下のような表示があると思います．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3519236/517726c4-9074-0334-0fe4-41930f383b9d.png)

というような感じでデータを送信して見られると思います．

また，プロンプト上で
```bash
mongosh
```
を実行するとMongoDBシェルが開きます．
```bash
use lidar_database
```
でデータベースを選択し
```bash
db.lidar_data.find().pretty()
```
を実行することで先ほどのwebで見た内容が見られます．