自动化http请求脚本

#!/bin/bash

# =============================================
# 一键算法镜像验证脚本
# 支持指定算法编号或默认验证 00-50 范围内存在的算法
# =============================================

# ========== 可配置参数区 ==========
SCRIPT_DIR="/home/yaJiangBuild/team04/"                    # 启动脚本所在目录
IMAGE_SCRIPT_PREFIX="team04_algorithm"       # 启动脚本前缀
ALGORITHM_START=0                            # 算法编号起始
ALGORITHM_END=50                             # 算法编号结束

TEST_IMAGE_PATH="/data/test"                 # 测试素材根路径
RESULT_ROOT_PATH="/data/picture-result"      # 结果输出根路径(算法服务生成)
AUTO_TEST_SCRIPT="/data/auto_test.sh"        # 评测脚本路径
VIDEO_RESULT_ROOT_PATH="/data/video-result"
HEALTH_CHECK_IP="127.0.0.1"              # 健康检查和接口调用 IP
HEALTH_CHECK_ENDPOINT="/health"              # 健康检查端点
PICTURE_ANALYSIS_ENDPOINT="/picture/analysis" # 图片分析接口
VIDEO_ANALYSIS_ENDPOINT="/video/create" # 视频分析接口

RUN_TEST_LOG="/home/test"

# 视频算法编号列表(其余视为图片算法)
VIDEO_ALGORITHMS=(5 6 8 14 37)
alg_handle_num=1

TEST_ID=$(date +%Y%m%d_%H%M%S)
LOG_FILE="/data/logs/algorithm_verify_${TEST_ID}.log"
# =============================================

source_id_global=""

# 确保日志目录存在
LOG_DIR=$(dirname "$LOG_FILE")
if [ ! -d "$LOG_DIR" ]; then
    mkdir -p "$LOG_DIR" || error_exit "无法创建日志目录: $LOG_DIR"
fi

# 日志函数
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}

image_exists() {
    local image="$1"
    docker inspect "$image" > /dev/null 2>&1
}

