2025年10月1日 星期三

LATE​X 排版和對齊方式的進階問題

 這是關於 LATEX 排版和對齊方式的進階問題。在 LATEX 中,控制文字、數學公式和圖片等元素的對齊方式有很多技巧,具體取決於您想對齊的內容範圍

以下是 LATEX 中主要的對齊排版方式分類及常用的命令:


1. 基本文字/段落對齊 (Text/Paragraph Alignment)

這些命令用於控制整個段落或區塊的文字在頁面上的對齊方式。

對齊方式 環境/命令說明
居中對齊 (Center)將內容水平居中。 是環境,會添加垂直間距; 是命令,通常用於環境內或圖/表題。
左對齊 (Flush Left)將內容靠左對齊,右邊不對齊 (參差不齊)。
右對齊 (Flush Right)將內容靠右對齊,左邊不對齊 (參差不齊)。
兩端對齊 (Justified)這是預設行為 預設會自動調整單字間距,使文字同時靠左和靠右對齊 (除了最後一行)。

2. 跨行/多行內容對齊 (Multi-line Content Alignment)

當您需要讓多個獨立的元素(如圖形、表格或程式碼區塊)並排或以特定方式排列時,會用到這些。

對齊方式 環境/命令說明
並排對齊這是解決您先前並排作者區塊問題的關鍵。 它能創建一個小區域,讓多個 並排放置。
自動間距插入彈性的水平空間。例如: 可以將文字推向兩端。
手動間距插入固定的水平間距(例如 )。
垂直堆疊這是用於表格的基本環境,但也可以用來強制垂直堆疊並對齊區塊內容。

3. 數學公式對齊 (Mathematical Alignment)

使用 amsmath 宏包可以提供強大的公式對齊功能。

對齊方式 環境/命令說明
多行居中多個獨立的公式分別居中對齊。
單一對齊點最常用的環境。使用 符號作為對齊點(通常是等號 或不等號 等)。
多組對齊點允許在一行內有兩組以上的對齊點,用於並排的多個方程式組。
單一長公式將一個很長的公式拆分成多行,並且所有行都對齊在第一個等號。

4. 表格與欄位對齊 (Table and Column Alignment)

使用 tabular 環境時,可以為表格的每一欄指定對齊方式。

欄位符號說明範例
欄位內容左對齊 (Left)
欄位內容居中對齊 (Center)
欄位內容右對齊 (Right)
指定寬度後兩端對齊 (Paragraph)
欄位之間插入指定文字或間距

總結與建議

對於您之前遇到的作者區塊並排問題,最標準且實用的解決方案是使用 minipage 環境。它能將您的兩個作者區塊視為獨立的「小頁面」,然後強制它們並排顯示在一行中,如下所示:

程式碼片段
\begin{center}
    \begin{minipage}[t]{0.48\textwidth} \centering % 第一個區塊
        ... 作者 A 資訊 ...
    \end{minipage}%
    \hfill%
    \begin{minipage}[t]{0.48\textwidth} \centering % 第二個區塊
        ... 作者 B 資訊 ...
    \end{minipage}
\end{center}

請問您目前主要是在處理文字排版、表格製作還是數學公式對齊呢?我可以提供更具體的範例。

2025年9月26日 星期五

PiShrink ( SDcard img file 最佳精簡工具)

 要精簡 Raspberry Pi (Pi3) SD 卡映像檔 (.img 檔案) 的大小,最推薦且最常用的工具是 PiShrink

當您使用 dd 或其他工具製作 SD 卡映像檔時,即使卡片上只使用了很少的空間,生成的 .img 檔案大小仍會與您 SD 卡的總容量一樣大。PiShrink 這個 Bash 腳本就是用來解決這個問題的。


最佳精簡工具:PiShrink

