// // This file is a part of UERANSIM project. // Copyright (c) 2023 ALİ GÜNGÖR. // // https://github.com/aligungr/UERANSIM/ // See README, LICENSE, and CONTRIBUTING files for licensing details. // #include "task.hpp" #include #include #include namespace nr::gnb { // 切换清理定时器 static constexpr const int TIMER_ID_HANDOVER_CLEANUP = 3001; static constexpr const int TIMER_PERIOD_HANDOVER_CLEANUP = 5000; // 5秒检查一次 NgapTask::NgapTask(TaskBase *base) : m_base{base}, m_ueNgapIdCounter{}, m_ueIdCounter{100}, m_downlinkTeidCounter{}, m_isInitialized{} { m_logger = base->logBase->makeUniqueLogger("ngap"); } void NgapTask::onStart() { for (auto &amfConfig : m_base->config->amfConfigs) createAmfContext(amfConfig); if (m_amfCtx.empty()) m_logger->warn("No AMF configuration is provided"); for (auto &amfCtx : m_amfCtx) { auto msg = std::make_unique(NmGnbSctp::CONNECTION_REQUEST); msg->clientId = amfCtx.second->ctxId; msg->localAddress = m_base->config->ngapIp; msg->localPort = 0; msg->remoteAddress = amfCtx.second->address; msg->remotePort = amfCtx.second->port; msg->ppid = sctp::PayloadProtocolId::NGAP; msg->associatedTask = this; m_base->sctpTask->push(std::move(msg)); } // 启动切换清理定时器 setTimer(TIMER_ID_HANDOVER_CLEANUP, TIMER_PERIOD_HANDOVER_CLEANUP); } void NgapTask::onLoop() { auto msg = take(); if (!msg) return; switch (msg->msgType) { case NtsMessageType::TIMER_EXPIRED: { auto &w = dynamic_cast(*msg); if (w.timerId == TIMER_ID_HANDOVER_CLEANUP) { checkAndCleanupExpiredHandovers(); setTimer(TIMER_ID_HANDOVER_CLEANUP, TIMER_PERIOD_HANDOVER_CLEANUP); } break; } case NtsMessageType::GNB_RRC_TO_NGAP: { auto &w = dynamic_cast(*msg); switch (w.present) { case NmGnbRrcToNgap::INITIAL_NAS_DELIVERY: { handleInitialNasTransport(w.ueId, w.pdu, w.rrcEstablishmentCause, w.sTmsi); break; } case NmGnbRrcToNgap::UPLINK_NAS_DELIVERY: { handleUplinkNasTransport(w.ueId, w.pdu); break; } case NmGnbRrcToNgap::RADIO_LINK_FAILURE: { handleRadioLinkFailure(w.ueId); break; } case NmGnbRrcToNgap::HANDOVER_TRIGGER: { // 处理来自RRC的切换触发 triggerHandover(w.ueId, w.targetCellId, w.targetGnbId); break; } case NmGnbRrcToNgap::HANDOVER_REQUEST_ACK: { // 处理来自RRC的切换请求确认 sendHandoverRequestAcknowledge(w.ueId, w.sourceToTargetContainer); break; } case NmGnbRrcToNgap::HANDOVER_REQUEST_FAILURE: { // 处理来自RRC的切换请求失败 m_logger->err("RRC reported handover request failure for UE {}", w.ueId); // 清理切换上下文 auto *ueCtx = findUeContext(w.ueId); if (ueCtx) { // 删除为切换创建的UE上下文 deleteUeContext(w.ueId); m_logger->info("Cleaned up UE context {} due to handover failure", w.ueId); } break; } case NmGnbRrcToNgap::HANDOVER_RRC_COMPLETE: { // UE已在目标侧完成RRC重配置,正式发起Path Switch sendPathSwitchRequest(w.ueId); break; } } break; } case NtsMessageType::GNB_SCTP: { auto &w = dynamic_cast(*msg); switch (w.present) { case NmGnbSctp::ASSOCIATION_SETUP: handleAssociationSetup(w.clientId, w.associationId, w.inStreams, w.outStreams); break; case NmGnbSctp::RECEIVE_MESSAGE: handleSctpMessage(w.clientId, w.stream, w.buffer); break; case NmGnbSctp::ASSOCIATION_SHUTDOWN: handleAssociationShutdown(w.clientId); break; default: m_logger->unhandledNts(*msg); break; } break; } default: { m_logger->unhandledNts(*msg); break; } } } void NgapTask::onQuit() { for (auto &i : m_ueCtx) delete i.second; for (auto &i : m_amfCtx) delete i.second; m_ueCtx.clear(); m_amfCtx.clear(); } } // namespace nr::gnb