# 停止并删除容器
stop_container() {
    local alg_id=$(printf "%02d" $((10#$1))) # 格式化为两位数
    local container_name="team04_algorithm_${alg_id}"
    local timestamp=$source_id_global
    docker ps -a
    if image_exists "${container_name}"; then
      log "🧹 开始清理: $container_name"
    else
       log "🧹 清理完成: $container_name"
       exit 0
    fi

    container_console_log_file="/data/tmp/team04/algorithm_${alg_id}/${container_name}_console_${timestamp}.log"
    log "😄 保存容器控制台日志: 容器名:$container_name, 容器日志文件路径:${container_console_log_file}"
    docker logs $container_name >> ${container_console_log_file}
    cp -rf ${container_console_log_file} "${RUN_TEST_LOG}/"

    log "🧹 正在清理容器: $container_name"
    if docker ps -a --format '{{.Names}}' | grep -qw "$container_name"; then
        if docker ps --format '{{.Names}}' | grep -qw "$container_name"; then
            docker stop "$container_name" > /dev/null 2>&1 && \
                log "✅ 容器 $container_name 已停止"
        fi
        docker rm -f "$container_name" > /dev/null 2>&1 && \
            log "✅ 容器 $container_name 已删除"
    else
        log "🔍 容器 $container_name 不存在,无需清理"
    fi

    alg_service_logs_file="/data/tmp/team04/algorithm_${alg_id}/logs_${timestamp}"
    if [[ -d "/data/tmp/team04/algorithm_${alg_id}/logs" ]]; then
      log "📁 算法分析服务日志文件: ${alg_service_logs_file}"
      mv "/data/tmp/team04/algorithm_${alg_id}/logs" ${alg_service_logs_file}
      log "✅ 容器控制台日志保存完成、算法服务日志备份完成"
      mkdir -p "${RUN_TEST_LOG}/${alg_id}"

      # 判断文件夹是否存在
      if [ -d "${alg_service_logs_file}" ]; then
        log "📁 算法分析服务日志文件: ${alg_service_logs_file} 存在"
        cp -rf ${alg_service_logs_file}/* "${RUN_TEST_LOG}/${alg_id}/"
      else
        log "文件夹${alg_service_logs_file}不存在"
      fi
    else
        echo "文件夹不存在"
    fi
}

error_exit() {
    log "❌ 错误: $*"
    exit 1
}

# 检查是否为图片算法
is_picture_algorithm() {
    local alg_num=$((10#$1))  # 使用10#强制转换为十进制
    for vdo_alg in "${VIDEO_ALGORITHMS[@]}"; do
        if [[ "$vdo_alg" -eq "$alg_num" ]]; then
            return 1
        fi
    done
    return 0
}

# 获取素材数量(jpg 或 MP4)
get_media_count() {
    local media_dir=$1
    local count=0
    if is_picture_algorithm "$ALG_ID"; then
        count=$(find "$media_dir" -maxdepth 1 -name "*.jpg" -o -name "*.JPG" -o -name "*.jpeg" -o -name "*.JPEG" | wc -l)
        echo "${count}"
    return 0
    else
        # count=$(find "$media_dir" -maxdepth 1 -name "*.mp4" -o -name "*.MP4" | wc -l)
        #  log "$ALG_ID video count $count"
        # 改为获取视频时长
        # 视频:统计所有 .mp4 文件的总时长(秒)
        local total_duration=0
        local duration
        local video_files=("$media_dir"/*.mp4 "$media_dir"/*.MP4)
        total_duration=0

        for video_file in "${video_files[@]}"; do
            # 检查文件是否存在(避免通配符未匹配)
            [ -f "$video_file" ] || continue

            duration=$(ffprobe -v quiet -show_entries format=duration \
                -of csv=p=0 "$video_file" 2>/dev/null || echo "0")

            if [[ "$duration" =~ ^[0-9]+\.?[0-9]*$ ]]; then
                total_duration=$(echo "$total_duration + $duration" | bc -l)
            fi
        done

        # 四舍五入保留 2 位小数(可选)
        total_duration=$(printf "%.2f" "$total_duration")
        echo "$total_duration"
        return 0
    fi
}

# 等待容器启动并健康检查
wait_for_health() {
    local port=$1
    local max_retries=60
    local retry=0
    local url="http://${HEALTH_CHECK_IP}:${port}${HEALTH_CHECK_ENDPOINT}"

    log "🔄 正在进行健康检查: $url"

    while [ $retry -lt $max_retries ]; do
        sleep 5
        response=$(curl -s -w "%{http_code}" -X POST "$url" -H "Content-Type: application/json" --connect-timeout 5 -o /dev/null)
        if [ "$response" = "200" ]; then
            # 再检查返回 body 中 code 是否为 0
            body=$(curl -s -X POST "$url" -H "Content-Type: application/json")
            code=$(echo "$body" | grep -o '"code":[0-9]*' | cut -d: -f2)
            if [ "$code" = "0" ]; then
                log "✅ 健康检查通过"
                return 0
            else
                log "⚠️ 接口返回 code 非 0: $body"
            fi
        else
            log "⏳ 健康检查未通过 (HTTP $response),重试 $((retry+1))/$max_retries"
        fi
        retry=$((retry + 1))
    done

    error_exit "❌ 健康检查超时或失败,终止算法 $ALG_ID 验证"
}

# 调用分析接口
trigger_analysis() {
    source_id_global=""
    local port=$1
    local timestamp=$(date +%Y%m%d%H%M%S%3N)
    local source_id="${ALG_ID}-${timestamp}"
    log "source_id:$source_id"
    local task_id="${ALG_ID}-${timestamp}"
    local source_path="${TEST_IMAGE_PATH}/${ALG_ID}/"
    log "source_path:$source_path"
    if [ ! -d "$source_path" ]; then
        log "❌ 素材路径不存在: $source_path  跳过"
        return 0
    fi


    local media_count=$(get_media_count "$source_path")
    log "ℹ️ 素材数量(图片是文件数据,视频是视频总时长): ${media_count}"
    if [[ "$media_count" == "0" || "$media_count" == "0.00" || "$media_count" == "0.0" ]]; then
      log "⚠️  注意: 素材数量为 0: $source_path, media_count: ${media_count}"
    else
      log "ℹ️ 素材数量(图片是文件数据,视频是视频总时长): ${media_count}"
    fi

    local url
    local payload

    if is_picture_algorithm "$ALG_ID"; then
        url="http://${HEALTH_CHECK_IP}:${port}${PICTURE_ANALYSIS_ENDPOINT}"
        payload='{"sourceId":"'"$source_id"'","sourcePath":"'"$source_path"'"}'
    else
        url="http://${HEALTH_CHECK_IP}:${port}${VIDEO_ANALYSIS_ENDPOINT}"
        payload='{"taskId":"'"$task_id"'","sourcePath":"'"$source_path"'"}'
    fi

    log "🚀 调用分析接口: $url"
    log "📄 请求体: $payload"

    response=$(curl -s -w "%{http_code}" -X POST "$url" \
        -H "Content-Type: application/json" \
        -d "$payload" \
        --connect-timeout 10 \
        -o /tmp/curl_response_body.txt)

    http_code="${response: -3}"

    if [ "$http_code" != "200" ]; then
        error_exit "❌ 接口调用失败,HTTP 状态码: $http_code,响应: $(cat /tmp/curl_response_body.txt)"
    fi

    log "✅ 接口调用成功,响应: $(cat /tmp/curl_response_body.txt)"

    # 返回时间戳用于结果目录匹配
    echo "$source_id"
    source_id_global="$source_id"
}

# 监控 end.log 生成
wait_for_end_log() {
    local identifier=$1  # 如 23-202508152100000
    local max_wait=600  # 最大等待 10分
    local check_interval=1
    local result_dir=""

    local start_time=$(date +%s)
    # 查找 RESULT_ROOT_PATH 下所有以 "$ALG_ID-" 开头的目录,按修改时间排序取最新
    local result_dir
    local result_dir_tmp
    if is_picture_algorithm "$ALG_ID"; then
      result_dir="$RESULT_ROOT_PATH/$identifier/end.log"
      result_dir_tmp="图片"
    else
      result_dir="$VIDEO_RESULT_ROOT_PATH/$identifier/end.log"
      result_dir_tmp="视频"
    fi
    log "🔍 监控结果目录中是否包含 end.log, 结果文件路径: ${result_dir}"
    echo -n "${result_dir_tmp}分析中:"
    while true; do
        echo -n "=="
        if [ -n "$result_dir" ] && [ -f "$result_dir" ]; then
            echo -e "\n"
            log "✅ 检测到 end.log: $result_dir"
            log "📁 end文件内容: $(cat ${result_dir})"
            break
        fi

#        local elapsed=$(( $(date +%s) - start_time ))
#        if [ $elapsed -ge $max_wait ]; then
#            error_exit "❌ 超时:$max_wait 秒内未生成 end.log"
#        fi

        sleep $check_interval
    done
}

convertDateTime(){
  local timestamp=$1
  # 截取各部分
  year=${timestamp:0:4}     # 2025
  month=${timestamp:4:2}   # 08
  day=${timestamp:6:2}     # 23
  hour=${timestamp:8:2}    # 16
  minute=${timestamp:10:2}  # 14
  second=${timestamp:12:2}  # 04
  millisecond=${timestamp:14:3}  # 059
  # 拼接格式
  formatted="${year}-${month}-${day} ${hour}:${minute}:${second}.${millisecond}"
  echo "$formatted"
  return 0
}

# 计算推理时间并生成报告  duration: 单位毫秒
generate_report() {
    local start_time=$1
    local a=$(convertDateTime $2)
    # 2025-08-23 16:14:04.059 转换成时间戳
    end_time=$(date -d "$a" +%s%3N)
    log "转换后的时间戳: $end_time,   a=$a"
    local duration=$((end_time - start_time))
    local source_path="${TEST_IMAGE_PATH}/${ALG_ID}/"
    local media_count=$(get_media_count "$source_path")

    duration=$(echo "${duration} * 100" | bc)
    media_count=$(echo "${media_count} * 100" | bc)
    media_count=$(printf "%.0f" "$media_count")
    local avg_time_per_item=0
    if [ "$media_count" -gt 0 ]; then
        avg_time_per_item=$(echo "scale=2; $duration / $media_count" | bc -l)
    fi

    log "end time:${end_time}"
    log "📊 推理时间报告:"
    log "   算法编号: $ALG_ID"
    log "   算法句柄数量: ${alg_handle_num}"
    log "   素材数量: $media_count 图片是文件,时间是总时长"
    log "   总耗时: ${duration}ms"
    log "   平均每项耗时: ${avg_time_per_item}ms"
}

# 执行评测脚本
run_evaluation() {
    log "🧪 正在执行评测脚本: $AUTO_TEST_SCRIPT $ALG_ID"
    if [ -x "$AUTO_TEST_SCRIPT" ]; then
        # 修改为指定算法编号 素材路径 sourceID
        log "⭐⭐⭐⭐⭐ 测评脚本执行中... 算法编号: $ALG_ID, 素材路径: ${TEST_IMAGE_PATH}, sourceID: ${source_id_global}"
        if "$AUTO_TEST_SCRIPT" "$ALG_ID" "${TEST_IMAGE_PATH}" "${source_id_global}" 2>&1 | tee -a "$LOG_FILE"; then
            log "✅ 评测执行成功"
        else
            log "⚠️ 评测脚本执行失败,退出码: $?"
        fi
    else
        log "⚠️ 评测脚本不存在或不可执行: $AUTO_TEST_SCRIPT"
    fi
}

# 验证单个算法
verify_algorithm() {
    ALG_ID=$(printf "%02d" $((10#$1))) # 格式化为两位数

    # 设置 trap:无论脚本如何退出(正常或错误),都执行清理
    trap 'stop_container $ALG_ID' EXIT

    log "🔍 开始验证算法: $ALG_ID"

    # 1. 查找启动脚本
    SCRIPT_PATH="${SCRIPT_DIR}/${IMAGE_SCRIPT_PREFIX}_${ALG_ID}.sh"
    if [ ! -f "$SCRIPT_PATH" ]; then
        log "⏭️ 未找到启动脚本: $SCRIPT_PATH,跳过"
        return 0
    fi
    #先判断路径是否存在
    local source_path="${TEST_IMAGE_PATH}/${ALG_ID}/"
    log "source_path:$source_path"
    if [ ! -d "$source_path" ]; then
        log "❌ 素材路径不存在: $source_path  跳过"
        return 0
    fi
    alg_handle_num=$(cat $SCRIPT_PATH | grep "AlgHdlSum=")

    # 2. 启动镜像
    log "🐳 启动容器: $SCRIPT_PATH"
    if bash "$SCRIPT_PATH"; then
        log "✅ 容器启动命令执行成功"
    else
        error_exit "❌ 容器启动失败"
    fi

    # 3. 健康检查端口
    PORT=$((10000 + 10#$ALG_ID))  # 10# 避免八进制解析

    # 4. 等待健康检查通过
    wait_for_health "$PORT"

    # 5. 调用分析接口,获取 source_id(含时间戳)
#    source_id=$(trigger_analysis "$PORT")
    trigger_analysis "$PORT"
    if [ -z "$source_id_global" ]; then
        log "❌ 未能获取 source_id"
    else
      log "sourceId trigger_analysis:  ${source_id_global}"
      # 记录开始时间ms
      START_TIME=$(date +%s%3N)
      log "start time:$START_TIME"
      # 6. 监控 end.log
      #    result_dir=$()
      wait_for_end_log "$source_id_global"


      local result_dir_end_log_tmp
      if is_picture_algorithm "$ALG_ID"; then
        result_dir_end_log_tmp="$RESULT_ROOT_PATH/${source_id_global}/end.log"
      else
        result_dir_end_log_tmp="$VIDEO_RESULT_ROOT_PATH/${source_id_global}/end.log"
      fi

      # 读取 end.log 第一行作为时间戳
      end_timestamp=$(cat "$result_dir_end_log_tmp" | head -n1 | tr -d '\r\n')
      # 验证是否为纯数字时间戳(可选)
      if [[ "$end_timestamp" =~ ^[0-9]{17}$ ]]; then
          log "✅ 检测到 end.log 并读取时间戳: $end_timestamp"
      else
        log "⚠️ end.log 内容格式错误(非17位时间戳): $end_timestamp, 使用系统时间"
        end_timestamp=$(date "+%Y%m%d%H%M%S%3N")
      fi
      echo "$end_timestamp"

      # 7. 生成报告
      generate_report "$START_TIME" "$end_timestamp"
      # 8. 执行评测
      run_evaluation

      # 分析算法输出结果
      verify_alg_result $1
    fi

    # 9停止镜像
    stop_container $1

    log "✅ 算法 $ALG_ID 验证完成\n"
}

# 验证所有算法分析结果
# 验证所有算法分析结果
verify_alg_result() {
  local alg_id=$(printf "%02d" $((10#$1))) # 格式化为两位数
  local dataPath="${TEST_IMAGE_PATH}/${alg_id}"  # 素材路径
  local result_dir=""
  local count=""
  local result_count=""
  if is_picture_algorithm "$1"; then
    result_dir="$RESULT_ROOT_PATH/${source_id_global}"
    result_dir_tmp="图片"
    count=$(find "${dataPath}" -maxdepth 1 -name "*.jpg" -o -name "*.JPG" -o -name "*.jpeg" -o -name "*.JPEG" | wc -l)
    result_count=$(find "${result_dir}" -maxdepth 1 -name "*.json" | wc -l)
  else
    result_dir="$VIDEO_RESULT_ROOT_PATH/${source_id_global}"
    result_dir_tmp="视频"
    count=$(find "${dataPath}" -maxdepth 1 -name "*.mp4" -o -name "*.MP4" | wc -l)
    return 0
  fi
  log "📊 ${result_dir_tmp}算法分析结果目录: ${result_dir}, ${result_dir_tmp}分析结果数量:${result_count}"
  log "📊 ${result_dir_tmp}目录:${dataPath}, ${result_dir_tmp}数量:${count}"

  if [ "${result_count}" -eq 0 ]; then
    log "❌ ${result_dir_tmp}分析结果为0,可能是分析失败"
    return 0
  fi

  # result_count != count
  if [ "${result_count}" -ne "${count}" ]; then
    log "❌ ${result_dir_tmp}分析结果数量不匹配: ${result_count} != ${count}"
    return 0
  fi

  # 挨个校验 结果的json文件
  local result_ok=0
  local result_fail=0
  echo -n "${result_dir_tmp}结果分析中..."
  for file in ${result_dir}/*; do
    ext="${file##*.}"
    filename=$(basename "$file")
    base="${filename%.*}"
    if [[ "$ext" != "json" ]]; then
      continue
    fi

    # 获取json内容
    if [[ -s "$file" ]]; then
        file_content=$(cat $file)
        code=$(echo "${file_content}" | jq -r '.code')
        if [[ "$code" -eq 0 ]]; then
            result_ok=$((result_ok+1))
        else
            log "❌ code 不为 0,错误码: $code, 结果文件:${file}"
            result_fail=$((result_fail+1))
        fi
    else
        log "❌ 检测结果为空  可能检测失败"
    fi
    echo -n "."
  done
  echo -e "\n"
  log "✅ ${result_dir_tmp}分析结果校验完成: 成功 ${result_ok}, 失败 ${result_fail}"
}

# ============ 主程序 ============

main() {
    log "🎉📌📌📌📌📌📌📌 日志保存在: $RUN_TEST_LOG"
    local target_alg=$1

    TEST_IMAGE_PATH=$2
    if [ -z "$TEST_IMAGE_PATH" ]; then
        TEST_IMAGE_PATH="/data/test"
        echo "默认素材路径: $TEST_IMAGE_PATH"
    fi
    echo "素材路径: $TEST_IMAGE_PATH"
    folder_name=$(basename "$TEST_IMAGE_PATH")
    echo "文件夹: $folder_name"
    TEST_ID="${folder_name}_${TEST_ID}"
    echo "TEST_ID: $TEST_ID"
    RUN_TEST_LOG="${RUN_TEST_LOG}/${TEST_ID}"
    mkdir -p $RUN_TEST_LOG
    LOG_FILE="/data/logs/algorithm_verify_${TEST_ID}.log"
    echo "LOG_FILE: $LOG_FILE"
    
    # 输入IP
    HEALTH_CHECK_IP=$3
    if [ -z "$HEALTH_CHECK_IP" ]; then
            HEALTH_CHECK_IP="127.0.0.1"
            echo "IP地址: $HEALTH_CHECK_IP"
        fi
    echo "IP地址: $HEALTH_CHECK_IP"

    if [ -z "$target_alg" ]; then
      log "📌 验证所有的需要 输入 all"
    elif [ "$target_alg" == "all" ]; then
        log "📌 默认验证算法编号 $ALGORITHM_START 到 $ALGORITHM_END 中存在的镜像"
        for i in $(seq $ALGORITHM_START $ALGORITHM_END); do
            if is_picture_algorithm "$i"; then
               echo "分析图片$i"
            else
               echo "分析视频$i"
            fi
            verify_algorithm "$i"
        done
    elif [ "$target_alg" == "images" ]; then
        log "📌 仅测试图片算法的镜像"
        image_alg_ids=(12 13 18 22 23 24 25 26 28 31 32 33 34 7 9)
        for image_alg_id in "${image_alg_ids[@]}"; do
            if is_picture_algorithm "$image_alg_id"; then
               echo "分析图片$image_alg_id"
            else
               echo "分析视频$image_alg_id"
            fi
            verify_algorithm "$image_alg_id"
        done
    elif [[ $target_alg == array* ]]; then
        echo "✅ 测试指定多个算法编号:$target_alg"
        # 去掉前缀 array:
        target_alg=${target_alg#array:}
        log "✅✅✅✅✅ 目标算法编号: $target_alg"
        # 逗号分割
        IFS=',' read -r -a array <<< "$target_alg"
        for alg_id in "${array[@]}"; do
            echo "算法编号:${alg_id}"
            if is_picture_algorithm "$alg_id"; then
               echo "分析图片$alg_id"
            else
               echo "分析视频$alg_id"
            fi
            verify_algorithm "$alg_id"
        done

    elif [ "$target_alg" == "temp" ]; then
        log "📌 仅测试图片算法的镜像"
        image_alg_ids=(7 13 22 25)
        for image_alg_id in "${image_alg_ids[@]}"; do
            if is_picture_algorithm "$image_alg_id"; then
               echo "分析图片$image_alg_id"
            else
               echo "分析视频$image_alg_id"
            fi
            verify_algorithm "$image_alg_id"
        done
    elif [ "$target_alg" == "video" ]; then
        log "📌 仅测试视频算法的镜像"
        video_alg_ids=(14 37 5 6 8)
        for video_alg_id in "${video_alg_ids[@]}"; do
            if is_picture_algorithm "${video_alg_id}"; then
               echo "分析图片${video_alg_id}"
            else
               echo "分析视频${video_alg_id}"
            fi
            verify_algorithm "${video_alg_id}"
        done
    else
        if [[ "$target_alg" =~ ^[0-9]{1,2}$ ]] && [ "$target_alg" -ge $ALGORITHM_START ] && [ "$target_alg" -le $ALGORITHM_END ]; then
            log "📌 正在验证指定算法: $target_alg"
            verify_algorithm "$target_alg"
        else
            error_exit "❌ 无效的算法编号: $target_alg,应为 $ALGORITHM_START-$ALGORITHM_END 之间的整数"
        fi
    fi

    log "🎉 所有验证任务完成,日志保存在: $LOG_FILE"
    log "🎉🎉🎉📌📌📌📌📌📌 日志保存在: $RUN_TEST_LOG"
    cp $LOG_FILE ${RUN_TEST_LOG}
}

# ============ 执行 ============
main "$@"

 

posted @ 2025-09-16 09:32  一字千金  阅读(12)  评论(0)    收藏  举报