PiShrink 是一個開源的 Bash 腳本,它會自動執行以下步驟,將映像檔大小縮減到只比實際使用的空間大一點點:

  1. 檢查和修復檔案系統:確保檔案系統沒有錯誤。

  2. 縮小分割區:將主要的根分割區 (rootfs) 縮小到剛好能容納所有資料的大小。

  3. 截斷映像檔:將 .img 檔案的整體大小截斷到縮小後的分割區結尾處。

  4. 自動擴展:最棒的是,它會確保縮小後的映像檔在下次啟動時,仍會自動擴展 (resize),以利用您燒錄 SD 卡的全部空間。

如何使用 PiShrink

PiShrink 通常需要在 LinuxWindows Subsystem for Linux (WSL) 環境中運行。

1. 安裝 PiShrink (在 Linux 或 WSL 中)

打開終端機,執行以下指令:

Bash
# 1. 下載腳本
wget https://raw.githubusercontent.com/Drewsif/PiShrink/master/pishrink.sh

# 2. 賦予執行權限
chmod +x pishrink.sh

# 3. 將腳本移動到系統路徑,方便直接使用
sudo mv pishrink.sh /usr/local/bin

2. 執行映像檔精簡

假設您的原始映像檔名為 full_backup.img,您可以在終端機中執行:

Bash
# 基本用法:縮減映像檔,縮減後的檔案會覆蓋原檔案
sudo pishrink.sh full_backup.img

如果您想保留原檔案,並輸出一個新的縮減版檔案,可以指定第二個檔名:

Bash
# 輸出到新檔案
sudo pishrink.sh full_backup.img shrunk_pi.img

3. (可選) 搭配壓縮功能

為了進一步減少檔案大小,您可以在縮減後直接進行壓縮,特別是在檔案有許多空位時,壓縮效果會非常好:

  • 使用 gzip 壓縮 (.gz 格式):

    Bash
    sudo pishrink.sh -z full_backup.img
    # 輸出檔案為 full_backup.img.gz
    
  • 使用 xz 壓縮 (.xz 格式,壓縮率更高):

    Bash
    sudo pishrink.sh -Z full_backup.img
    # 輸出檔案為 full_backup.img.xz
    

另一種優化:製作映像檔前的準備

如果您的目標是最小化映像檔,可以在製作 .img 檔案之前,先在 Pi 上進行「零填充」(Zero-Fill) 的操作。

