#!/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=1TEST_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" ]; thenmkdir -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_globaldocker ps -aif image_exists "${container_name}"; thenlog "🧹 开始清理: $container_name"elselog "🧹 清理完成: $container_name"exit 0ficontainer_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"; thenif docker ps --format '{{.Names}}' | grep -qw "$container_name"; thendocker stop "$container_name" > /dev/null 2>&1 && \log "✅ 容器 $container_name 已停止"fidocker rm -f "$container_name" > /dev/null 2>&1 && \log "✅ 容器 $container_name 已删除"elselog "🔍 容器 $container_name 不存在,无需清理"fialg_service_logs_file="/data/tmp/team04/algorithm_${alg_id}/logs_${timestamp}"if [[ -d "/data/tmp/team04/algorithm_${alg_id}/logs" ]]; thenlog "📁 算法分析服务日志文件: ${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}" ]; thenlog "📁 算法分析服务日志文件: ${alg_service_logs_file} 存在"cp -rf ${alg_service_logs_file}/* "${RUN_TEST_LOG}/${alg_id}/"elselog "文件夹${alg_service_logs_file}不存在"fielseecho "文件夹不存在"fi }error_exit() {log "❌ 错误: $*"exit 1 }# 检查是否为图片算法 is_picture_algorithm() {local alg_num=$((10#$1)) # 使用10#强制转换为十进制for vdo_alg in "${VIDEO_ALGORITHMS[@]}"; doif [[ "$vdo_alg" -eq "$alg_num" ]]; thenreturn 1fidonereturn 0 }# 获取素材数量(jpg 或 MP4) get_media_count() {local media_dir=$1local count=0if is_picture_algorithm "$ALG_ID"; thencount=$(find "$media_dir" -maxdepth 1 -name "*.jpg" -o -name "*.JPG" -o -name "*.jpeg" -o -name "*.JPEG" | wc -l)echo "${count}"return 0else# count=$(find "$media_dir" -maxdepth 1 -name "*.mp4" -o -name "*.MP4" | wc -l)# log "$ALG_ID video count $count"# 改为获取视频时长# 视频:统计所有 .mp4 文件的总时长(秒)local total_duration=0local durationlocal video_files=("$media_dir"/*.mp4 "$media_dir"/*.MP4)total_duration=0for video_file in "${video_files[@]}"; do# 检查文件是否存在(避免通配符未匹配)[ -f "$video_file" ] || continueduration=$(ffprobe -v quiet -show_entries format=duration \-of csv=p=0 "$video_file" 2>/dev/null || echo "0")if [[ "$duration" =~ ^[0-9]+\.?[0-9]*$ ]]; thentotal_duration=$(echo "$total_duration + $duration" | bc -l)fidone# 四舍五入保留 2 位小数(可选)total_duration=$(printf "%.2f" "$total_duration")echo "$total_duration"return 0fi }# 等待容器启动并健康检查 wait_for_health() {local port=$1local max_retries=60local retry=0local url="http://${HEALTH_CHECK_IP}:${port}${HEALTH_CHECK_ENDPOINT}"log "🔄 正在进行健康检查: $url"while [ $retry -lt $max_retries ]; dosleep 5response=$(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 是否为 0body=$(curl -s -X POST "$url" -H "Content-Type: application/json")code=$(echo "$body" | grep -o '"code":[0-9]*' | cut -d: -f2)if [ "$code" = "0" ]; thenlog "✅ 健康检查通过"return 0elselog "⚠️ 接口返回 code 非 0: $body"fielselog "⏳ 健康检查未通过 (HTTP $response),重试 $((retry+1))/$max_retries"firetry=$((retry + 1))doneerror_exit "❌ 健康检查超时或失败,终止算法 $ALG_ID 验证" }# 调用分析接口 trigger_analysis() {source_id_global=""local port=$1local 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" ]; thenlog "❌ 素材路径不存在: $source_path 跳过"return 0filocal media_count=$(get_media_count "$source_path")log "ℹ️ 素材数量(图片是文件数据,视频是视频总时长): ${media_count}"if [[ "$media_count" == "0" || "$media_count" == "0.00" || "$media_count" == "0.0" ]]; thenlog "⚠️ 注意: 素材数量为 0: $source_path, media_count: ${media_count}"elselog "ℹ️ 素材数量(图片是文件数据,视频是视频总时长): ${media_count}"filocal urllocal payloadif is_picture_algorithm "$ALG_ID"; thenurl="http://${HEALTH_CHECK_IP}:${port}${PICTURE_ANALYSIS_ENDPOINT}"payload='{"sourceId":"'"$source_id"'","sourcePath":"'"$source_path"'"}'elseurl="http://${HEALTH_CHECK_IP}:${port}${VIDEO_ANALYSIS_ENDPOINT}"payload='{"taskId":"'"$task_id"'","sourcePath":"'"$source_path"'"}'filog "🚀 调用分析接口: $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" ]; thenerror_exit "❌ 接口调用失败,HTTP 状态码: $http_code,响应: $(cat /tmp/curl_response_body.txt)"filog "✅ 接口调用成功,响应: $(cat /tmp/curl_response_body.txt)"# 返回时间戳用于结果目录匹配echo "$source_id"source_id_global="$source_id" }# 监控 end.log 生成 wait_for_end_log() {local identifier=$1 # 如 23-202508152100000local max_wait=600 # 最大等待 10分local check_interval=1local result_dir=""local start_time=$(date +%s)# 查找 RESULT_ROOT_PATH 下所有以 "$ALG_ID-" 开头的目录,按修改时间排序取最新local result_dirlocal result_dir_tmpif is_picture_algorithm "$ALG_ID"; thenresult_dir="$RESULT_ROOT_PATH/$identifier/end.log"result_dir_tmp="图片"elseresult_dir="$VIDEO_RESULT_ROOT_PATH/$identifier/end.log"result_dir_tmp="视频"filog "🔍 监控结果目录中是否包含 end.log, 结果文件路径: ${result_dir}"echo -n "${result_dir_tmp}分析中:"while true; doecho -n "=="if [ -n "$result_dir" ] && [ -f "$result_dir" ]; thenecho -e "\n"log "✅ 检测到 end.log: $result_dir"log "📁 end文件内容: $(cat ${result_dir})"breakfi# local elapsed=$(( $(date +%s) - start_time )) # if [ $elapsed -ge $max_wait ]; then # error_exit "❌ 超时:$max_wait 秒内未生成 end.log" # fisleep $check_intervaldone }convertDateTime(){local timestamp=$1# 截取各部分year=${timestamp:0:4} # 2025month=${timestamp:4:2} # 08day=${timestamp:6:2} # 23hour=${timestamp:8:2} # 16minute=${timestamp:10:2} # 14second=${timestamp:12:2} # 04millisecond=${timestamp:14:3} # 059# 拼接格式formatted="${year}-${month}-${day} ${hour}:${minute}:${second}.${millisecond}"echo "$formatted"return 0 }# 计算推理时间并生成报告 duration: 单位毫秒 generate_report() {local start_time=$1local 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=0if [ "$media_count" -gt 0 ]; thenavg_time_per_item=$(echo "scale=2; $duration / $media_count" | bc -l)filog "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# 修改为指定算法编号 素材路径 sourceIDlog "⭐⭐⭐⭐⭐ 测评脚本执行中... 算法编号: $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"; thenlog "✅ 评测执行成功"elselog "⚠️ 评测脚本执行失败,退出码: $?"fielselog "⚠️ 评测脚本不存在或不可执行: $AUTO_TEST_SCRIPT"fi }# 验证单个算法 verify_algorithm() {ALG_ID=$(printf "%02d" $((10#$1))) # 格式化为两位数# 设置 trap:无论脚本如何退出(正常或错误),都执行清理trap 'stop_container $ALG_ID' EXITlog "🔍 开始验证算法: $ALG_ID"# 1. 查找启动脚本SCRIPT_PATH="${SCRIPT_DIR}/${IMAGE_SCRIPT_PREFIX}_${ALG_ID}.sh"if [ ! -f "$SCRIPT_PATH" ]; thenlog "⏭️ 未找到启动脚本: $SCRIPT_PATH,跳过"return 0fi#先判断路径是否存在local source_path="${TEST_IMAGE_PATH}/${ALG_ID}/"log "source_path:$source_path"if [ ! -d "$source_path" ]; thenlog "❌ 素材路径不存在: $source_path 跳过"return 0fialg_handle_num=$(cat $SCRIPT_PATH | grep "AlgHdlSum=")# 2. 启动镜像log "🐳 启动容器: $SCRIPT_PATH"if bash "$SCRIPT_PATH"; thenlog "✅ 容器启动命令执行成功"elseerror_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" ]; thenlog "❌ 未能获取 source_id"elselog "sourceId trigger_analysis: ${source_id_global}"# 记录开始时间msSTART_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_tmpif is_picture_algorithm "$ALG_ID"; thenresult_dir_end_log_tmp="$RESULT_ROOT_PATH/${source_id_global}/end.log"elseresult_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}$ ]]; thenlog "✅ 检测到 end.log 并读取时间戳: $end_timestamp"elselog "⚠️ end.log 内容格式错误(非17位时间戳): $end_timestamp, 使用系统时间"end_timestamp=$(date "+%Y%m%d%H%M%S%3N")fiecho "$end_timestamp"# 7. 生成报告generate_report "$START_TIME" "$end_timestamp"# 8. 执行评测run_evaluation# 分析算法输出结果verify_alg_result $1fi# 9停止镜像stop_container $1log "✅ 算法 $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"; thenresult_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)elseresult_dir="$VIDEO_RESULT_ROOT_PATH/${source_id_global}"result_dir_tmp="视频"count=$(find "${dataPath}" -maxdepth 1 -name "*.mp4" -o -name "*.MP4" | wc -l)return 0filog "📊 ${result_dir_tmp}算法分析结果目录: ${result_dir}, ${result_dir_tmp}分析结果数量:${result_count}"log "📊 ${result_dir_tmp}目录:${dataPath}, ${result_dir_tmp}数量:${count}"if [ "${result_count}" -eq 0 ]; thenlog "❌ ${result_dir_tmp}分析结果为0,可能是分析失败"return 0fi# result_count != countif [ "${result_count}" -ne "${count}" ]; thenlog "❌ ${result_dir_tmp}分析结果数量不匹配: ${result_count} != ${count}"return 0fi# 挨个校验 结果的json文件local result_ok=0local result_fail=0echo -n "${result_dir_tmp}结果分析中..."for file in ${result_dir}/*; doext="${file##*.}"filename=$(basename "$file")base="${filename%.*}"if [[ "$ext" != "json" ]]; thencontinuefi# 获取json内容if [[ -s "$file" ]]; thenfile_content=$(cat $file)code=$(echo "${file_content}" | jq -r '.code')if [[ "$code" -eq 0 ]]; thenresult_ok=$((result_ok+1))elselog "❌ code 不为 0,错误码: $code, 结果文件:${file}"result_fail=$((result_fail+1))fielselog "❌ 检测结果为空 可能检测失败"fiecho -n "."doneecho -e "\n"log "✅ ${result_dir_tmp}分析结果校验完成: 成功 ${result_ok}, 失败 ${result_fail}" }# ============ 主程序 ============main() {log "🎉📌📌📌📌📌📌📌 日志保存在: $RUN_TEST_LOG"local target_alg=$1TEST_IMAGE_PATH=$2if [ -z "$TEST_IMAGE_PATH" ]; thenTEST_IMAGE_PATH="/data/test"echo "默认素材路径: $TEST_IMAGE_PATH"fiecho "素材路径: $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_LOGLOG_FILE="/data/logs/algorithm_verify_${TEST_ID}.log"echo "LOG_FILE: $LOG_FILE"# 输入IPHEALTH_CHECK_IP=$3if [ -z "$HEALTH_CHECK_IP" ]; thenHEALTH_CHECK_IP="127.0.0.1"echo "IP地址: $HEALTH_CHECK_IP"fiecho "IP地址: $HEALTH_CHECK_IP"if [ -z "$target_alg" ]; thenlog "📌 验证所有的需要 输入 all"elif [ "$target_alg" == "all" ]; thenlog "📌 默认验证算法编号 $ALGORITHM_START 到 $ALGORITHM_END 中存在的镜像"for i in $(seq $ALGORITHM_START $ALGORITHM_END); doif is_picture_algorithm "$i"; thenecho "分析图片$i"elseecho "分析视频$i"fiverify_algorithm "$i"doneelif [ "$target_alg" == "images" ]; thenlog "📌 仅测试图片算法的镜像"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[@]}"; doif is_picture_algorithm "$image_alg_id"; thenecho "分析图片$image_alg_id"elseecho "分析视频$image_alg_id"fiverify_algorithm "$image_alg_id"doneelif [[ $target_alg == array* ]]; thenecho "✅ 测试指定多个算法编号:$target_alg"# 去掉前缀 array:target_alg=${target_alg#array:}log "✅✅✅✅✅ 目标算法编号: $target_alg"# 逗号分割IFS=',' read -r -a array <<< "$target_alg"for alg_id in "${array[@]}"; doecho "算法编号:${alg_id}"if is_picture_algorithm "$alg_id"; thenecho "分析图片$alg_id"elseecho "分析视频$alg_id"fiverify_algorithm "$alg_id"doneelif [ "$target_alg" == "temp" ]; thenlog "📌 仅测试图片算法的镜像"image_alg_ids=(7 13 22 25)for image_alg_id in "${image_alg_ids[@]}"; doif is_picture_algorithm "$image_alg_id"; thenecho "分析图片$image_alg_id"elseecho "分析视频$image_alg_id"fiverify_algorithm "$image_alg_id"doneelif [ "$target_alg" == "video" ]; thenlog "📌 仅测试视频算法的镜像"video_alg_ids=(14 37 5 6 8)for video_alg_id in "${video_alg_ids[@]}"; doif is_picture_algorithm "${video_alg_id}"; thenecho "分析图片${video_alg_id}"elseecho "分析视频${video_alg_id}"fiverify_algorithm "${video_alg_id}"doneelseif [[ "$target_alg" =~ ^[0-9]{1,2}$ ]] && [ "$target_alg" -ge $ALGORITHM_START ] && [ "$target_alg" -le $ALGORITHM_END ]; thenlog "📌 正在验证指定算法: $target_alg"verify_algorithm "$target_alg"elseerror_exit "❌ 无效的算法编号: $target_alg,应为 $ALGORITHM_START-$ALGORITHM_END 之间的整数"fifilog "🎉 所有验证任务完成,日志保存在: $LOG_FILE"log "🎉🎉🎉📌📌📌📌📌📌 日志保存在: $RUN_TEST_LOG"cp $LOG_FILE ${RUN_TEST_LOG} }# ============ 执行 ============ main "$@"