#!/bin/bash # Auto N2 Handover Test Script # 自动启动两个基站和一个UE,完成N2切换测试 set -e # 颜色定义 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # 日志函数 log_info() { echo -e "${GREEN}[INFO]${NC} $1" } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" } log_error() { echo -e "${RED}[ERROR]${NC} $1" } log_step() { echo -e "${BLUE}[STEP]${NC} $1" } # 获取脚本目录 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" UERANSIM_DIR="$(dirname "$SCRIPT_DIR")" # 配置文件路径 GNB1_CONFIG="$UERANSIM_DIR/config/free5gc-gnb.yaml" GNB2_CONFIG="$UERANSIM_DIR/config/free5gc-gnb2.yaml" UE_CONFIG="$UERANSIM_DIR/config/free5gc-ue.yaml" # 日志目录 LOG_DIR="$UERANSIM_DIR/logs" mkdir -p "$LOG_DIR" # PID文件 GNB1_PID_FILE="$LOG_DIR/gnb1.pid" GNB2_PID_FILE="$LOG_DIR/gnb2.pid" UE_PID_FILE="$LOG_DIR/ue.pid" # 清理函数 cleanup() { log_step "开始清理进程..." # 停止UE if [ -f "$UE_PID_FILE" ]; then UE_PID=$(cat "$UE_PID_FILE") if kill -0 "$UE_PID" 2>/dev/null; then log_info "停止UE进程 (PID: $UE_PID)" sudo kill "$UE_PID" || true fi rm -f "$UE_PID_FILE" fi # 停止gNB进程 if [ -f "$GNB1_PID_FILE" ]; then GNB1_PID=$(cat "$GNB1_PID_FILE") if kill -0 "$GNB1_PID" 2>/dev/null; then log_info "停止gNB1进程 (PID: $GNB1_PID)" sudo kill "$GNB1_PID" || true fi rm -f "$GNB1_PID_FILE" fi if [ -f "$GNB2_PID_FILE" ]; then GNB2_PID=$(cat "$GNB2_PID_FILE") if kill -0 "$GNB2_PID" 2>/dev/null; then log_info "停止gNB2进程 (PID: $GNB2_PID)" sudo kill "$GNB2_PID" || true fi rm -f "$GNB2_PID_FILE" fi # 清理所有相关进程 sudo pkill -f "nr-gnb" || true sudo pkill -f "nr-ue" || true log_info "清理完成" } # 检查进程状态 check_process() { local pid=$1 local name=$2 if kill -0 "$pid" 2>/dev/null; then log_info "$name 运行正常 (PID: $pid)" return 0 else log_error "$name 进程异常退出" return 1 fi } # 等待gNB连接到AMF wait_gnb_ready() { local gnb_name=$1 local log_file=$2 local max_wait=30 local count=0 log_info "等待 $gnb_name 连接到AMF..." while [ $count -lt $max_wait ]; do if grep -q "NG Setup procedure is successful" "$log_file" 2>/dev/null; then log_info "$gnb_name 成功连接到AMF" return 0 fi sleep 1 count=$((count + 1)) done log_error "$gnb_name 连接AMF超时" return 1 } # 等待UE注册完成 wait_ue_registered() { local log_file=$1 local max_wait=30 local count=0 log_info "等待UE完成注册..." while [ $count -lt $max_wait ]; do if grep -q "Initial Registration is successful" "$log_file" 2>/dev/null; then log_info "UE注册成功" return 0 fi sleep 1 count=$((count + 1)) done log_error "UE注册超时" return 1 } # 获取UE ID get_ue_id() { local gnb_name=$1 local ue_list_output ue_list_output=$("$UERANSIM_DIR/build/nr-cli" "$gnb_name" -e "ue-list" 2>/dev/null || echo "") if [ -n "$ue_list_output" ]; then echo "$ue_list_output" | grep "ue-id:" | head -1 | sed 's/.*ue-id: *//' | awk '{print $1}' else echo "" fi } # 检查handover是否成功 check_handover_success() { local source_gnb=$1 local target_gnb=$2 local original_ue_id=$3 log_info "检查handover结果..." # 检查目标gNB是否有UE local target_ue_id target_ue_id=$(get_ue_id "$target_gnb") # 检查源gNB的UE数量 local source_ue_count source_ue_count=$("$UERANSIM_DIR/build/nr-cli" "$source_gnb" -e "ue-list" 2>/dev/null | grep -c "ue-id:" || echo "0") log_info "源gNB ($source_gnb) UE数量: $source_ue_count" log_info "目标gNB ($target_gnb) UE ID: ${target_ue_id:-无}" # 如果目标gNB有UE,认为handover成功(源gNB可能需要更多时间清理) if [ -n "$target_ue_id" ]; then log_info "✅ Handover成功!" log_info " 目标gNB ($target_gnb): UE ID = $target_ue_id" if [ "$source_ue_count" -eq 0 ]; then log_info " 源gNB ($source_gnb): UE已完全清理" else log_warn " 源gNB ($source_gnb): UE还未完全清理 (数量: $source_ue_count) - 这是正常的,清理可能需要更多时间" fi return 0 else log_error "❌ Handover失败!" log_error " 源gNB UE数量: $source_ue_count" log_error " 目标gNB UE ID: ${target_ue_id:-无}" return 1 fi } # 主函数 main() { log_step "=== 自动N2 Handover测试开始 ===" # 设置退出时清理 trap cleanup EXIT # 1. 清理现有进程 log_step "步骤1: 清理现有进程" cleanup sleep 2 # 2. 启动gNB1 (目标gNB) log_step "步骤2: 启动gNB1 (192.168.8.117)" sudo "$UERANSIM_DIR/build/nr-gnb" -c "$GNB1_CONFIG" > "$LOG_DIR/gnb1.log" 2>&1 & GNB1_PID=$! echo "$GNB1_PID" > "$GNB1_PID_FILE" log_info "gNB1已启动 (PID: $GNB1_PID)" # 等待gNB1连接AMF if ! wait_gnb_ready "gNB1" "$LOG_DIR/gnb1.log"; then log_error "gNB1启动失败" exit 1 fi # 3. 启动gNB2 (源gNB) log_step "步骤3: 启动gNB2 (192.168.8.118)" sleep 3 sudo "$UERANSIM_DIR/build/nr-gnb" -c "$GNB2_CONFIG" > "$LOG_DIR/gnb2.log" 2>&1 & GNB2_PID=$! echo "$GNB2_PID" > "$GNB2_PID_FILE" log_info "gNB2已启动 (PID: $GNB2_PID)" # 等待gNB2连接AMF if ! wait_gnb_ready "gNB2" "$LOG_DIR/gnb2.log"; then log_error "gNB2启动失败" exit 1 fi # 4. 启动UE并连接到gNB2 log_step "步骤4: 启动UE并连接到gNB2" sleep 3 sudo "$UERANSIM_DIR/build/nr-ue" -c "$UE_CONFIG" > "$LOG_DIR/ue.log" 2>&1 & UE_PID=$! echo "$UE_PID" > "$UE_PID_FILE" log_info "UE已启动 (PID: $UE_PID)" # 等待UE注册完成 if ! wait_ue_registered "$LOG_DIR/ue.log"; then log_error "UE注册失败" exit 1 fi # 5. 检查初始状态 log_step "步骤5: 检查初始UE分布" sleep 2 # 确定UE连接到哪个gNB log_info "检查gNB1的UE列表..." UE_LIST_GNB1=$("$UERANSIM_DIR/build/nr-cli" "UERANSIM-gnb-460-0-1" -e "ue-list" 2>/dev/null || echo "") log_info "gNB1 UE列表输出: '$UE_LIST_GNB1'" log_info "检查gNB2的UE列表..." UE_LIST_GNB2=$("$UERANSIM_DIR/build/nr-cli" "UERANSIM-gnb-460-0-16" -e "ue-list" 2>/dev/null || echo "") log_info "gNB2 UE列表输出: '$UE_LIST_GNB2'" UE_ID_GNB1=$(echo "$UE_LIST_GNB1" | grep "ue-id:" | head -1 | sed 's/.*ue-id: *//' | awk '{print $1}') UE_ID_GNB2=$(echo "$UE_LIST_GNB2" | grep "ue-id:" | head -1 | sed 's/.*ue-id: *//' | awk '{print $1}') if [ -n "$UE_ID_GNB1" ]; then SOURCE_GNB="UERANSIM-gnb-460-0-1" TARGET_GNB="UERANSIM-gnb-460-0-16" TARGET_CELL="16" UE_ID="$UE_ID_GNB1" log_info "UE连接到gNB1,UE ID: $UE_ID" elif [ -n "$UE_ID_GNB2" ]; then SOURCE_GNB="UERANSIM-gnb-460-0-16" TARGET_GNB="UERANSIM-gnb-460-0-1" TARGET_CELL="1" UE_ID="$UE_ID_GNB2" log_info "UE连接到gNB2,UE ID: $UE_ID" else log_error "未找到UE连接" exit 1 fi # 6. 执行handover log_step "步骤6: 执行N2 Handover" log_info "从 $SOURCE_GNB 切换UE $UE_ID 到目标Cell $TARGET_CELL" HANDOVER_OUTPUT=$("$UERANSIM_DIR/build/nr-cli" "$SOURCE_GNB" -e "handover $UE_ID $TARGET_CELL" 2>&1) log_info "Handover命令输出: $HANDOVER_OUTPUT" # 7. 等待handover完成 log_step "步骤7: 等待handover完成" sleep 15 # 等待handover完成 (8秒清理时间 + 7秒缓冲) # 8. 验证handover结果 log_step "步骤8: 验证handover结果" if check_handover_success "$SOURCE_GNB" "$TARGET_GNB" "$UE_ID"; then log_step "=== ✅ N2 Handover测试成功完成 ===" # 显示最终状态 echo "" log_info "最终UE分布:" echo "gNB1 (UERANSIM-gnb-460-0-1):" "$UERANSIM_DIR/build/nr-cli" "UERANSIM-gnb-460-0-1" -e "ue-list" 2>/dev/null || echo " 无UE连接" echo "" echo "gNB2 (UERANSIM-gnb-460-0-16):" "$UERANSIM_DIR/build/nr-cli" "UERANSIM-gnb-460-0-16" -e "ue-list" 2>/dev/null || echo " 无UE连接" # 保持运行以便观察 echo "" log_info "测试完成!按Ctrl+C退出并清理进程" while true; do sleep 5 # 检查进程状态 if ! check_process "$GNB1_PID" "gNB1" || ! check_process "$GNB2_PID" "gNB2" || ! check_process "$UE_PID" "UE"; then log_warn "发现进程异常,退出测试" break fi done else log_step "=== ❌ N2 Handover测试失败 ===" exit 1 fi } # 检查是否以root权限运行 if [ "$EUID" -ne 0 ]; then echo -e "${RED}[ERROR]${NC} 请使用sudo运行此脚本" exit 1 fi # 检查构建目录 if [ ! -f "$UERANSIM_DIR/build/nr-gnb" ] || [ ! -f "$UERANSIM_DIR/build/nr-ue" ]; then log_error "找不到UERANSIM构建文件,请先运行make编译" exit 1 fi # 检查配置文件 for config in "$GNB1_CONFIG" "$GNB2_CONFIG" "$UE_CONFIG"; do if [ ! -f "$config" ]; then log_error "找不到配置文件: $config" exit 1 fi done # 运行主函数 main "$@"