當您從 SD 卡建立映像檔時,任何未使用的空間都會被複製為隨機資料。如果您先將所有空閒空間用零填滿,然後再製作映像檔,壓縮工具 (例如 PiShrink 的 -z-Z 參數,或外部的 zip/7z) 就可以更有效地壓縮這些零資料,使最終檔案更小。

  1. 在 Pi 上清理並零填充: 在您的 Pi 系統運行時,打開終端機並執行以下指令:

    Bash
    # 1. 清理不必要的軟體和快取
    sudo apt autoremove -y && sudo apt clean
    
    # 2. 刪除暫存檔和日誌 (可選)
    sudo rm -rf /var/log/*
    sudo rm -rf /tmp/*
    
    # 3. 零填充所有剩餘的可用空間
    sudo dd if=/dev/zero of=/zero.fill bs=4M
    # 執行時會出現 "No space left on device" (空間不足) 的錯誤,這是正常的!
    # 這代表零已經填滿所有空位。
    
    # 4. 刪除零填充檔案,釋放空間
    sudo rm -f /zero.fill
    
    # 5. 關機並取出 SD 卡製作映像檔
    sudo shutdown now
    
  2. 使用 dd 製作映像檔:將 SD 卡插入電腦,然後使用 dd 或 Win32DiskImager 等工具製作完整的 .img 檔案。

  3. 最後運行 PiShrink:將這個零填充後的 .img 檔案,再運行 PiShrink 進行精簡和壓縮。

PM2 自身有一個 startup 功能

PM2 自身有一個 startup 功能,可以自動產生一個 Systemd 服務文件。如果這個方法可行,它可以避免手動處理 NVM 複雜的環境變數。

  1. pi 身份執行 PM2 啟動服務: 在您的 pi 終端機中,執行這兩個指令。

    # 儲存當前正在運行的服務列表
    pm2 save 
    
    # 產生並設定開機自啟服務(它會嘗試設定 User=pi)
    # 輸出會是一串長指令,類似 `sudo env PATH=$PATH:... pm2 startup systemd -u pi --hp /home/pi`
    pm2 startup
    
  2. 執行輸出的 sudo 指令: 複製並執行 pm2 startup 輸出給您的那串 sudo 指令。這條指令通常會建立一個名為 pm2-pi.service 的 Systemd 檔案,並且已經為您配置好了 NVM 的路徑和 User=pi這是最簡單的方法,PM2 官方推薦。



[pm2 start mjpg_video ]

[USB1 dev/video0]
pm2 start "mjpg_streamer -i 'input_uvc.so -d /dev/video0 -r 640x480 -f 10' -o 'output_http.so -w /usr/local/share/mjpg-streamer/www -p 8080'" --name "mjpg-cam" --interpreter none

[USB2 dev/vodeo2]
pm2 start "mjpg_streamer -i 'input_uvc.so -d /dev/video2 -r 640x480 -f 10' -o 'output_http.so -w /usr/local/share/mjpg-streamer/www -p 8081'" --name "mjpg-cam" --interpreter none

PM2 的安裝與基本使用(Pi3)

 

PM2 的安裝與基本使用

步驟一:全域安裝 PM2

為了讓 PM2 能夠在系統的任何地方運行,您需要使用 -g 參數進行全域(Global)安裝。

Bash
# 使用 npm 安裝最新的 PM2 版本
npm install pm2 -g

專業建議: 由於您提到使用 Docker Compose 部署,更建議在您的 Node.js 服務(gg1_node)的 Dockerfile 中安裝 PM2,並將其設定為容器的 ENTRYPOINT,這樣可以確保應用程式在容器內穩定運行和自動重啟。

步驟二:檢查 PM2 是否安裝成功

安裝完成後,運行版本檢查指令來確認 PM2 已經就緒。

Bash
pm2 --version

步驟三:啟動您的 Node.js 應用程式

假設您的 Node.js 應用程式的啟動檔案是 server.jsapp.js

Bash
# 啟動您的應用程式,並將其命名為 'aiot-backend'
# 這樣 PM2 會在背景運行它,並監控它是否崩潰
pm2 start server.js --name "aiot-backend"

# (或使用您的專案結構)
# pm2 start ./services/gg1_node/index.js --name "gg1_node"

步驟四:查看運行狀態

使用 listls 指令可以查看所有由 PM2 管理的應用程式的狀態、PID、記憶體使用量等資訊。

Bash
pm2 list

讓 PM2 在系統重啟後自動啟動

這是生產環境中最關鍵的一步。如果您的樹莓派重啟,您會需要 PM2 自動重新啟動所有應用程式。

1. 產生啟動指令碼

PM2 會根據您當前的運行清單(pm2 list),自動產生一個適用於您系統(Raspberry Pi OS 使用 Systemd)的啟動服務檔案。

Bash
# 產生並設定系統啟動服務(適用於 Systemd, 即大多數現代 Linux 發行版)
pm2 startup

執行後,PM2 會輸出一個類似 sudo env PATH=$PATH:/usr/bin /usr/local/lib/node_modules/pm2/bin/pm2 startup systemd -u pi --hp /home/pi 的指令。

2. 執行產生的指令

複製並執行 上一步中 PM2 給出的 sudo env PATH=... 那一長串指令。

3. 儲存當前清單

將目前運行中的應用程式清單(即 pm2 list 看到的內容)儲存起來,這樣在系統重啟時,PM2 服務才能知道要啟動哪些應用程式。

Bash
pm2 save

常用 PM2 指令總結

指令目的
pm2 start <file> --name <name>啟動並監控一個應用程式。
pm2 restart <name>重新啟動指定的應用程式。
pm2 stop <name>停止指定的應用程式。
pm2 delete <name>從 PM2 清單中刪除應用程式。
pm2 list顯示所有應用程式的狀態清單。
pm2 logs顯示所有應用程式的即時日誌。
pm2 monitor顯示一個終端機儀表板,監控 CPU/記憶體使用情況。
pm2 startup產生系統自動啟動的設定指令。
pm2 save儲存當前的應用程式清單,以便重啟後能自動還原。

使用 PM2,您可以確保您的 IOT 服務在 Pi 3 上更加健壯和可靠!

AIOT_Server 建立 Node.js 專案啟動流程

 AIOT_Server 專案建立一個專業且標準的 Node.js 專案啟動流程

這個流程將遵循您的技術棧(Node.js, Express, MySQL, Redis, MQTT)和部署要求(Docker Compose),確保程式碼的穩定性、可維護性及與各服務的連線正確性。


Node.js 專案 (gg1_node) 啟動流程設計

1. 專案初始化與結構

在開始撰寫程式碼之前,先建立一個標準化的專案結構:

  1. 初始化專案:

    Bash
    mkdir gg1_node
    cd gg1_node
    npm init -y
    
  2. 安裝核心依賴套件:

    Bash
    # 核心 Web 框架、資料庫、環境變數
    npm install express mysql2 ioredis dotenv bcryptjs jsonwebtoken
    
    # 開發依賴 (Dev Dependencies)
    npm install -D nodemon # 開發時自動重啟
    
  3. 建議的檔案結構:

    gg1_node/
    ├── node_modules/
    ├── .env                # 環境變數設定 (開發用)
    ├── .gitignore
    ├── package.json
    ├── server.js           # 專案主入口 (啟動流程核心)
    ├── config/
    │   ├── db.js           # 資料庫 (MySQL) 連線配置
    │   ├── redis.js        # Redis 連線配置
    │   └── mqtt.js         # MQTT Broker 連線配置
    ├── controllers/        # 處理業務邏輯 (例如 userController.js)
    ├── models/             # 資料模型 (例如 userModel.js)
    ├── routes/             # 路由定義 (例如 authRoutes.js, apiRoutes.js)
    └── services/           # 外部服務 (例如 emailService.js, authService.js)
    

2. 核心啟動腳本 (server.js)

server.js 應該專注於環境準備服務連線的初始化工作,確保所有依賴服務都準備就緒後,才啟動 Web Server 監聽請求。

啟動流程步驟:

  1. 載入配置與環境變數:確保 .env 檔案中的變數(如 DB 連線字串、Port 號、JWT Secret)被正確載入。

  2. 初始化資料庫連線 (MySQL):測試與 MySQL 資料庫的連線,如果連線失敗,則中斷啟動並報告錯誤。

  3. 初始化快取/Token 服務 (Redis):測試與 Redis 服務的連線,確保 Email 驗證 Token 可以被存取。

  4. 初始化 MQTT 連線 (Broker):連線到 Mosquitto Broker,並設定基本的訂閱 (Subscribe) 處理器。

  5. 啟動 Express 伺服器:開始監聽指定的 Port 號,並準備處理外部請求。

JavaScript
// server.js 範例程式碼

require('dotenv').config(); // 步驟 1: 載入環境變數
const express = require('express');
const app = express();
const { connectMySQL } = require('./config/db');     // 假設您在 config/db.js 中定義了連線函數
const { connectRedis } = require('./config/redis'); // 假設您在 config/redis.js 中定義了連線函數
const { connectMQTT } = require('./config/mqtt');   // 假設您在 config/mqtt.js 中定義了連線函數
const authRoutes = require('./routes/authRoutes');

const PORT = process.env.PORT || 3000;

// Middleware 設定
app.use(express.json()); // 處理 JSON 格式的請求體

// 路由設定
app.use('/api/auth', authRoutes); // 註冊、登入、驗證等路由

/**
 * 專案啟動主函數
 */
async function startServer() {
    try {
        // 步驟 2: 初始化資料庫連線 (MySQL)
        await connectMySQL();
        console.log('✅ MySQL 資料庫連線成功');

        // 步驟 3: 初始化快取服務 (Redis)
        await connectRedis();
        console.log('✅ Redis 連線成功');

        // 步驟 4: 初始化 MQTT 連線
        await connectMQTT();
        console.log('✅ MQTT Broker 連線成功');

        // 步驟 5: 啟動 Express 伺服器
        app.listen(PORT, () => {
            console.log(`🚀 AIOT Server 成功啟動,監聽 Port ${PORT}`);
            console.log(`   (服務名稱: gg1_node)`);
        });
        
    } catch (error) {
        console.error('❌ 伺服器啟動失敗,關鍵服務無法連線或初始化:', error.message);
        // 如果關鍵服務 (DB/Redis/MQTT) 失敗,通常應退出應用程式
        process.exit(1); 
    }
}

startServer();

3. package.json 啟動指令

為了配合 PM2Docker,設定好啟動指令:

JSON
// package.json 的 scripts 部分
"scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js",
    "pm2": "pm2 start server.js --name gg1_node --watch"
},
指令用途
npm start生產環境Docker 容器中的標準啟動指令 (使用 PM2 則替換為 pm2 start)。
npm run dev開發環境 使用 nodemon 進行自動重啟。
npm run pm2在樹莓派上直接使用 PM2 啟動並監控服務。

透過這個流程,您可以確保您的 Node.js 後端服務gg1_node)在處理任何請求之前,所有的數據庫和 MQTT 服務都是就緒的,從而大大提高系統的穩定性。

接下來,您想先定義 config/db.js 中的 MySQL 連線邏輯,還是 services/emailService.js 中的 Email 驗證發送邏輯?

AIOT_Server 的規劃建議

(使用 MQTT, Redis, MySQL, MongoDB, Node.js, Python, Docker Compose)的技術規劃方向


AIOT_Server 的規劃建議

1. 完善 Node.js 後端服務(gg1_node

您已經處理了 Node.js 版本和 PM2 的問題,下一步應該專注於核心服務邏輯:

  • 實作 Email 註冊與驗證:根據您文件中的流程(使用 MySQL 存使用者資料,Redis 存驗證 Token/時效性資料),需要選定並實作郵件服務(例如:使用 NodeMailer 搭配 SendGrid 或 Mailgun)。

  • 使用者資料模型(MySQL):定義 users 表格,包含 email (PK), password_hash, is_verified (Boolean), created_at 等欄位。

  • Token 處理(Redis):使用 Redis 的 SETEXEXPIRE 功能來儲存 <Token>:<User Email> 鍵值對,並設定 24 小時的有效期限,確保安全性。

2. 規劃 Docker Compose 服務堆疊

為了實現您快速部署的目標,您需要開始撰寫 docker-compose.yml 檔案,定義所有服務:

服務名稱技術/用途備註
gg1_nodeNode.js / Express您的後台網站服務,負責 API 介面、會員管理、網頁呈現。
mosquittoMQTT Broker處理所有 IOT 設備的連線和即時資料傳輸。
redisRedis DB用於快取、Session 管理、Email 驗證 Token 暫存、MQTT 狀態/即時數據暫存。
mysqlMySQL DB用於會員管理IOT 專案/系統/設備階層資料Lev 憑證授權等永久性資料。
mongodbMongoDB DB考慮用於儲存歷史 IOT 數據(如時間序列資料)或非結構化的 Log 資料。

3. IOT 核心資料結構與權限規劃

根據您定義的 IOT 階層與權限:

  • 資料結構(MySQL/MongoDB)

    • IOT_Project:專案名稱、擁有者 (Lev1_User_ID)。

    • IOT_sys:系統名稱、所屬專案 (Project_ID)。

    • IOT_dev:設備名稱、所屬系統 (Sys_ID)。

    • IOT_feature:數據點/功能名稱、所屬設備 (Dev_ID)。

  • 憑證與授權機制

    • 建立 permissionsauthorizations 表格,用於儲存 Lev2 (管理權) 和 Lev3 (監看權) 的授權資訊:User_ID, Resource_Type (Project/Sys/Dev), Resource_ID, Permission_Level (2 or 3), Start_Time, End_Time

    • 這將是您 API 路由和 MQTT 主題訂閱授權時的核心依據。