diff --git a/README.md b/README.md index 22e33a6..50ada4f 100755 --- a/README.md +++ b/README.md @@ -6,10 +6,11 @@ Difference Equation Analyzer 是一個用來設計、檢視與驗證離散時間 - 設計 Lowpass、Highpass、Bandpass、Notch、1P1Z、2P1Z、2P2Z、PID、SOGI 濾波器。 - 比較浮點係數與 fixed-point 係數的頻率響應。 -- 手動微調 b/a 係數、a1/a2 組合、Q 格式位元數與 fixed-point 整數係數。 +- 手動微調 b/a 係數、a1/a2 組合(支援 δ/r 參數微調)、Q 格式位元數與 fixed-point 整數係數。 +- 透過瀏覽器 Web Serial API 直接將 fixed-point 係數命令送到 MCU(需透過 HTTPS 連線)。 - 上傳 CSV 時域資料,套用目前差分方程並繪製輸入/輸出波形。 - 匯出包含濾波輸出欄位的 CSV。 -- 透過 USB serial 將 fixed-point 係數命令送到 MCU。 +- 支援深色模式 (Dark Mode) 與響應式 UI。 ## 專案結構 @@ -17,14 +18,15 @@ Difference Equation Analyzer 是一個用來設計、檢視與驗證離散時間 dea_api.py FastAPI 入口與 HTTP route dea/ 後端核心模組 bode.py 頻率響應計算 - config.py 限制值與 MCU serial 設定 + config.py 限制值與伺服器端 serial 設定 csv_processing.py CSV 讀取、驗證、濾波與匯出 filter_design.py 濾波器設計 - mcu.py MCU serial port 列表與命令寫入 + mcu.py MCU serial port 列表與命令寫入 (後端備用支援) schemas.py Pydantic request schema security.py LAN 存取限制與安全標頭 validation.py 係數、頻率與有限值驗證 src/ Vue 3 前端原始碼 +certs/ HTTPS 自簽憑證與產生腳本 static/ FastAPI 對外提供的靜態 UI static/build/ Vite build 輸出 tests/ unittest 測試 @@ -42,86 +44,66 @@ python3 -m venv .venv .venv/bin/pip install -r requirements.txt ``` -安裝前端依賴: +安裝前端依賴並建置: ```bash npm install -``` - -建置前端: - -```bash npm run build ``` -Vite 會輸出到: +## HTTPS 設定 -```text -static/build/app.js -static/build/app.css -``` +為了支援 Web Serial API(瀏覽器直接存取本機連接埠),後端必須透過 HTTPS 連線提供服務。 + +1. **產生自簽憑證**: + ```bash + bash certs/generate_cert.sh + ``` + 這會在 `certs/` 目錄下產生 `key.pem` 與 `cert.pem`。 + +2. **信任憑證**: + 由於是自簽憑證,瀏覽器會顯示安全性警告。請在「進階」中選擇「繼續前往」或將其加入信任清單。 ## 執行 -本機開發可直接啟動 FastAPI: +### 手動啟動 (HTTPS) ```bash -.venv/bin/uvicorn dea_api:app --host 127.0.0.1 --port 8000 +.venv/bin/uvicorn dea_api:app --host 0.0.0.0 --port 8000 --ssl-keyfile certs/key.pem --ssl-certfile certs/cert.pem ``` -瀏覽器開啟: - -```text -http://localhost:8000/ui/ -``` - -部署在區網主機時可用: - -```text -http://:8000/ui/ -``` +瀏覽器開啟:`https://:8000/ui/` 或本機 `https://localhost:8000/ui/` 根路徑 `/` 會自動導向 `/ui/`。 -## API +### 使用 systemd -目前主要 API: +1. 檢查 `dea.service` 中的路徑是否正確(需確認 `ExecStart` 已加上 `--ssl-keyfile` 等參數)。 +2. 啟用並啟動服務: + ```bash + sudo cp dea.service /etc/systemd/system/ + sudo systemctl daemon-reload + sudo systemctl enable dea.service + sudo systemctl start dea.service + ``` -- `POST /api/design`:依濾波器參數產生 b/a 係數。 -- `POST /api/bode`:計算單組 b/a 係數的頻率響應。 -- `POST /api/bode/compare`:一次計算浮點與 fixed-point 兩組頻率響應。 -- `POST /api/filter`:上傳 CSV 並回傳圖表用的濾波結果。 -- `POST /api/filter/download`:上傳 CSV 並下載包含 `<欄位>_filtered` 的結果 CSV。 -- `GET /api/mcu/ports`:列出可用 serial ports。 -- `POST /api/mcu/write`:送出 MCU 命令。 - -MCU 寫入命令格式固定為: - -```text -bodeplot=b0,b1,b2,a1,a2 +查看狀態與 Log: +```bash +sudo systemctl status dea.service +sudo journalctl -u dea.service -f ``` -前端會使用目前 fixed-point 係數自動組出此命令。 +## MCU 連接埠 (Web Serial) -## CSV 上傳 +本工具優先使用瀏覽器的 Web Serial API 直接與 MCU 通訊,無需伺服器端具備 serial 權限。 -限制與行為: +- **連線**:點擊 UI 上的「選擇連接埠並連線」,在瀏覽器彈出視窗中選擇正確的 MCU 裝置。 +- **寫值**:點擊「寫值到 MCU」,系統會將目前定點數係數格式化為 `bodeplot=b0,b1,b2,a1,a2` 並送出。 +- **限制**:需使用 Chrome 或 Edge 瀏覽器,且必須透過 HTTPS (或 localhost) 連線。 -- CSV 檔案大小上限為 32 MB。 -- CSV 必須有標題列與至少一筆資料。 -- 可選擇要濾波的訊號欄位;若第一欄像 `time`、`t`、`sec`、`x`、`index`,前端會預設選第二欄。 -- 圖表回應最多回傳 5000 點,避免大型 CSV 讓前端過重。 -- 下載 CSV 會保留完整資料,不套用圖表降採樣。 +## 後端 MCU Serial 設定 (備用) -專案根目錄的 `chirp_signal.csv` 是上傳測試範例,約 19 MB、500001 行,欄位為: - -```text -time,value -``` - -## MCU Serial 設定 - -MCU serial 預設值在 `dea/config.py`,也可用環境變數覆蓋: +若無法使用 Web Serial,後端仍保留透過伺服器端實體連線寫入的 API。預設值在 `dea/config.py`,也可用環境變數覆蓋: ```bash MCU_SERIAL_PORT=/dev/ttyUSB2 @@ -137,42 +119,47 @@ Environment="MCU_BAUD_RATE=115200" Environment="MCU_WRITE_TIMEOUT=1.0" ``` -更新 service 後重新載入: +## API -```bash -sudo systemctl daemon-reload -sudo systemctl restart dea.service +目前主要 API: + +- `POST /api/design`:依濾波器參數產生 b/a 係數。 +- `POST /api/bode`:計算單組 b/a 係數的頻率響應。 +- `POST /api/bode/compare`:一次計算浮點與 fixed-point 兩組頻率響應。 +- `POST /api/filter`:上傳 CSV 並回傳圖表用的濾波結果。 +- `POST /api/filter/download`:上傳 CSV 並下載包含 `<欄位>_filtered` 的結果 CSV。 +- `GET /api/mcu/ports`:列出伺服器端可用 serial ports (後端模式)。 +- `POST /api/mcu/write`:送出伺服器端 MCU 命令 (後端模式)。 + +MCU 寫入命令格式固定為: + +```text +bodeplot=b0,b1,b2,a1,a2 ``` -注意:MCU 功能需要 `pyserial>=3.5`,已列在 `requirements.txt`。 +## a1/a2 微調 (δ/r 參數) -## systemd +針對二階濾波器 (2P1Z/2P2Z),UI 提供了進階的步階微調功能: -查看服務狀態: +- **δ (Delta)**:控制極點的頻率/阻尼平衡點。 +- **r**:控制極點的半徑/頻寬。 +- 公式:`a1 = -a0 - r + δ`,`a2 = r + δ` (定點數模式) 或 `a1 = -1 - r + δ`,`a2 = r + δ` (浮點數模式)。 +- 支援透過 Shift + 滑鼠滾輪或觸控拖曳進行高精度微調。 -```bash -sudo systemctl status dea.service -``` +## CSV 上傳 -啟動、停止、重啟: +限制與行為: -```bash -sudo systemctl start dea.service -sudo systemctl stop dea.service -sudo systemctl restart dea.service -``` +- CSV 檔案大小上限為 32 MB。 +- CSV 必須有標題列與至少一筆資料。 +- 可選擇要濾波的訊號欄位;若第一欄像 `time`、`t`、`sec`、`x`、`index`,前端會預設選第二欄。 +- 圖表回應最多回傳 5000 點,避免大型 CSV 讓前端過重。 +- 下載 CSV 會保留完整資料,不套用圖表降採樣。 -查看 log: +專案根目錄的 `chirp_signal.csv` 是上傳測試範例,約 19 MB、500001 行,欄位為: -```bash -sudo journalctl -u dea.service -f -``` - -若修改 `/etc/systemd/system/dea.service`: - -```bash -sudo systemctl daemon-reload -sudo systemctl restart dea.service +```text +time,value ``` ## 測試與檢查 @@ -208,38 +195,25 @@ sudo systemctl restart dea.service - API middleware 只允許 private / loopback IP 存取。 - 回應會加上 CSP、`Referrer-Policy`、`X-Content-Type-Options: nosniff`、`X-Frame-Options: DENY`。 -- CSP 的 script 來源允許本機與 Plotly CDN: - -```text -'self' https://cdn.plot.ly -``` - +- CSP 的 script 來源允許本機與 Plotly CDN。 - 係數數量上限為 64。 - CSV 上限為 32 MB。 - 頻率、係數、`a[0]` 與 CSV 訊號欄位都有後端驗證。 ## 常見問題 +### 為什麼無法連線連接埠? +1. 確認是否使用 HTTPS 連線。 +2. 確認瀏覽器是否支援 Web Serial API (推薦使用 Chrome/Edge)。 +3. 檢查 USB 傳輸線是否連接正確。 + ### 頁面沒有更新 - 1. 瀏覽器按 `Ctrl+F5` 強制重新整理。 -2. 確認前端 build 檔存在: - -```bash -ls static/build/app.js static/build/app.css -``` - -3. 重新 build 並重啟服務: - -```bash -npm run build -sudo systemctl restart dea.service -``` +2. 執行 `npm run build` 重新建置前端。 +3. 執行 `sudo systemctl restart dea.service`。 ### systemd 顯示 `status=203/EXEC` - 通常是 `.venv` 或 `uvicorn` 路徑不正確。可重建 venv: - ```bash rm -rf .venv python3 -m venv .venv @@ -248,21 +222,7 @@ sudo systemctl restart dea.service ``` ### 找不到 pytest - 本專案不使用 pytest。請改用: - ```bash .venv/bin/python -m unittest discover -s tests ``` - -### MCU COM Port清單是空的 - -1. 確認 USB 裝置已插入。 -2. 確認執行服務的使用者有 serial port 權限。 -3. 檢查預設 port: - -```bash -echo $MCU_SERIAL_PORT -``` - -4. 可在頁面按「更新」重新讀取 serial ports。 diff --git a/certs/generate_cert.sh b/certs/generate_cert.sh old mode 100644 new mode 100755 index 472cefc..6c6e02a --- a/certs/generate_cert.sh +++ b/certs/generate_cert.sh @@ -1,27 +1,27 @@ -#!/usr/bin/env bash -# 產生自簽 SSL 憑證供 Uvicorn HTTPS 使用 -# 用法: bash certs/generate_cert.sh - -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -CERT_FILE="${SCRIPT_DIR}/cert.pem" -KEY_FILE="${SCRIPT_DIR}/key.pem" +#!/usr/bin/env bash +# 產生自簽 SSL 憑證供 Uvicorn HTTPS 使用 +# 用法: bash certs/generate_cert.sh + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +CERT_FILE="${SCRIPT_DIR}/cert.pem" +KEY_FILE="${SCRIPT_DIR}/key.pem" DAYS=3650 if [ -f "$CERT_FILE" ] && [ -f "$KEY_FILE" ]; then - echo "憑證已存在: ${CERT_FILE}, ${KEY_FILE}" - echo "如需重新產生,請先刪除後再執行。" - exit 0 -fi - -openssl req -x509 -newkey rsa:2048 -nodes \ + echo "憑證已存在: ${CERT_FILE}, ${KEY_FILE}" + echo "如需重新產生,請先刪除後再執行。" + exit 0 +fi + +openssl req -x509 -newkey rsa:2048 -nodes \ -keyout "$KEY_FILE" \ -out "$CERT_FILE" \ -days "$DAYS" \ -subj "/CN=$(hostname)" \ -addext "subjectAltName=DNS:$(hostname),IP:0.0.0.0" - -echo "已產生自簽憑證(有效 ${DAYS} 天):" -echo " cert: ${CERT_FILE}" -echo " key: ${KEY_FILE}" + +echo "已產生自簽憑證(有效 ${DAYS} 天):" +echo " cert: ${CERT_FILE}" +echo " key: ${KEY_FILE}" diff --git a/src/App.vue b/src/App.vue index 6307e81..225319d 100644 --- a/src/App.vue +++ b/src/App.vue @@ -28,20 +28,19 @@