mirror of https://github.com/pvnis/srsRAN_4G.git
Merge branch 'next' into agpl_next
# Conflicts: # srsue/src/set_net_admin_caps.ccmaster
commit
358070fe30
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2023 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "srsgnb/hdr/stack/ric/e2sm.h"
|
||||||
|
#include "srsran/common/common.h"
|
||||||
|
#include "srsran/common/interfaces_common.h"
|
||||||
|
#include "srsran/interfaces/enb_metrics_interface.h"
|
||||||
|
#include "srsran/srsran.h"
|
||||||
|
|
||||||
|
#ifndef SRSRAN_E2_INTERFACES_H
|
||||||
|
#define SRSRAN_E2_INTERFACES_H
|
||||||
|
|
||||||
|
namespace srsenb {
|
||||||
|
|
||||||
|
class e2_interface_metrics
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual bool pull_metrics(enb_metrics_t* m) = 0;
|
||||||
|
|
||||||
|
virtual bool register_e2sm(e2sm* sm) = 0;
|
||||||
|
virtual bool unregister_e2sm(e2sm* sm) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace srsenb
|
||||||
|
|
||||||
|
#endif // SRSRAN_ENB_INTERFACES_H
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2023 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* File: metrics_e2.h
|
||||||
|
* Description: Metrics class passing to E2 agent.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef SRSENB_METRICS_E2_H
|
||||||
|
#define SRSENB_METRICS_E2_H
|
||||||
|
|
||||||
|
#include "srsran/interfaces/e2_metrics_interface.h"
|
||||||
|
#include "srsran/interfaces/enb_metrics_interface.h"
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <queue>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#define METRICS_BUFFER_SIZE 20
|
||||||
|
namespace srsenb {
|
||||||
|
|
||||||
|
class metrics_e2 : public srsran::metrics_listener<enb_metrics_t>, public e2_interface_metrics
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
metrics_e2(enb_metrics_interface* enb_) : do_print(false) {}
|
||||||
|
void set_metrics(const enb_metrics_t& m, const uint32_t period_usec) override;
|
||||||
|
bool pull_metrics(enb_metrics_t* m) override;
|
||||||
|
void stop() override{};
|
||||||
|
|
||||||
|
bool register_e2sm(e2sm* sm) override;
|
||||||
|
bool unregister_e2sm(e2sm* sm) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::atomic<bool> do_print = {false};
|
||||||
|
std::queue<enb_metrics_t> metrics_queue;
|
||||||
|
enb_metrics_interface* enb = nullptr;
|
||||||
|
std::vector<e2sm*> e2sm_vec;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace srsenb
|
||||||
|
|
||||||
|
#endif // SRSENB_METRICS_STDOUT_H
|
@ -0,0 +1,59 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2023 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "srsenb/hdr/metrics_e2.h"
|
||||||
|
#include "srsran/phy/utils/vector.h"
|
||||||
|
|
||||||
|
using namespace srsenb;
|
||||||
|
|
||||||
|
void metrics_e2::set_metrics(const enb_metrics_t& m, const uint32_t period_usec)
|
||||||
|
{
|
||||||
|
if (metrics_queue.size() > METRICS_BUFFER_SIZE) {
|
||||||
|
metrics_queue.pop();
|
||||||
|
metrics_queue.push(m);
|
||||||
|
} else {
|
||||||
|
metrics_queue.push(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
// send new enb metrics to all registered SMs
|
||||||
|
for (auto sm_ : e2sm_vec) {
|
||||||
|
sm_->receive_e2_metrics_callback(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool metrics_e2::register_e2sm(e2sm* sm)
|
||||||
|
{
|
||||||
|
e2sm_vec.push_back(sm);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool metrics_e2::unregister_e2sm(e2sm* sm)
|
||||||
|
{
|
||||||
|
auto it = std::find(e2sm_vec.begin(), e2sm_vec.end(), sm);
|
||||||
|
if (it != e2sm_vec.end()) {
|
||||||
|
e2sm_vec.erase(it);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool metrics_e2::pull_metrics(enb_metrics_t* m)
|
||||||
|
{
|
||||||
|
if (enb != nullptr) {
|
||||||
|
if (!metrics_queue.empty()) {
|
||||||
|
*m = metrics_queue.front();
|
||||||
|
metrics_queue.pop();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
@ -0,0 +1,134 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2023 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef E2_AGENT_H
|
||||||
|
#define E2_AGENT_H
|
||||||
|
|
||||||
|
#include "srsgnb/hdr/stack/ric/e2ap.h"
|
||||||
|
#include "srsran/common/network_utils.h"
|
||||||
|
#include "srsran/common/stack_procedure.h"
|
||||||
|
#include "srsran/common/task_scheduler.h"
|
||||||
|
#include "srsran/common/threads.h"
|
||||||
|
#include "srsran/interfaces/e2_metrics_interface.h"
|
||||||
|
#include "srsran/srsran.h"
|
||||||
|
|
||||||
|
static const int e2ap_ppid = 70;
|
||||||
|
|
||||||
|
enum e2_msg_type_t {
|
||||||
|
E2_SETUP_REQUEST,
|
||||||
|
E2_SUB_RESPONSE,
|
||||||
|
E2_SUB_DEL_RESPONSE,
|
||||||
|
E2_INDICATION,
|
||||||
|
E2_RESET,
|
||||||
|
E2_RESET_RESPONSE
|
||||||
|
};
|
||||||
|
|
||||||
|
struct e2_agent_args_t {
|
||||||
|
bool enable;
|
||||||
|
std::string ric_ip;
|
||||||
|
uint32_t ric_port;
|
||||||
|
std::string ric_bind_ip;
|
||||||
|
uint32_t ric_bind_port;
|
||||||
|
int32_t max_ric_setup_retries;
|
||||||
|
uint32_t ric_connect_timer;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace srsenb {
|
||||||
|
class e2_agent : public srsran::thread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
e2_agent(srslog::basic_logger& logger, srsenb::e2_interface_metrics* _gnb_metrics);
|
||||||
|
~e2_agent() = default;
|
||||||
|
|
||||||
|
// Initiate and Stop
|
||||||
|
bool init(e2_agent_args_t args);
|
||||||
|
void stop();
|
||||||
|
void run_thread();
|
||||||
|
void tic();
|
||||||
|
bool is_ric_connected();
|
||||||
|
|
||||||
|
// Send messages to RIC
|
||||||
|
bool send_sctp(srsran::unique_byte_buffer_t& buf);
|
||||||
|
bool send_e2_msg(e2_msg_type_t msg_type);
|
||||||
|
bool queue_send_e2ap_pdu(e2_ap_pdu_c send_pdu);
|
||||||
|
bool send_e2ap_pdu(e2_ap_pdu_c send_pdu);
|
||||||
|
bool send_reset_response();
|
||||||
|
|
||||||
|
// Handle messages received from RIC
|
||||||
|
bool
|
||||||
|
handle_ric_rx_msg(srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags);
|
||||||
|
bool handle_e2_rx_pdu(srsran::byte_buffer_t* pdu);
|
||||||
|
bool handle_e2_init_msg(asn1::e2ap::init_msg_s& init_msg);
|
||||||
|
bool handle_e2_successful_outcome(asn1::e2ap::successful_outcome_s& successful_outcome);
|
||||||
|
bool handle_e2_unsuccessful_outcome(asn1::e2ap::unsuccessful_outcome_s& unsuccessful_outcome);
|
||||||
|
bool handle_e2_setup_response(e2setup_resp_s setup_response);
|
||||||
|
bool handle_ric_subscription_request(ricsubscription_request_s ric_subscription_request);
|
||||||
|
bool handle_ric_subscription_delete_request(ricsubscription_delete_request_s ricsubscription_delete_request);
|
||||||
|
bool handle_subscription_modification_request(uint32_t ric_subscription_modification_request);
|
||||||
|
bool handle_subscription_modification_confirm(uint32_t ric_subscription_modification_confirm);
|
||||||
|
bool handle_subscription_modification_refuse(uint32_t ric_subscription_modification_refuse);
|
||||||
|
|
||||||
|
bool handle_reset_response(reset_resp_s& reset_response);
|
||||||
|
bool handle_reset_request(reset_request_s& reset_request);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool connect_ric();
|
||||||
|
bool setup_e2();
|
||||||
|
|
||||||
|
e2_agent_args_t _args;
|
||||||
|
srsran::task_scheduler task_sched;
|
||||||
|
srsran::task_queue_handle ric_rece_task_queue;
|
||||||
|
srsran::unique_socket ric_socket;
|
||||||
|
srsran::socket_manager rx_sockets;
|
||||||
|
srslog::basic_logger& logger;
|
||||||
|
struct sockaddr_in ric_addr = {}; // RIC address
|
||||||
|
bool running = false;
|
||||||
|
bool ric_connected = false;
|
||||||
|
srsran::unique_timer ric_connect_timer;
|
||||||
|
srsran::unique_timer e2_setup_timeout;
|
||||||
|
|
||||||
|
srsenb::e2_interface_metrics* gnb_metrics = nullptr;
|
||||||
|
e2ap e2ap_;
|
||||||
|
|
||||||
|
// procedures
|
||||||
|
class e2_setup_proc_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct e2setupresult {
|
||||||
|
bool success = false;
|
||||||
|
enum class cause_t { timeout, failure } cause;
|
||||||
|
};
|
||||||
|
struct e2connectresult {
|
||||||
|
bool success = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit e2_setup_proc_t(e2_agent* e2_agent_) : e2_agent_ptr(e2_agent_) {}
|
||||||
|
srsran::proc_outcome_t init();
|
||||||
|
srsran::proc_outcome_t step() { return srsran::proc_outcome_t::yield; }
|
||||||
|
srsran::proc_outcome_t react(const e2connectresult& event);
|
||||||
|
srsran::proc_outcome_t react(const e2setupresult& event);
|
||||||
|
void then(const srsran::proc_state_t& result);
|
||||||
|
const char* name() const { return "RIC Connection"; }
|
||||||
|
uint16_t connect_count = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
srsran::proc_outcome_t start_ric_connection();
|
||||||
|
|
||||||
|
e2_agent* e2_agent_ptr = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
srsran::proc_t<e2_setup_proc_t> e2_setup_proc;
|
||||||
|
};
|
||||||
|
} // namespace srsenb
|
||||||
|
|
||||||
|
#endif /* E2_AGENT_H */
|
@ -0,0 +1,122 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2023 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "e2sm_kpm.h"
|
||||||
|
#include "srsran/asn1/e2ap.h"
|
||||||
|
#include "srsran/common/task_scheduler.h"
|
||||||
|
#include "srsran/common/timers.h"
|
||||||
|
#include "srsran/interfaces/e2_metrics_interface.h"
|
||||||
|
#include "srsran/interfaces/enb_metrics_interface.h"
|
||||||
|
#include "srsran/srsran.h"
|
||||||
|
|
||||||
|
#ifndef RIC_E2AP_H
|
||||||
|
#define RIC_E2AP_H
|
||||||
|
|
||||||
|
namespace srsenb {
|
||||||
|
using namespace asn1::e2ap;
|
||||||
|
using namespace asn1::e2sm_kpm;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t ric_id;
|
||||||
|
uint16_t plmn_id;
|
||||||
|
} global_ric_id_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
e2node_component_interface_type_e interface_type;
|
||||||
|
std::string amf_name;
|
||||||
|
std::string request_part;
|
||||||
|
std::string response_part;
|
||||||
|
} e2_node_component_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t ric_requestor_id;
|
||||||
|
uint32_t ric_instance_id;
|
||||||
|
uint32_t ra_nfunction_id;
|
||||||
|
std::vector<uint32_t> admitted_actions;
|
||||||
|
std::vector<uint32_t> not_admitted_actions;
|
||||||
|
} ric_subscription_reponse_t;
|
||||||
|
|
||||||
|
class e2_agent;
|
||||||
|
|
||||||
|
class e2ap
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
e2ap(srslog::basic_logger& logger,
|
||||||
|
e2_agent* _e2_agent,
|
||||||
|
srsenb::e2_interface_metrics* _gnb_metrics,
|
||||||
|
srsran::task_scheduler* _task_sched_ptr);
|
||||||
|
~e2ap();
|
||||||
|
|
||||||
|
e2_ap_pdu_c generate_setup_request();
|
||||||
|
int process_setup_response(e2setup_resp_s setup_response);
|
||||||
|
int process_setup_failure();
|
||||||
|
int process_subscription_request(ricsubscription_request_s ric_subscription_request);
|
||||||
|
int process_subscription_delete_request(ricsubscription_delete_request_s ricsubscription_delete_request);
|
||||||
|
int process_subscription_modification_request(uint32_t ric_subscription_modification_request);
|
||||||
|
int process_subscription_modification_confirm(uint32_t ric_subscription_modification_confirm);
|
||||||
|
int process_subscription_modification_refuse(uint32_t ric_subscription_modification_refuse);
|
||||||
|
e2_ap_pdu_c generate_subscription_response(ric_subscription_reponse_t ric_subscription_reponse);
|
||||||
|
e2_ap_pdu_c generate_subscription_failure(ric_subscription_reponse_t ric_subscription_reponse);
|
||||||
|
e2_ap_pdu_c generate_subscription_delete_response(ric_subscription_reponse_t ric_subscription_reponse);
|
||||||
|
e2_ap_pdu_c generate_subscription_delete_failure(ric_subscription_reponse_t ric_subscription_reponse);
|
||||||
|
e2_ap_pdu_c generate_subscription_delete_required(ric_subscription_reponse_t ric_subscription_reponse);
|
||||||
|
e2_ap_pdu_c generate_subscription_modification_response();
|
||||||
|
e2_ap_pdu_c generate_subscription_modification_failure();
|
||||||
|
e2_ap_pdu_c generate_subscription_modification_required();
|
||||||
|
|
||||||
|
e2_ap_pdu_c generate_indication(ric_indication_t& ric_indication);
|
||||||
|
e2_ap_pdu_c generate_reset_request();
|
||||||
|
e2_ap_pdu_c generate_reset_response();
|
||||||
|
int process_reset_request(reset_request_s reset_request);
|
||||||
|
int process_reset_response(reset_resp_s reset_response);
|
||||||
|
|
||||||
|
int process_e2_setup_failure(e2setup_fail_s e2setup_failure);
|
||||||
|
int process_ric_service_update_failure(ricservice_upd_fail_s ric_service_update_failure);
|
||||||
|
int process_e2_node_config_update_failure(e2node_cfg_upd_fail_s e2node_config_update_failure);
|
||||||
|
int process_e2_removal_failure(e2_removal_fail_s e2_remove_failure);
|
||||||
|
|
||||||
|
bool queue_send_e2ap_pdu(e2_ap_pdu_c e2ap_pdu);
|
||||||
|
|
||||||
|
int get_reset_id();
|
||||||
|
bool get_func_desc(uint32_t ran_func_id, RANfunction_description& fdesc);
|
||||||
|
bool send_setup_request() { return !e2_established && pending_e2_setup; }
|
||||||
|
|
||||||
|
class ric_subscription;
|
||||||
|
|
||||||
|
private:
|
||||||
|
srslog::basic_logger& logger;
|
||||||
|
e2_agent* _e2_agent;
|
||||||
|
e2sm_kpm e2sm_;
|
||||||
|
bool e2_established = false;
|
||||||
|
srsran::unique_timer e2_procedure_timeout;
|
||||||
|
bool pending_e2_setup = true;
|
||||||
|
bool pending_e2_node_config_update = false;
|
||||||
|
bool pending_ric_service_update = false;
|
||||||
|
bool pending_e2_removal = false;
|
||||||
|
|
||||||
|
int setup_procedure_transaction_id = 0;
|
||||||
|
uint64_t plmn_id = 0x05f510;
|
||||||
|
uint64_t gnb_id = 1;
|
||||||
|
global_ric_id_t global_ric_id = {};
|
||||||
|
std::map<uint32_t, RANfunction_description> ran_functions;
|
||||||
|
srsenb::e2_interface_metrics* gnb_metrics = nullptr;
|
||||||
|
srsran::task_scheduler* task_sched_ptr = nullptr;
|
||||||
|
bool reset_response_received = false;
|
||||||
|
int reset_transaction_id = 1;
|
||||||
|
cause_c reset_cause = cause_c();
|
||||||
|
int reset_id = 1;
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<ric_subscription> > active_subscriptions;
|
||||||
|
};
|
||||||
|
} // namespace srsenb
|
||||||
|
#endif /* RIC_E2AP_H */
|
@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2023 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRSRAN_E2AP_RIC_SUBSCRIPTION_H
|
||||||
|
#define SRSRAN_E2AP_RIC_SUBSCRIPTION_H
|
||||||
|
|
||||||
|
#include "e2ap.h"
|
||||||
|
#include "srsran/common/task_scheduler.h"
|
||||||
|
#include "srsran/common/threads.h"
|
||||||
|
#include "srsran/srsran.h"
|
||||||
|
|
||||||
|
namespace srsenb {
|
||||||
|
|
||||||
|
class e2ap::ric_subscription
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ric_subscription(e2ap* e2ap, ricsubscription_request_s ric_subscription_request);
|
||||||
|
virtual ~ric_subscription() { parent = nullptr; };
|
||||||
|
|
||||||
|
uint32_t get_ric_requestor_id() { return ric_requestor_id; };
|
||||||
|
uint32_t get_ric_instance_id() { return ric_instance_id; };
|
||||||
|
bool is_initialized() { return initialized; };
|
||||||
|
|
||||||
|
void start_subscription();
|
||||||
|
void delete_subscription();
|
||||||
|
|
||||||
|
bool process_subscription_modification_request(uint32_t ric_subscription_modification_request);
|
||||||
|
bool process_subscription_modification_confirm(uint32_t ric_subscription_modification_confirm);
|
||||||
|
bool process_subscription_modification_refuse(uint32_t ric_subscription_modification_refuse);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void _send_subscription_response();
|
||||||
|
void _send_subscription_failure();
|
||||||
|
void _send_ric_indication();
|
||||||
|
uint32_t _generate_ric_indication_sn();
|
||||||
|
|
||||||
|
e2ap* parent = nullptr;
|
||||||
|
bool initialized = false;
|
||||||
|
|
||||||
|
uint32_t ric_requestor_id;
|
||||||
|
uint32_t ric_instance_id;
|
||||||
|
uint16_t ra_nfunction_id;
|
||||||
|
|
||||||
|
e2sm* sm_ptr = nullptr;
|
||||||
|
uint32_t reporting_period = 0; // ms
|
||||||
|
srsran::unique_timer reporting_timer; // for RIC indication reporting
|
||||||
|
|
||||||
|
std::vector<E2AP_RIC_action_t> admitted_actions;
|
||||||
|
std::vector<uint32_t> not_admitted_actions;
|
||||||
|
|
||||||
|
uint32_t _ric_indication_sn_gen = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace srsenb
|
||||||
|
#endif // SRSRAN_E2AP_RIC_SUBSCRIPTION_H
|
@ -0,0 +1,107 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2023 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "srsran/asn1/e2ap.h"
|
||||||
|
#include "srsran/common/byte_buffer.h"
|
||||||
|
#include "srsran/common/task_scheduler.h"
|
||||||
|
#include "srsran/interfaces/enb_metrics_interface.h"
|
||||||
|
#include "srsran/srsran.h"
|
||||||
|
|
||||||
|
#ifndef SRSRAN_E2SM_H
|
||||||
|
#define SRSRAN_E2SM_H
|
||||||
|
|
||||||
|
using namespace asn1::e2ap;
|
||||||
|
using namespace srsenb;
|
||||||
|
|
||||||
|
struct RANfunction_description;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
enum e2sm_event_trigger_type_t { E2SM_REPORT, E2SM_INSERT, E2SM_POLICY, UNKNOWN_TRIGGER };
|
||||||
|
e2sm_event_trigger_type_t type;
|
||||||
|
uint64_t report_period;
|
||||||
|
} RIC_event_trigger_definition_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t ric_action_id;
|
||||||
|
ri_caction_type_e ric_action_type;
|
||||||
|
uint32_t sm_local_ric_action_id;
|
||||||
|
} E2AP_RIC_action_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t ric_requestor_id;
|
||||||
|
uint32_t ric_instance_id;
|
||||||
|
uint32_t ra_nfunction_id;
|
||||||
|
uint32_t ri_caction_id;
|
||||||
|
bool ri_indication_sn_present;
|
||||||
|
uint32_t ri_indication_sn;
|
||||||
|
ri_cind_type_e indication_type;
|
||||||
|
srsran::unique_byte_buffer_t ri_cind_hdr;
|
||||||
|
srsran::unique_byte_buffer_t ri_cind_msg;
|
||||||
|
} ric_indication_t;
|
||||||
|
|
||||||
|
class e2sm
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
e2sm();
|
||||||
|
e2sm(std::string short_name,
|
||||||
|
std::string oid,
|
||||||
|
std::string func_description,
|
||||||
|
uint32_t revision,
|
||||||
|
srsran::task_scheduler* _task_sched_ptr) :
|
||||||
|
_short_name(short_name),
|
||||||
|
_oid(oid),
|
||||||
|
_func_description(func_description),
|
||||||
|
_revision(revision),
|
||||||
|
task_sched_ptr(_task_sched_ptr){};
|
||||||
|
virtual ~e2sm() = default;
|
||||||
|
|
||||||
|
std::string get_short_name() { return _short_name; };
|
||||||
|
std::string get_oid() { return _oid; };
|
||||||
|
std::string get_func_description() { return _func_description; };
|
||||||
|
uint32_t get_revision() { return _revision; };
|
||||||
|
|
||||||
|
virtual bool generate_ran_function_description(RANfunction_description& desc, ra_nfunction_item_s& ran_func) = 0;
|
||||||
|
virtual bool process_ric_event_trigger_definition(ricsubscription_request_s subscription_request,
|
||||||
|
RIC_event_trigger_definition_t& event_def) = 0;
|
||||||
|
virtual bool process_ric_action_definition(ri_caction_to_be_setup_item_s ric_action,
|
||||||
|
E2AP_RIC_action_t& action_entry) = 0;
|
||||||
|
virtual bool remove_ric_action_definition(E2AP_RIC_action_t& action_entry) = 0;
|
||||||
|
virtual bool generate_ric_indication_content(E2AP_RIC_action_t& action_entry, ric_indication_t& ric_indication) = 0;
|
||||||
|
|
||||||
|
virtual void receive_e2_metrics_callback(const enb_metrics_t& m) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint32_t _get_local_action_id() { return _registered_action_id_gen; };
|
||||||
|
uint32_t _generate_new_local_action_id() { return _registered_action_id_gen++; };
|
||||||
|
|
||||||
|
srsran::task_scheduler* task_sched_ptr = nullptr;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::string _short_name;
|
||||||
|
const std::string _oid;
|
||||||
|
const std::string _func_description;
|
||||||
|
const uint32_t _revision = 0;
|
||||||
|
|
||||||
|
uint32_t _registered_action_id_gen = 1000;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RANfunction_description {
|
||||||
|
bool accepted = false;
|
||||||
|
int function_instance = 0;
|
||||||
|
e2sm* sm_ptr = nullptr;
|
||||||
|
std::string function_shortname;
|
||||||
|
std::string function_e2_sm_oid;
|
||||||
|
std::string function_desc;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SRSRAN_E2SM_H
|
@ -0,0 +1,76 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2023 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "e2sm.h"
|
||||||
|
#include "e2sm_kpm_common.h"
|
||||||
|
#include "srsran/asn1/e2ap.h"
|
||||||
|
#include "srsran/asn1/e2sm.h"
|
||||||
|
#include "srsran/asn1/e2sm_kpm_v2.h"
|
||||||
|
#include "srsran/srsran.h"
|
||||||
|
|
||||||
|
#ifndef RIC_E2SM_KPM_H
|
||||||
|
#define RIC_E2SM_KPM_H
|
||||||
|
|
||||||
|
using namespace asn1::e2ap;
|
||||||
|
using namespace asn1::e2sm_kpm;
|
||||||
|
|
||||||
|
class e2sm_kpm_report_service;
|
||||||
|
|
||||||
|
class e2sm_kpm : public e2sm
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const std::string short_name;
|
||||||
|
static const std::string oid;
|
||||||
|
static const std::string func_description;
|
||||||
|
static const uint32_t revision;
|
||||||
|
|
||||||
|
e2sm_kpm(srslog::basic_logger& logger_, srsran::task_scheduler* _task_sched_ptr);
|
||||||
|
~e2sm_kpm();
|
||||||
|
|
||||||
|
virtual bool generate_ran_function_description(RANfunction_description& desc, ra_nfunction_item_s& ran_func);
|
||||||
|
virtual bool process_ric_event_trigger_definition(ricsubscription_request_s subscription_request,
|
||||||
|
RIC_event_trigger_definition_t& event_def);
|
||||||
|
virtual bool process_ric_action_definition(ri_caction_to_be_setup_item_s ric_action, E2AP_RIC_action_t& action_entry);
|
||||||
|
virtual bool remove_ric_action_definition(E2AP_RIC_action_t& action_entry);
|
||||||
|
virtual bool generate_ric_indication_content(E2AP_RIC_action_t& action_entry, ric_indication_t& ric_indication);
|
||||||
|
|
||||||
|
virtual void receive_e2_metrics_callback(const enb_metrics_t& m);
|
||||||
|
|
||||||
|
friend class e2sm_kpm_report_service;
|
||||||
|
friend class e2sm_kpm_report_service_style1;
|
||||||
|
friend class e2sm_kpm_report_service_style2;
|
||||||
|
friend class e2sm_kpm_report_service_style3;
|
||||||
|
friend class e2sm_kpm_report_service_style4;
|
||||||
|
friend class e2sm_kpm_report_service_style5;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _generate_indication_header(e2_sm_kpm_ind_hdr_s& hdr, srsran::unique_byte_buffer_t& buf);
|
||||||
|
bool _generate_indication_message(e2_sm_kpm_ind_msg_s& msg, srsran::unique_byte_buffer_t& buf);
|
||||||
|
bool _get_meas_definition(std::string meas_name, e2sm_kpm_metric_t& def);
|
||||||
|
std::vector<std::string> _get_supported_meas(uint32_t level_mask);
|
||||||
|
|
||||||
|
bool _collect_meas_value(e2sm_kpm_meas_def_t& meas_value, meas_record_item_c& item);
|
||||||
|
bool
|
||||||
|
_extract_integer_type_meas_value(e2sm_kpm_meas_def_t& meas_value, const enb_metrics_t& enb_metrics, uint32_t& value);
|
||||||
|
bool _extract_real_type_meas_value(e2sm_kpm_meas_def_t& meas_value, const enb_metrics_t& enb_metrics, float& value);
|
||||||
|
|
||||||
|
srslog::basic_logger& logger;
|
||||||
|
std::vector<e2sm_kpm_metric_t> supported_meas_types;
|
||||||
|
std::map<uint32_t, e2sm_kpm_report_service*> registered_actions_data;
|
||||||
|
|
||||||
|
srsran_random_t random_gen;
|
||||||
|
|
||||||
|
enb_metrics_t last_enb_metrics;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /*E2SM_KPM*/
|
@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2023 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "srsran/asn1/e2ap.h"
|
||||||
|
#include "srsran/asn1/e2sm.h"
|
||||||
|
#include "srsran/asn1/e2sm_kpm_v2.h"
|
||||||
|
#include "srsran/srsran.h"
|
||||||
|
|
||||||
|
#ifndef SRSRAN_E2SM_KPM_COMMON_H
|
||||||
|
#define SRSRAN_E2SM_KPM_COMMON_H
|
||||||
|
|
||||||
|
using namespace asn1::e2ap;
|
||||||
|
using namespace asn1::e2sm_kpm;
|
||||||
|
|
||||||
|
enum e2_metric_data_type_t { INTEGER, REAL };
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
std::string name;
|
||||||
|
bool supported;
|
||||||
|
e2_metric_data_type_t data_type;
|
||||||
|
std::string units;
|
||||||
|
bool min_val_present;
|
||||||
|
double min_val;
|
||||||
|
bool max_val_present;
|
||||||
|
double max_val;
|
||||||
|
uint32_t supported_labels;
|
||||||
|
uint32_t supported_scopes;
|
||||||
|
} e2sm_kpm_metric_t;
|
||||||
|
|
||||||
|
// TODO: define all labels and scopes
|
||||||
|
|
||||||
|
/* Labels supported for a metric */
|
||||||
|
enum e2sm_kpm_label_enum {
|
||||||
|
NO_LABEL = 0x0001,
|
||||||
|
MIN_LABEL = 0x0002,
|
||||||
|
MAX_LABEL = 0x0004,
|
||||||
|
AVG_LABEL = 0x0008,
|
||||||
|
SUM_LABEL = 0x0010,
|
||||||
|
UNKNOWN_LABEL = 0x8000
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string e2sm_kpm_label_2_str(e2sm_kpm_label_enum label);
|
||||||
|
|
||||||
|
/* Scopes supported for a metric */
|
||||||
|
enum e2sm_kpm_metric_scope_enum {
|
||||||
|
ENB_LEVEL = 0x0001,
|
||||||
|
CELL_LEVEL = 0x0002,
|
||||||
|
UE_LEVEL = 0x0004,
|
||||||
|
BEARER_LEVEL = 0x0008,
|
||||||
|
UNKNOWN_LEVEL = 0xffff
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
std::string name;
|
||||||
|
e2sm_kpm_label_enum label;
|
||||||
|
e2sm_kpm_metric_scope_enum scope;
|
||||||
|
meas_record_item_c::types data_type;
|
||||||
|
uint32_t ue_id; // TODO: do we need to use type ueid_c? or we translate to local RNTI?
|
||||||
|
uint32_t cell_id; // TODO: do we need to use type cgi_c? or we translate to local cell_id?
|
||||||
|
} e2sm_kpm_meas_def_t;
|
||||||
|
|
||||||
|
#endif // SRSRAN_E2SM_KPM_COMMON_H
|
@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2023 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRSRAN_E2SM_KPM_METRICS_H
|
||||||
|
#define SRSRAN_E2SM_KPM_METRICS_H
|
||||||
|
|
||||||
|
#include "e2sm_kpm_common.h"
|
||||||
|
#include "srsran/srsran.h"
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
// Measurements defined in 3GPP TS 28.552
|
||||||
|
std::vector<e2sm_kpm_metric_t> get_e2sm_kpm_28_552_metrics()
|
||||||
|
{
|
||||||
|
// TODO: add all metrics from 3GPP TS 28.552
|
||||||
|
std::vector<e2sm_kpm_metric_t> metrics;
|
||||||
|
// not supported metrics
|
||||||
|
metrics.push_back({"RRU.PrbTotDl", false, REAL, "%", true, 0, true, 100, NO_LABEL | AVG_LABEL, CELL_LEVEL | UE_LEVEL });
|
||||||
|
metrics.push_back({"RRU.PrbTotUl", false, REAL, "%", true, 0, true, 100, NO_LABEL | AVG_LABEL, CELL_LEVEL | UE_LEVEL });
|
||||||
|
// not supported metrics
|
||||||
|
metrics.push_back({"RRU.RachPreambleDedMean", false, REAL, "-", false, 0, false, 100, NO_LABEL, CELL_LEVEL | UE_LEVEL });
|
||||||
|
return metrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Measurements defined in 3GPP TS 32.425
|
||||||
|
std::vector<e2sm_kpm_metric_t> get_e2sm_kpm_34_425_metrics()
|
||||||
|
{
|
||||||
|
// TODO: add all metrics from 3GPP TS 32.425
|
||||||
|
std::vector<e2sm_kpm_metric_t> metrics;
|
||||||
|
return metrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
// E2SM_KPM O-RAN specific Measurements
|
||||||
|
std::vector<e2sm_kpm_metric_t> e2sm_kpm_oran_metrics()
|
||||||
|
{
|
||||||
|
// TODO: add all E2SM_KPM O-RAN specific Measurements
|
||||||
|
std::vector<e2sm_kpm_metric_t> metrics;
|
||||||
|
return metrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom Measurements
|
||||||
|
std::vector<e2sm_kpm_metric_t> e2sm_kpm_custom_metrics()
|
||||||
|
{
|
||||||
|
std::vector<e2sm_kpm_metric_t> metrics;
|
||||||
|
// supported metrics
|
||||||
|
metrics.push_back({"test", true, INTEGER, "", true, 0, true, 100, NO_LABEL, ENB_LEVEL | CELL_LEVEL | UE_LEVEL });
|
||||||
|
metrics.push_back({"random_int", true, INTEGER, "", true, 0, true, 100, NO_LABEL, CELL_LEVEL });
|
||||||
|
metrics.push_back({"cpu0_load", true, REAL, "", true, 0, true, 100, NO_LABEL, ENB_LEVEL });
|
||||||
|
metrics.push_back({"cpu_load", true, REAL, "", true, 0, true, 100, MIN_LABEL|MAX_LABEL|AVG_LABEL, ENB_LEVEL });
|
||||||
|
// not supported metrics
|
||||||
|
metrics.push_back({"test123", false, REAL, "", true, 0, true, 100, NO_LABEL, CELL_LEVEL | UE_LEVEL });
|
||||||
|
return metrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clang-format on
|
||||||
|
#endif // SRSRAN_E2SM_KPM_METRICS_H
|
@ -0,0 +1,173 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2023 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "srsgnb/hdr/stack/ric/e2sm_kpm.h"
|
||||||
|
#include "srsgnb/hdr/stack/ric/e2sm_kpm_common.h"
|
||||||
|
#include "srsran/asn1/e2ap.h"
|
||||||
|
#include "srsran/asn1/e2sm.h"
|
||||||
|
#include "srsran/asn1/e2sm_kpm_v2.h"
|
||||||
|
#include "srsran/common/timers.h"
|
||||||
|
#include "srsran/srsran.h"
|
||||||
|
|
||||||
|
#ifndef SRSRAN_E2SM_KPM_ACTION_DATA_H
|
||||||
|
#define SRSRAN_E2SM_KPM_ACTION_DATA_H
|
||||||
|
|
||||||
|
using namespace asn1::e2ap;
|
||||||
|
using namespace asn1::e2sm_kpm;
|
||||||
|
|
||||||
|
class e2sm_kpm_report_service
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
e2sm_kpm_report_service() = delete;
|
||||||
|
e2sm_kpm_report_service(e2sm_kpm* e2sm_kpm, uint16_t action_id, e2_sm_kpm_action_definition_s action_definition);
|
||||||
|
virtual ~e2sm_kpm_report_service() = default;
|
||||||
|
|
||||||
|
virtual bool _initialize_ric_ind_hdr();
|
||||||
|
virtual bool _initialize_ric_ind_msg() = 0;
|
||||||
|
virtual bool _collect_meas_data() = 0;
|
||||||
|
virtual bool is_ric_ind_ready() = 0;
|
||||||
|
virtual bool clear_collected_data() = 0;
|
||||||
|
|
||||||
|
virtual bool _start_meas_collection();
|
||||||
|
bool stop();
|
||||||
|
virtual bool _stop_meas_collection();
|
||||||
|
virtual bool _reschedule_meas_collection();
|
||||||
|
|
||||||
|
std::vector<e2sm_kpm_label_enum> _get_present_labels(const meas_info_item_s& action_meas_info_item);
|
||||||
|
meas_record_item_c::types
|
||||||
|
_get_meas_data_type(std::string meas_name, e2sm_kpm_label_enum label, meas_record_l& meas_record_list);
|
||||||
|
|
||||||
|
e2_sm_kpm_ind_hdr_s& get_ind_hdr() { return ric_ind_header_generic; };
|
||||||
|
e2_sm_kpm_ind_msg_s& get_ind_msg() { return ric_ind_message_generic; };
|
||||||
|
|
||||||
|
e2sm_kpm* parent;
|
||||||
|
uint16_t action_id;
|
||||||
|
e2_sm_kpm_action_definition_s action_def_generic;
|
||||||
|
e2_sm_kpm_ind_msg_s::ind_msg_formats_c_::types ind_msg_format;
|
||||||
|
e2_sm_kpm_ind_hdr_s ric_ind_header_generic;
|
||||||
|
e2_sm_kpm_ind_msg_s ric_ind_message_generic;
|
||||||
|
|
||||||
|
bool cell_global_id_present = false;
|
||||||
|
cgi_c cell_global_id;
|
||||||
|
|
||||||
|
// hdr format 1 in base class, as all types use it
|
||||||
|
e2_sm_kpm_ind_hdr_format1_s& ric_ind_header;
|
||||||
|
|
||||||
|
uint32_t granul_period = 0;
|
||||||
|
srsran::unique_timer meas_collection_timer; // for measurements collection
|
||||||
|
};
|
||||||
|
|
||||||
|
class e2sm_kpm_report_service_style1 : public e2sm_kpm_report_service
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
e2sm_kpm_report_service_style1(e2sm_kpm* e2sm_kpm,
|
||||||
|
uint16_t action_id,
|
||||||
|
e2_sm_kpm_action_definition_s action_definition);
|
||||||
|
virtual ~e2sm_kpm_report_service_style1() = default;
|
||||||
|
|
||||||
|
static bool process_ric_action_definition(e2sm_kpm* e2sm_kpm, e2_sm_kpm_action_definition_s& action_definition);
|
||||||
|
|
||||||
|
virtual bool _initialize_ric_ind_msg();
|
||||||
|
virtual bool _collect_meas_data();
|
||||||
|
virtual bool is_ric_ind_ready();
|
||||||
|
virtual bool clear_collected_data();
|
||||||
|
|
||||||
|
private:
|
||||||
|
meas_data_item_s&
|
||||||
|
_get_meas_data_item(std::string meas_name, e2sm_kpm_label_enum label, uint32_t ue_id, bool& ref_found);
|
||||||
|
|
||||||
|
e2_sm_kpm_action_definition_format1_s& action_def;
|
||||||
|
e2_sm_kpm_ind_msg_format1_s& ric_ind_message;
|
||||||
|
};
|
||||||
|
|
||||||
|
class e2sm_kpm_report_service_style2 : public e2sm_kpm_report_service
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
e2sm_kpm_report_service_style2(e2sm_kpm* e2sm_kpm,
|
||||||
|
uint16_t action_id,
|
||||||
|
e2_sm_kpm_action_definition_s action_definition);
|
||||||
|
virtual ~e2sm_kpm_report_service_style2() = default;
|
||||||
|
|
||||||
|
static bool process_ric_action_definition(e2sm_kpm* e2sm_kpm, e2_sm_kpm_action_definition_s& action_definition);
|
||||||
|
|
||||||
|
virtual bool _initialize_ric_ind_msg();
|
||||||
|
virtual bool _collect_meas_data();
|
||||||
|
virtual bool is_ric_ind_ready();
|
||||||
|
virtual bool clear_collected_data();
|
||||||
|
|
||||||
|
private:
|
||||||
|
e2_sm_kpm_action_definition_format2_s& action_def;
|
||||||
|
e2_sm_kpm_ind_msg_format1_s& ric_ind_message;
|
||||||
|
};
|
||||||
|
|
||||||
|
class e2sm_kpm_report_service_style3 : public e2sm_kpm_report_service
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
e2sm_kpm_report_service_style3(e2sm_kpm* e2sm_kpm,
|
||||||
|
uint16_t action_id,
|
||||||
|
e2_sm_kpm_action_definition_s action_definition);
|
||||||
|
virtual ~e2sm_kpm_report_service_style3() = default;
|
||||||
|
|
||||||
|
static bool process_ric_action_definition(e2sm_kpm* e2sm_kpm, e2_sm_kpm_action_definition_s& action_definition);
|
||||||
|
|
||||||
|
virtual bool _initialize_ric_ind_msg();
|
||||||
|
virtual bool _collect_meas_data();
|
||||||
|
virtual bool is_ric_ind_ready();
|
||||||
|
virtual bool clear_collected_data();
|
||||||
|
|
||||||
|
private:
|
||||||
|
e2_sm_kpm_action_definition_format3_s& action_def;
|
||||||
|
e2_sm_kpm_ind_msg_format2_s& ric_ind_message;
|
||||||
|
};
|
||||||
|
|
||||||
|
class e2sm_kpm_report_service_style4 : public e2sm_kpm_report_service
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
e2sm_kpm_report_service_style4(e2sm_kpm* e2sm_kpm,
|
||||||
|
uint16_t action_id,
|
||||||
|
e2_sm_kpm_action_definition_s action_definition);
|
||||||
|
virtual ~e2sm_kpm_report_service_style4() = default;
|
||||||
|
|
||||||
|
static bool process_ric_action_definition(e2sm_kpm* e2sm_kpm, e2_sm_kpm_action_definition_s& action_definition);
|
||||||
|
|
||||||
|
virtual bool _initialize_ric_ind_msg();
|
||||||
|
virtual bool _collect_meas_data();
|
||||||
|
virtual bool is_ric_ind_ready();
|
||||||
|
virtual bool clear_collected_data();
|
||||||
|
|
||||||
|
private:
|
||||||
|
e2_sm_kpm_action_definition_format4_s& action_def;
|
||||||
|
e2_sm_kpm_ind_msg_format3_s& ric_ind_message;
|
||||||
|
};
|
||||||
|
|
||||||
|
class e2sm_kpm_report_service_style5 : public e2sm_kpm_report_service
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
e2sm_kpm_report_service_style5(e2sm_kpm* e2sm_kpm,
|
||||||
|
uint16_t action_id,
|
||||||
|
e2_sm_kpm_action_definition_s action_definition);
|
||||||
|
virtual ~e2sm_kpm_report_service_style5() = default;
|
||||||
|
|
||||||
|
static bool process_ric_action_definition(e2sm_kpm* e2sm_kpm, e2_sm_kpm_action_definition_s& action_definition);
|
||||||
|
|
||||||
|
virtual bool _initialize_ric_ind_msg();
|
||||||
|
virtual bool _collect_meas_data();
|
||||||
|
virtual bool is_ric_ind_ready();
|
||||||
|
virtual bool clear_collected_data();
|
||||||
|
|
||||||
|
private:
|
||||||
|
e2_sm_kpm_action_definition_format5_s& action_def;
|
||||||
|
e2_sm_kpm_ind_msg_format3_s& ric_ind_message;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SRSRAN_E2SM_KPM_ACTION_DATA_H
|
@ -0,0 +1,13 @@
|
|||||||
|
#
|
||||||
|
# Copyright 2013-2023 Software Radio Systems Limited
|
||||||
|
#
|
||||||
|
# By using this file, you agree to the terms and conditions set
|
||||||
|
# forth in the LICENSE file which can be found at the top level of
|
||||||
|
# the distribution.
|
||||||
|
#
|
||||||
|
|
||||||
|
set(SOURCES e2_agent.cc e2ap_ric_subscription.cc e2ap.cc e2sm_kpm_common.cc e2sm_kpm.cc e2sm_kpm_report_service.cc)
|
||||||
|
add_library(srsgnb_ric STATIC ${SOURCES})
|
||||||
|
target_link_libraries(srsgnb_ric srsran_asn1 ric_e2)
|
||||||
|
|
||||||
|
add_subdirectory(test)
|
@ -0,0 +1,599 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2023 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "srsgnb/hdr/stack/ric/e2_agent.h"
|
||||||
|
#include "srsran/asn1/e2ap.h"
|
||||||
|
#include "srsran/common/standard_streams.h"
|
||||||
|
|
||||||
|
using namespace srsenb;
|
||||||
|
|
||||||
|
/*********************************************************
|
||||||
|
* RIC Connection
|
||||||
|
*********************************************************/
|
||||||
|
|
||||||
|
srsran::proc_outcome_t e2_agent::e2_setup_proc_t::init()
|
||||||
|
{
|
||||||
|
e2_agent_ptr->logger.info("Starting new RIC connection.");
|
||||||
|
connect_count++;
|
||||||
|
return start_ric_connection();
|
||||||
|
}
|
||||||
|
|
||||||
|
srsran::proc_outcome_t e2_agent::e2_setup_proc_t::start_ric_connection()
|
||||||
|
{
|
||||||
|
if (not e2_agent_ptr->running) {
|
||||||
|
e2_agent_ptr->logger.info("E2 Agent is not running anymore.");
|
||||||
|
return srsran::proc_outcome_t::error;
|
||||||
|
}
|
||||||
|
if (e2_agent_ptr->ric_connected) {
|
||||||
|
e2_agent_ptr->logger.info("E2 Agent is already connected to RIC");
|
||||||
|
return srsran::proc_outcome_t::success;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto connect_callback = [this]() {
|
||||||
|
bool connected = e2_agent_ptr->connect_ric();
|
||||||
|
|
||||||
|
auto notify_result = [this, connected]() {
|
||||||
|
e2_setup_proc_t::e2connectresult res;
|
||||||
|
res.success = connected;
|
||||||
|
e2_agent_ptr->e2_setup_proc.trigger(res);
|
||||||
|
};
|
||||||
|
e2_agent_ptr->task_sched.notify_background_task_result(notify_result);
|
||||||
|
};
|
||||||
|
srsran::get_background_workers().push_task(connect_callback);
|
||||||
|
e2_agent_ptr->logger.debug("Connection to RIC requested.");
|
||||||
|
|
||||||
|
return srsran::proc_outcome_t::yield;
|
||||||
|
}
|
||||||
|
|
||||||
|
srsran::proc_outcome_t e2_agent::e2_setup_proc_t::react(const srsenb::e2_agent::e2_setup_proc_t::e2connectresult& event)
|
||||||
|
{
|
||||||
|
if (event.success) {
|
||||||
|
e2_agent_ptr->logger.info("Connected to RIC. Sending setup request.");
|
||||||
|
e2_agent_ptr->e2_setup_timeout.run();
|
||||||
|
if (not e2_agent_ptr->setup_e2()) {
|
||||||
|
e2_agent_ptr->logger.error("E2 setup failed. Exiting...");
|
||||||
|
srsran::console("E2 setup failed\n");
|
||||||
|
e2_agent_ptr->running = false;
|
||||||
|
return srsran::proc_outcome_t::error;
|
||||||
|
}
|
||||||
|
e2_agent_ptr->logger.info("E2 setup request sent. Waiting for response.");
|
||||||
|
return srsran::proc_outcome_t::yield;
|
||||||
|
}
|
||||||
|
|
||||||
|
e2_agent_ptr->logger.info("Could not connected to RIC. Aborting");
|
||||||
|
return srsran::proc_outcome_t::error;
|
||||||
|
}
|
||||||
|
|
||||||
|
srsran::proc_outcome_t e2_agent::e2_setup_proc_t::react(const srsenb::e2_agent::e2_setup_proc_t::e2setupresult& event)
|
||||||
|
{
|
||||||
|
if (e2_agent_ptr->e2_setup_timeout.is_running()) {
|
||||||
|
e2_agent_ptr->e2_setup_timeout.stop();
|
||||||
|
}
|
||||||
|
if (event.success) {
|
||||||
|
e2_agent_ptr->logger.info("E2 Setup procedure completed successfully");
|
||||||
|
return srsran::proc_outcome_t::success;
|
||||||
|
}
|
||||||
|
e2_agent_ptr->logger.error("E2 Setup failed.");
|
||||||
|
srsran::console("E2 setup failed\n");
|
||||||
|
return srsran::proc_outcome_t::error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void e2_agent::e2_setup_proc_t::then(const srsran::proc_state_t& result)
|
||||||
|
{
|
||||||
|
if (result.is_error()) {
|
||||||
|
e2_agent_ptr->logger.info("Failed to initiate RIC connection. Attempting reconnection in %d seconds",
|
||||||
|
e2_agent_ptr->ric_connect_timer.duration() / 1000);
|
||||||
|
srsran::console("Failed to initiate RIC connection. Attempting reconnection in %d seconds\n",
|
||||||
|
e2_agent_ptr->ric_connect_timer.duration() / 1000);
|
||||||
|
e2_agent_ptr->rx_sockets.remove_socket(e2_agent_ptr->ric_socket.get_socket());
|
||||||
|
e2_agent_ptr->ric_socket.close();
|
||||||
|
e2_agent_ptr->logger.info("R2 Agent socket closed.");
|
||||||
|
e2_agent_ptr->ric_connect_timer.run();
|
||||||
|
if (e2_agent_ptr->_args.max_ric_setup_retries > 0 && connect_count > e2_agent_ptr->_args.max_ric_setup_retries) {
|
||||||
|
srsran_terminate("Error connecting to RIC");
|
||||||
|
}
|
||||||
|
// Try again with in 10 seconds
|
||||||
|
} else {
|
||||||
|
connect_count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************************
|
||||||
|
* E2 Agent class
|
||||||
|
*********************************************************/
|
||||||
|
|
||||||
|
e2_agent::e2_agent(srslog::basic_logger& logger, e2_interface_metrics* _gnb_metrics) :
|
||||||
|
task_sched(),
|
||||||
|
logger(logger),
|
||||||
|
rx_sockets(),
|
||||||
|
thread("E2_AGENT_THREAD"),
|
||||||
|
e2ap_(logger, this, _gnb_metrics, &task_sched),
|
||||||
|
e2_setup_proc(this)
|
||||||
|
{
|
||||||
|
gnb_metrics = _gnb_metrics;
|
||||||
|
ric_rece_task_queue = task_sched.make_task_queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2_agent::init(e2_agent_args_t args)
|
||||||
|
{
|
||||||
|
_args = args;
|
||||||
|
|
||||||
|
// Setup RIC reconnection timer
|
||||||
|
ric_connect_timer = task_sched.get_unique_timer();
|
||||||
|
auto ric_connect_run = [this](uint32_t tid) {
|
||||||
|
if (e2_setup_proc.is_busy()) {
|
||||||
|
logger.error("Failed to initiate RIC Setup procedure: procedure is busy.");
|
||||||
|
}
|
||||||
|
e2_setup_proc.launch();
|
||||||
|
};
|
||||||
|
ric_connect_timer.set(_args.ric_connect_timer * 1000, ric_connect_run);
|
||||||
|
// Setup timeout
|
||||||
|
e2_setup_timeout = task_sched.get_unique_timer();
|
||||||
|
uint32_t ric_setup_timeout_val = 5000;
|
||||||
|
e2_setup_timeout.set(ric_setup_timeout_val, [this](uint32_t tid) {
|
||||||
|
e2_setup_proc_t::e2setupresult res;
|
||||||
|
res.success = false;
|
||||||
|
res.cause = e2_setup_proc_t::e2setupresult::cause_t::timeout;
|
||||||
|
e2_setup_proc.trigger(res);
|
||||||
|
});
|
||||||
|
|
||||||
|
start(0);
|
||||||
|
running = true;
|
||||||
|
// starting RIC connection
|
||||||
|
if (not e2_setup_proc.launch()) {
|
||||||
|
logger.error("Failed to initiate RIC Setup procedure: error launching procedure.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void e2_agent::stop()
|
||||||
|
{
|
||||||
|
running = false;
|
||||||
|
wait_thread_finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
void e2_agent::tic()
|
||||||
|
{
|
||||||
|
// get tick every 1ms to advance timers
|
||||||
|
task_sched.tic();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2_agent::is_ric_connected()
|
||||||
|
{
|
||||||
|
return ric_connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2_agent::connect_ric()
|
||||||
|
{
|
||||||
|
using namespace srsran::net_utils;
|
||||||
|
logger.info("Connecting to RIC %s:%d", _args.ric_ip.c_str(), _args.ric_port);
|
||||||
|
// Open SCTP socket
|
||||||
|
if (not ric_socket.open_socket(addr_family::ipv4, socket_type::seqpacket, protocol_type::SCTP)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe to shutdown events
|
||||||
|
if (not ric_socket.sctp_subscribe_to_events()) {
|
||||||
|
ric_socket.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set SRTO_MAX
|
||||||
|
if (not ric_socket.sctp_set_rto_opts(6000)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set SCTP init options
|
||||||
|
if (not ric_socket.sctp_set_init_msg_opts(3, 5000)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind socket
|
||||||
|
if (not ric_socket.bind_addr(_args.ric_bind_ip.c_str(), _args.ric_bind_port)) {
|
||||||
|
ric_socket.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
logger.info("SCTP socket opened. fd=%d", ric_socket.fd());
|
||||||
|
|
||||||
|
// Connect to the AMF address
|
||||||
|
if (not ric_socket.connect_to(_args.ric_ip.c_str(), _args.ric_port, &ric_addr)) {
|
||||||
|
ric_socket.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
logger.info("SCTP socket connected with RIC. fd=%d", ric_socket.fd());
|
||||||
|
|
||||||
|
// Assign a handler to rx RIC packets
|
||||||
|
auto rx_callback =
|
||||||
|
[this](srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags) {
|
||||||
|
handle_ric_rx_msg(std::move(pdu), from, sri, flags);
|
||||||
|
};
|
||||||
|
rx_sockets.add_socket_handler(ric_socket.fd(),
|
||||||
|
srsran::make_sctp_sdu_handler(logger, ric_rece_task_queue, rx_callback));
|
||||||
|
|
||||||
|
logger.info("SCTP socket connected established with RIC");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2_agent::setup_e2()
|
||||||
|
{
|
||||||
|
return send_e2_msg(E2_SETUP_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
void e2_agent::run_thread()
|
||||||
|
{
|
||||||
|
while (running) {
|
||||||
|
task_sched.run_next_task();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2_agent::send_sctp(srsran::unique_byte_buffer_t& buf)
|
||||||
|
{
|
||||||
|
ssize_t ret;
|
||||||
|
ret = sctp_sendmsg(ric_socket.fd(),
|
||||||
|
buf->msg,
|
||||||
|
buf->N_bytes,
|
||||||
|
(struct sockaddr*)&ric_addr,
|
||||||
|
sizeof(ric_addr),
|
||||||
|
htonl(e2ap_ppid),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0);
|
||||||
|
if (ret == -1) {
|
||||||
|
printf("failed to send %d bytes\n", buf->N_bytes);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2_agent::send_e2_msg(e2_msg_type_t msg_type)
|
||||||
|
{
|
||||||
|
std::string message_name;
|
||||||
|
e2_ap_pdu_c send_pdu;
|
||||||
|
|
||||||
|
switch (msg_type) {
|
||||||
|
case e2_msg_type_t::E2_SETUP_REQUEST:
|
||||||
|
send_pdu = e2ap_.generate_setup_request();
|
||||||
|
message_name = "E2 SETUP REQUEST";
|
||||||
|
break;
|
||||||
|
case e2_msg_type_t::E2_RESET:
|
||||||
|
send_pdu = e2ap_.generate_reset_request();
|
||||||
|
message_name = "E2 RESET REQUEST";
|
||||||
|
break;
|
||||||
|
case e2_msg_type_t::E2_RESET_RESPONSE:
|
||||||
|
send_pdu = e2ap_.generate_reset_response();
|
||||||
|
message_name = "E2 RESET RESPONSE";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("Unknown E2AP message type\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return send_e2ap_pdu(send_pdu);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2_agent::queue_send_e2ap_pdu(e2_ap_pdu_c e2ap_pdu)
|
||||||
|
{
|
||||||
|
if (not ric_connected) {
|
||||||
|
logger.error("Aborting sending msg. Cause: RIC is not connected.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto send_e2ap_pdu_task = [this, e2ap_pdu]() { send_e2ap_pdu(e2ap_pdu); };
|
||||||
|
ric_rece_task_queue.push(send_e2ap_pdu_task);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2_agent::send_e2ap_pdu(e2_ap_pdu_c send_pdu)
|
||||||
|
{
|
||||||
|
srsran::unique_byte_buffer_t buf = srsran::make_byte_buffer();
|
||||||
|
if (buf == nullptr) {
|
||||||
|
// logger.error("Fatal Error: Couldn't allocate buffer for %s.", procedure_name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
asn1::bit_ref bref(buf->msg, buf->get_tailroom());
|
||||||
|
if (send_pdu.pack(bref) != asn1::SRSASN_SUCCESS) {
|
||||||
|
logger.error("Failed to pack TX E2 PDU");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
buf->N_bytes = bref.distance_bytes();
|
||||||
|
printf("try to send %d bytes to addr %s \n", buf->N_bytes, inet_ntoa(ric_addr.sin_addr));
|
||||||
|
if (!send_sctp(buf)) {
|
||||||
|
logger.error("failed to send");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2_agent::handle_ric_rx_msg(srsran::unique_byte_buffer_t pdu,
|
||||||
|
const sockaddr_in& from,
|
||||||
|
const sctp_sndrcvinfo& sri,
|
||||||
|
int flags)
|
||||||
|
{
|
||||||
|
// Handle Notification Case
|
||||||
|
if (flags & MSG_NOTIFICATION) {
|
||||||
|
// Received notification
|
||||||
|
union sctp_notification* notification = (union sctp_notification*)pdu->msg;
|
||||||
|
logger.info("SCTP Notification %04x", notification->sn_header.sn_type);
|
||||||
|
bool restart_e2 = false;
|
||||||
|
if (notification->sn_header.sn_type == SCTP_SHUTDOWN_EVENT) {
|
||||||
|
logger.info("SCTP Association Shutdown. Association: %d", sri.sinfo_assoc_id);
|
||||||
|
srsran::console("SCTP Association Shutdown. Association: %d\n", sri.sinfo_assoc_id);
|
||||||
|
restart_e2 = true;
|
||||||
|
} else if (notification->sn_header.sn_type == SCTP_PEER_ADDR_CHANGE &&
|
||||||
|
notification->sn_paddr_change.spc_state == SCTP_ADDR_UNREACHABLE) {
|
||||||
|
logger.info("SCTP peer addres unreachable. Association: %d", sri.sinfo_assoc_id);
|
||||||
|
srsran::console("SCTP peer address unreachable. Association: %d\n", sri.sinfo_assoc_id);
|
||||||
|
restart_e2 = true;
|
||||||
|
} else if (notification->sn_header.sn_type == SCTP_REMOTE_ERROR) {
|
||||||
|
logger.info("SCTP remote error. Association: %d", sri.sinfo_assoc_id);
|
||||||
|
srsran::console("SCTP remote error. Association: %d\n", sri.sinfo_assoc_id);
|
||||||
|
restart_e2 = true;
|
||||||
|
} else if (notification->sn_header.sn_type == SCTP_ASSOC_CHANGE) {
|
||||||
|
logger.info("SCTP association changed. Association: %d", sri.sinfo_assoc_id);
|
||||||
|
srsran::console("SCTP association changed. Association: %d\n", sri.sinfo_assoc_id);
|
||||||
|
}
|
||||||
|
if (restart_e2) {
|
||||||
|
logger.info("Restarting E2 connection");
|
||||||
|
srsran::console("Restarting E2 connection\n");
|
||||||
|
rx_sockets.remove_socket(ric_socket.get_socket());
|
||||||
|
ric_socket.close();
|
||||||
|
}
|
||||||
|
} else if (pdu->N_bytes == 0) {
|
||||||
|
logger.error("SCTP return 0 bytes. Closing socket");
|
||||||
|
ric_socket.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restart RIC connection procedure if we lost connection
|
||||||
|
if (not ric_socket.is_open()) {
|
||||||
|
ric_connected = false;
|
||||||
|
if (e2_setup_proc.is_busy()) {
|
||||||
|
logger.error("Failed to initiate RIC connection procedure, as it is already running.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
e2_setup_proc.launch();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((flags & MSG_NOTIFICATION) == 0 && pdu->N_bytes != 0) {
|
||||||
|
handle_e2_rx_pdu(pdu.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2_agent::handle_e2_rx_pdu(srsran::byte_buffer_t* pdu)
|
||||||
|
{
|
||||||
|
printf("E2_AGENT: Received %d bytes from RIC\n", pdu->N_bytes);
|
||||||
|
e2_ap_pdu_c pdu_c;
|
||||||
|
asn1::cbit_ref bref(pdu->msg, pdu->N_bytes);
|
||||||
|
if (pdu_c.unpack(bref) != asn1::SRSASN_SUCCESS) {
|
||||||
|
logger.error("Failed to unpack RX E2 PDU");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (pdu_c.type().value == e2_ap_pdu_c::types_opts::init_msg) {
|
||||||
|
logger.info("Received E2AP Init Message");
|
||||||
|
handle_e2_init_msg(pdu_c.init_msg());
|
||||||
|
} else if (pdu_c.type().value == e2_ap_pdu_c::types_opts::successful_outcome) {
|
||||||
|
logger.info("Received E2AP Successful Outcome");
|
||||||
|
handle_e2_successful_outcome(pdu_c.successful_outcome());
|
||||||
|
} else if (pdu_c.type().value == e2_ap_pdu_c::types_opts::unsuccessful_outcome) {
|
||||||
|
logger.info("Received E2AP Unsuccessful Outcome ");
|
||||||
|
handle_e2_unsuccessful_outcome(pdu_c.unsuccessful_outcome());
|
||||||
|
} else {
|
||||||
|
logger.warning("Received E2AP Unknown Message ");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2_agent::handle_e2_init_msg(asn1::e2ap::init_msg_s& init_msg)
|
||||||
|
{
|
||||||
|
using namespace asn1::e2ap;
|
||||||
|
if (init_msg.value.type() == e2_ap_elem_procs_o::init_msg_c::types_opts::ricsubscription_request) {
|
||||||
|
logger.info("Received E2AP RIC Subscription Request");
|
||||||
|
handle_ric_subscription_request(init_msg.value.ricsubscription_request());
|
||||||
|
} else if (init_msg.value.type() == e2_ap_elem_procs_o::init_msg_c::types_opts::ricsubscription_delete_request) {
|
||||||
|
logger.info("Received E2AP RIC Subscription Delete Request");
|
||||||
|
handle_ric_subscription_delete_request(init_msg.value.ricsubscription_delete_request());
|
||||||
|
} else if (init_msg.value.type() == e2_ap_elem_procs_o::init_msg_c::types_opts::ri_cctrl_request) {
|
||||||
|
logger.info("Received E2AP RIC Control Request");
|
||||||
|
// handle_ri_cctrl_request(init_msg.value.ri_cctrl_request());
|
||||||
|
} else if (init_msg.value.type() == e2_ap_elem_procs_o::init_msg_c::types_opts::e2conn_upd) {
|
||||||
|
logger.info("Received E2AP E2 Connection Update");
|
||||||
|
//handle_e2conn_upd(init_msg.value.e2conn_upd());
|
||||||
|
} else if (init_msg.value.type() == e2_ap_elem_procs_o::init_msg_c::types_opts::reset_request) {
|
||||||
|
logger.info("Received E2AP E2 Reset Request");
|
||||||
|
handle_reset_request(init_msg.value.reset_request());
|
||||||
|
} else if (init_msg.value.type() == e2_ap_elem_procs_o::init_msg_c::types_opts::e2_removal_request) {
|
||||||
|
logger.info("Received E2AP E2 Removal Request");
|
||||||
|
//handle_e2_removal_request(init_msg.value.e2_removal_request());
|
||||||
|
} else {
|
||||||
|
logger.warning("Received E2AP Unknown Init Message ");
|
||||||
|
}
|
||||||
|
// TODO check for different type of RIC generated init messages
|
||||||
|
// eg. RIC subscription request, RIC Reset request, RIC control request, RIC subscription delete request
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2_agent::handle_e2_successful_outcome(asn1::e2ap::successful_outcome_s& successful_outcome)
|
||||||
|
{
|
||||||
|
using namespace asn1::e2ap;
|
||||||
|
if (successful_outcome.value.type() == e2_ap_elem_procs_o::successful_outcome_c::types_opts::e2setup_resp) {
|
||||||
|
logger.info("Received E2AP E2 Setup Response");
|
||||||
|
handle_e2_setup_response(successful_outcome.value.e2setup_resp());
|
||||||
|
} else if (successful_outcome.value.type() ==
|
||||||
|
e2_ap_elem_procs_o::successful_outcome_c::types_opts::ricsubscription_resp) {
|
||||||
|
logger.info("Received E2AP RIC Subscription Response");
|
||||||
|
// handle_ric_subscription_response(successful_outcome.value.ric_subscription());
|
||||||
|
} else if (successful_outcome.value.type() == e2_ap_elem_procs_o::successful_outcome_c::types_opts::ri_cctrl_ack) {
|
||||||
|
logger.info("Received E2AP RIC Control acknowlegement \n");
|
||||||
|
// handle_ric_control_response(successful_outcome.value.ric_control());
|
||||||
|
} else if (successful_outcome.value.type() ==
|
||||||
|
e2_ap_elem_procs_o::successful_outcome_c::types_opts::ricservice_upd_ack) {
|
||||||
|
logger.info("Received E2AP RIC Service Update acknowlegement \n");
|
||||||
|
// handle_ric_service_update_ack(successful_outcome.value.ric_service_update());
|
||||||
|
} else if (successful_outcome.value.type() ==
|
||||||
|
e2_ap_elem_procs_o::successful_outcome_c::types_opts::ricsubscription_delete_resp) {
|
||||||
|
logger.info("Received E2AP RIC Subscription Delete Response \n");
|
||||||
|
// handle_ric_subscription_delete_response(successful_outcome.value.ric_subscription_delete());
|
||||||
|
} else if (successful_outcome.value.type() == e2_ap_elem_procs_o::successful_outcome_c::types_opts::reset_resp) {
|
||||||
|
logger.info("Received E2AP RIC Reset Response \n");
|
||||||
|
handle_reset_response(successful_outcome.value.reset_resp());
|
||||||
|
} else {
|
||||||
|
logger.info("Received E2AP Unknown Successful Outcome \n");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2_agent::handle_e2_setup_response(e2setup_resp_s setup_response)
|
||||||
|
{
|
||||||
|
if (e2ap_.process_setup_response(setup_response)) {
|
||||||
|
logger.error("Failed to process E2 Setup Response \n");
|
||||||
|
ric_connected = false;
|
||||||
|
e2_setup_proc_t::e2setupresult res;
|
||||||
|
res.success = false;
|
||||||
|
e2_setup_proc.trigger(res);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ric_connected = true;
|
||||||
|
e2_setup_proc_t::e2setupresult res;
|
||||||
|
res.success = true;
|
||||||
|
e2_setup_proc.trigger(res);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2_agent::handle_e2_unsuccessful_outcome(asn1::e2ap::unsuccessful_outcome_s& unsuccessful_outcome)
|
||||||
|
{
|
||||||
|
using namespace asn1::e2ap;
|
||||||
|
if (unsuccessful_outcome.value.type() == e2_ap_elem_procs_o::unsuccessful_outcome_c::types_opts::e2setup_fail) {
|
||||||
|
logger.info("Received E2AP E2 Setup Failure");
|
||||||
|
if (e2ap_.process_e2_setup_failure(unsuccessful_outcome.value.e2setup_fail())) {
|
||||||
|
logger.error("Failed to process E2 Setup Failure \n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (unsuccessful_outcome.value.type() ==
|
||||||
|
e2_ap_elem_procs_o::unsuccessful_outcome_c::types_opts::e2node_cfg_upd_fail) {
|
||||||
|
logger.info("Received E2node configuration update Failure");
|
||||||
|
if (e2ap_.process_e2_node_config_update_failure(unsuccessful_outcome.value.e2node_cfg_upd_fail())) {
|
||||||
|
logger.error("Failed to process E2node configuration update Failure \n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (unsuccessful_outcome.value.type() ==
|
||||||
|
e2_ap_elem_procs_o::unsuccessful_outcome_c::types_opts::ricservice_upd_fail) {
|
||||||
|
logger.info("Received E2AP RIC Service Update Failure \n");
|
||||||
|
if (e2ap_.process_ric_service_update_failure(unsuccessful_outcome.value.ricservice_upd_fail())) {
|
||||||
|
logger.error("Failed to process RIC service update failure \n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (unsuccessful_outcome.value.type() ==
|
||||||
|
e2_ap_elem_procs_o::unsuccessful_outcome_c::types_opts::e2_removal_fail) {
|
||||||
|
logger.info("Received E2AP removal Unsuccessful Outcome \n");
|
||||||
|
if (e2ap_.process_e2_removal_failure(unsuccessful_outcome.value.e2_removal_fail())) {
|
||||||
|
logger.error("Failed to process RIC service status failure \n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.info("Received E2AP Unknown Unsuccessful Outcome \n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2_agent::handle_ric_subscription_request(ricsubscription_request_s ric_subscription_request)
|
||||||
|
{
|
||||||
|
logger.info("Received RIC Subscription Request from RIC ID: %i (instance id %i) to RAN Function ID: %i",
|
||||||
|
ric_subscription_request->ri_crequest_id->ric_requestor_id,
|
||||||
|
ric_subscription_request->ri_crequest_id->ric_instance_id,
|
||||||
|
ric_subscription_request->ra_nfunction_id->value);
|
||||||
|
|
||||||
|
if (e2ap_.process_subscription_request(ric_subscription_request)) {
|
||||||
|
logger.error("Failed to process RIC subscription request \n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2_agent::handle_ric_subscription_delete_request(ricsubscription_delete_request_s ricsubscription_delete_request)
|
||||||
|
{
|
||||||
|
logger.info("Received RIC Subscription Delete request from RIC ID: %i (instance id %i) to RAN Function ID: %i",
|
||||||
|
ricsubscription_delete_request->ri_crequest_id->ric_requestor_id,
|
||||||
|
ricsubscription_delete_request->ri_crequest_id->ric_instance_id,
|
||||||
|
ricsubscription_delete_request->ra_nfunction_id->value);
|
||||||
|
|
||||||
|
if (e2ap_.process_subscription_delete_request(ricsubscription_delete_request)) {
|
||||||
|
logger.error("Failed to process RIC subscription delete request \n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2_agent::handle_subscription_modification_request(uint32_t ric_subscription_modification_request)
|
||||||
|
{
|
||||||
|
if (e2ap_.process_subscription_modification_request(ric_subscription_modification_request)) {
|
||||||
|
logger.error("Failed to process RIC subscription delete request \n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool e2_agent::handle_subscription_modification_confirm(uint32_t ric_subscription_modification_confirm)
|
||||||
|
{
|
||||||
|
if (e2ap_.process_subscription_modification_confirm(ric_subscription_modification_confirm)) {
|
||||||
|
logger.error("Failed to process RIC subscription delete request \n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool e2_agent::handle_subscription_modification_refuse(uint32_t ric_subscription_modification_refuse)
|
||||||
|
{
|
||||||
|
if (e2ap_.process_subscription_modification_refuse(ric_subscription_modification_refuse)) {
|
||||||
|
logger.error("Failed to process RIC subscription delete request \n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2_agent::handle_reset_request(reset_request_s& reset_request)
|
||||||
|
{
|
||||||
|
printf("Received E2AP E2 Reset Request \n");
|
||||||
|
// call process to handle reset request, if it fails log error and return false, else return true - success
|
||||||
|
if (e2ap_.process_reset_request(reset_request)) {
|
||||||
|
logger.error("Failed to process E2 Reset Request \n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Reset transaction with ID = {}", e2ap_.get_reset_id());
|
||||||
|
|
||||||
|
// send reset reset response
|
||||||
|
auto send_reset_resp = [this]() { send_e2_msg(E2_RESET_RESPONSE); };
|
||||||
|
ric_rece_task_queue.push(send_reset_resp);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2_agent::handle_reset_response(reset_resp_s& reset_response)
|
||||||
|
{
|
||||||
|
printf("Received E2AP E2 Reset Response \n");
|
||||||
|
// call process to handle reset reponse, if it fails log error, else return true - success
|
||||||
|
// all processing of message will be done in process_reset_response (e2ap.cc)
|
||||||
|
if (e2ap_.process_reset_response(reset_response)) {
|
||||||
|
logger.error("Failed to process E2 Reset Response \n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Reset Response successfully processed \n");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
@ -0,0 +1,456 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2023 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "srsgnb/hdr/stack/ric/e2ap.h"
|
||||||
|
#include "srsgnb/hdr/stack/ric/e2_agent.h"
|
||||||
|
#include "srsgnb/hdr/stack/ric/e2ap_ric_subscription.h"
|
||||||
|
|
||||||
|
e2ap::e2ap(srslog::basic_logger& logger,
|
||||||
|
e2_agent* _e2_agent,
|
||||||
|
srsenb::e2_interface_metrics* _gnb_metrics,
|
||||||
|
srsran::task_scheduler* _task_sched_ptr) :
|
||||||
|
logger(logger), _e2_agent(_e2_agent), e2sm_(logger, _task_sched_ptr), task_sched_ptr(_task_sched_ptr)
|
||||||
|
{
|
||||||
|
gnb_metrics = _gnb_metrics;
|
||||||
|
if (task_sched_ptr) {
|
||||||
|
e2_procedure_timeout = task_sched_ptr->get_unique_timer();
|
||||||
|
}
|
||||||
|
|
||||||
|
// register SM to receive enb metrics
|
||||||
|
gnb_metrics->register_e2sm(&e2sm_);
|
||||||
|
|
||||||
|
// add SMs to map
|
||||||
|
uint32_t local_ran_function_id = 147;
|
||||||
|
RANfunction_description add_func;
|
||||||
|
add_func.function_instance = 0;
|
||||||
|
add_func.sm_ptr = &e2sm_;
|
||||||
|
ran_functions[local_ran_function_id] = add_func;
|
||||||
|
}
|
||||||
|
|
||||||
|
e2ap::~e2ap(){};
|
||||||
|
|
||||||
|
bool e2ap::get_func_desc(uint32_t ran_func_id, RANfunction_description& fdesc)
|
||||||
|
{
|
||||||
|
if (ran_functions.count(ran_func_id)) {
|
||||||
|
fdesc = ran_functions.at(ran_func_id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2ap::queue_send_e2ap_pdu(e2_ap_pdu_c e2ap_pdu)
|
||||||
|
{
|
||||||
|
if (_e2_agent) {
|
||||||
|
_e2_agent->queue_send_e2ap_pdu(e2ap_pdu);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
e2_ap_pdu_c e2ap::generate_setup_request()
|
||||||
|
{
|
||||||
|
e2_ap_pdu_c pdu;
|
||||||
|
init_msg_s& initmsg = pdu.set_init_msg();
|
||||||
|
initmsg.load_info_obj(ASN1_E2AP_ID_E2SETUP);
|
||||||
|
|
||||||
|
e2setup_request_s& setup = initmsg.value.e2setup_request();
|
||||||
|
setup->transaction_id.crit = asn1::crit_opts::reject;
|
||||||
|
setup->transaction_id.value.value = setup_procedure_transaction_id;
|
||||||
|
setup->global_e2node_id.crit = asn1::crit_opts::reject;
|
||||||
|
|
||||||
|
auto& gnb_ = setup->global_e2node_id.value.set_gnb();
|
||||||
|
gnb_.global_g_nb_id.plmn_id.from_number(plmn_id);
|
||||||
|
gnb_.global_g_nb_id.gnb_id.gnb_id().from_number(gnb_id, 28); // eutra_cell_id has 28 bits
|
||||||
|
|
||||||
|
// add all supported e2SM functions
|
||||||
|
setup->ra_nfunctions_added.crit = asn1::crit_opts::reject;
|
||||||
|
auto& ra_nfunc_list = setup->ra_nfunctions_added.value;
|
||||||
|
ra_nfunc_list.resize(ran_functions.size());
|
||||||
|
|
||||||
|
uint32_t idx = 0;
|
||||||
|
for (auto& x : ran_functions) {
|
||||||
|
uint32_t local_ran_function_id = x.first;
|
||||||
|
e2sm* sm_ptr = x.second.sm_ptr;
|
||||||
|
|
||||||
|
ra_nfunction_item_s& ran_func = ra_nfunc_list[idx].value().ra_nfunction_item();
|
||||||
|
ran_func.ran_function_id = local_ran_function_id;
|
||||||
|
ran_func.ran_function_revision = sm_ptr->get_revision();
|
||||||
|
ran_func.ran_function_oid.from_string(sm_ptr->get_oid().c_str());
|
||||||
|
sm_ptr->generate_ran_function_description(x.second, ran_func);
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
setup->e2node_component_cfg_addition.crit = asn1::crit_opts::reject;
|
||||||
|
auto& list1 = setup->e2node_component_cfg_addition.value;
|
||||||
|
list1.resize(1);
|
||||||
|
e2node_component_cfg_addition_item_s& item1 = list1[0].value().e2node_component_cfg_addition_item();
|
||||||
|
item1.e2node_component_interface_type = e2node_component_interface_type_opts::ng;
|
||||||
|
item1.e2node_component_id.set_e2node_component_interface_type_ng().amf_name.from_string("nginterf");
|
||||||
|
item1.e2node_component_cfg.e2node_component_request_part.from_string("72657170617274");
|
||||||
|
item1.e2node_component_cfg.e2node_component_resp_part.from_string("72657370617274");
|
||||||
|
return pdu;
|
||||||
|
}
|
||||||
|
|
||||||
|
e2_ap_pdu_c e2ap::generate_subscription_response(ric_subscription_reponse_t ric_subscription_reponse)
|
||||||
|
{
|
||||||
|
e2_ap_pdu_c pdu;
|
||||||
|
successful_outcome_s& success = pdu.set_successful_outcome();
|
||||||
|
success.load_info_obj(ASN1_E2AP_ID_RICSUBSCRIPTION);
|
||||||
|
success.crit = asn1::crit_opts::reject;
|
||||||
|
ricsubscription_resp_s& sub_resp = success.value.ricsubscription_resp();
|
||||||
|
|
||||||
|
sub_resp->ri_crequest_id.crit = asn1::crit_opts::reject;
|
||||||
|
sub_resp->ri_crequest_id.id = ASN1_E2AP_ID_RI_CREQUEST_ID;
|
||||||
|
sub_resp->ri_crequest_id.value.ric_requestor_id = ric_subscription_reponse.ric_requestor_id;
|
||||||
|
sub_resp->ri_crequest_id.value.ric_instance_id = ric_subscription_reponse.ric_instance_id;
|
||||||
|
|
||||||
|
sub_resp->ra_nfunction_id.crit = asn1::crit_opts::reject;
|
||||||
|
sub_resp->ra_nfunction_id.id = ASN1_E2AP_ID_RA_NFUNCTION_ID;
|
||||||
|
sub_resp->ra_nfunction_id->value = ric_subscription_reponse.ra_nfunction_id;
|
||||||
|
|
||||||
|
sub_resp->ri_cactions_admitted.crit = asn1::crit_opts::reject;
|
||||||
|
auto& action_admit_list = sub_resp->ri_cactions_admitted.value;
|
||||||
|
action_admit_list.resize(ric_subscription_reponse.admitted_actions.size());
|
||||||
|
for (uint32_t i = 0; i < ric_subscription_reponse.admitted_actions.size(); i++) {
|
||||||
|
action_admit_list[i].load_info_obj(ASN1_E2AP_ID_RI_CACTION_ADMITTED_ITEM);
|
||||||
|
ri_caction_admitted_item_s& a_item = action_admit_list[i]->ri_caction_admitted_item();
|
||||||
|
a_item.ric_action_id = ric_subscription_reponse.admitted_actions[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ric_subscription_reponse.not_admitted_actions.size()) {
|
||||||
|
sub_resp->ri_cactions_not_admitted.crit = asn1::crit_opts::reject;
|
||||||
|
auto& action_not_admit_list = sub_resp->ri_cactions_not_admitted.value;
|
||||||
|
action_not_admit_list.resize(ric_subscription_reponse.not_admitted_actions.size());
|
||||||
|
for (uint32_t i = 0; i < ric_subscription_reponse.not_admitted_actions.size(); i++) {
|
||||||
|
action_not_admit_list[i].load_info_obj(ASN1_E2AP_ID_RI_CACTION_NOT_ADMITTED_ITEM);
|
||||||
|
ri_caction_not_admitted_item_s& not_a_item = action_not_admit_list[i]->ri_caction_not_admitted_item();
|
||||||
|
not_a_item.ric_action_id = ric_subscription_reponse.not_admitted_actions[i];
|
||||||
|
not_a_item.cause.set_misc(); // TODO: support cause properly
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pdu;
|
||||||
|
}
|
||||||
|
|
||||||
|
e2_ap_pdu_c e2ap::generate_subscription_failure(ric_subscription_reponse_t ric_subscription_reponse)
|
||||||
|
{
|
||||||
|
e2_ap_pdu_c pdu;
|
||||||
|
unsuccessful_outcome_s& failure = pdu.set_unsuccessful_outcome();
|
||||||
|
failure.load_info_obj(ASN1_E2AP_ID_RICSUBSCRIPTION);
|
||||||
|
failure.crit = asn1::crit_opts::reject;
|
||||||
|
ricsubscription_fail_s& sub_resp = failure.value.ricsubscription_fail();
|
||||||
|
|
||||||
|
sub_resp->ri_crequest_id.crit = asn1::crit_opts::reject;
|
||||||
|
sub_resp->ri_crequest_id.id = ASN1_E2AP_ID_RI_CREQUEST_ID;
|
||||||
|
sub_resp->ri_crequest_id.value.ric_requestor_id = ric_subscription_reponse.ric_requestor_id;
|
||||||
|
sub_resp->ri_crequest_id.value.ric_instance_id = ric_subscription_reponse.ric_instance_id;
|
||||||
|
|
||||||
|
sub_resp->ra_nfunction_id.crit = asn1::crit_opts::reject;
|
||||||
|
sub_resp->ra_nfunction_id.id = ASN1_E2AP_ID_RA_NFUNCTION_ID;
|
||||||
|
sub_resp->ra_nfunction_id->value = ric_subscription_reponse.ra_nfunction_id;
|
||||||
|
|
||||||
|
sub_resp->cause->set_misc(); // TODO: set the cause and crit_diagnostics properly
|
||||||
|
sub_resp->crit_diagnostics_present = false;
|
||||||
|
|
||||||
|
return pdu;
|
||||||
|
}
|
||||||
|
|
||||||
|
e2_ap_pdu_c e2ap::generate_subscription_delete_response(ric_subscription_reponse_t ric_subscription_reponse)
|
||||||
|
{
|
||||||
|
e2_ap_pdu_c pdu;
|
||||||
|
successful_outcome_s& success = pdu.set_successful_outcome();
|
||||||
|
success.load_info_obj(ASN1_E2AP_ID_RICSUBSCRIPTION_DELETE);
|
||||||
|
success.crit = asn1::crit_opts::reject;
|
||||||
|
ricsubscription_delete_resp_s& sub_resp = success.value.ricsubscription_delete_resp();
|
||||||
|
|
||||||
|
sub_resp->ri_crequest_id.crit = asn1::crit_opts::reject;
|
||||||
|
sub_resp->ri_crequest_id->ric_requestor_id = ric_subscription_reponse.ric_requestor_id;
|
||||||
|
sub_resp->ri_crequest_id->ric_instance_id = ric_subscription_reponse.ric_instance_id;
|
||||||
|
|
||||||
|
sub_resp->ra_nfunction_id.crit = asn1::crit_opts::reject;
|
||||||
|
sub_resp->ra_nfunction_id->value = ric_subscription_reponse.ra_nfunction_id;
|
||||||
|
|
||||||
|
return pdu;
|
||||||
|
}
|
||||||
|
|
||||||
|
e2_ap_pdu_c e2ap::generate_subscription_delete_failure(ric_subscription_reponse_t ric_subscription_reponse)
|
||||||
|
{
|
||||||
|
e2_ap_pdu_c pdu;
|
||||||
|
unsuccessful_outcome_s& failure = pdu.set_unsuccessful_outcome();
|
||||||
|
failure.load_info_obj(ASN1_E2AP_ID_RICSUBSCRIPTION);
|
||||||
|
failure.crit = asn1::crit_opts::reject;
|
||||||
|
ricsubscription_delete_fail_s& sub_resp = failure.value.ricsubscription_delete_fail();
|
||||||
|
|
||||||
|
sub_resp->ri_crequest_id.crit = asn1::crit_opts::reject;
|
||||||
|
sub_resp->ri_crequest_id.id = ASN1_E2AP_ID_RI_CREQUEST_ID;
|
||||||
|
sub_resp->ri_crequest_id.value.ric_requestor_id = ric_subscription_reponse.ric_requestor_id;
|
||||||
|
sub_resp->ri_crequest_id.value.ric_instance_id = ric_subscription_reponse.ric_instance_id;
|
||||||
|
|
||||||
|
sub_resp->ra_nfunction_id.crit = asn1::crit_opts::reject;
|
||||||
|
sub_resp->ra_nfunction_id.id = ASN1_E2AP_ID_RA_NFUNCTION_ID;
|
||||||
|
sub_resp->ra_nfunction_id->value = ric_subscription_reponse.ra_nfunction_id;
|
||||||
|
|
||||||
|
sub_resp->cause->set_misc(); // TODO: set the cause and crit_diagnostics properly
|
||||||
|
sub_resp->crit_diagnostics_present = false;
|
||||||
|
|
||||||
|
return pdu;
|
||||||
|
}
|
||||||
|
|
||||||
|
e2_ap_pdu_c e2ap::generate_subscription_delete_required(ric_subscription_reponse_t ric_subscription_reponse)
|
||||||
|
{
|
||||||
|
// TODO: available in e2ap-v3
|
||||||
|
e2_ap_pdu_c pdu;
|
||||||
|
return pdu;
|
||||||
|
}
|
||||||
|
|
||||||
|
e2_ap_pdu_c e2ap::generate_subscription_modification_response()
|
||||||
|
{
|
||||||
|
// TODO: available in e2ap-v3
|
||||||
|
e2_ap_pdu_c pdu;
|
||||||
|
return pdu;
|
||||||
|
}
|
||||||
|
|
||||||
|
e2_ap_pdu_c e2ap::generate_subscription_modification_failure()
|
||||||
|
{
|
||||||
|
// TODO: available in e2ap-v3
|
||||||
|
e2_ap_pdu_c pdu;
|
||||||
|
return pdu;
|
||||||
|
}
|
||||||
|
|
||||||
|
e2_ap_pdu_c e2ap::generate_subscription_modification_required()
|
||||||
|
{
|
||||||
|
// TODO: available in e2ap-v3
|
||||||
|
e2_ap_pdu_c pdu;
|
||||||
|
return pdu;
|
||||||
|
}
|
||||||
|
|
||||||
|
int e2ap::process_setup_response(e2setup_resp_s setup_response)
|
||||||
|
{
|
||||||
|
if (setup_response->transaction_id.value.value == 0) {
|
||||||
|
// TODO: transaction_id reset? check specs
|
||||||
|
setup_procedure_transaction_id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setup_procedure_transaction_id == setup_response->transaction_id.value.value) {
|
||||||
|
setup_procedure_transaction_id++;
|
||||||
|
e2_established = true;
|
||||||
|
} else {
|
||||||
|
logger.error("Received setup response with wrong transaction id");
|
||||||
|
return SRSRAN_ERROR;
|
||||||
|
}
|
||||||
|
global_ric_id.plmn_id = setup_response->global_ric_id.value.plmn_id.to_number();
|
||||||
|
global_ric_id.ric_id = setup_response->global_ric_id.value.ric_id.to_number();
|
||||||
|
|
||||||
|
if (setup_response->ra_nfunctions_accepted_present) {
|
||||||
|
for (int i = 0; i < (int)setup_response->ra_nfunctions_accepted.value.size(); i++) {
|
||||||
|
uint32_t ran_func_id = setup_response->ra_nfunctions_accepted.value[i]->ra_nfunction_id_item().ran_function_id;
|
||||||
|
if (ran_functions.find(ran_func_id) == ran_functions.end()) {
|
||||||
|
logger.error("Received setup response with unknown ran function id %d", ran_func_id);
|
||||||
|
} else {
|
||||||
|
logger.info("Received setup response with ran function id %d", ran_func_id);
|
||||||
|
ran_functions[ran_func_id].accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int e2ap::process_subscription_request(ricsubscription_request_s ric_subscription_request)
|
||||||
|
{
|
||||||
|
std::unique_ptr<e2ap::ric_subscription> new_ric_subs =
|
||||||
|
std::make_unique<e2ap::ric_subscription>(this, ric_subscription_request);
|
||||||
|
|
||||||
|
if (new_ric_subs->is_initialized()) {
|
||||||
|
new_ric_subs->start_subscription();
|
||||||
|
active_subscriptions.push_back(std::move(new_ric_subs));
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int e2ap::process_subscription_delete_request(ricsubscription_delete_request_s ricsubscription_delete_request)
|
||||||
|
{
|
||||||
|
bool ric_subs_found = false;
|
||||||
|
for (auto it = active_subscriptions.begin(); it != active_subscriptions.end(); it++) {
|
||||||
|
if ((**it).get_ric_requestor_id() == ricsubscription_delete_request->ri_crequest_id->ric_requestor_id and
|
||||||
|
(**it).get_ric_instance_id() == ricsubscription_delete_request->ri_crequest_id->ric_instance_id) {
|
||||||
|
ric_subs_found = true;
|
||||||
|
(**it).delete_subscription();
|
||||||
|
active_subscriptions.erase(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (not ric_subs_found) {
|
||||||
|
// TODO: send failure
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int e2ap::process_subscription_modification_request(uint32_t ric_subscription_modification_request)
|
||||||
|
{
|
||||||
|
// TODO: implement, here only placeholder
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int e2ap::process_subscription_modification_confirm(uint32_t ric_subscription_modification_confirm)
|
||||||
|
{
|
||||||
|
// TODO: implement, here only placeholder
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int e2ap::process_subscription_modification_refuse(uint32_t ric_subscription_modification_refuse)
|
||||||
|
{
|
||||||
|
// TODO: implement, here only placeholder
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
e2_ap_pdu_c e2ap::generate_indication(ric_indication_t& ric_indication)
|
||||||
|
{
|
||||||
|
using namespace asn1::e2ap;
|
||||||
|
e2_ap_pdu_c pdu;
|
||||||
|
|
||||||
|
init_msg_s& initmsg = pdu.set_init_msg();
|
||||||
|
initmsg.load_info_obj(ASN1_E2AP_ID_RI_CIND);
|
||||||
|
initmsg.crit = asn1::crit_opts::reject;
|
||||||
|
|
||||||
|
ri_cind_s& indication = initmsg.value.ri_cind();
|
||||||
|
indication->ri_crequest_id.crit = asn1::crit_opts::reject;
|
||||||
|
indication->ri_crequest_id.value.ric_requestor_id = ric_indication.ric_requestor_id;
|
||||||
|
indication->ri_crequest_id.value.ric_instance_id = ric_indication.ric_instance_id;
|
||||||
|
|
||||||
|
indication->ra_nfunction_id.crit = asn1::crit_opts::reject;
|
||||||
|
indication->ra_nfunction_id.value = ric_indication.ra_nfunction_id;
|
||||||
|
|
||||||
|
indication->ri_caction_id.crit = asn1::crit_opts::reject;
|
||||||
|
indication->ri_caction_id.value = ric_indication.ri_caction_id;
|
||||||
|
|
||||||
|
if (ric_indication.ri_indication_sn_present) {
|
||||||
|
indication->ri_cind_sn_present = true;
|
||||||
|
indication->ri_cind_sn.crit = asn1::crit_opts::reject;
|
||||||
|
indication->ri_cind_sn->value = ric_indication.ri_indication_sn;
|
||||||
|
}
|
||||||
|
|
||||||
|
indication->ri_cind_type.crit = asn1::crit_opts::reject;
|
||||||
|
indication->ri_cind_type.value = ric_indication.indication_type;
|
||||||
|
|
||||||
|
indication->ri_cind_hdr.crit = asn1::crit_opts::reject;
|
||||||
|
indication->ri_cind_hdr->resize(ric_indication.ri_cind_hdr->N_bytes);
|
||||||
|
std::copy(ric_indication.ri_cind_hdr->msg,
|
||||||
|
ric_indication.ri_cind_hdr->msg + ric_indication.ri_cind_hdr->N_bytes,
|
||||||
|
indication->ri_cind_hdr->data());
|
||||||
|
|
||||||
|
indication->ri_cind_msg.crit = asn1::crit_opts::reject;
|
||||||
|
indication->ri_cind_msg->resize(ric_indication.ri_cind_msg->N_bytes);
|
||||||
|
std::copy(ric_indication.ri_cind_msg->msg,
|
||||||
|
ric_indication.ri_cind_msg->msg + ric_indication.ri_cind_msg->N_bytes,
|
||||||
|
indication->ri_cind_msg->data());
|
||||||
|
|
||||||
|
return pdu;
|
||||||
|
}
|
||||||
|
|
||||||
|
e2_ap_pdu_c e2ap::generate_reset_request()
|
||||||
|
{
|
||||||
|
using namespace asn1::e2ap;
|
||||||
|
e2_ap_pdu_c pdu;
|
||||||
|
init_msg_s& request = pdu.set_init_msg();
|
||||||
|
request.load_info_obj(ASN1_E2AP_ID_RESET);
|
||||||
|
reset_request_s& reset_request = request.value.reset_request();
|
||||||
|
reset_request->transaction_id.crit = asn1::crit_opts::reject;
|
||||||
|
reset_request->transaction_id.value.value = reset_transaction_id;
|
||||||
|
reset_request->cause.crit = asn1::crit_opts::ignore;
|
||||||
|
reset_request->cause.value.set_misc();
|
||||||
|
return pdu;
|
||||||
|
}
|
||||||
|
|
||||||
|
e2_ap_pdu_c e2ap::generate_reset_response()
|
||||||
|
{
|
||||||
|
e2_ap_pdu_c pdu;
|
||||||
|
successful_outcome_s& response = pdu.set_successful_outcome();
|
||||||
|
response.load_info_obj(ASN1_E2AP_ID_RESET);
|
||||||
|
reset_resp_s& reset_response = response.value.reset_resp();
|
||||||
|
reset_response->transaction_id.crit = asn1::crit_opts::reject;
|
||||||
|
reset_response->transaction_id.value.value = reset_transaction_id;
|
||||||
|
return pdu;
|
||||||
|
}
|
||||||
|
|
||||||
|
int e2ap::process_reset_request(reset_request_s reset_request)
|
||||||
|
{
|
||||||
|
reset_id = reset_request->transaction_id.value;
|
||||||
|
|
||||||
|
// TODO: Parse and store the cause for future extension of the e2_agent
|
||||||
|
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int e2ap::process_reset_response(reset_resp_s reset_response)
|
||||||
|
{
|
||||||
|
// TO DO process reset response from RIC
|
||||||
|
reset_response_received = true;
|
||||||
|
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int e2ap::get_reset_id()
|
||||||
|
{
|
||||||
|
return reset_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// implementation of e2ap failure functions
|
||||||
|
int e2ap::process_e2_setup_failure(e2setup_fail_s e2setup_failure)
|
||||||
|
{
|
||||||
|
if (e2setup_failure->transaction_id.value.value == 0) {
|
||||||
|
// TODO: transaction_id reset? check specs
|
||||||
|
setup_procedure_transaction_id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setup_procedure_transaction_id == e2setup_failure->transaction_id.value.value) {
|
||||||
|
setup_procedure_transaction_id++;
|
||||||
|
} else {
|
||||||
|
logger.error("Received setup failure with wrong transaction id");
|
||||||
|
}
|
||||||
|
if (e2setup_failure->tn_linfo_present) {
|
||||||
|
logger.error("Received setup failure with transport layer info");
|
||||||
|
}
|
||||||
|
if (e2setup_failure->time_to_wait_present) {
|
||||||
|
logger.error("Received setup failure with time to wait");
|
||||||
|
e2_procedure_timeout.set(e2setup_failure->time_to_wait.value.to_number(), [this](int trans_id) {
|
||||||
|
logger.info("E2AP procedure timeout expired transaction id %d", trans_id);
|
||||||
|
pending_e2_setup = false;
|
||||||
|
});
|
||||||
|
e2_procedure_timeout.run();
|
||||||
|
}
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int e2ap::process_e2_node_config_update_failure(e2node_cfg_upd_fail_s e2node_config_update_failure)
|
||||||
|
{
|
||||||
|
pending_e2_node_config_update = false;
|
||||||
|
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int e2ap::process_ric_service_update_failure(ricservice_upd_fail_s service_update_failure)
|
||||||
|
{
|
||||||
|
pending_ric_service_update = false;
|
||||||
|
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int e2ap::process_e2_removal_failure(e2_removal_fail_s e2removal_failure)
|
||||||
|
{
|
||||||
|
pending_e2_removal = false;
|
||||||
|
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
@ -0,0 +1,218 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2023 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "srsgnb/hdr/stack/ric/e2ap_ric_subscription.h"
|
||||||
|
|
||||||
|
e2ap::ric_subscription::ric_subscription(e2ap* e2ap, ricsubscription_request_s ric_subscription_request) :
|
||||||
|
parent(e2ap),
|
||||||
|
initialized(false),
|
||||||
|
ric_requestor_id(ric_subscription_request->ri_crequest_id->ric_requestor_id),
|
||||||
|
ric_instance_id(ric_subscription_request->ri_crequest_id->ric_instance_id),
|
||||||
|
ra_nfunction_id(ric_subscription_request->ra_nfunction_id->value),
|
||||||
|
reporting_timer(parent->task_sched_ptr->get_unique_timer())
|
||||||
|
{
|
||||||
|
RANfunction_description ran_func_desc;
|
||||||
|
if (!parent->get_func_desc(ra_nfunction_id, ran_func_desc)) {
|
||||||
|
parent->logger.debug("Cannot find RAN function with ID: %i\n", ra_nfunction_id);
|
||||||
|
this->_send_subscription_failure();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sm_ptr = ran_func_desc.sm_ptr;
|
||||||
|
if (sm_ptr == nullptr) {
|
||||||
|
parent->logger.debug("No valid pointer to SM with RAN function id: %i\n", ra_nfunction_id);
|
||||||
|
this->_send_subscription_failure();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RIC_event_trigger_definition_t event_trigger;
|
||||||
|
if (sm_ptr->process_ric_event_trigger_definition(ric_subscription_request, event_trigger)) {
|
||||||
|
if (event_trigger.type == RIC_event_trigger_definition_t::e2sm_event_trigger_type_t::E2SM_REPORT) {
|
||||||
|
reporting_period = event_trigger.report_period;
|
||||||
|
reporting_period = 3000; // TODO: to remove, keep it 3s for testing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ri_cactions_to_be_setup_list_l& action_list =
|
||||||
|
ric_subscription_request->ricsubscription_details->ric_action_to_be_setup_list;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < action_list.size(); i++) {
|
||||||
|
ri_caction_to_be_setup_item_s action_item = action_list[i]->ri_caction_to_be_setup_item();
|
||||||
|
|
||||||
|
E2AP_RIC_action_t candidate_action;
|
||||||
|
candidate_action.ric_action_id = action_item.ric_action_id;
|
||||||
|
candidate_action.ric_action_type = action_item.ric_action_type;
|
||||||
|
|
||||||
|
if (sm_ptr->process_ric_action_definition(action_item, candidate_action)) {
|
||||||
|
parent->logger.debug("Admitted action %i (type: %i), mapped to SM local action ID: %i",
|
||||||
|
candidate_action.ric_action_id,
|
||||||
|
candidate_action.ric_action_type,
|
||||||
|
candidate_action.sm_local_ric_action_id);
|
||||||
|
|
||||||
|
printf("Admitted action %i, mapped to SM local action ID: %i\n",
|
||||||
|
candidate_action.ric_action_id,
|
||||||
|
candidate_action.sm_local_ric_action_id);
|
||||||
|
|
||||||
|
admitted_actions.push_back(candidate_action);
|
||||||
|
|
||||||
|
if (action_item.ric_subsequent_action_present) {
|
||||||
|
parent->logger.debug("--Action %i (type: %i) contains subsequent action of type %i with wait time: %i",
|
||||||
|
action_item.ric_action_id,
|
||||||
|
action_item.ric_action_type,
|
||||||
|
action_item.ric_subsequent_action.ric_subsequent_action_type,
|
||||||
|
action_item.ric_subsequent_action.ric_time_to_wait);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parent->logger.debug("Not admitted action %i (type: %i)", action_item.ric_action_id, action_item.ric_action_type);
|
||||||
|
not_admitted_actions.push_back(action_item.ric_action_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (admitted_actions.size() == 0) {
|
||||||
|
parent->logger.debug("No Action admitted -> remove subscription for RAN function id: %i", ra_nfunction_id);
|
||||||
|
printf("No Action admitted -> remove subscription for RAN function id: %i\n", ra_nfunction_id);
|
||||||
|
this->_send_subscription_failure();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void e2ap::ric_subscription::start_subscription()
|
||||||
|
{
|
||||||
|
this->_send_subscription_response();
|
||||||
|
|
||||||
|
if (reporting_period) {
|
||||||
|
printf("Start sending RIC indication msgs every %i ms\n", reporting_period);
|
||||||
|
parent->logger.debug("Start sending RIC indication msgs every %i ms", reporting_period);
|
||||||
|
reporting_timer.set(reporting_period, [this](uint32_t tid) { _send_ric_indication(); });
|
||||||
|
reporting_timer.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void e2ap::ric_subscription::_send_subscription_response()
|
||||||
|
{
|
||||||
|
parent->logger.debug("Send RIC Subscription Response to RIC Requestor ID: %i\n", ric_requestor_id);
|
||||||
|
ric_subscription_reponse_t ric_subscription_reponse;
|
||||||
|
ric_subscription_reponse.ric_requestor_id = ric_requestor_id;
|
||||||
|
ric_subscription_reponse.ric_instance_id = ric_instance_id;
|
||||||
|
ric_subscription_reponse.ra_nfunction_id = ra_nfunction_id;
|
||||||
|
|
||||||
|
for (auto& action : admitted_actions) {
|
||||||
|
ric_subscription_reponse.admitted_actions.push_back(action.ric_action_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& action : not_admitted_actions) {
|
||||||
|
ric_subscription_reponse.not_admitted_actions.push_back(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
e2_ap_pdu_c send_pdu = parent->generate_subscription_response(ric_subscription_reponse);
|
||||||
|
parent->queue_send_e2ap_pdu(send_pdu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void e2ap::ric_subscription::_send_subscription_failure()
|
||||||
|
{
|
||||||
|
parent->logger.debug("Send RIC Subscription Failure Response to RIC Requestor ID: %i\n", ric_requestor_id);
|
||||||
|
ric_subscription_reponse_t ric_subscription_reponse;
|
||||||
|
ric_subscription_reponse.ric_requestor_id = ric_requestor_id;
|
||||||
|
ric_subscription_reponse.ric_instance_id = ric_instance_id;
|
||||||
|
ric_subscription_reponse.ra_nfunction_id = ra_nfunction_id;
|
||||||
|
|
||||||
|
e2_ap_pdu_c send_pdu = parent->generate_subscription_failure(ric_subscription_reponse);
|
||||||
|
parent->queue_send_e2ap_pdu(send_pdu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void e2ap::ric_subscription::delete_subscription()
|
||||||
|
{
|
||||||
|
if (reporting_timer.is_running()) {
|
||||||
|
parent->logger.debug("Stop sending RIC indication msgs");
|
||||||
|
reporting_timer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
ric_subscription_reponse_t ric_subscription_reponse;
|
||||||
|
ric_subscription_reponse.ric_requestor_id = ric_requestor_id;
|
||||||
|
ric_subscription_reponse.ric_instance_id = ric_instance_id;
|
||||||
|
ric_subscription_reponse.ra_nfunction_id = ra_nfunction_id;
|
||||||
|
|
||||||
|
// remove registered actions from SM
|
||||||
|
if (sm_ptr) {
|
||||||
|
for (auto& action : admitted_actions) {
|
||||||
|
sm_ptr->remove_ric_action_definition(action);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
e2_ap_pdu_c send_pdu = parent->generate_subscription_delete_failure(ric_subscription_reponse);
|
||||||
|
parent->queue_send_e2ap_pdu(send_pdu);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent->logger.debug("Send RIC Subscription Delete Response to RIC Requestor ID: %i\n", ric_requestor_id);
|
||||||
|
|
||||||
|
e2_ap_pdu_c send_pdu = parent->generate_subscription_delete_response(ric_subscription_reponse);
|
||||||
|
parent->queue_send_e2ap_pdu(send_pdu);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2ap::ric_subscription::process_subscription_modification_request(uint32_t ric_subscription_modification_request)
|
||||||
|
{
|
||||||
|
// TODO: implement, currently not supported in ans1
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2ap::ric_subscription::process_subscription_modification_confirm(uint32_t ric_subscription_modification_confirm)
|
||||||
|
{
|
||||||
|
// TODO: implement, currently not supported in ans1
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2ap::ric_subscription::process_subscription_modification_refuse(uint32_t ric_subscription_modification_refuse)
|
||||||
|
{
|
||||||
|
// TODO: implement, currently not supported in ans1
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t e2ap::ric_subscription::_generate_ric_indication_sn()
|
||||||
|
{
|
||||||
|
uint32_t sn = _ric_indication_sn_gen;
|
||||||
|
_ric_indication_sn_gen++;
|
||||||
|
if (_ric_indication_sn_gen > 65535) {
|
||||||
|
_ric_indication_sn_gen = 0;
|
||||||
|
}
|
||||||
|
return sn;
|
||||||
|
};
|
||||||
|
|
||||||
|
void e2ap::ric_subscription::_send_ric_indication()
|
||||||
|
{
|
||||||
|
if (sm_ptr == nullptr) {
|
||||||
|
parent->logger.error("SM pointer not set in subscription: %i\n", ric_requestor_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& action : admitted_actions) {
|
||||||
|
printf("Sending RIC indication msg to RIC Requestor ID: %i\n", ric_requestor_id);
|
||||||
|
ric_indication_t ric_indication;
|
||||||
|
ric_indication.ric_requestor_id = ric_requestor_id;
|
||||||
|
ric_indication.ric_instance_id = ric_instance_id;
|
||||||
|
ric_indication.ra_nfunction_id = ra_nfunction_id;
|
||||||
|
ric_indication.ri_caction_id = action.ric_action_id;
|
||||||
|
ric_indication.ri_indication_sn_present = true;
|
||||||
|
ric_indication.ri_indication_sn = _generate_ric_indication_sn();
|
||||||
|
if (sm_ptr->generate_ric_indication_content(action, ric_indication)) {
|
||||||
|
e2_ap_pdu_c send_pdu = parent->generate_indication(ric_indication);
|
||||||
|
parent->queue_send_e2ap_pdu(send_pdu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reschedule sending RIC indication
|
||||||
|
if (reporting_period) {
|
||||||
|
reporting_timer.run();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,452 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2023 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "srsgnb/hdr/stack/ric/e2sm_kpm.h"
|
||||||
|
#include "srsgnb/hdr/stack/ric/e2sm_kpm_metrics.h"
|
||||||
|
#include "srsgnb/hdr/stack/ric/e2sm_kpm_report_service.h"
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
|
const std::string e2sm_kpm::short_name = "ORAN-E2SM-KPM";
|
||||||
|
const std::string e2sm_kpm::oid = "1.3.6.1.4.1.53148.1.2.2.2";
|
||||||
|
const std::string e2sm_kpm::func_description = "KPM Monitor";
|
||||||
|
const uint32_t e2sm_kpm::revision = 0;
|
||||||
|
|
||||||
|
e2sm_kpm::e2sm_kpm(srslog::basic_logger& logger_, srsran::task_scheduler* _task_sched_ptr) :
|
||||||
|
e2sm(short_name, oid, func_description, revision, _task_sched_ptr), logger(logger_)
|
||||||
|
{
|
||||||
|
random_gen = srsran_random_init(1234);
|
||||||
|
|
||||||
|
// add supported metrics
|
||||||
|
for (auto& metric : get_e2sm_kpm_28_552_metrics()) {
|
||||||
|
if (metric.supported) {
|
||||||
|
supported_meas_types.push_back(metric);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto& metric : get_e2sm_kpm_34_425_metrics()) {
|
||||||
|
if (metric.supported) {
|
||||||
|
supported_meas_types.push_back(metric);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto& metric : e2sm_kpm_oran_metrics()) {
|
||||||
|
if (metric.supported) {
|
||||||
|
supported_meas_types.push_back(metric);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto& metric : e2sm_kpm_custom_metrics()) {
|
||||||
|
if (metric.supported) {
|
||||||
|
supported_meas_types.push_back(metric);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
e2sm_kpm::~e2sm_kpm()
|
||||||
|
{
|
||||||
|
srsran_random_free(random_gen);
|
||||||
|
}
|
||||||
|
bool e2sm_kpm::generate_ran_function_description(RANfunction_description& desc, ra_nfunction_item_s& ran_func)
|
||||||
|
{
|
||||||
|
desc.function_shortname = short_name;
|
||||||
|
desc.function_e2_sm_oid = oid;
|
||||||
|
desc.function_desc = func_description;
|
||||||
|
|
||||||
|
e2_sm_kpm_ra_nfunction_description_s e2sm_kpm_ra_nfunction_description;
|
||||||
|
e2sm_kpm_ra_nfunction_description.ran_function_name.ran_function_short_name.from_string(short_name.c_str());
|
||||||
|
e2sm_kpm_ra_nfunction_description.ran_function_name.ran_function_e2_sm_oid.from_string(oid.c_str());
|
||||||
|
e2sm_kpm_ra_nfunction_description.ran_function_name.ran_function_description.from_string(func_description.c_str());
|
||||||
|
if (desc.function_instance) {
|
||||||
|
e2sm_kpm_ra_nfunction_description.ran_function_name.ran_function_instance_present = true;
|
||||||
|
e2sm_kpm_ra_nfunction_description.ran_function_name.ran_function_instance = desc.function_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// O-RAN.WG3.E2SM-KPM-R003-v03.00, 7.3.1 Event Trigger Style Types
|
||||||
|
auto& event_trigger_style_list = e2sm_kpm_ra_nfunction_description.ric_event_trigger_style_list;
|
||||||
|
event_trigger_style_list.resize(1);
|
||||||
|
event_trigger_style_list[0].ric_event_trigger_style_type = 1;
|
||||||
|
event_trigger_style_list[0].ric_event_trigger_style_name.from_string("Periodic report");
|
||||||
|
event_trigger_style_list[0].ric_event_trigger_format_type = 1; // uses RIC Event Trigger Definition Format 1
|
||||||
|
|
||||||
|
// O-RAN.WG3.E2SM-KPM-R003-v03.00, 7.4.1 REPORT Service Style Type
|
||||||
|
auto& report_style_list = e2sm_kpm_ra_nfunction_description.ric_report_style_list;
|
||||||
|
report_style_list.resize(1);
|
||||||
|
|
||||||
|
report_style_list[0].ric_report_style_type = 1;
|
||||||
|
report_style_list[0].ric_report_style_name.from_string("E2 Node Measurement");
|
||||||
|
report_style_list[0].ric_action_format_type = 1;
|
||||||
|
report_style_list[0].ric_ind_hdr_format_type = 1;
|
||||||
|
report_style_list[0].ric_ind_msg_format_type = 1;
|
||||||
|
|
||||||
|
std::vector<std::string> supported_enb_meas = _get_supported_meas(ENB_LEVEL | CELL_LEVEL);
|
||||||
|
for (const auto& metric : supported_enb_meas) {
|
||||||
|
meas_info_action_item_s meas_info_item;
|
||||||
|
meas_info_item.meas_name.from_string(metric.c_str());
|
||||||
|
report_style_list[0].meas_info_action_list.push_back(meas_info_item);
|
||||||
|
break; // TODO: add only one as flexric does not like long setup_request msg and crashes
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: seems that flexric does not like long setup_request msg and crashes, note: wireshark decodes it correctly
|
||||||
|
// see: nearRT-RIC: flexric/src/ric/msg_handler_ric.c:88:
|
||||||
|
// generate_setup_response: Assertion `req->ran_func_item[i].def.len < 127' failed
|
||||||
|
report_style_list[1].ric_report_style_type = 2;
|
||||||
|
report_style_list[1].ric_report_style_name.from_string("E2 Node Measurement for a single UE");
|
||||||
|
report_style_list[1].ric_action_format_type = 2;
|
||||||
|
report_style_list[1].ric_ind_hdr_format_type = 1;
|
||||||
|
report_style_list[1].ric_ind_msg_format_type = 1;
|
||||||
|
// TODO: add all supported UE LEVEL metrics
|
||||||
|
report_style_list[1].meas_info_action_list.resize(1);
|
||||||
|
report_style_list[1].meas_info_action_list[0].meas_name.from_string("RRU.PrbTotDl");
|
||||||
|
// A measurement ID can be used for subscription instead of a measurement type if an identifier of a certain
|
||||||
|
// measurement type was exposed by an E2 Node via the RAN Function Definition IE.
|
||||||
|
// measurement name to ID mapping (local to the E2 node), here only an example:
|
||||||
|
// report_style_list[1].meas_info_action_list[0].meas_id = 123;
|
||||||
|
|
||||||
|
report_style_list[2].ric_report_style_type = 3;
|
||||||
|
report_style_list[2].ric_report_style_name.from_string("Condition-based, UE-level E2 Node Measurement");
|
||||||
|
report_style_list[2].ric_action_format_type = 3;
|
||||||
|
report_style_list[2].ric_ind_hdr_format_type = 1;
|
||||||
|
report_style_list[2].ric_ind_msg_format_type = 2;
|
||||||
|
// TODO: add all supported UE LEVEL metrics
|
||||||
|
report_style_list[2].meas_info_action_list.resize(1);
|
||||||
|
report_style_list[2].meas_info_action_list[0].meas_name.from_string("RRU.PrbTotDl");
|
||||||
|
|
||||||
|
report_style_list[3].ric_report_style_type = 4;
|
||||||
|
report_style_list[3].ric_report_style_name.from_string("Common Condition-based, UE-level Measurement");
|
||||||
|
report_style_list[3].ric_action_format_type = 4;
|
||||||
|
report_style_list[3].ric_ind_hdr_format_type = 1;
|
||||||
|
report_style_list[3].ric_ind_msg_format_type = 3;
|
||||||
|
// TODO: add all supported UE LEVEL metrics
|
||||||
|
report_style_list[3].meas_info_action_list.resize(1);
|
||||||
|
report_style_list[3].meas_info_action_list[0].meas_name.from_string("RRU.PrbTotDl");
|
||||||
|
|
||||||
|
report_style_list[4].ric_report_style_type = 5;
|
||||||
|
report_style_list[4].ric_report_style_name.from_string("E2 Node Measurement for multiple UEs");
|
||||||
|
report_style_list[4].ric_action_format_type = 5;
|
||||||
|
report_style_list[4].ric_ind_hdr_format_type = 1;
|
||||||
|
report_style_list[4].ric_ind_msg_format_type = 3;
|
||||||
|
// TODO: add all supported UE LEVEL metrics
|
||||||
|
report_style_list[4].meas_info_action_list.resize(1);
|
||||||
|
report_style_list[4].meas_info_action_list[0].meas_name.from_string("RRU.PrbTotDl");
|
||||||
|
*/
|
||||||
|
logger.info("Generating RAN function description");
|
||||||
|
srsran::unique_byte_buffer_t buf = srsran::make_byte_buffer();
|
||||||
|
asn1::bit_ref bref(buf->msg, buf->get_tailroom());
|
||||||
|
if (e2sm_kpm_ra_nfunction_description.pack(bref) != asn1::SRSASN_SUCCESS) {
|
||||||
|
printf("Failed to pack TX E2 PDU\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
buf->N_bytes = bref.distance_bytes();
|
||||||
|
|
||||||
|
ran_func.ran_function_definition.resize(buf->N_bytes);
|
||||||
|
std::copy(buf->msg, buf->msg + buf->N_bytes, ran_func.ran_function_definition.data());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm::process_ric_event_trigger_definition(ricsubscription_request_s subscription_request,
|
||||||
|
RIC_event_trigger_definition_t& event_def)
|
||||||
|
{
|
||||||
|
e2_sm_kpm_event_trigger_definition_s trigger_def;
|
||||||
|
asn1::cbit_ref bref(subscription_request->ricsubscription_details->ric_event_trigger_definition.data(),
|
||||||
|
subscription_request->ricsubscription_details->ric_event_trigger_definition.size());
|
||||||
|
|
||||||
|
if (trigger_def.unpack(bref) != asn1::SRSASN_SUCCESS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
event_def.type = RIC_event_trigger_definition_t::e2sm_event_trigger_type_t::E2SM_REPORT;
|
||||||
|
event_def.report_period = trigger_def.event_definition_formats.event_definition_format1().report_period;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm::process_ric_action_definition(ri_caction_to_be_setup_item_s ric_action, E2AP_RIC_action_t& action_entry)
|
||||||
|
{
|
||||||
|
bool admit_action = false;
|
||||||
|
e2_sm_kpm_action_definition_s e2sm_kpm_action_def;
|
||||||
|
asn1::cbit_ref bref(ric_action.ric_action_definition.data(), ric_action.ric_action_definition.size());
|
||||||
|
|
||||||
|
if (e2sm_kpm_action_def.unpack(bref) != asn1::SRSASN_SUCCESS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
action_entry.sm_local_ric_action_id = _get_local_action_id();
|
||||||
|
e2sm_kpm_report_service* report_service;
|
||||||
|
|
||||||
|
switch (e2sm_kpm_action_def.ric_style_type) {
|
||||||
|
case 1:
|
||||||
|
admit_action = e2sm_kpm_report_service_style1::process_ric_action_definition(this, e2sm_kpm_action_def);
|
||||||
|
if (admit_action) {
|
||||||
|
report_service =
|
||||||
|
new e2sm_kpm_report_service_style1(this, action_entry.sm_local_ric_action_id, e2sm_kpm_action_def);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
admit_action = e2sm_kpm_report_service_style2::process_ric_action_definition(this, e2sm_kpm_action_def);
|
||||||
|
if (admit_action) {
|
||||||
|
report_service =
|
||||||
|
new e2sm_kpm_report_service_style2(this, action_entry.sm_local_ric_action_id, e2sm_kpm_action_def);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
admit_action = e2sm_kpm_report_service_style3::process_ric_action_definition(this, e2sm_kpm_action_def);
|
||||||
|
if (admit_action) {
|
||||||
|
report_service =
|
||||||
|
new e2sm_kpm_report_service_style3(this, action_entry.sm_local_ric_action_id, e2sm_kpm_action_def);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
admit_action = e2sm_kpm_report_service_style4::process_ric_action_definition(this, e2sm_kpm_action_def);
|
||||||
|
if (admit_action) {
|
||||||
|
report_service =
|
||||||
|
new e2sm_kpm_report_service_style4(this, action_entry.sm_local_ric_action_id, e2sm_kpm_action_def);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
admit_action = e2sm_kpm_report_service_style5::process_ric_action_definition(this, e2sm_kpm_action_def);
|
||||||
|
if (admit_action) {
|
||||||
|
report_service =
|
||||||
|
new e2sm_kpm_report_service_style5(this, action_entry.sm_local_ric_action_id, e2sm_kpm_action_def);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
logger.info("Unknown RIC style type %i -> do not admit action %i (type %i)",
|
||||||
|
e2sm_kpm_action_def.ric_style_type,
|
||||||
|
ric_action.ric_action_id,
|
||||||
|
ric_action.ric_action_type);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (not admit_action) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_generate_new_local_action_id();
|
||||||
|
|
||||||
|
registered_actions_data.insert(
|
||||||
|
std::pair<uint32_t, e2sm_kpm_report_service*>(action_entry.sm_local_ric_action_id, report_service));
|
||||||
|
|
||||||
|
return admit_action;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm::remove_ric_action_definition(E2AP_RIC_action_t& action_entry)
|
||||||
|
{
|
||||||
|
if (registered_actions_data.count(action_entry.sm_local_ric_action_id)) {
|
||||||
|
registered_actions_data.at(action_entry.sm_local_ric_action_id)->stop();
|
||||||
|
delete registered_actions_data.at(action_entry.sm_local_ric_action_id);
|
||||||
|
registered_actions_data.erase(action_entry.sm_local_ric_action_id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm::generate_ric_indication_content(E2AP_RIC_action_t& action_entry, ric_indication_t& ric_indication)
|
||||||
|
{
|
||||||
|
uint32_t action_id = action_entry.sm_local_ric_action_id;
|
||||||
|
if (!registered_actions_data.count(action_id)) {
|
||||||
|
logger.info("Unknown RIC action ID: %i (type %i) (SM local RIC action ID: %i)",
|
||||||
|
action_entry.ric_action_id,
|
||||||
|
action_entry.ric_action_type,
|
||||||
|
action_entry.sm_local_ric_action_id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
e2sm_kpm_report_service* report_service = registered_actions_data.at(action_id);
|
||||||
|
|
||||||
|
if (not report_service->is_ric_ind_ready()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ric_indication.indication_type = ri_cind_type_opts::report;
|
||||||
|
|
||||||
|
// header is the same for all RIC service styles, i.e., type 1
|
||||||
|
ric_indication.ri_cind_hdr = srsran::make_byte_buffer();
|
||||||
|
this->_generate_indication_header(report_service->get_ind_hdr(), ric_indication.ri_cind_hdr);
|
||||||
|
|
||||||
|
logger.info("Generating E2-SM-KPM Indication Message");
|
||||||
|
ric_indication.ri_cind_msg = srsran::make_byte_buffer();
|
||||||
|
this->_generate_indication_message(report_service->get_ind_msg(), ric_indication.ri_cind_msg);
|
||||||
|
|
||||||
|
// clear data collected for this action
|
||||||
|
report_service->clear_collected_data();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm::_generate_indication_header(e2_sm_kpm_ind_hdr_s& hdr, srsran::unique_byte_buffer_t& buf)
|
||||||
|
{
|
||||||
|
asn1::bit_ref bref(buf->msg, buf->get_tailroom());
|
||||||
|
if (hdr.pack(bref) != asn1::SRSASN_SUCCESS) {
|
||||||
|
printf("IND HEADER: Failed to pack TX E2 PDU\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
buf->N_bytes = bref.distance_bytes();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm::_generate_indication_message(e2_sm_kpm_ind_msg_s& msg, srsran::unique_byte_buffer_t& buf)
|
||||||
|
{
|
||||||
|
logger.info("Generating E2-SM-KPM Indication Message");
|
||||||
|
asn1::bit_ref bref(buf->msg, buf->get_tailroom());
|
||||||
|
if (msg.pack(bref) != asn1::SRSASN_SUCCESS) {
|
||||||
|
printf("IND MSG: Failed to pack TX E2 PDU\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
buf->N_bytes = bref.distance_bytes();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm::_get_meas_definition(std::string meas_name, e2sm_kpm_metric_t& def)
|
||||||
|
{
|
||||||
|
auto name_matches = [&meas_name](const e2sm_kpm_metric_t& x) {
|
||||||
|
return (x.name == meas_name.c_str() or x.name == meas_name);
|
||||||
|
};
|
||||||
|
auto it = std::find_if(supported_meas_types.begin(), supported_meas_types.end(), name_matches);
|
||||||
|
if (it == supported_meas_types.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
def = *it;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> e2sm_kpm::_get_supported_meas(uint32_t level_mask)
|
||||||
|
{
|
||||||
|
std::vector<std::string> supported_meas;
|
||||||
|
for (auto& metric : supported_meas_types) {
|
||||||
|
if ((level_mask & ENB_LEVEL) and (metric.supported_scopes & ENB_LEVEL)) {
|
||||||
|
supported_meas.push_back(metric.name);
|
||||||
|
} else if ((level_mask & CELL_LEVEL) and (metric.supported_scopes & CELL_LEVEL)) {
|
||||||
|
supported_meas.push_back(metric.name);
|
||||||
|
} else if ((level_mask & UE_LEVEL) and (metric.supported_scopes & UE_LEVEL)) {
|
||||||
|
supported_meas.push_back(metric.name);
|
||||||
|
} else if ((level_mask & BEARER_LEVEL) and (metric.supported_scopes & BEARER_LEVEL)) {
|
||||||
|
supported_meas.push_back(metric.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return supported_meas;
|
||||||
|
}
|
||||||
|
|
||||||
|
void e2sm_kpm::receive_e2_metrics_callback(const enb_metrics_t& m)
|
||||||
|
{
|
||||||
|
last_enb_metrics = m;
|
||||||
|
logger.debug("e2sm_kpm received new enb metrics, CPU0 Load: %.1f", last_enb_metrics.sys.cpu_load[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm::_collect_meas_value(e2sm_kpm_meas_def_t& meas_value, meas_record_item_c& item)
|
||||||
|
{
|
||||||
|
// here we implement logic of measurement data collection, currently we only read from enb_metrics
|
||||||
|
if (meas_value.data_type == meas_record_item_c::types::options::integer) {
|
||||||
|
uint32_t value;
|
||||||
|
if (_extract_integer_type_meas_value(meas_value, last_enb_metrics, value)) {
|
||||||
|
item.set_integer() = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// data_type == meas_record_item_c::types::options::real;
|
||||||
|
float value;
|
||||||
|
if (_extract_real_type_meas_value(meas_value, last_enb_metrics, value)) {
|
||||||
|
real_s real_value;
|
||||||
|
// TODO: real value seems to be not supported in asn1???
|
||||||
|
// real_value.value = value;
|
||||||
|
item.set_real() = real_value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm::_extract_integer_type_meas_value(e2sm_kpm_meas_def_t& meas_value,
|
||||||
|
const enb_metrics_t& enb_metrics,
|
||||||
|
uint32_t& value)
|
||||||
|
{
|
||||||
|
// TODO: maybe add ID to metric types in e2sm_kpm_metrics definitions, so we do not have to compare strings?
|
||||||
|
// TODO: make string comparison case insensitive
|
||||||
|
// all integer type measurements
|
||||||
|
// test: no_label
|
||||||
|
if (meas_value.name.c_str() == std::string("test")) {
|
||||||
|
switch (meas_value.label) {
|
||||||
|
case NO_LABEL:
|
||||||
|
if (meas_value.scope & ENB_LEVEL) {
|
||||||
|
value = (int32_t)enb_metrics.sys.cpu_load[0];
|
||||||
|
printf("extract last \"test\" value as int, (filled with ENB_LEVEL metric: CPU0_load) value %i \n", value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (meas_value.scope & CELL_LEVEL) {
|
||||||
|
uint32_t cell_id = meas_value.cell_id;
|
||||||
|
value = (int32_t)enb_metrics.stack.mac.cc_info[cell_id].cc_rach_counter;
|
||||||
|
printf("extract last \"test\" value as int, (filled with CELL_LEVEL metric: cc_rach_counter) value %i \n",
|
||||||
|
value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (meas_value.scope & UE_LEVEL) {
|
||||||
|
uint32_t ue_id = meas_value.ue_id;
|
||||||
|
value = (int32_t)enb_metrics.stack.mac.ues[ue_id].ul_rssi;
|
||||||
|
printf("extract last \"test\" value as int, (filled with UE_LEVEL metric: ul_rssi) value %i \n", value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// random_int: no_label
|
||||||
|
if (meas_value.name.c_str() == std::string("random_int")) {
|
||||||
|
switch (meas_value.label) {
|
||||||
|
case NO_LABEL:
|
||||||
|
value = srsran_random_uniform_int_dist(random_gen, 0, 100);
|
||||||
|
printf("extract last \"random_int\" value as int, random value %i \n", value);
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm::_extract_real_type_meas_value(e2sm_kpm_meas_def_t& meas_value,
|
||||||
|
const enb_metrics_t& enb_metrics,
|
||||||
|
float& value)
|
||||||
|
{
|
||||||
|
// all real type measurements
|
||||||
|
// cpu0_load: no_label
|
||||||
|
if (meas_value.name.c_str() == std::string("cpu0_load")) {
|
||||||
|
switch (meas_value.label) {
|
||||||
|
case NO_LABEL:
|
||||||
|
value = enb_metrics.sys.cpu_load[0];
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cpu_load: min,max,avg
|
||||||
|
if (meas_value.name.c_str() == std::string("cpu_load")) {
|
||||||
|
uint32_t size;
|
||||||
|
switch (meas_value.label) {
|
||||||
|
case MIN_LABEL:
|
||||||
|
value = *std::min_element(enb_metrics.sys.cpu_load.begin(), enb_metrics.sys.cpu_load.end());
|
||||||
|
return true;
|
||||||
|
case MAX_LABEL:
|
||||||
|
value = *std::max_element(enb_metrics.sys.cpu_load.begin(), enb_metrics.sys.cpu_load.end());
|
||||||
|
return true;
|
||||||
|
case AVG_LABEL:
|
||||||
|
size = enb_metrics.sys.cpu_load.size();
|
||||||
|
value = std::accumulate(enb_metrics.sys.cpu_load.begin(), enb_metrics.sys.cpu_load.end(), 0.0 / size);
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2023 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "srsgnb/hdr/stack/ric/e2sm_kpm_common.h"
|
||||||
|
|
||||||
|
std::string e2sm_kpm_label_2_str(e2sm_kpm_label_enum label)
|
||||||
|
{
|
||||||
|
switch (label) {
|
||||||
|
case NO_LABEL:
|
||||||
|
return "NO_LABEL";
|
||||||
|
case MIN_LABEL:
|
||||||
|
return "MIN_LABEL";
|
||||||
|
case MAX_LABEL:
|
||||||
|
return "MAX_LABEL";
|
||||||
|
case AVG_LABEL:
|
||||||
|
return "AVG_LABEL";
|
||||||
|
case SUM_LABEL:
|
||||||
|
return "SUM_LABEL";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN_LABEL";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,585 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2023 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "srsgnb/hdr/stack/ric/e2sm_kpm_report_service.h"
|
||||||
|
|
||||||
|
e2sm_kpm_report_service::e2sm_kpm_report_service(e2sm_kpm* e2sm_kpm,
|
||||||
|
uint16_t action_id,
|
||||||
|
e2_sm_kpm_action_definition_s action_definition) :
|
||||||
|
parent(e2sm_kpm),
|
||||||
|
action_id(action_id),
|
||||||
|
action_def_generic(action_definition),
|
||||||
|
ric_ind_header_generic(),
|
||||||
|
ric_ind_header(ric_ind_header_generic.ind_hdr_formats.ind_hdr_format1()),
|
||||||
|
meas_collection_timer(parent->task_sched_ptr->get_unique_timer())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<e2sm_kpm_label_enum>
|
||||||
|
e2sm_kpm_report_service::_get_present_labels(const meas_info_item_s& action_meas_info_item)
|
||||||
|
{
|
||||||
|
std::vector<e2sm_kpm_label_enum> labels;
|
||||||
|
// TODO: add all labels defined in e2sm_kpm doc
|
||||||
|
for (uint32_t l = 0; l < action_meas_info_item.label_info_list.size(); l++) {
|
||||||
|
if (action_meas_info_item.label_info_list[l].meas_label.no_label_present) {
|
||||||
|
labels.push_back(NO_LABEL);
|
||||||
|
}
|
||||||
|
if (action_meas_info_item.label_info_list[l].meas_label.min_present) {
|
||||||
|
labels.push_back(MIN_LABEL);
|
||||||
|
}
|
||||||
|
if (action_meas_info_item.label_info_list[l].meas_label.max_present) {
|
||||||
|
labels.push_back(MAX_LABEL);
|
||||||
|
}
|
||||||
|
if (action_meas_info_item.label_info_list[l].meas_label.avg_present) {
|
||||||
|
labels.push_back(AVG_LABEL);
|
||||||
|
}
|
||||||
|
if (action_meas_info_item.label_info_list[l].meas_label.sum_present) {
|
||||||
|
labels.push_back(SUM_LABEL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return labels;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service::_initialize_ric_ind_hdr()
|
||||||
|
{
|
||||||
|
// TODO: set the remaining fields properly (they are optional)
|
||||||
|
ric_ind_header.collet_start_time.from_number(std::time(0));
|
||||||
|
// ric_ind_header.file_formatversion.from_string(hdr.file_formatversion);
|
||||||
|
// ric_ind_header.sender_name.from_string(hdr.sender_name);
|
||||||
|
// ric_ind_header.sender_type.from_string(hdr.sender_type);
|
||||||
|
// ric_ind_header.vendor_name.from_string(hdr.vendor_name);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
meas_record_item_c::types e2sm_kpm_report_service::_get_meas_data_type(std::string meas_name,
|
||||||
|
e2sm_kpm_label_enum label,
|
||||||
|
meas_record_l& meas_record_list)
|
||||||
|
{
|
||||||
|
meas_record_item_c::types data_type = meas_record_item_c::types::options::nulltype;
|
||||||
|
// if no data collected check the type using metric definition
|
||||||
|
if (meas_record_list.size() == 0) {
|
||||||
|
e2sm_kpm_metric_t metric_definition;
|
||||||
|
if (not parent->_get_meas_definition(meas_name, metric_definition)) {
|
||||||
|
parent->logger.debug("No definition for measurement type \"%s\"", metric_definition.name);
|
||||||
|
return data_type;
|
||||||
|
}
|
||||||
|
if (metric_definition.data_type == INTEGER) {
|
||||||
|
data_type = meas_record_item_c::types::options::integer;
|
||||||
|
} else {
|
||||||
|
data_type = meas_record_item_c::types::options::real;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// check the data type of the first element in the list
|
||||||
|
data_type = meas_record_list[0].type();
|
||||||
|
}
|
||||||
|
return data_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service::_start_meas_collection()
|
||||||
|
{
|
||||||
|
if (granul_period) {
|
||||||
|
printf("Start collecting measurements every %i ms\n", granul_period);
|
||||||
|
parent->logger.debug("Start collecting measurements every every %i ms", granul_period);
|
||||||
|
meas_collection_timer.set(granul_period, [this](uint32_t tid) { this->_collect_meas_data(); });
|
||||||
|
meas_collection_timer.run();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service::stop()
|
||||||
|
{
|
||||||
|
return _stop_meas_collection();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service::_stop_meas_collection()
|
||||||
|
{
|
||||||
|
if (meas_collection_timer.is_running()) {
|
||||||
|
printf("Stop collecting measurements every %i ms\n", granul_period);
|
||||||
|
parent->logger.debug("Stop collecting measurements every %i ms\n", granul_period);
|
||||||
|
meas_collection_timer.stop();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service::_reschedule_meas_collection()
|
||||||
|
{
|
||||||
|
if (granul_period) {
|
||||||
|
meas_collection_timer.run();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
e2sm_kpm_report_service_style1::e2sm_kpm_report_service_style1(e2sm_kpm* e2sm_kpm,
|
||||||
|
uint16_t action_id,
|
||||||
|
e2_sm_kpm_action_definition_s action_definition) :
|
||||||
|
e2sm_kpm_report_service(e2sm_kpm, action_id, action_definition),
|
||||||
|
action_def(action_def_generic.action_definition_formats.action_definition_format1()),
|
||||||
|
ric_ind_message(ric_ind_message_generic.ind_msg_formats.set_ind_msg_format1())
|
||||||
|
{
|
||||||
|
ind_msg_format = e2_sm_kpm_ind_msg_s::ind_msg_formats_c_::types_opts::ind_msg_format1;
|
||||||
|
granul_period = action_def.granul_period;
|
||||||
|
cell_global_id_present = action_def.cell_global_id_present;
|
||||||
|
if (cell_global_id_present) {
|
||||||
|
cell_global_id = action_def.cell_global_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->_initialize_ric_ind_hdr();
|
||||||
|
this->_initialize_ric_ind_msg();
|
||||||
|
|
||||||
|
granul_period = 1000; // TODO: overwrite for testing
|
||||||
|
_start_meas_collection();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service_style1::_initialize_ric_ind_msg()
|
||||||
|
{
|
||||||
|
meas_info_list_l action_meas_info_list = action_def.meas_info_list;
|
||||||
|
|
||||||
|
// ric_ind_message.granul_period_present = true;
|
||||||
|
// ric_ind_message.granul_period = granul_period; // TODO: our asn1 has some issues with this field
|
||||||
|
ric_ind_message.granul_period = 0;
|
||||||
|
ric_ind_message.meas_info_list.resize(action_meas_info_list.size());
|
||||||
|
ric_ind_message.meas_data.resize(action_meas_info_list.size());
|
||||||
|
|
||||||
|
// add measurement info
|
||||||
|
for (uint32_t i = 0; i < ric_ind_message.meas_info_list.size(); i++) {
|
||||||
|
// structs to fill
|
||||||
|
meas_info_item_s& meas_info_item = ric_ind_message.meas_info_list[i];
|
||||||
|
|
||||||
|
// measurements definition
|
||||||
|
meas_info_item_s& meas_def_item = action_meas_info_list[i];
|
||||||
|
std::string meas_name = meas_def_item.meas_type.meas_name().to_string();
|
||||||
|
|
||||||
|
meas_info_item.meas_type.set_meas_name().from_string(meas_name.c_str());
|
||||||
|
meas_info_item.label_info_list.resize(meas_def_item.label_info_list.size());
|
||||||
|
|
||||||
|
// TODO: add all labels defined in e2sm_kpm doc, make this part generic and put to the base class
|
||||||
|
for (uint32_t l = 0; l < meas_def_item.label_info_list.size(); l++) {
|
||||||
|
if (meas_def_item.label_info_list[l].meas_label.no_label_present) {
|
||||||
|
meas_info_item.label_info_list[l].meas_label.no_label_present = true;
|
||||||
|
meas_info_item.label_info_list[l].meas_label.no_label = meas_label_s::no_label_opts::true_value;
|
||||||
|
}
|
||||||
|
if (meas_def_item.label_info_list[l].meas_label.min_present) {
|
||||||
|
meas_info_item.label_info_list[l].meas_label.min_present = true;
|
||||||
|
meas_info_item.label_info_list[l].meas_label.min = meas_label_s::min_opts::true_value;
|
||||||
|
}
|
||||||
|
if (meas_def_item.label_info_list[l].meas_label.max_present) {
|
||||||
|
meas_info_item.label_info_list[l].meas_label.max_present = true;
|
||||||
|
meas_info_item.label_info_list[l].meas_label.max = meas_label_s::max_opts::true_value;
|
||||||
|
}
|
||||||
|
if (meas_def_item.label_info_list[l].meas_label.avg_present) {
|
||||||
|
meas_info_item.label_info_list[l].meas_label.avg_present = true;
|
||||||
|
meas_info_item.label_info_list[l].meas_label.avg = meas_label_s::avg_opts::true_value;
|
||||||
|
}
|
||||||
|
if (meas_def_item.label_info_list[l].meas_label.sum_present) {
|
||||||
|
meas_info_item.label_info_list[l].meas_label.sum_present = true;
|
||||||
|
meas_info_item.label_info_list[l].meas_label.sum = meas_label_s::sum_opts::true_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service_style1::process_ric_action_definition(e2sm_kpm* e2sm_kpm,
|
||||||
|
e2_sm_kpm_action_definition_s& action_def_generic)
|
||||||
|
{
|
||||||
|
e2_sm_kpm_action_definition_format1_s& action_definition =
|
||||||
|
action_def_generic.action_definition_formats.action_definition_format1();
|
||||||
|
|
||||||
|
bool cell_global_id_present = false;
|
||||||
|
uint64_t granul_period;
|
||||||
|
uint64_t eutra_cell_id;
|
||||||
|
uint64_t plmn_id;
|
||||||
|
ueid_c ue_id;
|
||||||
|
meas_info_list_l meas_info_list;
|
||||||
|
|
||||||
|
granul_period = action_definition.granul_period;
|
||||||
|
|
||||||
|
if (granul_period == 0) {
|
||||||
|
e2sm_kpm->logger.debug("Action granularity period of %i is not supported -> do not admitted action\n",
|
||||||
|
granul_period);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action_definition.cell_global_id_present) {
|
||||||
|
cell_global_id_present = true;
|
||||||
|
if (action_definition.cell_global_id.type() == cgi_c::types_opts::eutra_cgi) {
|
||||||
|
eutra_cell_id = action_definition.cell_global_id.eutra_cgi().eutra_cell_id.to_number();
|
||||||
|
plmn_id = action_definition.cell_global_id.eutra_cgi().plmn_id.to_number();
|
||||||
|
e2sm_kpm->logger.debug("plmn_id 0x%x, eutra_cell_id %i", plmn_id, eutra_cell_id);
|
||||||
|
// TODO: check if E2 node has cell_id and plmn_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, e2sm_kpm_label_enum> admitted_metrics;
|
||||||
|
meas_info_list = action_definition.meas_info_list;
|
||||||
|
for (uint32_t i = 0; i < meas_info_list.size(); i++) {
|
||||||
|
std::string meas_name = meas_info_list[i].meas_type.meas_name().to_string();
|
||||||
|
e2sm_kpm_metric_t metric_definition;
|
||||||
|
if (not e2sm_kpm->_get_meas_definition(meas_name, metric_definition)) {
|
||||||
|
printf("Unsupported measurement name: \"%s\" --> do not admit action\n", meas_name.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t nof_labels = 0;
|
||||||
|
// TODO: add all labels defined in e2sm_kpm doc, make this part generic and put to base class
|
||||||
|
// TODO: check if metric is supported at the requested level, i.e., CELL_LEVEL if cell_global_id_present == true,
|
||||||
|
// and ENB_LEVEL otherwise
|
||||||
|
for (uint32_t l = 0; l < meas_info_list[i].label_info_list.size(); l++) {
|
||||||
|
if (meas_info_list[i].label_info_list[l].meas_label.no_label_present) {
|
||||||
|
if (metric_definition.supported_labels & NO_LABEL) {
|
||||||
|
nof_labels++;
|
||||||
|
admitted_metrics[meas_name] = NO_LABEL;
|
||||||
|
} else {
|
||||||
|
printf("Unsupported label: NO_LABEL for metric \"%s\" --> do not admit action\n", meas_name.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (meas_info_list[i].label_info_list[l].meas_label.min_present) {
|
||||||
|
if (metric_definition.supported_labels & MIN_LABEL) {
|
||||||
|
nof_labels++;
|
||||||
|
admitted_metrics[meas_name] = MIN_LABEL;
|
||||||
|
} else {
|
||||||
|
printf("Unsupported label: MIN_LABEL for metric \"%s\" --> do not admit action\n", meas_name.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (meas_info_list[i].label_info_list[l].meas_label.max_present) {
|
||||||
|
if (metric_definition.supported_labels & MAX_LABEL) {
|
||||||
|
nof_labels++;
|
||||||
|
admitted_metrics[meas_name] = MAX_LABEL;
|
||||||
|
} else {
|
||||||
|
printf("Unsupported label: MAX_LABEL for metric \"%s\" --> do not admit action\n", meas_name.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (meas_info_list[i].label_info_list[l].meas_label.avg_present) {
|
||||||
|
if (metric_definition.supported_labels & AVG_LABEL) {
|
||||||
|
nof_labels++;
|
||||||
|
admitted_metrics[meas_name] = AVG_LABEL;
|
||||||
|
} else {
|
||||||
|
printf("Unsupported label: AVG_LABEL for metric \"%s\" --> do not admit action\n", meas_name.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: currently we use labels as choice (i.e., only one can be present) as documentation is not clear about it
|
||||||
|
if (nof_labels > 1) {
|
||||||
|
printf("Only one label per metric can be present, meas: \"%s\" has %i labels --> do not admit action\n",
|
||||||
|
meas_name.c_str(),
|
||||||
|
nof_labels);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Admitted action with the following metrics and labels: \n");
|
||||||
|
for (const auto& it : admitted_metrics) {
|
||||||
|
std::string meas_name = it.first;
|
||||||
|
std::string label_str = e2sm_kpm_label_2_str(it.second);
|
||||||
|
printf("--- Metric: \"%s\" with label: %s\n", meas_name.c_str(), label_str.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
meas_data_item_s& e2sm_kpm_report_service_style1::_get_meas_data_item(std::string meas_name,
|
||||||
|
e2sm_kpm_label_enum label,
|
||||||
|
uint32_t ue_id,
|
||||||
|
bool& ref_found)
|
||||||
|
{
|
||||||
|
meas_info_list_l& meas_info_list = ric_ind_message.meas_info_list;
|
||||||
|
ref_found = false;
|
||||||
|
// find proper index of the metric
|
||||||
|
for (uint32_t i = 0; i < meas_info_list.size(); i++) {
|
||||||
|
// measurements definition
|
||||||
|
meas_info_item_s meas_def_item = meas_info_list[i];
|
||||||
|
std::string meas_def_name = meas_def_item.meas_type.meas_name().to_string();
|
||||||
|
|
||||||
|
// check if the metric name matches
|
||||||
|
if (meas_def_name != meas_name.c_str()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the metric label matches
|
||||||
|
// TODO: add all labels defined in e2sm_kpm doc
|
||||||
|
for (uint32_t l = 0; l < meas_def_item.label_info_list.size(); l++) {
|
||||||
|
if (meas_def_item.label_info_list[l].meas_label.no_label_present and label == NO_LABEL) {
|
||||||
|
ref_found = true;
|
||||||
|
return ric_ind_message.meas_data[i];
|
||||||
|
}
|
||||||
|
if (meas_def_item.label_info_list[l].meas_label.min_present and label == MIN_LABEL) {
|
||||||
|
ref_found = true;
|
||||||
|
return ric_ind_message.meas_data[i];
|
||||||
|
}
|
||||||
|
if (meas_def_item.label_info_list[l].meas_label.max_present and label == MAX_LABEL) {
|
||||||
|
ref_found = true;
|
||||||
|
return ric_ind_message.meas_data[i];
|
||||||
|
}
|
||||||
|
if (meas_def_item.label_info_list[l].meas_label.avg_present and label == AVG_LABEL) {
|
||||||
|
ref_found = true;
|
||||||
|
return ric_ind_message.meas_data[i];
|
||||||
|
}
|
||||||
|
if (meas_def_item.label_info_list[l].meas_label.sum_present and label == SUM_LABEL) {
|
||||||
|
ref_found = true;
|
||||||
|
return ric_ind_message.meas_data[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO assert if match == false, has to be present as was created during initialization
|
||||||
|
ref_found = false;
|
||||||
|
return ric_ind_message.meas_data[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service_style1::_collect_meas_data()
|
||||||
|
{
|
||||||
|
meas_info_list_l& meas_info_list = ric_ind_message.meas_info_list;
|
||||||
|
for (uint32_t i = 0; i < meas_info_list.size(); i++) {
|
||||||
|
meas_info_item_s& meas_def_item = meas_info_list[i];
|
||||||
|
std::string meas_name = meas_def_item.meas_type.meas_name().to_string();
|
||||||
|
std::vector<e2sm_kpm_label_enum> labels = _get_present_labels(meas_def_item);
|
||||||
|
|
||||||
|
for (const auto& label : labels) {
|
||||||
|
// TODO: probably some labels need a special processing (e.g., use bin width that needs to be stored)
|
||||||
|
// get a proper record list
|
||||||
|
bool ref_found = false;
|
||||||
|
meas_data_item_s& meas_data_item = _get_meas_data_item(meas_name, label, 0, ref_found);
|
||||||
|
if (not ref_found) {
|
||||||
|
parent->logger.info("Cannot find a meas record list, action_id %i, metric \"%s\" label: %i",
|
||||||
|
action_id,
|
||||||
|
meas_name.c_str(),
|
||||||
|
label);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get data type
|
||||||
|
meas_record_item_c::types data_type = _get_meas_data_type(meas_name, label, meas_data_item.meas_record);
|
||||||
|
|
||||||
|
// extract a needed value from enb metrics and add to the proper meas record list
|
||||||
|
e2sm_kpm_meas_def_t meas_value;
|
||||||
|
meas_value.name = meas_name;
|
||||||
|
meas_value.label = label;
|
||||||
|
meas_value.scope = ENB_LEVEL;
|
||||||
|
if (cell_global_id_present) {
|
||||||
|
meas_value.scope = CELL_LEVEL;
|
||||||
|
meas_value.cell_id = 0;
|
||||||
|
}
|
||||||
|
meas_value.data_type = data_type;
|
||||||
|
|
||||||
|
meas_record_item_c item;
|
||||||
|
if (not parent->_collect_meas_value(meas_value, item)) {
|
||||||
|
parent->logger.info("Cannot extract value \"%s\" label: %i", meas_name.c_str(), label);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// save meas value in the proper record list
|
||||||
|
meas_data_item.meas_record.push_back(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reschedule measurement collection
|
||||||
|
_reschedule_meas_collection();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service_style1::is_ric_ind_ready()
|
||||||
|
{
|
||||||
|
// TODO: check if only NO_VALUES, if so then skip
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service_style1::clear_collected_data()
|
||||||
|
{
|
||||||
|
ric_ind_header.collet_start_time.from_number(std::time(0));
|
||||||
|
for (uint32_t i = 0; i < ric_ind_message.meas_data.size(); ++i) {
|
||||||
|
ric_ind_message.meas_data[i].meas_record.clear();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
e2sm_kpm_report_service_style2::e2sm_kpm_report_service_style2(e2sm_kpm* e2sm_kpm,
|
||||||
|
uint16_t action_id,
|
||||||
|
e2_sm_kpm_action_definition_s action_definition) :
|
||||||
|
e2sm_kpm_report_service(e2sm_kpm, action_id, action_definition),
|
||||||
|
action_def(action_def_generic.action_definition_formats.action_definition_format2()),
|
||||||
|
ric_ind_message(ric_ind_message_generic.ind_msg_formats.set_ind_msg_format1())
|
||||||
|
{
|
||||||
|
ind_msg_format = e2_sm_kpm_ind_msg_s::ind_msg_formats_c_::types_opts::ind_msg_format1;
|
||||||
|
this->_initialize_ric_ind_hdr();
|
||||||
|
this->_initialize_ric_ind_msg();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service_style2::_initialize_ric_ind_msg()
|
||||||
|
{
|
||||||
|
// TODO: implement
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service_style2::process_ric_action_definition(e2sm_kpm* e2sm_kpm,
|
||||||
|
e2_sm_kpm_action_definition_s& action_def_generic)
|
||||||
|
{
|
||||||
|
// TODO: implement
|
||||||
|
// note: similar to e2sm_kpm_report_service_style1::process_ric_action_definition but in addition
|
||||||
|
// we need to check whether measurement is supported at UE_LEVEL
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service_style2::_collect_meas_data()
|
||||||
|
{
|
||||||
|
// TODO: implement
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service_style2::is_ric_ind_ready()
|
||||||
|
{
|
||||||
|
// TODO: check if only NO_VALUES, if so then skip
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service_style2::clear_collected_data()
|
||||||
|
{
|
||||||
|
// TODO: implement
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
e2sm_kpm_report_service_style3::e2sm_kpm_report_service_style3(e2sm_kpm* e2sm_kpm,
|
||||||
|
uint16_t action_id,
|
||||||
|
e2_sm_kpm_action_definition_s action_definition) :
|
||||||
|
e2sm_kpm_report_service(e2sm_kpm, action_id, action_definition),
|
||||||
|
action_def(action_def_generic.action_definition_formats.action_definition_format3()),
|
||||||
|
ric_ind_message(ric_ind_message_generic.ind_msg_formats.set_ind_msg_format2())
|
||||||
|
{
|
||||||
|
ind_msg_format = e2_sm_kpm_ind_msg_s::ind_msg_formats_c_::types_opts::ind_msg_format1;
|
||||||
|
this->_initialize_ric_ind_hdr();
|
||||||
|
this->_initialize_ric_ind_msg();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service_style3::_initialize_ric_ind_msg()
|
||||||
|
{
|
||||||
|
// TODO: implement
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service_style3::process_ric_action_definition(e2sm_kpm* e2sm_kpm,
|
||||||
|
e2_sm_kpm_action_definition_s& action_def_generic)
|
||||||
|
{
|
||||||
|
// TODO: implement
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service_style3::_collect_meas_data()
|
||||||
|
{
|
||||||
|
// TODO: implement
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service_style3::is_ric_ind_ready()
|
||||||
|
{
|
||||||
|
// TODO: check if only NO_VALUES, if so then skip
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service_style3::clear_collected_data()
|
||||||
|
{
|
||||||
|
// TODO: implement
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
e2sm_kpm_report_service_style4::e2sm_kpm_report_service_style4(e2sm_kpm* e2sm_kpm,
|
||||||
|
uint16_t action_id,
|
||||||
|
e2_sm_kpm_action_definition_s action_definition) :
|
||||||
|
e2sm_kpm_report_service(e2sm_kpm, action_id, action_definition),
|
||||||
|
action_def(action_def_generic.action_definition_formats.action_definition_format4()),
|
||||||
|
ric_ind_message(ric_ind_message_generic.ind_msg_formats.set_ind_msg_format3())
|
||||||
|
{
|
||||||
|
ind_msg_format = e2_sm_kpm_ind_msg_s::ind_msg_formats_c_::types_opts::ind_msg_format1;
|
||||||
|
this->_initialize_ric_ind_hdr();
|
||||||
|
this->_initialize_ric_ind_msg();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service_style4::_initialize_ric_ind_msg()
|
||||||
|
{
|
||||||
|
// TODO: implement
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service_style4::process_ric_action_definition(e2sm_kpm* e2sm_kpm,
|
||||||
|
e2_sm_kpm_action_definition_s& action_def_generic)
|
||||||
|
{
|
||||||
|
// TODO: implement
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service_style4::_collect_meas_data()
|
||||||
|
{
|
||||||
|
// TODO: implement
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service_style4::is_ric_ind_ready()
|
||||||
|
{
|
||||||
|
// TODO: check if only NO_VALUES, if so then skip
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service_style4::clear_collected_data()
|
||||||
|
{
|
||||||
|
// TODO: implement
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
e2sm_kpm_report_service_style5::e2sm_kpm_report_service_style5(e2sm_kpm* e2sm_kpm,
|
||||||
|
uint16_t action_id,
|
||||||
|
e2_sm_kpm_action_definition_s action_definition) :
|
||||||
|
e2sm_kpm_report_service(e2sm_kpm, action_id, action_definition),
|
||||||
|
action_def(action_def_generic.action_definition_formats.action_definition_format5()),
|
||||||
|
ric_ind_message(ric_ind_message_generic.ind_msg_formats.set_ind_msg_format3())
|
||||||
|
{
|
||||||
|
ind_msg_format = e2_sm_kpm_ind_msg_s::ind_msg_formats_c_::types_opts::ind_msg_format1;
|
||||||
|
this->_initialize_ric_ind_hdr();
|
||||||
|
this->_initialize_ric_ind_msg();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service_style5::_initialize_ric_ind_msg()
|
||||||
|
{
|
||||||
|
// TODO: implement
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service_style5::process_ric_action_definition(e2sm_kpm* e2sm_kpm,
|
||||||
|
e2_sm_kpm_action_definition_s& action_def_generic)
|
||||||
|
{
|
||||||
|
// TODO: implement
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service_style5::_collect_meas_data()
|
||||||
|
{
|
||||||
|
// TODO: implement
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service_style5::is_ric_ind_ready()
|
||||||
|
{
|
||||||
|
// TODO: check if only NO_VALUES, if so then skip
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e2sm_kpm_report_service_style5::clear_collected_data()
|
||||||
|
{
|
||||||
|
// TODO: implement
|
||||||
|
return false;
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
#
|
||||||
|
# Copyright 2013-2023 Software Radio Systems Limited
|
||||||
|
#
|
||||||
|
# By using this file, you agree to the terms and conditions set
|
||||||
|
# forth in the LICENSE file which can be found at the top level of
|
||||||
|
# the distribution.
|
||||||
|
#
|
||||||
|
|
||||||
|
add_executable(e2ap_test e2ap_test.cc)
|
||||||
|
target_link_libraries(e2ap_test srsran_common ric_e2 srsgnb_ric srsenb_upper srsgnb_stack ${SCTP_LIBRARIES})
|
||||||
|
|
||||||
|
add_test(e2ap_test e2ap_test)
|
@ -0,0 +1,177 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2023 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "srsgnb/hdr/stack/ric/e2ap.h"
|
||||||
|
#include "srsran/asn1/e2ap.h"
|
||||||
|
#include "srsran/common/test_common.h"
|
||||||
|
#include "srsran/interfaces/e2_metrics_interface.h"
|
||||||
|
#include "srsran/interfaces/enb_metrics_interface.h"
|
||||||
|
#include "srsran/srsran.h"
|
||||||
|
|
||||||
|
class dummy_metrics_interface : public srsenb::e2_interface_metrics
|
||||||
|
{
|
||||||
|
bool pull_metrics(srsenb::enb_metrics_t* m) { return true; }
|
||||||
|
bool register_e2sm(e2sm* sm) { return true; }
|
||||||
|
bool unregister_e2sm(e2sm* sm) { return true; }
|
||||||
|
};
|
||||||
|
// function to test the encoding of the E2AP message
|
||||||
|
void test_reference_e2ap_setup_request()
|
||||||
|
{
|
||||||
|
uint8_t e2ap_msg_foreign[] = {
|
||||||
|
0x00, 0x01, 0x00, 0x80, 0xa3, 0x00, 0x00, 0x04, 0x00, 0x31, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x09, 0x00,
|
||||||
|
0x05, 0xf5, 0x10, 0x30, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0a, 0x00, 0x53, 0x00, 0x00, 0x08, 0x00, 0x4e, 0x00, 0x00,
|
||||||
|
0x93, 0x38, 0x00, 0x30, 0x4f, 0x52, 0x41, 0x4e, 0x2d, 0x45, 0x32, 0x53, 0x4d, 0x2d, 0x4b, 0x50, 0x4d, 0x00, 0x00,
|
||||||
|
0x18, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x35, 0x33, 0x31, 0x34, 0x38, 0x2e,
|
||||||
|
0x31, 0x2e, 0x32, 0x2e, 0x32, 0x2e, 0x32, 0x05, 0x00, 0x4b, 0x50, 0x4d, 0x20, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f,
|
||||||
|
0x72, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x4f, 0x52, 0x41, 0x4e, 0x2d, 0x45, 0x32, 0x53, 0x4d, 0x2d, 0x4b, 0x50, 0x4d,
|
||||||
|
0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x33, 0x00, 0x2c, 0x00, 0x01, 0x80, 0x44, 0x55, 0x4d, 0x4d, 0x59, 0x20,
|
||||||
|
0x4f, 0x41, 0x49, 0x2d, 0x41, 0x4d, 0x46, 0x00, 0x0c, 0x46, 0x41, 0x4b, 0x45, 0x20, 0x52, 0x45, 0x51, 0x55, 0x45,
|
||||||
|
0x53, 0x54, 0x0d, 0x46, 0x41, 0x4b, 0x45, 0x20, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45};
|
||||||
|
|
||||||
|
asn1::cbit_ref bref(&e2ap_msg_foreign[0], sizeof(e2ap_msg_foreign));
|
||||||
|
e2_ap_pdu_c pdu;
|
||||||
|
asn1::SRSASN_CODE unpack_ret = pdu.unpack(bref);
|
||||||
|
TESTASSERT_EQ(asn1::SRSASN_SUCCESS, unpack_ret);
|
||||||
|
printf("Unpacked E2AP PDU %d\n", (int)unpack_ret);
|
||||||
|
auto& ran_func_data = pdu.init_msg()
|
||||||
|
.value.e2setup_request()
|
||||||
|
->ra_nfunctions_added.value[0]
|
||||||
|
.value()
|
||||||
|
.ra_nfunction_item()
|
||||||
|
.ran_function_definition;
|
||||||
|
srsran::byte_buffer_t ran_function_def;
|
||||||
|
asn1::cbit_ref ran_func_bref(ran_function_def.msg, ran_function_def.get_tailroom());
|
||||||
|
std::copy(ran_func_data.data(), ran_func_data.data() + ran_func_data.size(), ran_function_def.begin());
|
||||||
|
e2_sm_kpm_ra_nfunction_description_s e2sm_kpm_ra_nfunction_description;
|
||||||
|
asn1::SRSASN_CODE nfunc_unpack = e2sm_kpm_ra_nfunction_description.unpack(ran_func_bref);
|
||||||
|
TESTASSERT_EQ(asn1::SRSASN_SUCCESS, nfunc_unpack);
|
||||||
|
printf("Unpacked E2SM PDU (KPM RAN function description) %d\n", (int)nfunc_unpack);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_native_e2ap_setup_request()
|
||||||
|
{
|
||||||
|
srsran::unique_byte_buffer_t buf = srsran::make_byte_buffer();
|
||||||
|
e2_ap_pdu_c pdu, pdu2;
|
||||||
|
srslog::basic_logger& logger = srslog::fetch_basic_logger("E2AP");
|
||||||
|
dummy_metrics_interface dummy_metrics;
|
||||||
|
e2ap e2ap_(logger, nullptr, &dummy_metrics, NULL);
|
||||||
|
pdu = e2ap_.generate_setup_request();
|
||||||
|
|
||||||
|
asn1::bit_ref bref(buf->msg, buf->get_tailroom());
|
||||||
|
if (pdu.pack(bref) != asn1::SRSASN_SUCCESS) {
|
||||||
|
printf("Failed to pack TX E2 PDU\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
asn1::cbit_ref bref2(buf->msg, buf->get_tailroom());
|
||||||
|
|
||||||
|
asn1::SRSASN_CODE unpack_ret = pdu2.unpack(bref2);
|
||||||
|
TESTASSERT_EQ(asn1::SRSASN_SUCCESS, unpack_ret);
|
||||||
|
printf("Unpacked native E2AP PDU %d\n", (int)unpack_ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_reference_e2ap_subscription_request()
|
||||||
|
{
|
||||||
|
uint8_t e2ap_msg_foreign[] = {0x00, 0x08, 0x40, 0x2b, 0x00, 0x00, 0x03, 0x00, 0x1d, 0x00, 0x05, 0x00,
|
||||||
|
0x00, 0x7b, 0x00, 0x15, 0x00, 0x05, 0x00, 0x02, 0x00, 0x01, 0x00, 0x1e,
|
||||||
|
0x00, 0x15, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04, 0x00, 0x00, 0x13, 0x40,
|
||||||
|
0x0a, 0x60, 0x01, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04, 0x02, 0x00};
|
||||||
|
|
||||||
|
asn1::cbit_ref bref(&e2ap_msg_foreign[0], sizeof(e2ap_msg_foreign));
|
||||||
|
e2_ap_pdu_c pdu;
|
||||||
|
asn1::SRSASN_CODE unpack_ret = pdu.unpack(bref);
|
||||||
|
TESTASSERT_EQ(asn1::SRSASN_SUCCESS, unpack_ret);
|
||||||
|
printf("Unpacked E2AP PDU (subscription request) %d\n", (int)unpack_ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_native_e2ap_subscription_response()
|
||||||
|
{
|
||||||
|
srsran::unique_byte_buffer_t buf = srsran::make_byte_buffer();
|
||||||
|
e2_ap_pdu_c pdu, pdu2;
|
||||||
|
srslog::basic_logger& logger = srslog::fetch_basic_logger("E2AP");
|
||||||
|
dummy_metrics_interface dummy_metrics;
|
||||||
|
e2ap e2ap_(logger, nullptr, &dummy_metrics, NULL);
|
||||||
|
|
||||||
|
ric_subscription_reponse_t ric_subscription_reponse;
|
||||||
|
ric_subscription_reponse.ric_requestor_id = 1021;
|
||||||
|
ric_subscription_reponse.ric_instance_id = 0;
|
||||||
|
ric_subscription_reponse.ra_nfunction_id = 147;
|
||||||
|
ric_subscription_reponse.admitted_actions.push_back(0);
|
||||||
|
|
||||||
|
pdu = e2ap_.generate_subscription_response(ric_subscription_reponse);
|
||||||
|
|
||||||
|
asn1::bit_ref bref(buf->msg, buf->get_tailroom());
|
||||||
|
if (pdu.pack(bref) != asn1::SRSASN_SUCCESS) {
|
||||||
|
printf("Failed to pack TX E2 PDU\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
asn1::cbit_ref bref2(buf->msg, buf->get_tailroom());
|
||||||
|
asn1::SRSASN_CODE unpack_ret = pdu2.unpack(bref2);
|
||||||
|
TESTASSERT_EQ(asn1::SRSASN_SUCCESS, unpack_ret);
|
||||||
|
printf("Unpacked native E2AP PDU (subscription response) %d\n", (int)unpack_ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_native_e2ap_reset_request()
|
||||||
|
{
|
||||||
|
srsran::unique_byte_buffer_t buf = srsran::make_byte_buffer();
|
||||||
|
e2_ap_pdu_c pdu, pdu2;
|
||||||
|
srslog::basic_logger& logger = srslog::fetch_basic_logger("E2AP");
|
||||||
|
dummy_metrics_interface dummy_metrics;
|
||||||
|
e2ap e2ap_(logger, nullptr, &dummy_metrics, NULL);
|
||||||
|
|
||||||
|
pdu = e2ap_.generate_reset_request();
|
||||||
|
asn1::bit_ref bref(buf->msg, buf->get_tailroom());
|
||||||
|
if (pdu.pack(bref) != asn1::SRSASN_SUCCESS) {
|
||||||
|
printf("Failed to pack TX E2 PDU\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
asn1::cbit_ref bref2(buf->msg, buf->get_tailroom());
|
||||||
|
asn1::SRSASN_CODE unpack_ret = pdu2.unpack(bref2);
|
||||||
|
TESTASSERT_EQ(asn1::SRSASN_SUCCESS, unpack_ret);
|
||||||
|
printf("Unpacked native E2AP PDU RESET %d\n", (int)unpack_ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_native_e2ap_reset_response()
|
||||||
|
{
|
||||||
|
srsran::unique_byte_buffer_t buf = srsran::make_byte_buffer();
|
||||||
|
e2_ap_pdu_c pdu, pdu2;
|
||||||
|
srslog::basic_logger& logger = srslog::fetch_basic_logger("E2AP");
|
||||||
|
dummy_metrics_interface dummy_metrics;
|
||||||
|
e2ap e2ap_(logger, nullptr, &dummy_metrics, NULL);
|
||||||
|
|
||||||
|
pdu = e2ap_.generate_reset_response();
|
||||||
|
asn1::bit_ref bref(buf->msg, buf->get_tailroom());
|
||||||
|
if (pdu.pack(bref) != asn1::SRSASN_SUCCESS) {
|
||||||
|
printf("Failed to pack TX E2 PDU\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
asn1::cbit_ref bref2(buf->msg, buf->get_tailroom());
|
||||||
|
asn1::SRSASN_CODE unpack_ret = pdu2.unpack(bref2);
|
||||||
|
TESTASSERT_EQ(asn1::SRSASN_SUCCESS, unpack_ret);
|
||||||
|
printf("Unpacked native E2AP PDU RESET RESPONSE %d\n", (int)unpack_ret);
|
||||||
|
}
|
||||||
|
// add tets for set-up request and response
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
test_reference_e2ap_setup_request();
|
||||||
|
test_native_e2ap_setup_request();
|
||||||
|
test_reference_e2ap_subscription_request();
|
||||||
|
test_native_e2ap_subscription_response();
|
||||||
|
test_native_e2ap_reset_request();
|
||||||
|
test_native_e2ap_reset_response();
|
||||||
|
// call reset test functions here
|
||||||
|
return 0;
|
||||||
|
}
|
@ -1,47 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2013-2022 Software Radio Systems Limited
|
|
||||||
*
|
|
||||||
* This file is part of srsRAN.
|
|
||||||
*
|
|
||||||
* srsRAN is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* srsRAN is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* A copy of the GNU Affero General Public License can be found in
|
|
||||||
* the LICENSE file in the top-level directory of this distribution
|
|
||||||
* and at http://www.gnu.org/licenses/.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
|
||||||
{
|
|
||||||
if (argc != 2) {
|
|
||||||
std::cout << "Please call with the binary to provide net admin capabilities to as a parameter." << std::endl;
|
|
||||||
std::cout << "E.g. ./set_net_admin_caps myprogCalling " << std::endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string command("setcap 'cap_net_admin=eip' ");
|
|
||||||
command += argv[1];
|
|
||||||
|
|
||||||
std::cout << "Calling " << command << " with root rights." << std::endl;
|
|
||||||
setuid(0);
|
|
||||||
system(command.c_str());
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
Loading…
Reference in New Issue