Files
ueransim/scripts/auto_n2_handover.sh
2025-09-11 06:16:32 +00:00

347 lines
9.6 KiB
Bash
Executable File
Vendored
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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连接到gNB1UE 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连接到gNB2UE 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 "$@"