Commit 4c07dca3 authored by Robert Schmidt's avatar Robert Schmidt

Merge remote-tracking branch 'origin/kpm-rc-cleanup' into integration_2024_w12

parents 0ecd346b 7535e999
add_library(e2_ran_func_cuup STATIC
init_ran_func.c
read_setup_ran.c
../flexric/test/rnd/fill_rnd_data_e2_setup_req.c # this is not rnd data; it is used to fill E2 Node Component Configuration Addition List in E2 Setup Request message
CUSTOMIZED/ran_func_gtp.c # current implementation doesn't take split architecture into account, neither CU/DU nor CU-UP/CU-CP
CUSTOMIZED/ran_func_pdcp.c # current implementation doesn't take split architecture into account, neither CU/DU nor CU-UP/CU-CP
O-RAN/ran_e2sm_ue_id.c
O-RAN/ran_func_kpm.c
O-RAN/ran_func_kpm_subs.c
O-RAN/ran_func_rc.c
O-RAN/ran_func_rc_subs.c
CUSTOMIZED/ran_func_gtp.c # GTP SM not yet implemented in CU-UP
CUSTOMIZED/ran_func_pdcp.c
CUSTOMIZED/ran_func_tc.c # currently, not implemented; therefore, filling rnd data
../flexric/test/rnd/fill_rnd_data_tc.c
O-RAN/ran_func_kpm.c # this file should only contain PDCP-U/GTP; to be done in the future
../flexric/test/rnd/fill_rnd_data_kpm.c # this dependancy will be taken out once RAN Function Definition is implemented
O-RAN/ran_func_rc.c # this file should only contain PDCP-U/GTP; to be done in the future
O-RAN/ran_func_rc_subs.c
../flexric/test/rnd/fill_rnd_data_rc.c # this dependancy will be taken out once RAN Function Definition is implemented
)
target_link_libraries(e2_ran_func_cuup PUBLIC asn1_nr_rrc nr_rrc asn1_nr_rrc_hdrs e2_time_obj kpm_ric_info_common_obj 3gpp_derived_ie_obj)
target_link_libraries(e2_ran_func_cuup PUBLIC asn1_nr_rrc nr_rrc asn1_nr_rrc_hdrs e2_time_obj kpm_ric_info_common_obj 3gpp_derived_ie_obj sm_common_ie_obj)
target_compile_definitions(e2_ran_func_cuup PUBLIC ${E2AP_VERSION} ${KPM_VERSION} NGRAN_GNB_CUUP)
add_library(e2_ran_func_du_cucp_cuup STATIC
init_ran_func.c
read_setup_ran.c
../flexric/test/rnd/fill_rnd_data_e2_setup_req.c # this is not rnd data; it is used to fill E2 Node Component Configuration Addition List in E2 Setup Request message
CUSTOMIZED/ran_func_gtp.c # current implementation doesn't take split architecture into account, neither CU/DU nor CU-UP/CU-CP
CUSTOMIZED/ran_func_pdcp.c # current implementation doesn't take split architecture into account, neither CU/DU nor CU-UP/CU-CP
O-RAN/ran_func_kpm.c # this file should only contain RRC/PDCP-C; to be done in the future
# when nr-softmodem is divided in separate executables
../flexric/test/rnd/fill_rnd_data_kpm.c # this dependancy will be taken out once RAN Function Definition is implemented
O-RAN/ran_func_rc.c # this file should only contain RRC/PDCP-C; to be done in the future
# when nr-softmodem is divided in separate executables
O-RAN/ran_e2sm_ue_id.c
O-RAN/ran_func_kpm.c
O-RAN/ran_func_kpm_subs.c
O-RAN/ran_func_rc.c
O-RAN/ran_func_rc_subs.c
../flexric/test/rnd/fill_rnd_data_rc.c # this dependancy will be taken out once RAN Function Definition is implemented
../flexric/src/sm/rc_sm/ie/rc_data_ie.c
CUSTOMIZED/ran_func_gtp.c
CUSTOMIZED/ran_func_pdcp.c
CUSTOMIZED/ran_func_mac.c
CUSTOMIZED/ran_func_rlc.c
CUSTOMIZED/ran_func_slice.c
CUSTOMIZED/ran_func_tc.c
../flexric/test/rnd/fill_rnd_data_tc.c
CUSTOMIZED/ran_func_slice.c # currently, not implemented; therefore, filling rnd data
../flexric/test/rnd/fill_rnd_data_slice.c
CUSTOMIZED/ran_func_tc.c # currently, not implemented; therefore, filling rnd data
../flexric/test/rnd/fill_rnd_data_tc.c
)
target_link_libraries(e2_ran_func_du_cucp_cuup PUBLIC asn1_nr_rrc nr_rrc asn1_nr_rrc_hdrs e2_time_obj kpm_ric_info_common_obj 3gpp_derived_ie_obj e2sm_rc_ir_obj sm_common_ie_obj e2_agent)
target_compile_definitions(e2_ran_func_du_cucp_cuup PUBLIC ${E2AP_VERSION} ${KPM_VERSION} NGRAN_GNB_DU NGRAN_GNB_CUCP NGRAN_GNB_CUUP)
target_link_libraries(e2_ran_func_du_cucp_cuup PUBLIC asn1_nr_rrc nr_rrc asn1_nr_rrc_hdrs e2_time_obj kpm_ric_info_common_obj 3gpp_derived_ie_obj e2sm_rc_ir_obj sm_common_ie_obj)
target_compile_definitions(e2_ran_func_du_cucp_cuup PUBLIC ${E2AP_VERSION} ${KPM_VERSION} NGRAN_GNB_DU NGRAN_GNB_CUCP NGRAN_GNB_CUUP)
# Current implementation:
# - we only have one executable nr-softmodem for 4 node types: DU, CU, gNB-mono, CU-CP
# In the future, when nr-softmodem is separated, linking should be done as following:
# - DU: e2_ran_func_du
# - CU-CP : e2_ran_func_cucp
# - CU-UP: e2_ran_func_cuup (this is available at the moment, executable nr-cuup)
# - CU: e2_ran_func_cucp + e2_ran_func_cuup
# - gNB-mono: e2_ran_func_du + e2_ran_func_cucp + e2_ran_func_cuup
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "ran_e2sm_ue_id.h"
ue_id_e2sm_t fill_e2sm_gnb_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context, __attribute__((unused))const uint32_t rrc_ue_id, __attribute__((unused))const ue_id_t cucp_ue_id)
{
ue_id_e2sm_t ue_id = {0};
ue_id.type = GNB_UE_ID_E2SM;
// 6.2.3.16
// Mandatory
// AMF UE NGAP ID
ue_id.gnb.amf_ue_ngap_id = rrc_ue_context->amf_ue_ngap_id;
// Mandatory
//GUAMI 6.2.3.17
ue_id.gnb.guami.plmn_id = (e2sm_plmn_t) {
.mcc = rrc_ue_context->ue_guami.mcc,
.mnc = rrc_ue_context->ue_guami.mnc,
.mnc_digit_len = rrc_ue_context->ue_guami.mnc_len
};
ue_id.gnb.guami.amf_region_id = rrc_ue_context->ue_guami.amf_region_id;
ue_id.gnb.guami.amf_set_id = rrc_ue_context->ue_guami.amf_set_id;
ue_id.gnb.guami.amf_ptr = rrc_ue_context->ue_guami.amf_pointer;
// RAN UE ID
// Optional
// 6.2.3.25
// OCTET STRING (SIZE (8))
// Defined in TS 38.473 (F1AP)
// clause 9.2.2.1
// UE CONTEXT SETUP REQUEST
ue_id.gnb.ran_ue_id = calloc(1, sizeof(uint64_t));
assert(ue_id.gnb.ran_ue_id != NULL);
*ue_id.gnb.ran_ue_id = rrc_ue_context->rrc_ue_id;
return ue_id;
}
ue_id_e2sm_t fill_e2sm_cu_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context, const uint32_t rrc_ue_id, const ue_id_t cucp_ue_id)
{
ue_id_e2sm_t ue_id = fill_e2sm_gnb_ue_id_data(rrc_ue_context, rrc_ue_id, cucp_ue_id);
// gNB-CU UE F1AP ID List
// C-ifCUDUseparated
ue_id.gnb.gnb_cu_ue_f1ap_lst_len = 1;
ue_id.gnb.gnb_cu_ue_f1ap_lst = calloc(ue_id.gnb.gnb_cu_ue_f1ap_lst_len, sizeof(uint32_t));
assert(ue_id.gnb.gnb_cu_ue_f1ap_lst != NULL && "Memory exhausted");
ue_id.gnb.gnb_cu_ue_f1ap_lst[0] = rrc_ue_context->rrc_ue_id;
return ue_id;
}
ue_id_e2sm_t fill_e2sm_du_ue_id_data(__attribute__((unused))const gNB_RRC_UE_t *rrc_ue_context, const uint32_t rrc_ue_id, __attribute__((unused))const ue_id_t cucp_ue_id)
{
ue_id_e2sm_t ue_id = {0};
ue_id.type = GNB_DU_UE_ID_E2SM;
// 6.2.3.21
// gNB CU UE F1AP
// Mandatory
ue_id.gnb_du.gnb_cu_ue_f1ap = rrc_ue_id;
// 6.2.3.25
// RAN UE ID
// Optional
ue_id.gnb_du.ran_ue_id = calloc(1, sizeof(uint64_t));
assert(ue_id.gnb_du.ran_ue_id != NULL);
*ue_id.gnb_du.ran_ue_id = rrc_ue_id;
return ue_id;
}
ue_id_e2sm_t fill_e2sm_cucp_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context, const uint32_t rrc_ue_id, const ue_id_t cucp_ue_id)
{
ue_id_e2sm_t ue_id = fill_e2sm_gnb_ue_id_data(rrc_ue_context, rrc_ue_id, cucp_ue_id);
//gNB-CU-CP UE E1AP ID List
//C-ifCPUPseparated
ue_id.gnb.gnb_cu_cp_ue_e1ap_lst_len = 1;
ue_id.gnb.gnb_cu_cp_ue_e1ap_lst = calloc(ue_id.gnb.gnb_cu_cp_ue_e1ap_lst_len, sizeof(uint32_t));
assert(ue_id.gnb.gnb_cu_cp_ue_e1ap_lst != NULL && "Memory exhausted");
ue_id.gnb.gnb_cu_cp_ue_e1ap_lst[0] = rrc_ue_context->rrc_ue_id;
return ue_id;
}
ue_id_e2sm_t fill_e2sm_cuup_ue_id_data(__attribute__((unused))const gNB_RRC_UE_t *rrc_ue_context, __attribute__((unused))const uint32_t rrc_ue_id, const ue_id_t cucp_ue_id)
{
ue_id_e2sm_t ue_id = {0};
ue_id.type = GNB_CU_UP_UE_ID_E2SM;
// 6.2.3.20
// Mandatory
ue_id.gnb_cu_up.gnb_cu_cp_ue_e1ap = cucp_ue_id; // RAN UE NGAP ID = gNB UE NGAP ID = rrc_ue_id
// 6.2.3.25
// RAN UE ID
// Optional
ue_id.gnb_cu_up.ran_ue_id = calloc(1, sizeof(uint64_t));
assert(ue_id.gnb_cu_up.ran_ue_id != NULL);
*ue_id.gnb_cu_up.ran_ue_id = cucp_ue_id; // RAN UE NGAP ID = gNB UE NGAP ID = rrc_ue_id
return ue_id;
}
get_ue_id fill_ue_id_data[END_NGRAN_NODE_TYPE] =
{
NULL,
NULL,
fill_e2sm_gnb_ue_id_data,
NULL,
NULL,
fill_e2sm_cu_ue_id_data,
NULL,
fill_e2sm_du_ue_id_data,
NULL,
fill_e2sm_cucp_ue_id_data,
fill_e2sm_cuup_ue_id_data,
};
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#ifndef RAN_E2SM_UE_ID_H
#define RAN_E2SM_UE_ID_H
#include "openair2/E2AP/flexric/src/agent/../sm/sm_io.h"
#include "openair2/RRC/NR/rrc_gNB_UE_context.h"
ue_id_e2sm_t fill_e2sm_gnb_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context, const uint32_t rrc_ue_id, const ue_id_t cucp_ue_id);
ue_id_e2sm_t fill_e2sm_cu_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context, const uint32_t rrc_ue_id, const ue_id_t cucp_ue_id);
ue_id_e2sm_t fill_e2sm_du_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context, const uint32_t rrc_ue_id, const ue_id_t cucp_ue_id);
ue_id_e2sm_t fill_e2sm_cucp_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context, const uint32_t rrc_ue_id, const ue_id_t cucp_ue_id);
ue_id_e2sm_t fill_e2sm_cuup_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context, const uint32_t rrc_ue_id, const ue_id_t cucp_ue_id);
typedef ue_id_e2sm_t (*get_ue_id)(const gNB_RRC_UE_t *rrc_ue_context, const uint32_t rrc_ue_id, const ue_id_t cucp_ue_id);
extern get_ue_id fill_ue_id_data[END_NGRAN_NODE_TYPE];
#endif
......@@ -20,141 +20,70 @@
*/
#include "ran_func_kpm.h"
#include "openair2/E2AP/flexric/test/rnd/fill_rnd_data_kpm.h" // this dependancy will be taken out once RAN Function Definition is implemented
#include "openair2/E2AP/flexric/src/util/time_now_us.h"
#include "common/utils/assertions.h"
#if defined (NGRAN_GNB_DU)
#include "openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h"
#include "openair2/LAYER2/nr_rlc/nr_rlc_entity.h"
#include "openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h"
#endif
#if defined (NGRAN_GNB_CUCP)
#include "openair2/RRC/NR/rrc_gNB_UE_context.h"
#include "openair3/NGAP/ngap_gNB_ue_context.h"
#endif
#include "ran_func_kpm_subs.h"
#include "ran_e2sm_ue_id.h"
#if defined (NGRAN_GNB_CUUP)
#include "openair2/LAYER2/nr_pdcp/nr_pdcp_entity.h"
#include "openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h"
#include "openair2/E1AP/e1ap_common.h"
#endif
#include "openair2/E2AP/flexric/src/util/time_now_us.h"
#include "openair2/F1AP/f1ap_ids.h"
#include <assert.h>
#include <stdio.h>
/* Please note that only one executable nr-softmodem exists for DU, CU, CU-CP and gNB-mono nodes;
the layer separation should be done better in the future */
#if defined (NGRAN_GNB_CUUP)
typedef struct uldlcounter {
uint32_t dl;
uint32_t ul;
} uldlcounter_t;
static pthread_once_t once_kpm_mutex = PTHREAD_ONCE_INIT;
static bool nssai_matches(nssai_t a_nssai, uint8_t b_sst, const uint32_t *b_sd)
static void init_once_kpm(void)
{
AssertFatal(b_sd == NULL || *b_sd <= 0xffffff, "illegal SD %d\n", *b_sd);
if (b_sd == NULL) {
return a_nssai.sst == b_sst && a_nssai.sd == 0xffffff;
} else {
return a_nssai.sst == b_sst && a_nssai.sd == *b_sd;
}
init_kpm_subs_data();
}
static uldlcounter_t last_pdcp_sdu_total_bytes[MAX_MOBILES_PER_GNB] = {0};
static gnb_cu_up_e2sm_t fill_gnb_cu_up_data(const ue_id_t cucp_ue_id)
static ngran_node_t get_e2_node_type(void)
{
gnb_cu_up_e2sm_t gnb_cu_up = {0};
ngran_node_t node_type = 0;
// 6.2.3.20
// Mandatory
gnb_cu_up.gnb_cu_cp_ue_e1ap = cucp_ue_id;
// 6.2.3.25
// RAN UE ID
// Optional
gnb_cu_up.ran_ue_id = NULL;
#if defined(NGRAN_GNB_DU) && defined(NGRAN_GNB_CUUP) && defined(NGRAN_GNB_CUCP)
node_type = RC.nrrrc[0]->node_type;
#elif defined (NGRAN_GNB_CUUP)
node_type = ngran_gNB_CUUP;
#endif
return gnb_cu_up;
return node_type;
}
typedef struct {
ue_id_t pdcp_ue_id_list[MAX_MOBILES_PER_GNB]; // list of matched UEs on PDCP-U level
size_t num_ues;
size_t sz;
ue_id_e2sm_t* ue_id;
} matched_ues_pdcp_t;
// Optional
// only used to retreive MAC/RLC stats
NR_UE_info_t* ue_info_list;
}arr_ue_id_t;
__attribute__((unused))
static size_t filter_ues_by_s_nssai_in_cuup(test_cond_e const condition, uint8_t sst, const uint32_t *sd, matched_ues_pdcp_t* matches)
static meas_data_lst_t fill_kpm_meas_data_item(const meas_info_format_1_lst_t* meas_info_lst, const size_t len, const uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx)
{
DevAssert(matches != NULL);
AssertFatal(condition == EQUAL_TEST_COND, "Condition %d not yet implemented\n", condition);
meas_data_lst_t data_item = {0};
// Get NSSAI info from E1 context
const instance_t CUuniqInstance = 0;
const e1ap_upcp_inst_t *e1inst = getCxtE1(CUuniqInstance);
// Measurement Record
data_item.meas_record_len = len; // record data list length corresponds to info list length from action definition
// currently the CU-UP does not store the slices, so we can only do this "coarse filtering"
for(int s = 0; s < e1inst->cuup.setupReq.plmn[0].supported_slices; s++) {
if (nssai_matches(e1inst->cuup.setupReq.plmn[0].slice[s], sst, sd)) {
matches->num_ues = nr_pdcp_get_num_ues(matches->pdcp_ue_id_list, MAX_MOBILES_PER_GNB);
}
}
return matches->num_ues;
}
data_item.meas_record_lst = calloc(len, sizeof(meas_record_lst_t));
assert(data_item.meas_record_lst != NULL && "Memory exhausted");
static nr_pdcp_statistics_t get_pdcp_stats_per_drb(const uint32_t rrc_ue_id)
{
nr_pdcp_statistics_t pdcp = {0};
const int srb_flag = 0;
const int rb_id = 1; // at the moment, only 1 DRB is supported
for (size_t i = 0; i < len; i++) {
// Measurement Type as requested in Action Definition
assert(meas_info_lst[i].meas_type.type == NAME_MEAS_TYPE && "Only NAME supported");
// Get PDCP stats for specific DRB
const bool rc = nr_pdcp_get_statistics(rrc_ue_id, srb_flag, rb_id, &pdcp);
assert(rc == true && "Cannot get PDCP stats\n");
char* meas_info_name_str = cp_ba_to_str(meas_info_lst[i].meas_type.name);
return pdcp;
}
data_item.meas_record_lst[i] = get_kpm_meas_value(meas_info_name_str, gran_period_ms, ue_info, ue_idx);
static meas_record_lst_t fill_ue_pdcp_data(const uint32_t rrc_ue_id, const size_t ue_idx, const char * meas_info_name_str)
{
meas_record_lst_t meas_record = {0};
// Get PDCP stats per DRB
nr_pdcp_statistics_t pdcp = get_pdcp_stats_per_drb(rrc_ue_id);
// Measurement Type as requested in Action Definition
if (strcmp(meas_info_name_str, "DRB.PdcpSduVolumeDL") == 0) { // 3GPP TS 28.522 - section 5.1.2.1.1.1
meas_record.value = INTEGER_MEAS_VALUE;
// Get DL data volume delivered to PDCP layer
meas_record.int_val = (pdcp.rxsdu_bytes - last_pdcp_sdu_total_bytes[ue_idx].dl)*8/1000; // [kb]
last_pdcp_sdu_total_bytes[ue_idx].dl = pdcp.rxsdu_bytes;
/* note: this measurement is calculated as per spec */
} else if (strcmp(meas_info_name_str, "DRB.PdcpSduVolumeUL") == 0) { // 3GPP TS 28.522 - section 5.1.2.1.2.1
meas_record.value = INTEGER_MEAS_VALUE;
// Get UL data volume delivered from PDCP layer
meas_record.int_val = (pdcp.txsdu_bytes - last_pdcp_sdu_total_bytes[ue_idx].ul)*8/1000; // [kb]
last_pdcp_sdu_total_bytes[ue_idx].ul = pdcp.txsdu_bytes;
/* note: this measurement is calculated as per spec */
} else {
assert(false && "Measurement Name not yet implemented");
free(meas_info_name_str);
}
return meas_record;
return data_item;
}
static kpm_ind_msg_format_1_t fill_kpm_ind_msg_frm_1_in_cu(const uint32_t rrc_ue_id, const size_t ue_idx, const kpm_act_def_format_1_t * act_def_fr_1)
static kpm_ind_msg_format_1_t fill_kpm_ind_msg_frm_1(cudu_ue_info_pair_t ue_info, const size_t ue_idx, const kpm_act_def_format_1_t* act_def_fr_1)
{
kpm_ind_msg_format_1_t msg_frm_1 = {0};
// Measurement Data contains a set of Meas Records, each collected at each granularity period
msg_frm_1.meas_data_lst_len = 1; /* this value is equal to (kpm_ric_event_trigger_format_1.report_period_ms/act_def_fr_1->gran_period_ms)
please, check their values in xApp */
......@@ -162,32 +91,11 @@ static kpm_ind_msg_format_1_t fill_kpm_ind_msg_frm_1_in_cu(const uint32_t rrc_ue
msg_frm_1.meas_data_lst = calloc(msg_frm_1.meas_data_lst_len, sizeof(*msg_frm_1.meas_data_lst));
assert(msg_frm_1.meas_data_lst != NULL && "Memory exhausted" );
for (size_t i = 0; i<msg_frm_1.meas_data_lst_len; i++) {
// Measurement Record
msg_frm_1.meas_data_lst[i].meas_record_len = act_def_fr_1->meas_info_lst_len; // record data list length corresponds to info list length from action definition
msg_frm_1.meas_data_lst[i].meas_record_lst = calloc(msg_frm_1.meas_data_lst[i].meas_record_len, sizeof(meas_record_lst_t));
assert(msg_frm_1.meas_data_lst[i].meas_record_lst != NULL && "Memory exhausted");
for (size_t j = 0; j < msg_frm_1.meas_data_lst[i].meas_record_len; j++) { // each meas record corresponds to one meas type
// Measurement Type as requested in Action Definition
switch (act_def_fr_1->meas_info_lst[j].meas_type.type) {
case NAME_MEAS_TYPE: {
char meas_info_name_str[act_def_fr_1->meas_info_lst[j].meas_type.name.len + 1];
memcpy(meas_info_name_str, act_def_fr_1->meas_info_lst[j].meas_type.name.buf, act_def_fr_1->meas_info_lst[j].meas_type.name.len);
meas_info_name_str[act_def_fr_1->meas_info_lst[j].meas_type.name.len] = '\0';
msg_frm_1.meas_data_lst[i].meas_record_lst[j] = fill_ue_pdcp_data(rrc_ue_id, ue_idx, meas_info_name_str);
break;
}
case ID_MEAS_TYPE:
assert(false && "ID Measurement Type not yet implemented");
default:
assert(false && "Measurement Type not recognized");
}
}
}
msg_frm_1.meas_data_lst[0] = fill_kpm_meas_data_item(act_def_fr_1->meas_info_lst,
act_def_fr_1->meas_info_lst_len,
act_def_fr_1->gran_period_ms,
ue_info,
ue_idx);
// Measurement Information - OPTIONAL
msg_frm_1.meas_info_lst_len = act_def_fr_1->meas_info_lst_len;
......@@ -200,416 +108,241 @@ static kpm_ind_msg_format_1_t fill_kpm_ind_msg_frm_1_in_cu(const uint32_t rrc_ue
return msg_frm_1;
}
__attribute__((unused))
static kpm_ind_msg_format_3_t fill_kpm_ind_msg_frm_3_in_cuup(const matched_ues_pdcp_t* matched_ues,
const kpm_act_def_format_1_t* act_def_fr_1)
static cudu_ue_info_pair_t fill_ue_related_info(arr_ue_id_t* arr_ue_id, const size_t ue_idx)
{
cudu_ue_info_pair_t ue_info = {0};
if (arr_ue_id->ue_id[ue_idx].type == GNB_UE_ID_E2SM) {
ue_info.rrc_ue_id = *arr_ue_id->ue_id[ue_idx].gnb.ran_ue_id; // rrc_ue_id
ue_info.ue = &arr_ue_id->ue_info_list[ue_idx];
} else if (arr_ue_id->ue_id[ue_idx].type == GNB_CU_UP_UE_ID_E2SM) {
/* in OAI implementation, CU-UP ue id = CU-CP ue id
=> CU-UP ue id = rrc_ue_id, but it should not be the case by the spec */
ue_info.rrc_ue_id = *arr_ue_id->ue_id[ue_idx].gnb_cu_up.ran_ue_id; // cucp_ue_id = rrc_ue_id
} else if (arr_ue_id->ue_id[ue_idx].type == GNB_DU_UE_ID_E2SM) {
ue_info.rrc_ue_id = *arr_ue_id->ue_id[ue_idx].gnb_du.ran_ue_id; // rrc_ue_id
ue_info.ue = &arr_ue_id->ue_info_list[ue_idx];
}
return ue_info;
}
static kpm_ind_msg_format_3_t fill_kpm_ind_msg_frm_3(arr_ue_id_t* arr_ue_id, const kpm_act_def_format_1_t* act_def_fr_1)
{
assert(act_def_fr_1 != NULL);
kpm_ind_msg_format_3_t msg_frm_3 = {0};
// Fill UE Measurement Reports
msg_frm_3.ue_meas_report_lst_len = matched_ues->num_ues;
msg_frm_3.ue_meas_report_lst_len = arr_ue_id->sz;
msg_frm_3.meas_report_per_ue = calloc(msg_frm_3.ue_meas_report_lst_len, sizeof(meas_report_per_ue_t));
assert(msg_frm_3.meas_report_per_ue != NULL && "Memory exhausted");
for (size_t i = 0; i<msg_frm_3.ue_meas_report_lst_len; i++) {
// Fill UE ID data
msg_frm_3.meas_report_per_ue[i].ue_meas_report_lst.type = GNB_CU_UP_UE_ID_E2SM;
msg_frm_3.meas_report_per_ue[i].ue_meas_report_lst.gnb_cu_up = fill_gnb_cu_up_data(matched_ues->pdcp_ue_id_list[i]);
msg_frm_3.meas_report_per_ue[i].ue_meas_report_lst = cp_ue_id_e2sm(&arr_ue_id->ue_id[i]);
// Fill UE related info
msg_frm_3.meas_report_per_ue[i].ind_msg_format_1 =
fill_kpm_ind_msg_frm_1_in_cu(matched_ues->pdcp_ue_id_list[i], i, act_def_fr_1);
}
return msg_frm_3;
}
#endif
cudu_ue_info_pair_t ue_info = fill_ue_related_info(arr_ue_id, i);
#if defined (NGRAN_GNB_DU)
static uldlcounter_t last_rlc_pdu_total_bytes[MAX_MOBILES_PER_GNB] = {0};
static uldlcounter_t last_total_prbs[MAX_MOBILES_PER_GNB] = {0};
static gnb_e2sm_t fill_gnb_data(rrc_gNB_ue_context_t * ue_context_p)
{
gnb_e2sm_t gnb = {0};
// 6.2.3.16
// Mandatory
// AMF UE NGAP ID
gnb.amf_ue_ngap_id = ue_context_p->ue_context.rrc_ue_id;
// Mandatory
//GUAMI 6.2.3.17
gnb.guami.plmn_id = (e2sm_plmn_t) {
.mcc = ue_context_p->ue_context.ue_guami.mcc,
.mnc = ue_context_p->ue_context.ue_guami.mnc,
.mnc_digit_len = ue_context_p->ue_context.ue_guami.mnc_len
};
gnb.guami.amf_region_id = ue_context_p->ue_context.ue_guami.amf_region_id;
gnb.guami.amf_set_id = ue_context_p->ue_context.ue_guami.amf_set_id;
gnb.guami.amf_ptr = ue_context_p->ue_context.ue_guami.amf_pointer;
// gNB-CU UE F1AP ID List
// C-ifCUDUseparated
if (NODE_IS_CU(RC.nrrrc[0]->node_type)) {
gnb.gnb_cu_ue_f1ap_lst_len = 1;
gnb.gnb_cu_ue_f1ap_lst = calloc(gnb.gnb_cu_ue_f1ap_lst_len, sizeof(uint32_t));
assert(gnb.gnb_cu_ue_f1ap_lst != NULL);
for (size_t i = 0; i < gnb.gnb_cu_ue_f1ap_lst_len; i++) {
gnb.gnb_cu_ue_f1ap_lst[i] = ue_context_p->ue_context.rrc_ue_id;
}
// Fill UE measurements
msg_frm_3.meas_report_per_ue[i].ind_msg_format_1 = fill_kpm_ind_msg_frm_1(ue_info, i, act_def_fr_1);
}
return gnb;
return msg_frm_3;
}
static gnb_du_e2sm_t fill_gnb_du_data(const f1_ue_data_t * rrc_ue_id)
{
gnb_du_e2sm_t gnb_du = {0};
// 6.2.3.21
// gNB CU UE F1AP
// Mandatory
gnb_du.gnb_cu_ue_f1ap = rrc_ue_id->secondary_ue;
// 6.2.3.25
// RAN UE ID
// Optional
gnb_du.ran_ue_id = NULL;
return gnb_du;
}
typedef struct {
NR_UE_info_t* ue_list[MAX_MOBILES_PER_GNB];
size_t num_ues;
} matched_ues_mac_t;
static size_t filter_ues_by_s_nssai_in_du_or_monolithic(test_cond_e const condition,
uint8_t sst,
const uint32_t *sd,
matched_ues_mac_t* matches)
static void capture_sst_sd(test_cond_value_t* test_cond_value, uint8_t *sst, uint32_t **sd)
{
DevAssert(matches != NULL);
AssertFatal(condition == EQUAL_TEST_COND, "Condition %d not yet implemented", condition);
DevAssert(sst != NULL);
DevAssert(sd != NULL);
// Take MAC info
size_t i = 0;
UE_iterator (RC.nrmac[0]->UE_info.list, ue) {
NR_UE_sched_ctrl_t *sched_ctrl = &ue->UE_sched_ctrl;
// UE matches if any of its DRBs matches
for (int l = 0; l < sched_ctrl->dl_lc_num; ++l) {
long lcid = sched_ctrl->dl_lc_ids[l];
if (nssai_matches(sched_ctrl->dl_lc_nssai[lcid], sst, sd)) {
matches->ue_list[i++] = ue;
break;
// S-NSSAI is an OCTET_STRING, as defined by spec
switch (test_cond_value->type) {
case OCTET_STRING_TEST_COND_VALUE: {
if (test_cond_value->octet_string_value->len == 1) {
*sst = test_cond_value->octet_string_value->buf[0];
*sd = NULL;
} else {
DevAssert(test_cond_value->octet_string_value->len == 4);
uint8_t *buf = test_cond_value->octet_string_value->buf;
*sst = buf[0];
*sd = malloc(**sd);
**sd = buf[1] << 16 | buf[2] << 8 | buf[3];
}
break;
}
AssertFatal(i < MAX_MOBILES_PER_GNB, "cannot have more UEs than global UE number maximum\n");
default:
AssertFatal(false, "test condition value %d impossible\n", test_cond_value->type);
}
matches->num_ues = i;
return i;
}
static nr_rlc_statistics_t get_rlc_stats_per_drb(NR_UE_info_t const * UE)
{
assert(UE != NULL);
nr_rlc_statistics_t rlc = {0};
const int srb_flag = 0;
const int rb_id = 1; // at the moment, only 1 DRB is supported
// Get RLC stats for specific DRB
const bool rc = nr_rlc_get_statistics(UE->rnti, srb_flag, rb_id, &rlc);
assert(rc == true && "Cannot get RLC stats\n");
// Activate average sojourn time at the RLC buffer for specific DRB
nr_rlc_activate_avg_time_to_tx(UE->rnti, rb_id+3, 1);
return rlc;
}
static meas_record_lst_t fill_ue_mac_rlc_data(const NR_UE_info_t* UE, const size_t ue_idx, const char * meas_info_name_str, const uint32_t gran_period_ms)
static bool nssai_matches(nssai_t a_nssai, uint8_t b_sst, const uint32_t *b_sd)
{
assert(UE != NULL);
meas_record_lst_t meas_record = {0};
// Get RLC stats per DRB
nr_rlc_statistics_t rlc = get_rlc_stats_per_drb(UE);
// Measurement Type as requested in Action Definition
if (strcmp(meas_info_name_str, "DRB.UEThpDl") == 0) { // 3GPP TS 28.522 - section 5.1.1.3.1
meas_record.value = REAL_MEAS_VALUE;
// Calculate DL Thp
meas_record.real_val = (double)(rlc.txpdu_bytes - last_rlc_pdu_total_bytes[ue_idx].dl)*8/gran_period_ms; // [kbps]
last_rlc_pdu_total_bytes[ue_idx].dl = rlc.txpdu_bytes;
/* note: per spec, average UE throughput in DL (taken into consideration values from all UEs, and averaged)
here calculated as: UE specific throughput in DL */
} else if (strcmp(meas_info_name_str, "DRB.UEThpUl") == 0) { // 3GPP TS 28.522 - section 5.1.1.3.3
meas_record.value = REAL_MEAS_VALUE;
// Calculate UL Thp
meas_record.real_val = (double)(rlc.rxpdu_bytes - last_rlc_pdu_total_bytes[ue_idx].ul)*8/gran_period_ms; // [kbps]
last_rlc_pdu_total_bytes[ue_idx].ul = rlc.rxpdu_bytes;
/* note: per spec, average UE throughput in UL (taken into consideration values from all UEs, and averaged)
here calculated as: UE specific throughput in UL */
} else if (strcmp(meas_info_name_str, "DRB.RlcSduDelayDl") == 0) { // 3GPP TS 28.522 - section 5.1.3.3.3
meas_record.value = REAL_MEAS_VALUE;
// Get the value of sojourn time at the RLC buffer
meas_record.real_val = rlc.txsdu_avg_time_to_tx; // [μs]
/* note: by default this measurement is calculated for previous 100ms (openair2/LAYER2/nr_rlc/nr_rlc_entity.c:118, 173, 213); please, update according to your needs */
} else if (strcmp(meas_info_name_str, "RRU.PrbTotDl") == 0) { // 3GPP TS 28.522 - section 5.1.1.2.1
meas_record.value = INTEGER_MEAS_VALUE;
// Get the number of DL PRBs
meas_record.int_val = UE->mac_stats.dl.total_rbs - last_total_prbs[ue_idx].dl; // [PRBs]
last_total_prbs[ue_idx].dl = UE->mac_stats.dl.total_rbs;
/* note: per spec, DL PRB usage [%] = (total used PRBs for DL traffic / total available PRBs for DL traffic) * 100
here calculated as: aggregated DL PRBs (t) - aggregated DL PRBs (t-gran_period) */
} else if (strcmp(meas_info_name_str, "RRU.PrbTotUl") == 0) { // 3GPP TS 28.522 - section 5.1.1.2.2
meas_record.value = INTEGER_MEAS_VALUE;
// Get the number of UL PRBs
meas_record.int_val = UE->mac_stats.ul.total_rbs - last_total_prbs[ue_idx].ul; // [PRBs]
last_total_prbs[ue_idx].ul = UE->mac_stats.ul.total_rbs;
/* note: per spec, UL PRB usage [%] = (total used PRBs for UL traffic / total available PRBs for UL traffic) * 100
here calculated as: aggregated UL PRBs (t) - aggregated UL PRBs (t-gran_period) */
AssertFatal(b_sd == NULL || *b_sd <= 0xffffff, "illegal SD %d\n", *b_sd);
if (b_sd == NULL) {
return a_nssai.sst == b_sst && a_nssai.sd == 0xffffff;
} else {
assert(false && "Measurement Name not yet implemented");
return a_nssai.sst == b_sst && a_nssai.sd == *b_sd;
}
return meas_record;
}
static kpm_ind_msg_format_1_t fill_kpm_ind_msg_frm_1_in_du(const NR_UE_info_t* UE, const size_t ue_idx, const kpm_act_def_format_1_t * act_def_fr_1)
static arr_ue_id_t filter_ues_by_s_nssai_in_cu(const test_info_lst_t test_info)
{
kpm_ind_msg_format_1_t msg_frm_1 = {0};
// Measurement Data contains a set of Meas Records, each collected at each granularity period
msg_frm_1.meas_data_lst_len = 1; /* this value is equal to (kpm_ric_event_trigger_format_1.report_period_ms/act_def_fr_1->gran_period_ms)
please, check their values in xApp */
assert(test_info.S_NSSAI == TRUE_TEST_COND_TYPE && "Must be true");
assert(test_info.test_cond != NULL && "Must be filled, even though it's optional by the specification");
assert(*test_info.test_cond == EQUAL_TEST_COND && "Currently support EQUAL test condition");
assert(test_info.test_cond_value != NULL && "Must be filled, even though it's optional by the specification");
msg_frm_1.meas_data_lst = calloc(msg_frm_1.meas_data_lst_len, sizeof(*msg_frm_1.meas_data_lst));
assert(msg_frm_1.meas_data_lst != NULL && "Memory exhausted" );
for (size_t i = 0; i<msg_frm_1.meas_data_lst_len; i++) {
// Measurement Record
msg_frm_1.meas_data_lst[i].meas_record_len = act_def_fr_1->meas_info_lst_len; // record data list length corresponds to info list length from action definition
uint8_t sst = 0;
uint32_t *sd = NULL;
capture_sst_sd(test_info.test_cond_value, &sst, &sd);
msg_frm_1.meas_data_lst[i].meas_record_lst = calloc(msg_frm_1.meas_data_lst[i].meas_record_len, sizeof(meas_record_lst_t));
assert(msg_frm_1.meas_data_lst[i].meas_record_lst != NULL && "Memory exhausted");
arr_ue_id_t arr_ue_id = {.sz = 0};
arr_ue_id.ue_id = calloc(MAX_MOBILES_PER_GNB, sizeof(ue_id_e2sm_t));
assert(arr_ue_id.ue_id != NULL);
for (size_t j = 0; j < msg_frm_1.meas_data_lst[i].meas_record_len; j++) { // each meas record corresponds to one meas type
// Measurement Type as requested in Action Definition
switch (act_def_fr_1->meas_info_lst[j].meas_type.type) {
case NAME_MEAS_TYPE: {
char meas_info_name_str[act_def_fr_1->meas_info_lst[j].meas_type.name.len + 1];
memcpy(meas_info_name_str, act_def_fr_1->meas_info_lst[j].meas_type.name.buf, act_def_fr_1->meas_info_lst[j].meas_type.name.len);
meas_info_name_str[act_def_fr_1->meas_info_lst[j].meas_type.name.len] = '\0';
struct rrc_gNB_ue_context_s* rrc_ue_context = NULL;
RB_FOREACH(rrc_ue_context, rrc_nr_ue_tree_s, &RC.nrrrc[0]->rrc_ue_head) {
gNB_RRC_UE_t *ue = &rrc_ue_context->ue_context;
for (int p = 0; p < ue->nb_of_pdusessions; ++p) {
pdusession_t *pdu = &ue->pduSession[p].param;
if (nssai_matches(pdu->nssai, sst, sd)) {
arr_ue_id.ue_id[arr_ue_id.sz] = fill_ue_id_data[ngran_gNB_CU](ue, 0, 0);
msg_frm_1.meas_data_lst[i].meas_record_lst[j] = fill_ue_mac_rlc_data(UE, ue_idx, meas_info_name_str, act_def_fr_1->gran_period_ms);
arr_ue_id.sz++;
break;
}
case ID_MEAS_TYPE:
assert(false && "ID Measurement Type not yet implemented");
default:
assert(false && "Measurement Type not recognized");
}
}
AssertFatal(arr_ue_id.sz < MAX_MOBILES_PER_GNB, "cannot have more UEs than global UE number maximum\n");
}
// Measurement Information - OPTIONAL
msg_frm_1.meas_info_lst_len = act_def_fr_1->meas_info_lst_len;
msg_frm_1.meas_info_lst = calloc(msg_frm_1.meas_info_lst_len, sizeof(meas_info_format_1_lst_t));
assert(msg_frm_1.meas_info_lst != NULL && "Memory exhausted");
for (size_t i = 0; i < msg_frm_1.meas_info_lst_len; i++) {
msg_frm_1.meas_info_lst[i] = cp_meas_info_format_1_lst(&act_def_fr_1->meas_info_lst[i]);
}
free(sd); // if NULL, nothing happens
return msg_frm_1;
return arr_ue_id;
}
static kpm_ind_msg_format_3_t fill_kpm_ind_msg_frm_3_in_du(const matched_ues_mac_t* matched_ues,
const kpm_act_def_format_1_t* act_def_fr_1)
static arr_ue_id_t filter_ues_by_s_nssai_in_cuup(const test_info_lst_t test_info)
{
assert(act_def_fr_1 != NULL);
kpm_ind_msg_format_3_t msg_frm_3 = {0};
// Fill UE Measurement Reports
msg_frm_3.ue_meas_report_lst_len = matched_ues->num_ues;
msg_frm_3.meas_report_per_ue = calloc(msg_frm_3.ue_meas_report_lst_len, sizeof(meas_report_per_ue_t));
assert(msg_frm_3.meas_report_per_ue != NULL && "Memory exhausted");
for (size_t i = 0; i<msg_frm_3.ue_meas_report_lst_len; i++) {
// Fill UE ID data
f1_ue_data_t rrc_ue_id = du_get_f1_ue_data(matched_ues->ue_list[i]->rnti); // get gNB CU UE ID as rrc_ue_id
msg_frm_3.meas_report_per_ue[i].ue_meas_report_lst.type = GNB_DU_UE_ID_E2SM;
msg_frm_3.meas_report_per_ue[i].ue_meas_report_lst.gnb_du = fill_gnb_du_data(&rrc_ue_id);
// Fill UE related info
msg_frm_3.meas_report_per_ue[i].ind_msg_format_1 = fill_kpm_ind_msg_frm_1_in_du(matched_ues->ue_list[i], i, act_def_fr_1);
}
return msg_frm_3;
}
static kpm_ind_msg_format_1_t fill_kpm_ind_msg_frm_1_in_monolithic(const NR_UE_info_t* UE, const size_t ue_idx, const uint32_t rrc_ue_id, const kpm_act_def_format_1_t * act_def_fr_1)
{
kpm_ind_msg_format_1_t msg_frm_1 = {0};
// Measurement Data contains a set of Meas Records, each collected at each granularity period
msg_frm_1.meas_data_lst_len = 1; /* this value is equal to (kpm_ric_event_trigger_format_1.report_period_ms/act_def_fr_1->gran_period_ms)
please, check their values in xApp */
assert(test_info.S_NSSAI == TRUE_TEST_COND_TYPE && "Must be true");
assert(test_info.test_cond != NULL && "Must be filled, even though it's optional by the specification");
assert(*test_info.test_cond == EQUAL_TEST_COND && "Currently support EQUAL test condition");
assert(test_info.test_cond_value != NULL && "Must be filled, even though it's optional by the specification");
msg_frm_1.meas_data_lst = calloc(msg_frm_1.meas_data_lst_len, sizeof(*msg_frm_1.meas_data_lst));
assert(msg_frm_1.meas_data_lst != NULL && "Memory exhausted" );
for (size_t i = 0; i<msg_frm_1.meas_data_lst_len; i++) {
// Measurement Record
msg_frm_1.meas_data_lst[i].meas_record_len = act_def_fr_1->meas_info_lst_len; // record data list length corresponds to info list length from action definition
uint8_t sst = 0;
uint32_t *sd = NULL;
capture_sst_sd(test_info.test_cond_value, &sst, &sd);
msg_frm_1.meas_data_lst[i].meas_record_lst = calloc(msg_frm_1.meas_data_lst[i].meas_record_len, sizeof(meas_record_lst_t));
assert(msg_frm_1.meas_data_lst[i].meas_record_lst != NULL && "Memory exhausted");
arr_ue_id_t arr_ue_id = {.sz = 0};
arr_ue_id.ue_id = calloc(MAX_MOBILES_PER_GNB, sizeof(ue_id_e2sm_t));
assert(arr_ue_id.ue_id != NULL);
for (size_t j = 0; j < msg_frm_1.meas_data_lst[i].meas_record_len; j++) { // each meas record corresponds to one meas type
// Measurement Type as requested in Action Definition
switch (act_def_fr_1->meas_info_lst[j].meas_type.type) {
case NAME_MEAS_TYPE: {
char meas_info_name_str[act_def_fr_1->meas_info_lst[j].meas_type.name.len + 1];
memcpy(meas_info_name_str, act_def_fr_1->meas_info_lst[j].meas_type.name.buf, act_def_fr_1->meas_info_lst[j].meas_type.name.len);
meas_info_name_str[act_def_fr_1->meas_info_lst[j].meas_type.name.len] = '\0';
// Get NSSAI info from E1 context
const instance_t e1_inst = 0;
const e1ap_upcp_inst_t *e1inst = getCxtE1(e1_inst);
if (strcmp(meas_info_name_str, "DRB.PdcpSduVolumeDL") == 0 || strcmp(meas_info_name_str, "DRB.PdcpSduVolumeUL") == 0) {
msg_frm_1.meas_data_lst[i].meas_record_lst[j] = fill_ue_pdcp_data(rrc_ue_id, ue_idx, meas_info_name_str);
} else {
msg_frm_1.meas_data_lst[i].meas_record_lst[j] = fill_ue_mac_rlc_data(UE, ue_idx, meas_info_name_str, act_def_fr_1->gran_period_ms);
}
break;
}
case ID_MEAS_TYPE:
assert(false && "ID Measurement Type not yet implemented");
// currently the CU-UP does not store the slices, so we can only do this "coarse filtering"
for(int s = 0; s < e1inst->cuup.setupReq.plmn[0].supported_slices; s++) {
if (nssai_matches(e1inst->cuup.setupReq.plmn[0].slice[s], sst, sd)) {
ue_id_t *pdcp_ue_id_list = calloc(MAX_MOBILES_PER_GNB, sizeof(ue_id_t));
assert(pdcp_ue_id_list != NULL);
default:
assert(false && "Measurement Type not recognized");
arr_ue_id.sz = nr_pdcp_get_num_ues(pdcp_ue_id_list, MAX_MOBILES_PER_GNB);
for (size_t i = 0; i < arr_ue_id.sz; i++) {
arr_ue_id.ue_id[i] = fill_ue_id_data[ngran_gNB_CUUP](NULL, 0, pdcp_ue_id_list[i]);
}
free(pdcp_ue_id_list);
}
}
// Measurement Information - OPTIONAL
msg_frm_1.meas_info_lst_len = act_def_fr_1->meas_info_lst_len;
msg_frm_1.meas_info_lst = calloc(msg_frm_1.meas_info_lst_len, sizeof(meas_info_format_1_lst_t));
assert(msg_frm_1.meas_info_lst != NULL && "Memory exhausted");
for (size_t i = 0; i < msg_frm_1.meas_info_lst_len; i++) {
msg_frm_1.meas_info_lst[i] = cp_meas_info_format_1_lst(&act_def_fr_1->meas_info_lst[i]);
}
free(sd); // if NULL, nothing happens
return msg_frm_1;
return arr_ue_id;
}
static kpm_ind_msg_format_3_t fill_kpm_ind_msg_frm_3_in_monolithic(const matched_ues_mac_t* matched_ues,
const kpm_act_def_format_1_t* act_def_fr_1)
static arr_ue_id_t filter_ues_by_s_nssai_in_du_or_monolithic(const test_info_lst_t test_info)
{
assert(act_def_fr_1 != NULL);
kpm_ind_msg_format_3_t msg_frm_3 = {0};
// Fill UE Measurement Reports
msg_frm_3.ue_meas_report_lst_len = matched_ues->num_ues;
msg_frm_3.meas_report_per_ue = calloc(msg_frm_3.ue_meas_report_lst_len, sizeof(meas_report_per_ue_t));
assert(msg_frm_3.meas_report_per_ue != NULL && "Memory exhausted");
assert(test_info.S_NSSAI == TRUE_TEST_COND_TYPE && "Must be true");
assert(test_info.test_cond != NULL && "Must be filled, even though it's optional by the specification");
assert(*test_info.test_cond == EQUAL_TEST_COND && "Currently support EQUAL test condition");
assert(test_info.test_cond_value != NULL && "Must be filled, even though it's optional by the specification");
uint8_t sst = 0;
uint32_t *sd = NULL;
capture_sst_sd(test_info.test_cond_value, &sst, &sd);
for (size_t i = 0; i<msg_frm_3.ue_meas_report_lst_len; i++) {
// Fill UE ID data
rrc_gNB_ue_context_t* rrc_ue_context_list = rrc_gNB_get_ue_context_by_rnti_any_du(RC.nrrrc[0], matched_ues->ue_list[i]->rnti);
msg_frm_3.meas_report_per_ue[i].ue_meas_report_lst.type = GNB_UE_ID_E2SM;
msg_frm_3.meas_report_per_ue[i].ue_meas_report_lst.gnb = fill_gnb_data(rrc_ue_context_list);
// Fill UE related info
msg_frm_3.meas_report_per_ue[i].ind_msg_format_1 =
fill_kpm_ind_msg_frm_1_in_monolithic(matched_ues->ue_list[i], i, rrc_ue_context_list->ue_context.rrc_ue_id, act_def_fr_1);
}
arr_ue_id_t arr_ue_id = {.sz = 0};
arr_ue_id.ue_id = calloc(MAX_MOBILES_PER_GNB, sizeof(ue_id_e2sm_t));
assert(arr_ue_id.ue_id != NULL);
return msg_frm_3;
}
#endif
arr_ue_id.ue_info_list = calloc(MAX_MOBILES_PER_GNB, sizeof(NR_UE_info_t));
assert(arr_ue_id.ue_info_list != NULL);
#if defined (NGRAN_GNB_CUCP)
typedef struct {
uint32_t rrc_ue_id_list[MAX_MOBILES_PER_GNB]; // list of matched UEs on RRC level containing only rrc_ue_id (gNB CU UE ID)
size_t num_ues;
const ngran_node_t node_type = get_e2_node_type();
} matched_ues_rrc_t;
// Take MAC info
UE_iterator (RC.nrmac[0]->UE_info.list, ue) {
NR_UE_sched_ctrl_t *sched_ctrl = &ue->UE_sched_ctrl;
// UE matches if any of its DRBs matches
for (int l = 0; l < sched_ctrl->dl_lc_num; ++l) {
long lcid = sched_ctrl->dl_lc_ids[l];
if (nssai_matches(sched_ctrl->dl_lc_nssai[lcid], sst, sd)) {
if (node_type == ngran_gNB_DU) {
f1_ue_data_t rrc_ue_id = du_get_f1_ue_data(ue->rnti); // gNB CU UE ID = rrc_ue_id
arr_ue_id.ue_id[arr_ue_id.sz] = fill_ue_id_data[ngran_gNB_DU](NULL, rrc_ue_id.secondary_ue, 0);
} else {
rrc_gNB_ue_context_t* rrc_ue_context = rrc_gNB_get_ue_context_by_rnti(RC.nrrrc[0], -1, ue->rnti);
arr_ue_id.ue_id[arr_ue_id.sz] = fill_ue_id_data[ngran_gNB](&rrc_ue_context->ue_context, 0, 0);
}
static size_t filter_ues_by_s_nssai_in_cu(test_cond_e const condition, uint8_t sst, const uint32_t *sd, matched_ues_rrc_t* matches)
{
DevAssert(matches != NULL);
AssertFatal(condition == EQUAL_TEST_COND, "Condition %d not yet implemented\n", condition);
// store NR_UE_info_t
arr_ue_id.ue_info_list[arr_ue_id.sz] = *ue;
struct rrc_gNB_ue_context_s *ue_context_p1 = NULL;
size_t i = 0;
RB_FOREACH(ue_context_p1, rrc_nr_ue_tree_s, &RC.nrrrc[0]->rrc_ue_head) {
gNB_RRC_UE_t *ue = &ue_context_p1->ue_context;
for (int p = 0; p < ue->nb_of_pdusessions; ++p) {
pdusession_t *pdu = &ue->pduSession[0].param;
if (nssai_matches(pdu->nssai, sst, sd)) {
matches->rrc_ue_id_list[i++] = ue_context_p1->ue_context.rrc_ue_id;
arr_ue_id.sz++;
break;
}
}
AssertFatal(i < MAX_MOBILES_PER_GNB, "cannot have more UEs than global UE number maximum\n");
AssertFatal(arr_ue_id.sz < MAX_MOBILES_PER_GNB, "cannot have more UEs than global UE number maximum\n");
}
matches->num_ues = i;
return i;
}
static kpm_ind_msg_format_3_t fill_kpm_ind_msg_frm_3_in_cu(const matched_ues_rrc_t* matched_ues,
const kpm_act_def_format_1_t* act_def_fr_1)
{
assert(act_def_fr_1 != NULL);
kpm_ind_msg_format_3_t msg_frm_3 = {0};
// Fill UE Measurement Reports
free(sd); // if NULL, nothing happens
msg_frm_3.ue_meas_report_lst_len = matched_ues->num_ues;
msg_frm_3.meas_report_per_ue = calloc(msg_frm_3.ue_meas_report_lst_len, sizeof(meas_report_per_ue_t));
assert(msg_frm_3.meas_report_per_ue != NULL && "Memory exhausted");
return arr_ue_id;
}
for (size_t i = 0; i<msg_frm_3.ue_meas_report_lst_len; i++) {
// Fill UE ID data
rrc_gNB_ue_context_t* rrc_ue_context_list = rrc_gNB_get_ue_context(RC.nrrrc[0], matched_ues->rrc_ue_id_list[i]);
msg_frm_3.meas_report_per_ue[i].ue_meas_report_lst.type = GNB_UE_ID_E2SM;
msg_frm_3.meas_report_per_ue[i].ue_meas_report_lst.gnb = fill_gnb_data(rrc_ue_context_list);
typedef arr_ue_id_t (*ue_type_matcher)(const test_info_lst_t test_info);
// Fill UE related info
msg_frm_3.meas_report_per_ue[i].ind_msg_format_1 =
fill_kpm_ind_msg_frm_1_in_cu(matched_ues->rrc_ue_id_list[i], i, act_def_fr_1);
}
static ue_type_matcher match_s_nssai_test_cond_type[END_NGRAN_NODE_TYPE] =
{
NULL,
NULL,
filter_ues_by_s_nssai_in_du_or_monolithic,
NULL,
NULL,
filter_ues_by_s_nssai_in_cu,
NULL,
filter_ues_by_s_nssai_in_du_or_monolithic,
NULL,
NULL, // for CU-CP we don't use this
filter_ues_by_s_nssai_in_cuup,
};
typedef ue_type_matcher (*filter_ues)[END_NGRAN_NODE_TYPE];
static filter_ues match_cond_arr[END_TEST_COND_TYPE_KPM_V2_01] = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
&match_s_nssai_test_cond_type,
};
return msg_frm_3;
}
#endif
static kpm_ric_ind_hdr_format_1_t kpm_ind_hdr_frm_1(void)
{
......@@ -627,60 +360,10 @@ static kpm_ric_ind_hdr_format_1_t kpm_ind_hdr_frm_1(void)
hdr_frm_1.fileformat_version = NULL;
// Check E2 Node NG-RAN Type
#if defined (NGRAN_GNB_DU)
if (RC.nrrrc[0]->node_type == ngran_gNB_DU) {
hdr_frm_1.sender_name = calloc(1, sizeof(byte_array_t));
hdr_frm_1.sender_name->buf = calloc(strlen("OAI-DU") + 1, sizeof(char));
memcpy(hdr_frm_1.sender_name->buf, "OAI-DU", strlen("OAI-DU"));
hdr_frm_1.sender_name->len = strlen("OAI-DU");
hdr_frm_1.sender_type = calloc(1, sizeof(byte_array_t));
hdr_frm_1.sender_type->buf = calloc(strlen("DU") + 1, sizeof(char));
memcpy(hdr_frm_1.sender_type->buf, "DU", strlen("DU"));
hdr_frm_1.sender_type->len = strlen("DU");
} else if (RC.nrrrc[0]->node_type == ngran_gNB_CU) {
hdr_frm_1.sender_name = calloc(1, sizeof(byte_array_t));
hdr_frm_1.sender_name->buf = calloc(strlen("OAI-CU") + 1, sizeof(char));
memcpy(hdr_frm_1.sender_name->buf, "OAI-CU", strlen("OAI-CU"));
hdr_frm_1.sender_name->len = strlen("OAI-CU");
hdr_frm_1.sender_type = calloc(1, sizeof(byte_array_t));
hdr_frm_1.sender_type->buf = calloc(strlen("CU") + 1, sizeof(char));
memcpy(hdr_frm_1.sender_type->buf, "CU", strlen("CU"));
hdr_frm_1.sender_type->len = strlen("CU");
} else if (RC.nrrrc[0]->node_type == ngran_gNB) {
hdr_frm_1.sender_name = calloc(1, sizeof(byte_array_t));
hdr_frm_1.sender_name->buf = calloc(strlen("OAI-MONO") + 1, sizeof(char));
memcpy(hdr_frm_1.sender_name->buf, "OAI-MONO", strlen("OAI-MONO"));
hdr_frm_1.sender_name->len = strlen("OAI-MONO");
hdr_frm_1.sender_type = calloc(1, sizeof(byte_array_t));
hdr_frm_1.sender_type->buf = calloc(strlen("MONO") + 1, sizeof(char));
memcpy(hdr_frm_1.sender_type->buf, "MONO", strlen("MONO"));
hdr_frm_1.sender_type->len = strlen("MONO");
} else if (RC.nrrrc[0]->node_type == ngran_gNB_CUCP) {
return hdr_frm_1; // nothing to fill
} else {
assert(0!=0 && "Unknown node type");
}
#endif
#if defined (NGRAN_GNB_CUUP)
const ngran_node_t node_type = get_e2_node_type();
const char* sender_name = get_ngran_name(node_type);
hdr_frm_1.sender_name = calloc(1, sizeof(byte_array_t));
hdr_frm_1.sender_name->buf = calloc(strlen("OAI-CU-UP") + 1, sizeof(char));
memcpy(hdr_frm_1.sender_name->buf, "OAI-CU-UP", strlen("OAI-CU-UP"));
hdr_frm_1.sender_name->len = strlen("OAI-CU-UP");
hdr_frm_1.sender_type = calloc(1, sizeof(byte_array_t));
hdr_frm_1.sender_type->buf = calloc(strlen("CU-UP") + 1, sizeof(char));
memcpy(hdr_frm_1.sender_type->buf, "CU-UP", strlen("CU-UP"));
hdr_frm_1.sender_type->len = strlen("CU-UP");
#endif
hdr_frm_1.vendor_name = calloc(1, sizeof(byte_array_t));
hdr_frm_1.vendor_name->buf = calloc(strlen("OAI") + 1, sizeof(char));
memcpy(hdr_frm_1.vendor_name->buf, "OAI", strlen("OAI"));
hdr_frm_1.vendor_name->len = strlen("OAI");
*hdr_frm_1.sender_name = cp_str_to_ba(sender_name);
return hdr_frm_1;
}
......@@ -695,40 +378,6 @@ kpm_ind_hdr_t kpm_ind_hdr(void)
return hdr;
}
static void capture_sst_sd(test_info_lst_t* test, uint8_t *sst, uint32_t **sd)
{
DevAssert(test != NULL && test->test_cond_value != NULL);
DevAssert(sst != NULL);
DevAssert(sd != NULL);
// we made a mistake in the past: NSSAI is supposed to be an OCTET_STRING,
// but earlier version of the RAN function and the xApp used integer, so
// handle this gracefully by accepting integer as well
switch (test->test_cond_value->type) {
case INTEGER_TEST_COND_VALUE: {
AssertFatal(*test->test_cond_value->int_value <= 0xff, "illegal SST %ld\n", *test->test_cond_value->int_value);
*sst = *test->test_cond_value->int_value;
*sd = NULL;
break;
}
case OCTET_STRING_TEST_COND_VALUE: {
if (test->test_cond_value->octet_string_value->len == 1) {
*sst = test->test_cond_value->octet_string_value->buf[0];
*sd = NULL;
} else {
DevAssert(test->test_cond_value->octet_string_value->len == 4);
uint8_t *buf = test->test_cond_value->octet_string_value->buf;
*sst = buf[0];
*sd = malloc(**sd);
**sd = buf[1] << 16 | buf[2] << 8 | buf[3];
}
break;
}
default:
AssertFatal(false, "test condition value %d impossible\n", test->test_cond_value->type);
}
}
bool read_kpm_sm(void* data)
{
assert(data != NULL);
......@@ -748,113 +397,17 @@ bool read_kpm_sm(void* data)
// Filter the UE by the test condition criteria
kpm_act_def_format_4_t const* frm_4 = &kpm->act_def->frm_4; // 8.2.1.2.4
for (size_t i = 0; i < frm_4->matching_cond_lst_len; i++) {
switch (frm_4->matching_cond_lst[i].test_info_lst.test_cond_type) {
case GBR_TEST_COND_TYPE: {
assert(0 != 0 && "Not implemented");
break;
}
case AMBR_TEST_COND_TYPE: {
assert(0 != 0 && "Not implemented");
break;
}
case IsStat_TEST_COND_TYPE: {
assert(0 != 0 && "Not implemented");
break;
}
case IsCatM_TEST_COND_TYPE: {
assert(0 != 0 && "Not implemented");
break;
}
case DL_RSRP_TEST_COND_TYPE: {
assert(0 != 0 && "Not implemented");
break;
}
case DL_RSRQ_TEST_COND_TYPE: {
assert(0 != 0 && "Not implemented");
break;
}
case UL_RSRP_TEST_COND_TYPE: {
assert(0 != 0 && "Not implemented");
break;
}
case CQI_TEST_COND_TYPE: {
assert(0 != 0 && "Not implemented");
break;
}
case fiveQI_TEST_COND_TYPE: {
assert(0 != 0 && "Not implemented");
break;
}
case QCI_TEST_COND_TYPE: {
;
assert(0 != 0 && "Not implemented");
break;
}
case S_NSSAI_TEST_COND_TYPE: {
assert(frm_4->matching_cond_lst[i].test_info_lst.S_NSSAI == TRUE_TEST_COND_TYPE && "Must be true");
assert(frm_4->matching_cond_lst[i].test_info_lst.test_cond != NULL && "Even though is optional..");
assert(frm_4->matching_cond_lst[i].test_info_lst.test_cond_value != NULL && "Even though is optional..");
test_cond_e const test_cond = *frm_4->matching_cond_lst[i].test_info_lst.test_cond;
uint8_t sst = 0;
uint32_t *sd = NULL;
capture_sst_sd(&frm_4->matching_cond_lst[i].test_info_lst, &sst, &sd);
// Check E2 Node NG-RAN Type
/* Current implementation:
- we only have one executable nr-softmodem for 4 node types: DU, CU, gNB-mono, CU-CP
In the future, when nr-softmodem is separated, linking should be done as following:
DU: e2_ran_func_du
CU-CP : e2_ran_func_cucp
CU-UP: e2_ran_func_cuup (this is available at the moment, executable nr-cuup)
CU: e2_ran_func_cucp + e2_ran_func_cuup
gNB-mono: e2_ran_func_du + e2_ran_func_cucp + e2_ran_func_cuup */
#if defined (NGRAN_GNB_DU)
if (RC.nrrrc[0]->node_type == ngran_gNB_DU) {
matched_ues_mac_t matched_ues = {0};
matched_ues.num_ues = filter_ues_by_s_nssai_in_du_or_monolithic(test_cond, sst, sd, &matched_ues);
if (matched_ues.num_ues == 0) return false;
kpm->ind.msg.frm_3 = fill_kpm_ind_msg_frm_3_in_du(&matched_ues, &frm_4->action_def_format_1);
} else if (RC.nrrrc[0]->node_type == ngran_gNB_CU) {
matched_ues_rrc_t matched_ues = {0};
matched_ues.num_ues = filter_ues_by_s_nssai_in_cu(test_cond, sst, sd, &matched_ues);
if (matched_ues.num_ues == 0) return false;
kpm->ind.msg.frm_3 = fill_kpm_ind_msg_frm_3_in_cu(&matched_ues, &frm_4->action_def_format_1);
} else if (RC.nrrrc[0]->node_type == ngran_gNB) {
matched_ues_mac_t matched_ues = {0};
matched_ues.num_ues = filter_ues_by_s_nssai_in_du_or_monolithic(test_cond, sst, sd, &matched_ues);
if (matched_ues.num_ues == 0) return false;
kpm->ind.msg.frm_3 = fill_kpm_ind_msg_frm_3_in_monolithic(&matched_ues, &frm_4->action_def_format_1);
} else if (RC.nrrrc[0]->node_type == ngran_gNB_CUCP) {
return false; // at the moment, measurements regarding CU-CP node are not yet implemented
} else {
assert(false && "NG-RAN Type not implemented");
}
#elif defined (NGRAN_GNB_CUUP)
matched_ues_pdcp_t matched_ues = {0};
matched_ues.num_ues = filter_ues_by_s_nssai_in_cuup(test_cond, sst, sd, &matched_ues);
if (matched_ues.num_ues == 0) return false;
kpm->ind.msg.frm_3 = fill_kpm_ind_msg_frm_3_in_cuup(&matched_ues, &frm_4->action_def_format_1);
#endif
free(sd); // if NULL, nothing happens
break;
}
default:
assert(false && "Unknown Test condition");
}
}
const test_info_lst_t test_info = frm_4->matching_cond_lst[i].test_info_lst;
const ngran_node_t node_type = get_e2_node_type();
/* Based on the node type (gNB-mono, CU, DU,...) and matching condition (S-NSSAI, DL RSRP, GBR,...)
get array of E2SM UE IDs with optionally their MAC info */
ue_type_matcher fp_match_cond_type = (*match_cond_arr[test_info.test_cond_type])[node_type];
arr_ue_id_t arr_ue_id = fp_match_cond_type(test_info);
if (arr_ue_id.sz == 0) return false;
kpm->ind.msg.frm_3 = fill_kpm_ind_msg_frm_3(&arr_ue_id, &frm_4->action_def_format_1);
}
break;
}
......@@ -866,13 +419,117 @@ bool read_kpm_sm(void* data)
return true;
}
static const char* kpm_meas_du[] = {
"DRB.RlcSduDelayDl",
"DRB.UEThpDl",
"DRB.UEThpUl",
"RRU.PrbTotDl",
"RRU.PrbTotUl",
};
static const char* kpm_meas_gnb[] = {
"DRB.PdcpSduVolumeDL",
"DRB.PdcpSduVolumeUL",
"DRB.RlcSduDelayDl",
"DRB.UEThpDl",
"DRB.UEThpUl",
"RRU.PrbTotDl",
"RRU.PrbTotUl",
};
static const char* kpm_meas_cuup[] = {
"DRB.PdcpSduVolumeDL",
"DRB.PdcpSduVolumeUL",
};
typedef const char** meas_list;
static const meas_list ran_def_kpm[END_NGRAN_NODE_TYPE] = {
NULL,
NULL,
kpm_meas_gnb,
NULL,
NULL,
kpm_meas_cuup, // at the moment, for CU, we use the same function as for CU-UP
NULL,
kpm_meas_du,
NULL,
NULL, // at the moment, no measurement is implemented in CU-CP
kpm_meas_cuup,
};
void read_kpm_setup_sm(void* e2ap)
{
assert(e2ap != NULL);
// assert(e2ap->type == KPM_V3_0_AGENT_IF_E2_SETUP_ANS_V0);
kpm_e2_setup_t* kpm = (kpm_e2_setup_t*)(e2ap);
kpm->ran_func_def = fill_rnd_kpm_ran_func_def();
/* Fill the RAN Function Definition with currently supported measurements */
// RAN Function Name is already filled in fill_ran_function_name() in kpm_sm_agent.c
// Fill supported measurements depending on the E2 node
// [1, 65535]
// 3GPP TS 28.552
const ngran_node_t node_type = get_e2_node_type();
const char** kpm_meas = ran_def_kpm[node_type];
if (kpm_meas == NULL) return; // e.g. CU-CP node, doesn't support any measurements
size_t sz = 0;
while (kpm_meas[sz] != NULL) {
sz++;
}
// Sequence of Report styles
const size_t sz_report = 1;
kpm->ran_func_def.sz_ric_report_style_list = sz_report;
kpm->ran_func_def.ric_report_style_list = calloc(sz_report, sizeof(ric_report_style_item_t));
assert(kpm->ran_func_def.ric_report_style_list != NULL && "Memory exhausted");
ric_report_style_item_t* report_item = &kpm->ran_func_def.ric_report_style_list[0];
report_item->report_style_type = STYLE_4_RIC_SERVICE_REPORT;
const char report_style_name[] = "Common Condition-based, UE-level Measurement";
report_item->report_style_name = cp_str_to_ba(report_style_name);
report_item->act_def_format_type = FORMAT_4_ACTION_DEFINITION;
report_item->meas_info_for_action_lst_len = sz;
report_item->meas_info_for_action_lst = calloc(sz, sizeof(meas_info_for_action_lst_t));
assert(report_item->meas_info_for_action_lst != NULL && "Memory exhausted");
for(size_t i = 0; i < sz; i++) {
meas_info_for_action_lst_t* meas_item = &report_item->meas_info_for_action_lst[i];
// 8.3.9
meas_item->name = cp_str_to_ba(kpm_meas[i]);
// 8.3.10 - OPTIONAL
meas_item->id = NULL;
// 8.3.26 - OPTIONAL
meas_item->bin_range_def = NULL;
}
// Sequence of Event Trigger styles
const size_t sz_ev_tr = 1;
kpm->ran_func_def.sz_ric_event_trigger_style_list = sz_ev_tr;
kpm->ran_func_def.ric_event_trigger_style_list = calloc(sz_ev_tr, sizeof(ric_event_trigger_style_item_t));
assert(kpm->ran_func_def.ric_event_trigger_style_list != NULL && "Memory exhausted");
ric_event_trigger_style_item_t* ev_tr_item = &kpm->ran_func_def.ric_event_trigger_style_list[0];
ev_tr_item->style_type = STYLE_1_RIC_EVENT_TRIGGER;
const char ev_style_name[] = "Periodic Report";
ev_tr_item->style_name = cp_str_to_ba(ev_style_name);
ev_tr_item->format_type = FORMAT_1_RIC_EVENT_TRIGGER;
// Supported RIC Indication Formats
report_item->ind_hdr_format_type = FORMAT_1_INDICATION_HEADER; // 8.3.5
report_item->ind_msg_format_type = FORMAT_3_INDICATION_MESSAGE; // 8.3.5
// E2 Setup Request is sent periodically until the connection is established
// RC subscritpion data should be initialized only once
const int ret = pthread_once(&once_kpm_mutex, init_once_kpm);
DevAssert(ret == 0);
}
sm_ag_if_ans_t write_ctrl_kpm_sm(void const* src)
......
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "ran_func_kpm_subs.h"
#include <search.h>
/* measurements that need to store values from previous reporting period have a limitation
when it comes to multiple subscriptions to the same UEs; ric_req_id is unique per subscription */
typedef struct uldlcounter {
uint32_t dl;
uint32_t ul;
} uldlcounter_t;
static uldlcounter_t last_pdcp_sdu_total_bytes[MAX_MOBILES_PER_GNB] = {0};
static nr_pdcp_statistics_t get_pdcp_stats_per_drb(const uint32_t rrc_ue_id, const int rb_id)
{
nr_pdcp_statistics_t pdcp = {0};
const int srb_flag = 0;
// Get PDCP stats for specific DRB
const bool rc = nr_pdcp_get_statistics(rrc_ue_id, srb_flag, rb_id, &pdcp);
assert(rc == true && "Cannot get PDCP stats\n");
return pdcp;
}
/* 3GPP TS 28.522 - section 5.1.2.1.1.1
note: this measurement is calculated as per spec */
static meas_record_lst_t fill_DRB_PdcpSduVolumeDL(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx)
{
meas_record_lst_t meas_record = {0};
// Get PDCP stats per DRB
const int rb_id = 1; // at the moment, only 1 DRB is supported
nr_pdcp_statistics_t pdcp = get_pdcp_stats_per_drb(ue_info.rrc_ue_id, rb_id);
meas_record.value = INTEGER_MEAS_VALUE;
// Get DL data volume delivered to PDCP layer
meas_record.int_val = (pdcp.rxsdu_bytes - last_pdcp_sdu_total_bytes[ue_idx].dl)*8/1000; // [kb]
last_pdcp_sdu_total_bytes[ue_idx].dl = pdcp.rxsdu_bytes;
return meas_record;
}
/* 3GPP TS 28.522 - section 5.1.2.1.2.1
note: this measurement is calculated as per spec */
static meas_record_lst_t fill_DRB_PdcpSduVolumeUL(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx)
{
meas_record_lst_t meas_record = {0};
// Get PDCP stats per DRB
const int rb_id = 1; // at the moment, only 1 DRB is supported
nr_pdcp_statistics_t pdcp = get_pdcp_stats_per_drb(ue_info.rrc_ue_id, rb_id);
meas_record.value = INTEGER_MEAS_VALUE;
// Get UL data volume delivered from PDCP layer
meas_record.int_val = (pdcp.txsdu_bytes - last_pdcp_sdu_total_bytes[ue_idx].ul)*8/1000; // [kb]
last_pdcp_sdu_total_bytes[ue_idx].ul = pdcp.txsdu_bytes;
return meas_record;
}
#if defined (NGRAN_GNB_DU)
static uldlcounter_t last_rlc_pdu_total_bytes[MAX_MOBILES_PER_GNB] = {0};
static uldlcounter_t last_total_prbs[MAX_MOBILES_PER_GNB] = {0};
static nr_rlc_statistics_t get_rlc_stats_per_drb(const rnti_t rnti, const int rb_id)
{
nr_rlc_statistics_t rlc = {0};
const int srb_flag = 0;
// Get RLC stats for specific DRB
const bool rc = nr_rlc_get_statistics(rnti, srb_flag, rb_id, &rlc);
assert(rc == true && "Cannot get RLC stats\n");
// Activate average sojourn time at the RLC buffer for specific DRB
nr_rlc_activate_avg_time_to_tx(rnti, rb_id+3, 1);
return rlc;
}
/* 3GPP TS 28.522 - section 5.1.3.3.3
note: by default this measurement is calculated for previous 100ms (openair2/LAYER2/nr_rlc/nr_rlc_entity.c:118, 173, 213); please, update according to your needs */
static meas_record_lst_t fill_DRB_RlcSduDelayDl(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, __attribute__((unused))const size_t ue_idx)
{
meas_record_lst_t meas_record = {0};
// Get RLC stats per DRB
const int rb_id = 1; // at the moment, only 1 DRB is supported
nr_rlc_statistics_t rlc = get_rlc_stats_per_drb(ue_info.ue->rnti, rb_id);
meas_record.value = REAL_MEAS_VALUE;
// Get the value of sojourn time at the RLC buffer
meas_record.real_val = rlc.txsdu_avg_time_to_tx; // [μs]
return meas_record;
}
/* 3GPP TS 28.522 - section 5.1.1.3.1
note: per spec, average UE throughput in DL (taken into consideration values from all UEs, and averaged)
here calculated as: UE specific throughput in DL */
static meas_record_lst_t fill_DRB_UEThpDl(uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx)
{
meas_record_lst_t meas_record = {0};
// Get RLC stats per DRB
const int rb_id = 1; // at the moment, only 1 DRB is supported
nr_rlc_statistics_t rlc = get_rlc_stats_per_drb(ue_info.ue->rnti, rb_id);
meas_record.value = REAL_MEAS_VALUE;
// Calculate DL Thp
meas_record.real_val = (double)(rlc.txpdu_bytes - last_rlc_pdu_total_bytes[ue_idx].dl)*8/gran_period_ms; // [kbps]
last_rlc_pdu_total_bytes[ue_idx].dl = rlc.txpdu_bytes;
return meas_record;
}
/* 3GPP TS 28.522 - section 5.1.1.3.3
note: per spec, average UE throughput in UL (taken into consideration values from all UEs, and averaged)
here calculated as: UE specific throughput in UL */
static meas_record_lst_t fill_DRB_UEThpUl(uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx)
{
meas_record_lst_t meas_record = {0};
// Get RLC stats per DRB
const int rb_id = 1; // at the moment, only 1 DRB is supported
nr_rlc_statistics_t rlc = get_rlc_stats_per_drb(ue_info.ue->rnti, rb_id);
meas_record.value = REAL_MEAS_VALUE;
// Calculate UL Thp
meas_record.real_val = (double)(rlc.rxpdu_bytes - last_rlc_pdu_total_bytes[ue_idx].ul)*8/gran_period_ms; // [kbps]
last_rlc_pdu_total_bytes[ue_idx].ul = rlc.rxpdu_bytes;
return meas_record;
}
/* 3GPP TS 28.522 - section 5.1.1.2.1
note: per spec, DL PRB usage [%] = (total used PRBs for DL traffic / total available PRBs for DL traffic) * 100
here calculated as: aggregated DL PRBs (t) - aggregated DL PRBs (t-gran_period) */
static meas_record_lst_t fill_RRU_PrbTotDl(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx)
{
meas_record_lst_t meas_record = {0};
meas_record.value = INTEGER_MEAS_VALUE;
// Get the number of DL PRBs
meas_record.int_val = ue_info.ue->mac_stats.dl.total_rbs - last_total_prbs[ue_idx].dl; // [PRBs]
last_total_prbs[ue_idx].dl = ue_info.ue->mac_stats.dl.total_rbs;
return meas_record;
}
/* 3GPP TS 28.522 - section 5.1.1.2.2
note: per spec, UL PRB usage [%] = (total used PRBs for UL traffic / total available PRBs for UL traffic) * 100
here calculated as: aggregated UL PRBs (t) - aggregated UL PRBs (t-gran_period) */
static meas_record_lst_t fill_RRU_PrbTotUl(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx)
{
meas_record_lst_t meas_record = {0};
meas_record.value = INTEGER_MEAS_VALUE;
// Get the number of UL PRBs
meas_record.int_val = ue_info.ue->mac_stats.ul.total_rbs - last_total_prbs[ue_idx].ul; // [PRBs]
last_total_prbs[ue_idx].ul = ue_info.ue->mac_stats.ul.total_rbs;
return meas_record;
}
#endif
static kv_measure_t lst_measure[] = {
{.key = "DRB.PdcpSduVolumeDL", .value = fill_DRB_PdcpSduVolumeDL },
{.key = "DRB.PdcpSduVolumeUL", .value = fill_DRB_PdcpSduVolumeUL },
#if defined (NGRAN_GNB_DU)
{.key = "DRB.RlcSduDelayDl", .value = fill_DRB_RlcSduDelayDl },
{.key = "DRB.UEThpDl", .value = fill_DRB_UEThpDl },
{.key = "DRB.UEThpUl", .value = fill_DRB_UEThpUl },
{.key = "RRU.PrbTotDl", .value = fill_RRU_PrbTotDl },
{.key = "RRU.PrbTotUl", .value = fill_RRU_PrbTotUl },
#endif
};
void init_kpm_subs_data(void)
{
const size_t ht_len = sizeof(lst_measure) / sizeof(lst_measure[0]);
hcreate(ht_len);
ENTRY kv_pair;
for (size_t i = 0; i < ht_len; i++) {
kv_pair.key = lst_measure[i].key;
kv_pair.data = &lst_measure[i];
hsearch(kv_pair, ENTER);
}
}
meas_record_lst_t get_kpm_meas_value(char* kpm_meas_name, uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx)
{
assert(kpm_meas_name != NULL);
ENTRY search_entry = {.key = kpm_meas_name};
ENTRY *found_entry = hsearch(search_entry, FIND);
assert(found_entry != NULL && "Unsupported KPM measurement name");
kv_measure_t *kv_found = (kv_measure_t *)found_entry->data;
meas_record_lst_t meas_record = kv_found->value(gran_period_ms, ue_info, ue_idx);
return meas_record;
}
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#ifndef RAN_FUNC_SM_KPM_SUBSCRIPTION_AGENT_H
#define RAN_FUNC_SM_KPM_SUBSCRIPTION_AGENT_H
#include "openair2/E2AP/flexric/src/sm/kpm_sm/kpm_data_ie_wrapper.h"
#include "openair2/LAYER2/NR_MAC_gNB/mac_proto.h"
#include "openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h"
#include "openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h"
typedef struct {
uint32_t rrc_ue_id;
NR_UE_info_t* ue;
} cudu_ue_info_pair_t;
typedef meas_record_lst_t (*kpm_meas_fp)(uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx);
typedef struct{
char* key;
kpm_meas_fp value;
} kv_measure_t;
void init_kpm_subs_data(void);
meas_record_lst_t get_kpm_meas_value(char* kpm_meas_name, uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx);
#endif
......@@ -22,7 +22,7 @@
#include "ran_func_rc.h"
#include "ran_func_rc_subs.h"
#include "ran_func_rc_extern.h"
#include "../../flexric/test/rnd/fill_rnd_data_rc.h" // this dependancy will be taken out once RAN Function Definition is implemented
#include "ran_e2sm_ue_id.h"
#include "../../flexric/src/sm/rc_sm/ie/ir/lst_ran_param.h"
#include "../../flexric/src/sm/rc_sm/ie/ir/ran_param_list.h"
#include "../../flexric/src/agent/e2_agent_api.h"
......@@ -35,17 +35,440 @@ static pthread_once_t once_rc_mutex = PTHREAD_ONCE_INIT;
static rc_subs_data_t rc_subs_data = {0};
static pthread_mutex_t rc_mutex = PTHREAD_MUTEX_INITIALIZER;
static ngran_node_t get_e2_node_type(void)
{
ngran_node_t node_type = 0;
#if defined(NGRAN_GNB_DU) && defined(NGRAN_GNB_CUUP) && defined(NGRAN_GNB_CUCP)
node_type = RC.nrrrc[0]->node_type;
#elif defined (NGRAN_GNB_CUUP)
node_type = ngran_gNB_CUUP;
#endif
return node_type;
}
static void init_once_rc(void)
{
init_rc_subs_data(&rc_subs_data);
}
static void fill_rc_ev_trig(ran_func_def_ev_trig_t* ev_trig)
{
// Sequence of EVENT TRIGGER styles
// [1 - 63]
ev_trig->sz_seq_ev_trg_style = 1;
ev_trig->seq_ev_trg_style = calloc(ev_trig->sz_seq_ev_trg_style, sizeof(seq_ev_trg_style_t));
assert(ev_trig->seq_ev_trg_style != NULL && "Memory exhausted");
seq_ev_trg_style_t* ev_trig_style = &ev_trig->seq_ev_trg_style[0];
// RIC Event Trigger Style Type
// Mandatory
// 9.3.3
// 6.2.2.2.
// INTEGER
ev_trig_style->style = 4;
// RIC Event Trigger Style Name
// Mandatory
// 9.3.4
// 6.2.2.3
//PrintableString(SIZE(1..150,...))
const char ev_style_name[] = "UE Information Change";
ev_trig_style->name = cp_str_to_ba(ev_style_name);
// RIC Event Trigger Format Type
// Mandatory
// 9.3.5
// 6.2.2.4.
// INTEGER
ev_trig_style->format = FORMAT_4_E2SM_RC_EV_TRIGGER_FORMAT;
// Sequence of RAN Parameters for L2 Variables
// [0 - 65535]
ev_trig->sz_seq_ran_param_l2_var = 0;
ev_trig->seq_ran_param_l2_var = NULL;
//Sequence of Call Process Types
// [0-65535]
ev_trig->sz_seq_call_proc_type = 0;
ev_trig->seq_call_proc_type = NULL;
// Sequence of RAN Parameters for Identifying UEs
// 0-65535
ev_trig->sz_seq_ran_param_id_ue = 0;
ev_trig->seq_ran_param_id_ue = NULL;
// Sequence of RAN Parameters for Identifying Cells
// 0-65535
ev_trig->sz_seq_ran_param_id_cell = 0;
ev_trig->seq_ran_param_id_cell = NULL;
}
static void fill_rc_report(ran_func_def_report_t* report)
{
// Sequence of REPORT styles
// [1 - 63]
report->sz_seq_report_sty = 1;
report->seq_report_sty = calloc(report->sz_seq_report_sty, sizeof(seq_report_sty_t));
assert(report->seq_report_sty != NULL && "Memory exhausted");
seq_report_sty_t* report_style = &report->seq_report_sty[0];
// RIC Report Style Type
// Mandatory
// 9.3.3
// 6.2.2.2.
// INTEGER
report_style->report_type = 4;
// RIC Report Style Name
// Mandatory
// 9.3.4
// 6.2.2.3.
// PrintableString(SIZE(1..150,...))
const char report_name[] = "UE Information";
report_style->name = cp_str_to_ba(report_name);
// Supported RIC Event Trigger Style Type
// Mandatory
// 9.3.3
// 6.2.2.2.
// INTEGER
report_style->ev_trig_type = FORMAT_4_E2SM_RC_EV_TRIGGER_FORMAT;
// RIC Report Action Format Type
// Mandatory
// 9.3.5
// 6.2.2.4.
// INTEGER
report_style->act_frmt_type = FORMAT_1_E2SM_RC_ACT_DEF;
// RIC Indication Header Format Type
// Mandatory
// 9.3.5
// 6.2.2.4.
// INTEGER
report_style->ind_hdr_type = FORMAT_1_E2SM_RC_IND_HDR;
// RIC Indication Message Format Type
// Mandatory
// 9.3.5
// 6.2.2.4.
// INTEGER
report_style->ind_msg_type = FORMAT_2_E2SM_RC_IND_MSG;
// Sequence of RAN Parameters Supported
// [0 - 65535]
report_style->sz_seq_ran_param = 1;
report_style->ran_param = calloc(report_style->sz_seq_ran_param, sizeof(seq_ran_param_3_t));
assert(report_style->ran_param != NULL && "Memory exhausted");
// RAN Parameter ID
// Mandatory
// 9.3.8
// [1- 4294967295]
report_style->ran_param[0].id = RRC_STATE_CHANGED_TO_E2SM_RC_RAN_PARAM_ID;
// RAN Parameter Name
// Mandatory
// 9.3.9
// [1-150]
const char ran_param_name[] = "RRC State";
report_style->ran_param[0].name = cp_str_to_ba(ran_param_name);
// RAN Parameter Definition
// Optional
// 9.3.51
report_style->ran_param[0].def = NULL;
}
static void fill_rc_control(ran_func_def_ctrl_t* ctrl)
{
// Sequence of CONTROL styles
// [1 - 63]
ctrl->sz_seq_ctrl_style = 1;
ctrl->seq_ctrl_style = calloc(ctrl->sz_seq_ctrl_style, sizeof(seq_ctrl_style_t));
assert(ctrl->seq_ctrl_style != NULL && "Memory exhausted");
seq_ctrl_style_t* ctrl_style = &ctrl->seq_ctrl_style[0];
// RIC Control Style Type
// Mandatory
// 9.3.3
// 6.2.2.2
ctrl_style->style_type = 1;
//RIC Control Style Name
//Mandatory
//9.3.4
// [1 -150]
const char control_name[] = "Radio Bearer Control";
ctrl_style->name = cp_str_to_ba(control_name);
// RIC Control Header Format Type
// Mandatory
// 9.3.5
ctrl_style->hdr = FORMAT_1_E2SM_RC_CTRL_HDR;
// RIC Control Message Format Type
// Mandatory
// 9.3.5
ctrl_style->msg = FORMAT_1_E2SM_RC_CTRL_MSG;
// RIC Call Process ID Format Type
// Optional
ctrl_style->call_proc_id_type = NULL;
// RIC Control Outcome Format Type
// Mandatory
// 9.3.5
ctrl_style->out_frmt = FORMAT_1_E2SM_RC_CTRL_OUT;
// Sequence of Control Actions
// [0-65535]
ctrl_style->sz_seq_ctrl_act = 1;
ctrl_style->seq_ctrl_act = calloc(ctrl_style->sz_seq_ctrl_act, sizeof(seq_ctrl_act_2_t));
assert(ctrl_style->seq_ctrl_act != NULL && "Memory exhausted");
seq_ctrl_act_2_t* ctrl_act = &ctrl_style->seq_ctrl_act[0];
// Control Action ID
// Mandatory
// 9.3.6
// [1-65535]
ctrl_act->id = 2;
// Control Action Name
// Mandatory
// 9.3.7
// [1-150]
const char control_act_name[] = "QoS flow mapping configuration";
ctrl_act->name = cp_str_to_ba(control_act_name);
// Sequence of Associated RAN Parameters
// [0-65535]
ctrl_act->sz_seq_assoc_ran_param = 2;
ctrl_act->assoc_ran_param = calloc(ctrl_act->sz_seq_assoc_ran_param, sizeof(seq_ran_param_3_t));
assert(ctrl_act->assoc_ran_param != NULL && "Memory exhausted");
seq_ran_param_3_t* assoc_ran_param = ctrl_act->assoc_ran_param;
// DRB ID
assoc_ran_param[0].id = 1;
const char ran_param_drb_id[] = "DRB ID";
assoc_ran_param[0].name = cp_str_to_ba(ran_param_drb_id);
assoc_ran_param[0].def = NULL;
// List of QoS Flows to be modified in DRB
assoc_ran_param[1].id = 2;
const char ran_param_list_qos_flow[] = "List of QoS Flows to be modified in DRB";
assoc_ran_param[1].name = cp_str_to_ba(ran_param_list_qos_flow);
assoc_ran_param[1].def = calloc(1, sizeof(ran_param_def_t));
assert(assoc_ran_param[1].def != NULL && "Memory exhausted");
assoc_ran_param[1].def->type = LIST_RAN_PARAMETER_DEF_TYPE;
assoc_ran_param[1].def->lst = calloc(1, sizeof(ran_param_type_t));
assert(assoc_ran_param[1].def->lst != NULL && "Memory exhausted");
ran_param_type_t* lst = assoc_ran_param[1].def->lst;
lst->sz_ran_param = 2;
lst->ran_param = calloc(lst->sz_ran_param, sizeof(ran_param_lst_struct_t));
assert(lst->ran_param != NULL && "Memory exhausted");
// QoS Flow Identifier
lst->ran_param[0].ran_param_id = 4;
const char ran_param_qos_flow_id[] = "QoS Flow Identifier";
lst->ran_param[0].ran_param_name = cp_str_to_ba(ran_param_qos_flow_id);
lst->ran_param[0].ran_param_def = NULL;
// QoS Flow Mapping Indication
lst->ran_param[1].ran_param_id = 5;
const char ran_param_qos_flow_mapping_ind[] = "QoS Flow Mapping Indication";
lst->ran_param[1].ran_param_name = cp_str_to_ba(ran_param_qos_flow_mapping_ind);
lst->ran_param[1].ran_param_def = NULL;
// Sequence of Associated RAN
// Parameters for Control Outcome
// [0- 255]
ctrl_style->sz_ran_param_ctrl_out = 0;
ctrl_style->ran_param_ctrl_out = NULL;
}
e2sm_rc_func_def_t fill_rc_ran_def_gnb(void)
{
e2sm_rc_func_def_t def = {0};
// RAN Function Definition for EVENT TRIGGER
// Optional
// 9.2.2.2
def.ev_trig = calloc(1, sizeof(ran_func_def_ev_trig_t));
assert(def.ev_trig != NULL && "Memory exhausted");
fill_rc_ev_trig(def.ev_trig);
// RAN Function Definition for REPORT
// Optional
// 9.2.2.3
def.report = calloc(1, sizeof(ran_func_def_report_t));
assert(def.report != NULL && "Memory exhausted");
fill_rc_report(def.report);
// RAN Function Definition for INSERT
// Optional
// 9.2.2.4
def.insert = NULL;
// RAN Function Definition for CONTROL
// Optional
// 9.2.2.5
def.ctrl = calloc(1, sizeof(ran_func_def_ctrl_t));
assert(def.ctrl != NULL && "Memory exhausted");
fill_rc_control(def.ctrl);
// RAN Function Definition for POLICY
// Optional
// 9.2.2.6
def.policy = NULL;
return def;
}
static e2sm_rc_func_def_t fill_rc_ran_def_cu(void)
{
e2sm_rc_func_def_t def = {0};
// RAN Function Definition for EVENT TRIGGER
// Optional
// 9.2.2.2
def.ev_trig = calloc(1, sizeof(ran_func_def_ev_trig_t));
assert(def.ev_trig != NULL && "Memory exhausted");
fill_rc_ev_trig(def.ev_trig);
// RAN Function Definition for REPORT
// Optional
// 9.2.2.3
def.report = calloc(1, sizeof(ran_func_def_report_t));
assert(def.report != NULL && "Memory exhausted");
fill_rc_report(def.report);
// RAN Function Definition for INSERT
// Optional
// 9.2.2.4
def.insert = NULL;
// RAN Function Definition for CONTROL
// Optional
// 9.2.2.5
def.ctrl = calloc(1, sizeof(ran_func_def_ctrl_t));
assert(def.ctrl != NULL && "Memory exhausted");
fill_rc_control(def.ctrl);
// RAN Function Definition for POLICY
// Optional
// 9.2.2.6
def.policy = NULL;
return def;
}
static e2sm_rc_func_def_t fill_rc_ran_def_null(void)
{
e2sm_rc_func_def_t def = {0};
// RAN Function Definition for EVENT TRIGGER
// Optional
// 9.2.2.2
def.ev_trig = NULL;
// RAN Function Definition for REPORT
// Optional
// 9.2.2.3
def.report = NULL;
// RAN Function Definition for INSERT
// Optional
// 9.2.2.4
def.insert = NULL;
// RAN Function Definition for CONTROL
// Optional
// 9.2.2.5
def.ctrl = NULL;
// RAN Function Definition for POLICY
// Optional
// 9.2.2.6
def.policy = NULL;
return def;
}
static e2sm_rc_func_def_t fill_rc_ran_def_cucp(void)
{
e2sm_rc_func_def_t def = {0};
// RAN Function Definition for EVENT TRIGGER
// Optional
// 9.2.2.2
def.ev_trig = calloc(1, sizeof(ran_func_def_ev_trig_t));
assert(def.ev_trig != NULL && "Memory exhausted");
fill_rc_ev_trig(def.ev_trig);
// RAN Function Definition for REPORT
// Optional
// 9.2.2.3
def.report = calloc(1, sizeof(ran_func_def_report_t));
assert(def.report != NULL && "Memory exhausted");
fill_rc_report(def.report);
// RAN Function Definition for INSERT
// Optional
// 9.2.2.4
def.insert = NULL;
// RAN Function Definition for CONTROL
// Optional
// 9.2.2.5
def.ctrl = NULL;
// RAN Function Definition for POLICY
// Optional
// 9.2.2.6
def.policy = NULL;
return def;
}
typedef e2sm_rc_func_def_t (*fp_rc_func_def)(void);
static const fp_rc_func_def ran_def_rc[END_NGRAN_NODE_TYPE] =
{
NULL,
NULL,
fill_rc_ran_def_gnb,
NULL,
NULL,
fill_rc_ran_def_cu,
NULL,
fill_rc_ran_def_null, // DU - at the moment, no Service is supported
NULL,
fill_rc_ran_def_cucp,
fill_rc_ran_def_null, // CU-UP - at the moment, no Service is supported
};
void read_rc_setup_sm(void* data)
{
assert(data != NULL);
// assert(data->type == RAN_CTRL_V1_3_AGENT_IF_E2_SETUP_ANS_V0);
rc_e2_setup_t* rc = (rc_e2_setup_t*)data;
rc->ran_func_def = fill_rc_ran_func_def();
/* Fill the RAN Function Definition with currently supported measurements */
// RAN Function Name is already filled in fill_ran_function_name() in rc_sm_agent.c
const ngran_node_t node_type = get_e2_node_type();
rc->ran_func_def = ran_def_rc[node_type]();
// E2 Setup Request is sent periodically until the connection is established
// RC subscritpion data should be initialized only once
......@@ -56,53 +479,6 @@ void read_rc_setup_sm(void* data)
RB_PROTOTYPE(ric_id_2_param_id_trees, ric_req_id_s, entries, cmp_ric_req_id);
static ue_id_e2sm_t fill_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context)
{
ue_id_e2sm_t ue_id = {0};
// Check the E2 node type
#if defined (NGRAN_GNB_CUUP) && defined (NGRAN_GNB_CUCP)
ue_id.type = GNB_UE_ID_E2SM;
if (RC.nrrrc[0]->node_type == ngran_gNB_CU) {
// gNB-CU UE F1AP ID List
// C-ifCUDUseparated
ue_id.gnb.gnb_cu_ue_f1ap_lst_len = 1;
ue_id.gnb.gnb_cu_ue_f1ap_lst = calloc(ue_id.gnb.gnb_cu_ue_f1ap_lst_len, sizeof(uint32_t));
assert(ue_id.gnb.gnb_cu_ue_f1ap_lst != NULL && "Memory exhausted");
for(size_t i = 0; i < ue_id.gnb.gnb_cu_ue_f1ap_lst_len; i++) {
ue_id.gnb.gnb_cu_ue_f1ap_lst[i] = rrc_ue_context->rrc_ue_id;
}
} else if (RC.nrrrc[0]->node_type == ngran_gNB_CUCP) {
//gNB-CU-CP UE E1AP ID List
//C-ifCPUPseparated
ue_id.gnb.gnb_cu_cp_ue_e1ap_lst_len = 1;
ue_id.gnb.gnb_cu_cp_ue_e1ap_lst = calloc(ue_id.gnb.gnb_cu_cp_ue_e1ap_lst_len, sizeof(uint32_t));
assert(ue_id.gnb.gnb_cu_cp_ue_e1ap_lst != NULL && "Memory exhausted");
for(size_t i = 0; i < ue_id.gnb.gnb_cu_cp_ue_e1ap_lst_len; i++) {
ue_id.gnb.gnb_cu_cp_ue_e1ap_lst[i] = rrc_ue_context->rrc_ue_id;
}
} else if (RC.nrrrc[0]->node_type == ngran_gNB_DU) {
assert(false && "RRC state change cannot be triggered from DU\n");
}
#elif defined (NGRAN_GNB_CUUP)
assert(false && "RRC state change cannot be triggered from CU-UP\n");
#endif
ue_id.gnb.amf_ue_ngap_id = rrc_ue_context->rrc_ue_id;
ue_id.gnb.guami.amf_ptr = rrc_ue_context->ue_guami.amf_pointer;
ue_id.gnb.guami.amf_region_id = rrc_ue_context->ue_guami.amf_region_id;
ue_id.gnb.guami.amf_set_id = rrc_ue_context->ue_guami.amf_set_id;
ue_id.gnb.guami.plmn_id.mcc = rrc_ue_context->ue_guami.mcc;
ue_id.gnb.guami.plmn_id.mnc = rrc_ue_context->ue_guami.mnc;
ue_id.gnb.guami.plmn_id.mnc_digit_len = rrc_ue_context->ue_guami.mnc_len;
return ue_id;
}
static seq_ran_param_t fill_rrc_state_change_seq_ran(const rc_sm_rrc_state_e rrc_state)
{
seq_ran_param_t seq_ran_param = {0};
......@@ -138,7 +514,8 @@ static rc_ind_data_t* fill_ue_rrc_state_change(const gNB_RRC_UE_t *rrc_ue_contex
// UE ID
// Mandatory
// 9.3.10
rc_ind->msg.frmt_2.seq_ue_id[0].ue_id = fill_ue_id_data(rrc_ue_context);
const ngran_node_t node_type = get_e2_node_type();
rc_ind->msg.frmt_2.seq_ue_id[0].ue_id = fill_ue_id_data[node_type](rrc_ue_context, 0, 0);
// Sequence of
// RAN Parameter
......@@ -221,39 +598,45 @@ sm_ag_if_ans_t write_ctrl_rc_sm(void const* data)
// assert(data->type == RAN_CONTROL_CTRL_V1_03 );
rc_ctrl_req_data_t const* ctrl = (rc_ctrl_req_data_t const*)data;
if(ctrl->hdr.format == FORMAT_1_E2SM_RC_CTRL_HDR){
if(ctrl->hdr.frmt_1.ric_style_type == 1 && ctrl->hdr.frmt_1.ctrl_act_id == 2){
printf("QoS flow mapping configuration \n");
e2sm_rc_ctrl_msg_frmt_1_t const* frmt_1 = &ctrl->msg.frmt_1;
for(size_t i = 0; i < frmt_1->sz_ran_param; ++i){
seq_ran_param_t const* rp = frmt_1->ran_param;
if(rp[i].ran_param_id == 1){
assert(rp[i].ran_param_val.type == ELEMENT_KEY_FLAG_TRUE_RAN_PARAMETER_VAL_TYPE );
printf("DRB ID %ld \n", rp[i].ran_param_val.flag_true->int_ran);
} else if(rp[i].ran_param_id == 2){
assert(rp[i].ran_param_val.type == LIST_RAN_PARAMETER_VAL_TYPE);
printf("List of QoS Flows to be modified \n");
for(size_t j = 0; j < ctrl->msg.frmt_1.ran_param[i].ran_param_val.lst->sz_lst_ran_param; ++j){
lst_ran_param_t const* lrp = rp[i].ran_param_val.lst->lst_ran_param;
// The following assertion should be true, but there is a bug in the std
// check src/sm/rc_sm/enc/rc_enc_asn.c:1085 and src/sm/rc_sm/enc/rc_enc_asn.c:984
// assert(lrp[j].ran_param_id == 3);
assert(lrp[j].ran_param_struct.ran_param_struct[0].ran_param_id == 4) ;
assert(lrp[j].ran_param_struct.ran_param_struct[0].ran_param_val.type == ELEMENT_KEY_FLAG_TRUE_RAN_PARAMETER_VAL_TYPE);
int64_t qfi = lrp[j].ran_param_struct.ran_param_struct[0].ran_param_val.flag_true->int_ran;
assert(qfi > -1 && qfi < 65);
assert(lrp[j].ran_param_struct.ran_param_struct[1].ran_param_id == 5);
assert(lrp[j].ran_param_struct.ran_param_struct[1].ran_param_val.type == ELEMENT_KEY_FLAG_FALSE_RAN_PARAMETER_VAL_TYPE);
int64_t dir = lrp[j].ran_param_struct.ran_param_struct[1].ran_param_val.flag_false->int_ran;
assert(dir == 0 || dir == 1);
printf("qfi = %ld dir %ld \n", qfi, dir);
}
}
}
}
}
assert(ctrl->hdr.format == FORMAT_1_E2SM_RC_CTRL_HDR && "Indication Header Format received not valid");
assert(ctrl->msg.format == FORMAT_1_E2SM_RC_CTRL_MSG && "Indication Message Format received not valid");
assert(ctrl->hdr.frmt_1.ctrl_act_id == 2 && "Currently only QoS flow mapping configuration supported");
printf("QoS flow mapping configuration\n");
const seq_ran_param_t* ran_param = ctrl->msg.frmt_1.ran_param;
// DRB ID
assert(ran_param[0].ran_param_id == 1 && "First RAN Parameter ID has to be DRB ID");
assert(ran_param[0].ran_param_val.type == ELEMENT_KEY_FLAG_TRUE_RAN_PARAMETER_VAL_TYPE);
printf("DRB ID %ld \n", ran_param[0].ran_param_val.flag_true->int_ran);
// List of QoS Flows to be modified in DRB
assert(ran_param[1].ran_param_id == 2 && "Second RAN Parameter ID has to be List of QoS Flows");
assert(ran_param[1].ran_param_val.type == LIST_RAN_PARAMETER_VAL_TYPE);
printf("List of QoS Flows to be modified in DRB\n");
const lst_ran_param_t* lrp = ran_param[1].ran_param_val.lst->lst_ran_param;
// The following assertion should be true, but there is a bug in the std
// check src/sm/rc_sm/enc/rc_enc_asn.c:1085 and src/sm/rc_sm/enc/rc_enc_asn.c:984
// assert(lrp->ran_param_struct.ran_param_struct[0].ran_param_id == 3);
// QoS Flow Identifier
assert(lrp->ran_param_struct.ran_param_struct[0].ran_param_id == 4);
assert(lrp->ran_param_struct.ran_param_struct[0].ran_param_val.type == ELEMENT_KEY_FLAG_TRUE_RAN_PARAMETER_VAL_TYPE);
int64_t qfi = lrp->ran_param_struct.ran_param_struct[0].ran_param_val.flag_true->int_ran;
assert(qfi > -1 && qfi < 65);
// QoS Flow Mapping Indication
assert(lrp->ran_param_struct.ran_param_struct[1].ran_param_id == 5);
assert(lrp->ran_param_struct.ran_param_struct[1].ran_param_val.type == ELEMENT_KEY_FLAG_FALSE_RAN_PARAMETER_VAL_TYPE);
int64_t dir = lrp->ran_param_struct.ran_param_struct[1].ran_param_val.flag_false->int_ran;
assert(dir == 0 || dir == 1);
printf("qfi = %ld, dir %ld \n", qfi, dir);
sm_ag_if_ans_t ans = {.type = CTRL_OUTCOME_SM_AG_IF_ANS_V0};
ans.ctrl_out.type = RAN_CTRL_V1_3_AGENT_IF_CTRL_ANS_V0;
......
......@@ -21,10 +21,109 @@
#include "read_setup_ran.h"
#include "../../E2AP/flexric/src/lib/e2ap/e2ap_node_component_config_add_wrapper.h"
#include "../../E2AP/flexric/test/rnd/fill_rnd_data_e2_setup_req.h"
#include <assert.h>
#include <stdlib.h>
#if defined(E2AP_V2) || defined(E2AP_V3)
// NGAP
static e2ap_node_component_config_add_t fill_ngap_e2ap_node_component_config_add(void)
{
e2ap_node_component_config_add_t dst = {0};
// Mandatory
// 9.2.26
dst.e2_node_comp_interface_type = NG_E2AP_NODE_COMP_INTERFACE_TYPE;
// Bug!! Optional in the standard, mandatory in ASN.1
// 9.2.32
dst.e2_node_comp_id.type = NG_E2AP_NODE_COMP_INTERFACE_TYPE;
const char ng_msg[] = "Dummy message";
dst.e2_node_comp_id.ng_amf_name = cp_str_to_ba(ng_msg);
// Mandatory
// 9.2.27
const char req[] = "NGAP Request Message sent";
const char res[] = "NGAP Response Message reveived";
dst.e2_node_comp_conf.request = cp_str_to_ba(req);
dst.e2_node_comp_conf.response = cp_str_to_ba(res);
return dst;
}
// F1AP
static e2ap_node_component_config_add_t fill_f1ap_e2ap_node_component_config_add(void)
{
e2ap_node_component_config_add_t dst = {0};
// Mandatory
// 9.2.26
dst.e2_node_comp_interface_type = F1_E2AP_NODE_COMP_INTERFACE_TYPE;
// Bug!! Optional in the standard, mandatory in ASN.1
// 9.2.32
dst.e2_node_comp_id.type = F1_E2AP_NODE_COMP_INTERFACE_TYPE;
dst.e2_node_comp_id.f1_gnb_du_id = 1023;
// Mandatory
// 9.2.27
const char req[] = "F1AP Request Message sent";
const char res[] = "F1AP Response Message reveived";
dst.e2_node_comp_conf.request = cp_str_to_ba(req);
dst.e2_node_comp_conf.response = cp_str_to_ba(res);
return dst;
}
// E1AP
static e2ap_node_component_config_add_t fill_e1ap_e2ap_node_component_config_add(void)
{
e2ap_node_component_config_add_t dst = {0};
// Mandatory
// 9.2.26
dst.e2_node_comp_interface_type = E1_E2AP_NODE_COMP_INTERFACE_TYPE;
// Bug!! Optional in the standard, mandatory in ASN.1
// 9.2.32
dst.e2_node_comp_id.type = E1_E2AP_NODE_COMP_INTERFACE_TYPE;
dst.e2_node_comp_id.e1_gnb_cu_up_id = 1025;
// Mandatory
// 9.2.27
const char req[] = "E1AP Request Message sent";
const char res[] = "E1AP Response Message reveived";
dst.e2_node_comp_conf.request = cp_str_to_ba(req);
dst.e2_node_comp_conf.response = cp_str_to_ba(res);
return dst;
}
// S1AP
static e2ap_node_component_config_add_t fill_s1ap_e2ap_node_component_config_add(void)
{
e2ap_node_component_config_add_t dst = {0};
// Mandatory
// 9.2.26
dst.e2_node_comp_interface_type = S1_E2AP_NODE_COMP_INTERFACE_TYPE;
// Bug!! Optional in the standard, mandatory in ASN.1
// 9.2.32
dst.e2_node_comp_id.type = S1_E2AP_NODE_COMP_INTERFACE_TYPE;
const char str[] = "S1 NAME";
dst.e2_node_comp_id.s1_mme_name = cp_str_to_ba(str);
// Mandatory
// 9.2.27
const char req[] = "S1AP Request Message sent";
const char res[] = "S1AP Response Message reveived";
dst.e2_node_comp_conf.request = cp_str_to_ba(req);
dst.e2_node_comp_conf.response = cp_str_to_ba(res);
return dst;
}
#endif
void read_setup_ran(void* data, const ngran_node_t node_type)
{
assert(data != NULL);
......
......@@ -13,6 +13,8 @@ Please see [NOTICE](NOTICE.md) file for third party software that is included in
This tutorial describes the steps of deployment 5G OAI RAN, with integrated E2 agent and a nearRT-RIC using O-RAN compliant FlexRIC.
[[_TOC_]]
# 1. Installation
## 1.1 Install prerequisites
......@@ -21,7 +23,7 @@ This tutorial describes the steps of deployment 5G OAI RAN, with integrated E2 a
On Ubuntu, you might want to use [this PPA](https://apt.kitware.com/) to install an up-to-date version.
- SWIG (at least v.4.0).
- SWIG (at least v.4.1).
We use SWIG as an interface generator to enable the multi-language feature (i.e., C/C++ and Python) for the xApps. Please, check your SWIG version (i.e, `swig
-version`) and install it from scratch if necessary as described here: https://swig.org/svn.html or via the code below:
......@@ -69,17 +71,11 @@ Currently available versions:
| E2AP v2.03 | Y | Y |
| E2AP v3.01 | Y | Y |
Please note that KPM SM v2.01 is supported only in FlexRIC, but not in OAI.
Please note that KPM SM v2.01 is supported only in FlexRIC, but not in OAI.
```bash
cd cmake_targets/
./build_oai -I -w SIMU --gNB --nrUE --build-e2 --ninja
```
If the openair2/E2AP/flexric folder is empty, try manually the following commands
```bash
git submodule init
git submodule update
```
* `-I` option is to install pre-requisites, you only need it the first time you build the softmodem or when some oai dependencies have changed.
......@@ -89,16 +85,22 @@ git submodule update
* `--ninja` is to use the ninja build tool, which speeds up compilation
* `--build-e2` option is to use the E2 agent, integrated within RAN.
If the openair2/E2AP/flexric folder is empty, try manually the following commands:
```bash
git submodule init
git submodule update
```
## 2.2 FlexRIC
- By default, FlexRIC will build the nearRT-RIC with E2AP v2 and KPM v2. If you want a different version, edit the variable E2AP\_VERSION and KPM\_VERSION at FlexRIC's CMakeLists.txt file. Note that OAI's and FlexRIC's E2AP\_VERSION and KPM\_VERSION need to match due to O-RAN incompatibilities among versions.
By default, FlexRIC will build the nearRT-RIC with E2AP v2 and KPM v2. If you want a different version, edit the variable E2AP\_VERSION and KPM\_VERSION at FlexRIC's CMakeLists.txt file. Note that OAI's and FlexRIC's E2AP\_VERSION and KPM\_VERSION need to match due to O-RAN incompatibilities among versions.
### 2.2.1 Clone the FlexRIC repository
```bash
git clone https://gitlab.eurecom.fr/mosaic5g/flexric flexric
cd flexric/
git checkout 29af8408afad3da36824bd7912562202023ae924
git checkout 34d6810f9ab435ac3a4023d5e1917669e24341da
```
### 2.2.2 Build FlexRIC
......@@ -113,27 +115,47 @@ sudo make install
By default the service model libraries will be installed in the path `/usr/local/lib/flexric` while the configuration file in `/usr/local/etc/flexric`.
#### 2.2.3.1 Available Service Models
1. KPM v02.03 and KPM v03.00 (`xapp_kpm_moni`)
At the moment, the following measurements are supported in OAI RAN:
* "DRB.PdcpSduVolumeDL"
* "DRB.PdcpSduVolumeUL"
* "DRB.RlcSduDelayDl"
* "DRB.UEThpDl"
* "DRB.UEThpUl"
* "RRU.PrbTotDl"
* "RRU.PrbTotUl"
# 3. Service Models available in OAI RAN
## 3.1 O-RAN
We assume that user is familiar with O-RAN WG3 specifications that can be found at https://orandownloadsweb.azurewebsites.net/specifications.
2. RC v01.03
Currently available RC xApps:
* `xapp_kpm_rc` - RAN control function "QoS flow mapping configuration" support, i.e. creating a new DRB.
* `xapp_rc_moni` - aperiodic subscription support for "UE RRC State Change"
### 3.1.1 E2SM-KPM
3. MAC + RLC + PDCP + GTP (`xapp_gtp_mac_rlc_pdcp_moni`)
As mentioned in section [2.1.2 Build OAI with E2 Agent](#212-build-oai-with-e2-agent), we support KPM v2.03/v3.00. Uses ASN.1 encoding.
Per O-RAN specifications, 5G measurements supported by KPM are specified in 3GPP TS 28.552.
From 3GPP TS 28.552, we support the following list:
* "DRB.PdcpSduVolumeDL"
* "DRB.PdcpSduVolumeUL"
* "DRB.RlcSduDelayDl"
* "DRB.UEThpDl"
* "DRB.UEThpUl"
* "RRU.PrbTotDl"
* "RRU.PrbTotUl"
From O-RAN.WG3.E2SM-KPM-version specification, we implemented:
* REPORT Service Style 4 ("Common condition-based, UE-level" - section 7.4.5) - fetch above measurements per each UE that matches common criteria (e.g. S-NSSAI).
### 3.1.2 E2SM-RC
We support RC v1.03. Uses ASN.1 encoding.
From ORAN.WG3.E2SM-RC-v01.03 specification, we implemented:
* REPORT Service Style 4 ("UE Information" - section 7.4.5) - aperiodic subscription for "UE RRC State Change"
* CONTROL Service Style 1 ("Radio Bearer Control" - section 7.6.2) - "QoS flow mapping configuration" (e.g creating a new DRB)
## 3.2 Custom Service Models
In addition, we support custom Service Models, such are MAC, RLC, PDCP, and GTP. Use plain encoding.
If you are interested in TC and SLICE SMs, please follow the instructions at https://gitlab.eurecom.fr/mosaic5g/flexric.
# 3. Start the process
# 4. Start the process
At this point, we assume the 5G Core Network is already running in the background. For more information, please follow the tutorial at https://gitlab.eurecom.fr/oai/openairinterface5g/-/blob/develop/doc/NR_SA_Tutorial_OAI_CN5G.md.
In order to configure E2 agent, please, add the following block in OAI's configuration file:
```bash
......@@ -142,61 +164,75 @@ e2_agent = {
sm_dir = "/usr/local/lib/flexric/"
}
```
* start the gNB-mono
```bash
cd oai/cmake_targets/ran_build/build
sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf --rfsim --sa -E
```
* if CU/DU split is used, start the gNB as follows
```bash
sudo ./nr-softmodem -O <path_to_du_conf_file> --rfsim --sa -E
sudo ./nr-softmodem -O <path_to_cu_conf_file> --sa
```
* if CU-CP/CU-UP/DU split is used, start the gNB as follows
```bash
sudo ./nr-softmodem -O <path_to_du_conf_file> --rfsim --sa -E
sudo ./nr-softmodem -O <path_to_cu_cp_conf_file> --sa
sudo ./nr-cuup -O <path_to_cu_up_conf_file> --sa
```
* start E2 Node agents
As per O-RAN.WG3.E2SM-v02.00 specifications, UE ID (section 6.2.2.6) representation in OAI is:
| | gNB-mono | CU | CU-CP | CU-UP | DU |
|:----------------------|:----------------|:----------------|:----------------|:----------------------|:-------------------|
| CHOICE UE ID case | GNB_UE_ID_E2SM | GNB_UE_ID_E2SM | GNB_UE_ID_E2SM | GNB_CU_UP_UE_ID_E2SM | GNB_DU_UE_ID_E2SM |
| AMF UE NGAP ID | amf_ue_ngap_id | amf_ue_ngap_id | amf_ue_ngap_id | | |
| GUAMI | guami | guami | guami | | |
| gNB-CU UE F1AP ID | | rrc_ue_id | | | rrc_ue_id |
| gNB-CU-CP UE E1AP ID | | | rrc_ue_id | rrc_ue_id | |
| RAN UE ID | rrc_ue_id | rrc_ue_id | rrc_ue_id | rrc_ue_id | rrc_ue_id |
* start the gNB-mono
```bash
cd oai/cmake_targets/ran_build/build
sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf --rfsim --sa -E
```
* if CU/DU split is used, start the gNB as follows
```bash
sudo ./nr-softmodem -O <path_to_du_conf_file> --rfsim --sa -E
sudo ./nr-softmodem -O <path_to_cu_conf_file> --sa
```
* if CU-CP/CU-UP/DU split is used, start the gNB as follows
```bash
sudo ./nr-softmodem -O <path_to_du_conf_file> --rfsim --sa -E
sudo ./nr-softmodem -O <path_to_cu_cp_conf_file> --sa
sudo ./nr-cuup -O <path_to_cu_up_conf_file> --sa
```
* start the nrUE
```bash
cd oai/cmake_targets/ran_build/build
sudo ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --rfsim --sa --uicc0.imsi 001010000000001 --rfsimulator.serveraddr 127.0.0.1
```
```bash
cd oai/cmake_targets/ran_build/build
sudo ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --rfsim --sa --uicc0.imsi 001010000000001 --rfsimulator.serveraddr 127.0.0.1
```
* start the nearRT-RIC
```bash
cd flexric
./build/examples/ric/nearRT-RIC
```
* start the KPM monitor xApp
```bash
cd flexric
./build/examples/xApp/c/monitor/xapp_kpm_moni
```
* start the RC monitor xApp
```bash
cd flexric
./build/examples/xApp/c/monitor/xapp_rc_moni
```
* start the RC control xApp
```bash
cd flexric
./build/examples/xApp/c/kpm_rc/xapp_kpm_rc
```
```bash
cd flexric
./build/examples/ric/nearRT-RIC
```
* start the (MAC + RLC + PDCP + GTP) monitor xApp
```bash
cd flexric
./build/examples/xApp/c/monitor/xapp_gtp_mac_rlc_pdcp_moni
```
* Start different xApps
* start the KPM monitor xApp - measurements stated in [3.1.1 E2SM-KPM](#311-e2sm-kpm) for each UE that matches S-NSSAI common criteria
```bash
cd flexric
./build/examples/xApp/c/monitor/xapp_kpm_moni
```
Note: we assume that each UE has only 1 DRB; CU-UP does not store the slices, therefore "coarse filtering" is used
* start the RC monitor xApp - aperiodic subscription for "UE RRC State Change"
```bash
cd flexric
./build/examples/xApp/c/monitor/xapp_rc_moni
```
* start the RC control xApp - RAN control function "QoS flow mapping configuration" (e.g. creating a new DRB)
```bash
cd flexric
./build/examples/xApp/c/kpm_rc/xapp_kpm_rc
```
* start the (MAC + RLC + PDCP + GTP) monitor xApp
```bash
cd flexric
./build/examples/xApp/c/monitor/xapp_gtp_mac_rlc_pdcp_moni
```
The latency that you observe in your monitor xApp is the latency from the E2 Agent to the nearRT-RIC and xApp.
Therefore, FlexRIC is well suited for use cases with ultra low-latency requirements.
Additionally, all the data received in the `xapp_gtp_mac_rlc_pdcp_moni` xApp is also written to /tmp/xapp_db in case that offline data processing is wanted (e.g., Machine Learning/Artificial Intelligence applications). You can browse the data using e.g., sqlitebrowser.
......
flexric @ 34d6810f
Subproject commit 7c424e882adae6ad72a24470d8c3e731189062c0
Subproject commit 34d6810f9ab435ac3a4023d5e1917669e24341da
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment