| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360 |
- #!/bin/bash
- # ========================================
- # 拾音器异响检测系统启动脚本
- # ========================================
- #
- # 使用方法:
- # ./start.sh # 前台运行
- # ./start.sh -d # 后台运行
- # ./start.sh --daemon # 后台运行
- # ./start.sh stop # 停止服务
- # ./start.sh restart # 重启服务
- # ./start.sh status # 查看状态
- #
- # 日志文件:
- # 前台运行:直接输出到控制台
- # 后台运行:logs/system.log(RotatingFileHandler 自动轮转)
- #
- # 切换到脚本所在目录
- cd "$(dirname "$0")"
- # PID文件路径
- PID_FILE="logs/pid.txt"
- STARTUP_TIMEOUT=5
- HEALTH_CHECK_INTERVAL=1
- # ========================================
- # 函数:按PID精确清理PID文件
- # ========================================
- cleanup_pid_file_if_matches() {
- local expected_pid="$1"
- if [ ! -f "$PID_FILE" ]; then
- return 0
- fi
- local current_pid
- current_pid=$(cat "$PID_FILE" 2>/dev/null)
- if [ -z "$expected_pid" ] || [ "$current_pid" = "$expected_pid" ]; then
- rm -f "$PID_FILE"
- fi
- }
- # ========================================
- # 函数:激活conda环境
- # ========================================
- activate_conda() {
- if command -v conda &> /dev/null; then
- # 激活 conda 环境
- source $(conda info --base)/etc/profile.d/conda.sh
- conda activate pump_asd
- echo "已激活 conda 环境: pump_asd"
- fi
- }
- # ========================================
- # 函数:检查PID是否为当前服务进程
- # ========================================
- is_expected_process() {
- local pid="$1"
- if [ -z "$pid" ]; then
- return 1
- fi
- if ! ps -p "$pid" > /dev/null 2>&1; then
- return 1
- fi
- local command
- command=$(ps -p "$pid" -o command= 2>/dev/null)
- case "$command" in
- *"run_with_auto_training.py"*)
- return 0
- ;;
- *)
- return 1
- ;;
- esac
- }
- # ========================================
- # 函数:检查进程是否运行
- # ========================================
- is_running() {
- if [ -f "$PID_FILE" ]; then
- PID=$(cat "$PID_FILE")
- # 不仅检查PID是否存在,还要确认是本服务进程,避免PID复用误判
- if is_expected_process "$PID"; then
- return 0 # 运行中
- fi
- cleanup_pid_file_if_matches "$PID"
- fi
- return 1 # 未运行
- }
- # ========================================
- # 函数:获取当前PID
- # ========================================
- get_pid() {
- if [ -f "$PID_FILE" ]; then
- cat "$PID_FILE"
- else
- echo ""
- fi
- }
- # ========================================
- # 函数:等待服务稳定启动
- # ========================================
- wait_for_service_ready() {
- local pid="$1"
- local elapsed=0
- while [ "$elapsed" -lt "$STARTUP_TIMEOUT" ]; do
- if ! is_expected_process "$pid"; then
- return 1
- fi
- sleep "$HEALTH_CHECK_INTERVAL"
- elapsed=$((elapsed + HEALTH_CHECK_INTERVAL))
- done
- return 0
- }
- # ========================================
- # 函数:后台监控PID,进程退出后自动清理PID文件
- # ========================================
- spawn_pid_watcher() {
- local watched_pid="$1"
- nohup bash -c '
- watched_pid="$1"
- pid_file="$2"
- while ps -p "$watched_pid" > /dev/null 2>&1; do
- sleep 2
- done
- if [ -f "$pid_file" ] && [ "$(cat "$pid_file" 2>/dev/null)" = "$watched_pid" ]; then
- rm -f "$pid_file"
- fi
- ' _ "$watched_pid" "$PID_FILE" > /dev/null 2>&1 &
- }
- # ========================================
- # 函数:启动服务
- # ========================================
- start_service() {
- # 检查是否已经运行
- if is_running; then
- echo "服务已在运行中, PID: $(get_pid)"
- echo "如需重启,请使用: ./start.sh restart"
- return 1
- fi
-
- # 激活conda环境
- activate_conda
-
- # 检查必要文件
- if [ ! -f "run_with_auto_training.py" ]; then
- echo "错误: run_with_auto_training.py 不存在"
- exit 1
- fi
- # 检查配置文件(YAML 或 DB 至少存在一个)
- if [ ! -f "config/pickup_config.db" ] && [ ! -f "config/rtsp_config.yaml" ]; then
- echo "错误: 找不到配置文件"
- echo "需要 config/pickup_config.db 或 config/rtsp_config.yaml 之一"
- exit 1
- fi
- # 创建日志目录
- mkdir -p logs
- # 启动服务
- echo "后台运行模式..."
- # stdout/stderr 丢弃,所有日志由 RotatingFileHandler 写入 logs/system.log
- nohup python run_with_auto_training.py > /dev/null 2>&1 &
- PID=$!
- echo $PID > "$PID_FILE"
-
- # 等待一段观察窗口,避免“刚启动1秒就退出”仍被误判为成功
- if wait_for_service_ready "$PID"; then
- spawn_pid_watcher "$PID"
- echo "服务启动成功, PID: $PID"
- echo "日志文件: logs/system.log"
- echo ""
- echo "查看日志: tail -f logs/system.log"
- echo "停止服务: ./start.sh stop"
- echo "重启服务: ./start.sh restart"
- else
- echo "服务启动失败,请检查日志: logs/system.log"
- cleanup_pid_file_if_matches "$PID"
- return 1
- fi
- }
- # ========================================
- # 函数:停止服务
- # ========================================
- stop_service() {
- if ! is_running; then
- echo "服务未运行"
- cleanup_pid_file_if_matches ""
- return 0
- fi
-
- PID=$(get_pid)
- echo "正在停止服务, PID: $PID"
-
- # 发送 SIGTERM 信号,优雅停止
- kill "$PID" 2>/dev/null
-
- # 等待进程结束(最多等待10秒)
- WAIT_COUNT=0
- while ps -p "$PID" > /dev/null 2>&1; do
- if [ $WAIT_COUNT -ge 10 ]; then
- echo "进程未响应,强制终止..."
- kill -9 "$PID" 2>/dev/null
- break
- fi
- sleep 1
- WAIT_COUNT=$((WAIT_COUNT + 1))
- echo "等待进程结束... ($WAIT_COUNT/10)"
- done
-
- cleanup_pid_file_if_matches "$PID"
- echo "服务已停止"
- }
- # ========================================
- # 函数:重启服务
- # ========================================
- restart_service() {
- echo "=========================================="
- echo "重启拾音器异响检测服务"
- echo "=========================================="
-
- stop_service
- echo ""
- sleep 2 # 等待2秒确保资源完全释放
- start_service
- }
- # ========================================
- # 函数:查看服务状态
- # ========================================
- show_status() {
- echo "=========================================="
- echo "拾音器异响检测服务状态"
- echo "=========================================="
-
- if is_running; then
- PID=$(get_pid)
- echo "状态: 运行中"
- echo "PID: $PID"
- echo ""
-
- # 显示进程信息
- echo "进程详情:"
- ps -p "$PID" -o pid,ppid,user,%cpu,%mem,etime,command | head -2
- echo ""
-
- # 显示最近日志
- echo "最近10行日志:"
- echo "------------------------------------------"
- tail -10 logs/system.log 2>/dev/null || echo "(无日志)"
- else
- echo "状态: 未运行"
- if [ -f "$PID_FILE" ]; then
- echo "注意: PID文件存在但进程已停止,可能是异常退出"
- cleanup_pid_file_if_matches ""
- fi
- fi
- }
- # ========================================
- # 函数:前台运行
- # ========================================
- run_foreground() {
- # 检查是否已经运行
- if is_running; then
- echo "服务已在后台运行中, PID: $(get_pid)"
- echo "请先停止: ./start.sh stop"
- return 1
- fi
-
- # 激活conda环境
- activate_conda
-
- # 检查必要文件
- if [ ! -f "run_with_auto_training.py" ]; then
- echo "错误: run_with_auto_training.py 不存在"
- exit 1
- fi
- # 检查配置文件(YAML 或 DB 至少存在一个)
- if [ ! -f "config/pickup_config.db" ] && [ ! -f "config/rtsp_config.yaml" ]; then
- echo "错误: 找不到配置文件"
- echo "需要 config/pickup_config.db 或 config/rtsp_config.yaml 之一"
- exit 1
- fi
- # 创建日志目录
- mkdir -p logs
- echo "前台运行模式..."
- python run_with_auto_training.py
- }
- # ========================================
- # 函数:显示帮助
- # ========================================
- show_help() {
- echo "拾音器异响检测系统 - 启动脚本"
- echo ""
- echo "用法: ./start.sh [命令]"
- echo ""
- echo "命令:"
- echo " (无参数) 前台运行"
- echo " -d, --daemon 后台运行"
- echo " start 后台启动服务"
- echo " stop 停止服务"
- echo " restart 重启服务"
- echo " status 查看服务状态"
- echo " help 显示帮助信息"
- echo ""
- echo "示例:"
- echo " ./start.sh -d # 后台启动"
- echo " ./start.sh restart # 重启服务"
- echo " ./start.sh status # 查看状态"
- }
- # ========================================
- # 主逻辑
- # ========================================
- case "$1" in
- stop)
- stop_service
- ;;
- restart)
- restart_service
- ;;
- status)
- show_status
- ;;
- start|-d|--daemon)
- start_service
- ;;
- help|--help|-h)
- show_help
- ;;
- "")
- run_foreground
- ;;
- *)
- echo "未知命令: $1"
- echo ""
- show_help
- exit 1
- ;;
- esac
|