From 2fef830d4258acc6182353fb40edf90b6b6bd817 Mon Sep 17 00:00:00 2001 From: Teodora Vladic Date: Wed, 23 Aug 2023 16:47:23 +0200 Subject: [PATCH 1/9] Update FlexRIC submodule --- .gitmodules | 2 +- openair2/E2AP/flexric | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 6d85cbb418..adf63b85a3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "openair2/E2AP/flexric"] path = openair2/E2AP/flexric url = https://gitlab.eurecom.fr/mosaic5g/flexric.git - branch = remotes/origin/mir_dev + branch = remotes/origin/service-models-integration diff --git a/openair2/E2AP/flexric b/openair2/E2AP/flexric index d3ff879135..ad8ea30ffb 160000 --- a/openair2/E2AP/flexric +++ b/openair2/E2AP/flexric @@ -1 +1 @@ -Subproject commit d3ff879135d036632d7938c2085dbf4577759225 +Subproject commit ad8ea30ffb67ed2d7b958f04fbc639c92311f79b From 8a4df37e8338b9871abf0c81887e74b21c5ea851 Mon Sep 17 00:00:00 2001 From: Chieh-Chun Chen Date: Wed, 23 Aug 2023 16:48:21 +0200 Subject: [PATCH 2/9] nr rlc: rename delete() to delete_entity() --- openair2/LAYER2/nr_rlc/nr_rlc_entity.c | 6 +++--- openair2/LAYER2/nr_rlc/nr_rlc_entity.h | 2 +- openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c | 4 ++-- openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.c | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_entity.c b/openair2/LAYER2/nr_rlc/nr_rlc_entity.c index a27cf4f048..e9fa7e5a6e 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_entity.c +++ b/openair2/LAYER2/nr_rlc/nr_rlc_entity.c @@ -99,7 +99,7 @@ nr_rlc_entity_t *new_nr_rlc_entity_am( ret->common.set_time = nr_rlc_entity_am_set_time; ret->common.discard_sdu = nr_rlc_entity_am_discard_sdu; ret->common.reestablishment = nr_rlc_entity_am_reestablishment; - ret->common.delete = nr_rlc_entity_am_delete; + ret->common.delete_entity = nr_rlc_entity_am_delete; ret->common.available_tx_space = nr_rlc_entity_am_available_tx_space; ret->common.get_stats = nr_rlc_entity_get_stats; @@ -158,7 +158,7 @@ nr_rlc_entity_t *new_nr_rlc_entity_um( ret->common.set_time = nr_rlc_entity_um_set_time; ret->common.discard_sdu = nr_rlc_entity_um_discard_sdu; ret->common.reestablishment = nr_rlc_entity_um_reestablishment; - ret->common.delete = nr_rlc_entity_um_delete; + ret->common.delete_entity = nr_rlc_entity_um_delete; ret->common.available_tx_space = nr_rlc_entity_um_available_tx_space; ret->common.get_stats = nr_rlc_entity_get_stats; @@ -198,7 +198,7 @@ nr_rlc_entity_t *new_nr_rlc_entity_tm( ret->common.set_time = nr_rlc_entity_tm_set_time; ret->common.discard_sdu = nr_rlc_entity_tm_discard_sdu; ret->common.reestablishment = nr_rlc_entity_tm_reestablishment; - ret->common.delete = nr_rlc_entity_tm_delete; + ret->common.delete_entity = nr_rlc_entity_tm_delete; ret->common.available_tx_space = nr_rlc_entity_tm_available_tx_space; ret->common.get_stats = nr_rlc_entity_get_stats; diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_entity.h b/openair2/LAYER2/nr_rlc/nr_rlc_entity.h index cd26094cf7..ec911e9217 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_entity.h +++ b/openair2/LAYER2/nr_rlc/nr_rlc_entity.h @@ -119,7 +119,7 @@ typedef struct nr_rlc_entity_t { void (*reestablishment)(struct nr_rlc_entity_t *entity); - void (*delete)(struct nr_rlc_entity_t *entity); + void (*delete_entity)(struct nr_rlc_entity_t *entity); int (*available_tx_space)(struct nr_rlc_entity_t *entity); diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c index 7a2d3ae0a5..02c1c72fb7 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c +++ b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c @@ -917,13 +917,13 @@ rlc_op_status_t rrc_rlc_config_req ( ue = nr_rlc_manager_get_ue(nr_rlc_ue_manager, ctxt_pP->rntiMaybeUEid); if (srb_flagP) { if (ue->srb[rb_idP-1] != NULL) { - ue->srb[rb_idP-1]->delete(ue->srb[rb_idP-1]); + ue->srb[rb_idP-1]->delete_entity(ue->srb[rb_idP-1]); ue->srb[rb_idP-1] = NULL; } else LOG_W(RLC, "removing non allocated SRB %ld, do nothing\n", rb_idP); } else { if (ue->drb[rb_idP-1] != NULL) { - ue->drb[rb_idP-1]->delete(ue->drb[rb_idP-1]); + ue->drb[rb_idP-1]->delete_entity(ue->drb[rb_idP-1]); ue->drb[rb_idP-1] = NULL; } else LOG_W(RLC, "removing non allocated DRB %ld, do nothing\n", rb_idP); diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.c b/openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.c index c8113baf35..1770446c9e 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.c +++ b/openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.c @@ -128,16 +128,16 @@ void nr_rlc_manager_remove_ue(nr_rlc_ue_manager_t *_m, int rnti) if (ue->srb0 != NULL) { /* deliver_sdu_data for srb0 is allocated, needs a free() */ free(ue->srb0->deliver_sdu_data); - ue->srb0->delete(ue->srb0); + ue->srb0->delete_entity(ue->srb0); } for (j = 0; j < 3; j++) if (ue->srb[j] != NULL) - ue->srb[j]->delete(ue->srb[j]); + ue->srb[j]->delete_entity(ue->srb[j]); for (j = 0; j < MAX_DRBS_PER_UE; j++) if (ue->drb[j] != NULL) - ue->drb[j]->delete(ue->drb[j]); + ue->drb[j]->delete_entity(ue->drb[j]); free(ue); From 76aa112cec8355b779bd5e289ba1a86a72ea2039 Mon Sep 17 00:00:00 2001 From: Chieh-Chun Chen Date: Wed, 23 Aug 2023 16:49:17 +0200 Subject: [PATCH 3/9] Add more RLC stats and correct RLC stats - Add GOL-delay and AVG-soujour - Correct the types of/measurement the tx/rxsdu_bytes and tx/rxsdu_occ_bytes - Update txsdu_avg_time_to_tx value only when measurements activated Co-authored-by: Teodora Vladic --- openair2/LAYER2/nr_rlc/nr_rlc_entity.c | 35 ++++++++++++++++++++-- openair2/LAYER2/nr_rlc/nr_rlc_entity.h | 36 ++++++++++------------- openair2/LAYER2/nr_rlc/nr_rlc_entity_am.c | 24 +++++++++++---- openair2/LAYER2/nr_rlc/nr_rlc_entity_tm.c | 6 ++-- openair2/LAYER2/nr_rlc/nr_rlc_entity_um.c | 6 ++-- openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c | 6 ++++ openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h | 4 +++ 7 files changed, 82 insertions(+), 35 deletions(-) diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_entity.c b/openair2/LAYER2/nr_rlc/nr_rlc_entity.c index e9fa7e5a6e..f0934ca599 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_entity.c +++ b/openair2/LAYER2/nr_rlc/nr_rlc_entity.c @@ -36,10 +36,36 @@ static void nr_rlc_entity_get_stats( nr_rlc_statistics_t *out) { // printf("Stats from the RLC entity asked\n"); + uint64_t time_now = time_average_now(); *out = entity->stats; + + // Get the correct HOL RLC-SDU + nr_rlc_sdu_segment_t* sdu; + if (entity->stats.mode == NR_RLC_AM) { + nr_rlc_entity_am_t* am_entity = (nr_rlc_entity_am_t *) entity; + if (am_entity->retransmit_list != NULL) { + sdu = am_entity->retransmit_list; + } else { + sdu = am_entity->tx_list; + } + } else if (entity->stats.mode == NR_RLC_UM) { + nr_rlc_entity_um_t* um_entity = (nr_rlc_entity_um_t *) entity; + sdu = um_entity->tx_list; + } else { + nr_rlc_entity_tm_t* tm_entity = (nr_rlc_entity_tm_t *) entity; + sdu = tm_entity->tx_list; + } + + // Compute HOL waittime, make sure that segmented packets don't have 'zero' time-of-arrival + if (sdu != NULL) { + out->txsdu_wt_us = time_now - sdu->sdu->time_of_arrival; + } else { + // No HOL packets --> wait-time == 0 + out->txsdu_wt_us = 0; + } + if (entity->avg_time_is_on) - out->txsdu_avg_time_to_tx = time_average_get_average(entity->txsdu_avg_time_to_tx, - time_average_now()); + out->txsdu_avg_time_to_tx = time_average_get_average(entity->txsdu_avg_time_to_tx, time_now); else out->txsdu_avg_time_to_tx = 0; } @@ -112,8 +138,11 @@ nr_rlc_entity_t *new_nr_rlc_entity_am( ret->common.stats.mode = NR_RLC_AM; + ret->common.stats.rxsdu_bytes = 0; // init default arrivals (SDU) counter + ret->common.stats.txsdu_bytes = 0; // init default transmits (SDU) counter + /* let's take average over the last 100 milliseconds - * initial_size of 1024 is arbitrary + * initial_size of 1024 (packets) is arbitrary */ ret->common.txsdu_avg_time_to_tx = time_average_new(100 * 1000, 1024); diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_entity.h b/openair2/LAYER2/nr_rlc/nr_rlc_entity.h index ec911e9217..7996d8c89e 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_entity.h +++ b/openair2/LAYER2/nr_rlc/nr_rlc_entity.h @@ -41,8 +41,7 @@ typedef struct { /* TX */ uint32_t txpdu_pkts; /* aggregated number of transmitted RLC PDUs */ uint32_t txpdu_bytes; /* aggregated amount of transmitted bytes in RLC PDUs */ - /* TODO? */ - uint32_t txpdu_wt_ms; /* aggregated head-of-line tx packet waiting time to be transmitted (i.e. send to the MAC layer) */ + uint32_t txpdu_wt_ms; /* TODO: aggregated head-of-line tx packet waiting time to be transmitted (i.e. send to the MAC layer) */ uint32_t txpdu_dd_pkts; /* aggregated number of dropped or discarded tx packets by RLC */ uint32_t txpdu_dd_bytes; /* aggregated amount of bytes dropped or discarded tx packets by RLC */ uint32_t txpdu_retx_pkts; /* aggregated number of tx pdus/pkts to be re-transmitted (only applicable to RLC AM) */ @@ -50,10 +49,8 @@ typedef struct { uint32_t txpdu_segmented; /* aggregated number of segmentations */ uint32_t txpdu_status_pkts; /* aggregated number of tx status pdus/pkts (only applicable to RLC AM) */ uint32_t txpdu_status_bytes; /* aggregated amount of tx status bytes (only applicable to RLC AM) */ - /* TODO? */ - uint32_t txbuf_occ_bytes; /* current tx buffer occupancy in terms of amount of bytes (average: NOT IMPLEMENTED) */ - /* TODO? */ - uint32_t txbuf_occ_pkts; /* current tx buffer occupancy in terms of number of packets (average: NOT IMPLEMENTED) */ + uint32_t txbuf_occ_bytes; /* (IMPLEMENTED) transmitting bytes currently in buffer */ + uint32_t txbuf_occ_pkts; /* TODO: current tx buffer occupancy in terms of number of packets (average: NOT IMPLEMENTED) */ /* txbuf_wd_ms: the time window for which the txbuf occupancy value is obtained - NOT IMPLEMENTED */ /* RX */ @@ -70,31 +67,28 @@ typedef struct { /* rxpdu_rotout_ms: flag indicating rx reordering timeout in ms - NOT IMPLEMENTED */ /* rxpdu_potout_ms: flag indicating the poll retransmit time out in ms - NOT IMPLEMENTED */ /* rxpdu_sptout_ms: flag indicating status prohibit timeout in ms - NOT IMPLEMENTED */ - /* TODO? */ - uint32_t rxbuf_occ_bytes; /* current rx buffer occupancy in terms of amount of bytes (average: NOT IMPLEMENTED) */ - /* TODO? */ - uint32_t rxbuf_occ_pkts; /* current rx buffer occupancy in terms of number of packets (average: NOT IMPLEMENTED) */ + uint32_t rxbuf_occ_bytes; /* (IMPLEMENTED) received bytes currently in buffer */ + uint32_t rxbuf_occ_pkts; /* TODO: current rx buffer occupancy in terms of number of packets (average: NOT IMPLEMENTED) */ /* SDU stats */ /* TX */ uint32_t txsdu_pkts; /* number of SDUs delivered */ - uint32_t txsdu_bytes; /* number of bytes of SDUs delivered */ + uint64_t txsdu_bytes; /* (UPDATED) number of SDUs bytes successfully transmitted so far (counter) */ + /* Average time for an SDU to be passed to MAC: + * Actually measures the time it takes for THE FULL SDU to be passed to MAC FOR THE FIRST TIME. + * Since the MAC schedules in advance, it does not measure the time of + * transmission over the air, just the time to reach the MAC layer. + * => to control it, 'txsdu_wt_us' that measures the current HOL-delay is added. + */ + double txsdu_avg_time_to_tx; /* (100ms-windowed) per-packet sojourn (SDU to PDU) in microseconds */ + uint32_t txsdu_wt_us; /* HOL delay of the current radio bearer, in microseconds */ /* RX */ uint32_t rxsdu_pkts; /* number of SDUs received */ - uint32_t rxsdu_bytes; /* number of bytes of SDUs received */ + uint64_t rxsdu_bytes; /* (UPDATED) number of SDUs bytes arrived so far (counter) */ uint32_t rxsdu_dd_pkts; /* number of dropped or discarded SDUs */ uint32_t rxsdu_dd_bytes; /* number of bytes of SDUs dropped or discarded */ - /* Average time for an SDU to be passed to MAC. - * Actually measures the time it takes for any part of an SDU to be - * passed to MAC for the first time, that is: the first TX of (part of) the - * SDU. - * Since the MAC schedules in advance, it does not measure the time of - * transmission over the air, just the time to reach the MAC layer. - */ - double txsdu_avg_time_to_tx; - } nr_rlc_statistics_t; typedef struct { diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.c b/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.c index 033b436255..6840e2810a 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.c +++ b/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.c @@ -224,7 +224,8 @@ static void reassemble_and_deliver(nr_rlc_entity_am_t *entity, int sn) sdu, so); entity->common.stats.txsdu_pkts++; - entity->common.stats.txsdu_bytes += so; + /* AM 'txsdu_bytes' now only count successfully transmitted bytes */ + // entity->common.stats.txsdu_bytes += so; } static void reception_actions(nr_rlc_entity_am_t *entity, nr_rlc_pdu_t *pdu) @@ -484,6 +485,8 @@ process_wait_list_head: end_wait_list = prev_wait_list; if (nr_rlc_free_sdu_segment(cur_wait_list)) { entity->tx_size -= sdu_size; + // Wait-ACK: count as successfully transmitted bytes + entity->common.stats.txsdu_bytes += sdu_size; entity->common.sdu_successful_delivery( entity->common.sdu_successful_delivery_data, (nr_rlc_entity_t *)entity, upper_layer_id); @@ -546,6 +549,8 @@ process_retransmit_list_head: + cur->size; if (nr_rlc_free_sdu_segment(cur)) { entity->tx_size -= sdu_size; + // Retransmit-ACK: count as successfully transmitted bytes + entity->common.stats.txsdu_bytes += sdu_size; entity->common.sdu_successful_delivery( entity->common.sdu_successful_delivery_data, (nr_rlc_entity_t *)entity, upper_layer_id); @@ -604,6 +609,8 @@ nacks_done: end_wait_list = prev_wait_list; if (nr_rlc_free_sdu_segment(cur_wait_list)) { entity->tx_size -= sdu_size; + // Wait-NACK done: count as successfully transmitted bytes + entity->common.stats.txsdu_bytes += sdu_size; entity->common.sdu_successful_delivery( entity->common.sdu_successful_delivery_data, (nr_rlc_entity_t *)entity, upper_layer_id); @@ -625,6 +632,8 @@ nacks_done: + cur->size; if (nr_rlc_free_sdu_segment(cur)) { entity->tx_size -= sdu_size; + // Retransmit-NACK done: count as successfully transmitted bytes + entity->common.stats.txsdu_bytes += sdu_size; entity->common.sdu_successful_delivery( entity->common.sdu_successful_delivery_data, (nr_rlc_entity_t *)entity, upper_layer_id); @@ -1619,11 +1628,11 @@ static int generate_tx_pdu(nr_rlc_entity_am_t *entity, char *buffer, int size) entity->common.stats.txpdu_pkts++; entity->common.stats.txpdu_bytes += ret_size; - if (sdu->sdu->time_of_arrival) { + /* No need to 'zero' time-of-arrival; + Segmented packets do need to be duplicated in time-sensitive use cases */ + if (entity->common.avg_time_is_on) { uint64_t time_now = time_average_now(); uint64_t waited_time = time_now - sdu->sdu->time_of_arrival; - /* set time_of_arrival to 0 so as to update stats only once */ - sdu->sdu->time_of_arrival = 0; time_average_add(entity->common.txsdu_avg_time_to_tx, time_now, waited_time); } @@ -1681,7 +1690,6 @@ void nr_rlc_entity_am_recv_sdu(nr_rlc_entity_t *_entity, nr_rlc_sdu_segment_t *sdu; entity->common.stats.rxsdu_pkts++; - entity->common.stats.rxsdu_bytes += size; if (size > NR_SDU_MAX) { LOG_E(RLC, "%s:%d:%s: fatal: SDU size too big (%d bytes)\n", @@ -1705,6 +1713,8 @@ void nr_rlc_entity_am_recv_sdu(nr_rlc_entity_t *_entity, } entity->tx_size += size; + // SDU received: Count as arrival bytes + entity->common.stats.rxsdu_bytes += size; sdu = nr_rlc_new_sdu(buffer, size, sdu_id); @@ -1899,6 +1909,10 @@ void nr_rlc_entity_am_discard_sdu(nr_rlc_entity_t *_entity, int sdu_id) + cur->size; entity->tx_size -= cur->sdu->size; + + /* Uncomment to assert if SDU are ever discarded */ + // assert(0 != 0 && "[RLC-TRAP] SDU discard should never be reached!"); + nr_rlc_free_sdu_segment(cur); } diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_entity_tm.c b/openair2/LAYER2/nr_rlc/nr_rlc_entity_tm.c index cfadf43d99..afad2d23a2 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_entity_tm.c +++ b/openair2/LAYER2/nr_rlc/nr_rlc_entity_tm.c @@ -79,11 +79,11 @@ static int generate_tx_pdu(nr_rlc_entity_tm_t *entity, char *buffer, int size) entity->common.stats.txpdu_pkts++; entity->common.stats.txpdu_bytes += size; - if (sdu->sdu->time_of_arrival) { + /* No need to 'zero' time-of-arrival; + Segmented packets do need to be duplicated in time-sensitive use cases */ + if (entity->common.avg_time_is_on) { uint64_t time_now = time_average_now(); uint64_t waited_time = time_now - sdu->sdu->time_of_arrival; - /* set time_of_arrival to 0 so as to update stats only once */ - sdu->sdu->time_of_arrival = 0; time_average_add(entity->common.txsdu_avg_time_to_tx, time_now, waited_time); } diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_entity_um.c b/openair2/LAYER2/nr_rlc/nr_rlc_entity_um.c index 43a2189c51..4a9a67e40c 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_entity_um.c +++ b/openair2/LAYER2/nr_rlc/nr_rlc_entity_um.c @@ -521,11 +521,11 @@ static int generate_tx_pdu(nr_rlc_entity_um_t *entity, char *buffer, int size) entity->common.stats.txpdu_pkts++; entity->common.stats.txpdu_bytes += size; - if (sdu->sdu->time_of_arrival) { + /* No need to 'zero' time-of-arrival; + Segmented packets do need to be duplicated in time-sensitive use cases */ + if (entity->common.avg_time_is_on) { uint64_t time_now = time_average_now(); uint64_t waited_time = time_now - sdu->sdu->time_of_arrival; - /* set time_of_arrival to 0 so as to update stats only once */ - sdu->sdu->time_of_arrival = 0; time_average_add(entity->common.txsdu_avg_time_to_tx, time_now, waited_time); } diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c index 02c1c72fb7..10e2ebc3e7 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c +++ b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c @@ -1070,6 +1070,12 @@ const bool nr_rlc_get_statistics( if (rb != NULL) { rb->get_stats(rb, out); ret = true; + + // Patch buffer status using OAI results (no need to change anything in the RB) + // rb->set_time(rb, nr_rlc_current_time); + nr_rlc_entity_buffer_status_t oai_stat = rb->buffer_status(rb, 1000*1000); + out->rxbuf_occ_bytes = oai_stat.status_size; + out->txbuf_occ_bytes = oai_stat.tx_size + oai_stat.retx_size; } else { ret = false; } diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h index 15fe286435..feeb371244 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h +++ b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h @@ -35,6 +35,8 @@ #include "NR_RadioBearerConfig.h" #include "NR_CellGroupConfig.h" #include "openair2/RRC/NR/nr_rrc_proto.h" +#include "nr_rlc_ue_manager.h" + struct NR_RLC_Config; struct NR_LogicalChannelConfig; @@ -67,3 +69,5 @@ void nr_rlc_activate_srb0(int rnti, struct gNB_MAC_INST_s *mac, void *rawUE, const uint8_t *sdu, sdu_size_t sdu_len, void *rawUE)); + +const bool nr_rlc_get_statistics(int rnti, int srb_flag, int rb_id, nr_rlc_statistics_t *out); From e8944c95aae831b3823730bb0ad5640d2d34ea30 Mon Sep 17 00:00:00 2001 From: Chieh-Chun Chen Date: Wed, 23 Aug 2023 16:49:05 +0200 Subject: [PATCH 4/9] Add Custom FlexRIC E2 Service Models Co-authored-by: Teodora Vladic --- .../RAN_FUNCTION/CUSTOMIZED/CMakeLists.txt | 1 + .../RAN_FUNCTION/CUSTOMIZED/ran_func_gtp.c | 49 +++++- .../RAN_FUNCTION/CUSTOMIZED/ran_func_gtp.h | 6 +- .../RAN_FUNCTION/CUSTOMIZED/ran_func_mac.c | 85 ++++++++++- .../RAN_FUNCTION/CUSTOMIZED/ran_func_mac.h | 6 +- .../RAN_FUNCTION/CUSTOMIZED/ran_func_pdcp.c | 140 +++++++++++++++++- .../RAN_FUNCTION/CUSTOMIZED/ran_func_pdcp.h | 8 +- .../RAN_FUNCTION/CUSTOMIZED/ran_func_rlc.c | 129 +++++++++++++++- .../RAN_FUNCTION/CUSTOMIZED/ran_func_rlc.h | 7 +- .../RAN_FUNCTION/CUSTOMIZED/ran_func_slice.h | 2 +- .../RAN_FUNCTION/CUSTOMIZED/ran_func_tc.h | 2 +- 11 files changed, 420 insertions(+), 15 deletions(-) diff --git a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/CMakeLists.txt b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/CMakeLists.txt index b165e1589b..bc5bfcb695 100644 --- a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/CMakeLists.txt +++ b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/CMakeLists.txt @@ -15,3 +15,4 @@ add_library(e2_ran_func_cust STATIC ../../flexric/src/util/time_now_us.c ) +target_link_libraries(e2_ran_func_cust PUBLIC asn1_nr_rrc nr_rrc asn1_nr_rrc_hdrs) diff --git a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_gtp.c b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_gtp.c index bd8b8b5881..71200a1eb5 100644 --- a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_gtp.c +++ b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_gtp.c @@ -1,13 +1,57 @@ #include "ran_func_gtp.h" -#include "../../flexric/test/rnd/fill_rnd_data_gtp.h" +#include "openair2/E2AP/flexric/test/rnd/fill_rnd_data_gtp.h" #include +static +const int mod_id = 0; + void read_gtp_sm(void * data) { assert(data != NULL); gtp_ind_data_t* gtp = (gtp_ind_data_t*)(data); - fill_gtp_ind_data(gtp); + //fill_gtp_ind_data(gtp); + + gtp->msg.tstamp = time_now_us(); + + NR_UEs_t *UE_info = &RC.nrmac[mod_id]->UE_info; + size_t num_ues = 0; + UE_iterator(UE_info->list, ue) { + if (ue) + num_ues += 1; + } + + gtp->msg.len = num_ues; + if(gtp->msg.len > 0){ + gtp->msg.ngut = calloc(gtp->msg.len, sizeof(gtp_ngu_t_stats_t) ); + assert(gtp->msg.ngut != NULL); + } + + size_t i = 0; + UE_iterator(UE_info->list, UE) + { + uint16_t const rnti = UE->rnti; + struct rrc_gNB_ue_context_s *ue_context_p = rrc_gNB_get_ue_context_by_rnti(RC.nrrrc[mod_id], rnti); + if (ue_context_p != NULL) { + gtp->msg.ngut[i].rnti = ue_context_p->ue_context.rnti; + int nb_pdu_session = ue_context_p->ue_context.nb_of_pdusessions; + if (nb_pdu_session > 0) { + int nb_pdu_idx = nb_pdu_session - 1; + gtp->msg.ngut[i].teidgnb = ue_context_p->ue_context.pduSession[nb_pdu_idx].param.gNB_teid_N3; + gtp->msg.ngut[i].teidupf = ue_context_p->ue_context.pduSession[nb_pdu_idx].param.UPF_teid_N3; + // TODO: one PDU session has multiple QoS Flow + int nb_qos_flow = ue_context_p->ue_context.pduSession[nb_pdu_idx].param.nb_qos; + if (nb_qos_flow > 0) { + gtp->msg.ngut[i].qfi = ue_context_p->ue_context.pduSession[nb_pdu_idx].param.qos[nb_qos_flow - 1].qfi; + } + } + } else { + LOG_W(NR_RRC,"rrc_gNB_get_ue_context return NULL\n"); + // if (gtp->msg.ngut != NULL) free(gtp->msg.ngut); + } + i++; + } + } void read_gtp_setup_sm(void* data) @@ -21,4 +65,3 @@ sm_ag_if_ans_t write_ctrl_gtp_sm(void const* src) assert(src != NULL); assert(0 !=0 && "Not supported"); } - diff --git a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_gtp.h b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_gtp.h index 6e88200401..5d28003c6f 100644 --- a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_gtp.h +++ b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_gtp.h @@ -1,7 +1,11 @@ #ifndef RAN_FUNC_SM_GTP_READ_WRITE_AGENT_H #define RAN_FUNC_SM_GTP_READ_WRITE_AGENT_H -#include "../../flexric/src/agent/e2_agent_api.h" +#include "openair2/E2AP/flexric/src/agent/../sm/sm_io.h" +#include "common/ran_context.h" +#include "openair2/LAYER2/NR_MAC_gNB/mac_proto.h" +#include "openair2/E2AP/flexric/src/util/time_now_us.h" +#include "openair2/RRC/NR/rrc_gNB_UE_context.h" void read_gtp_sm(void*); diff --git a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_mac.c b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_mac.c index 4b150e71ed..0f690f64c8 100644 --- a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_mac.c +++ b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_mac.c @@ -1,13 +1,94 @@ #include "ran_func_mac.h" -#include "../../flexric/test/rnd/fill_rnd_data_mac.h" +#include "openair2/E2AP/flexric/test/rnd/fill_rnd_data_mac.h" #include +static +const int mod_id = 0; + + void read_mac_sm(void* data) { assert(data != NULL); mac_ind_data_t* mac = (mac_ind_data_t*)data; - fill_mac_ind_data(mac); + //fill_mac_ind_data(mac); + + mac->msg.tstamp = time_now_us(); + + NR_UEs_t *UE_info = &RC.nrmac[mod_id]->UE_info; + size_t num_ues = 0; + UE_iterator(UE_info->list, ue) { + if (ue) + num_ues += 1; + } + + mac->msg.len_ue_stats = num_ues; + if(mac->msg.len_ue_stats > 0){ + mac->msg.ue_stats = calloc(mac->msg.len_ue_stats, sizeof(mac_ue_stats_impl_t)); + assert(mac->msg.ue_stats != NULL && "Memory exhausted" ); + } + + size_t i = 0; //TODO + UE_iterator(UE_info->list, UE) { + const NR_UE_sched_ctrl_t* sched_ctrl = &UE->UE_sched_ctrl; + mac_ue_stats_impl_t* rd = &mac->msg.ue_stats[i]; + + rd->frame = RC.nrmac[mod_id]->frame; + rd->slot = RC.nrmac[mod_id]->slot; + + rd->dl_aggr_tbs = UE->mac_stats.dl.total_bytes; + rd->ul_aggr_tbs = UE->mac_stats.ul.total_bytes; + + if (is_xlsch_in_slot(RC.nrmac[mod_id]->dlsch_slot_bitmap[rd->slot / 64], rd->slot)) { + rd->dl_curr_tbs = UE->mac_stats.dl.current_bytes; + rd->dl_sched_rb = UE->mac_stats.dl.current_rbs; + } + if (is_xlsch_in_slot(RC.nrmac[mod_id]->ulsch_slot_bitmap[rd->slot / 64], rd->slot)) { + rd->ul_curr_tbs = UE->mac_stats.ul.current_bytes; + rd->ul_sched_rb = sched_ctrl->sched_pusch.rbSize; + } + + rd->rnti = UE->rnti; + rd->dl_aggr_prb = UE->mac_stats.dl.total_rbs; + rd->ul_aggr_prb = UE->mac_stats.ul.total_rbs; + rd->dl_aggr_retx_prb = UE->mac_stats.dl.total_rbs_retx; + rd->ul_aggr_retx_prb = UE->mac_stats.ul.total_rbs_retx; + + rd->dl_aggr_bytes_sdus = UE->mac_stats.dl.lc_bytes[3]; + rd->ul_aggr_bytes_sdus = UE->mac_stats.ul.lc_bytes[3]; + + rd->dl_aggr_sdus = UE->mac_stats.dl.num_mac_sdu; + rd->ul_aggr_sdus = UE->mac_stats.ul.num_mac_sdu; + + rd->pusch_snr = (float) sched_ctrl->pusch_snrx10 / 10; //: float = -64; + rd->pucch_snr = (float) sched_ctrl->pucch_snrx10 / 10; //: float = -64; + + rd->wb_cqi = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.wb_cqi_1tb; + rd->dl_mcs1 = sched_ctrl->dl_bler_stats.mcs; + rd->dl_bler = sched_ctrl->dl_bler_stats.bler; + rd->ul_mcs1 = sched_ctrl->ul_bler_stats.mcs; + rd->ul_bler = sched_ctrl->ul_bler_stats.bler; + rd->dl_mcs2 = 0; + rd->ul_mcs2 = 0; + rd->phr = sched_ctrl->ph; + + const uint32_t bufferSize = sched_ctrl->estimated_ul_buffer - sched_ctrl->sched_ul_bytes; + rd->bsr = bufferSize; + + const size_t numDLHarq = 4; + rd->dl_num_harq = numDLHarq; + for (uint8_t j = 0; j < numDLHarq; ++j) + rd->dl_harq[j] = UE->mac_stats.dl.rounds[j]; + rd->dl_harq[numDLHarq] = UE->mac_stats.dl.errors; + + const size_t numUlHarq = 4; + rd->ul_num_harq = numUlHarq; + for (uint8_t j = 0; j < numUlHarq; ++j) + rd->ul_harq[j] = UE->mac_stats.ul.rounds[j]; + rd->ul_harq[numUlHarq] = UE->mac_stats.ul.errors; + + ++i; + } } void read_mac_setup_sm(void* data) diff --git a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_mac.h b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_mac.h index 26ae184b37..f81c9bc4ef 100644 --- a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_mac.h +++ b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_mac.h @@ -1,7 +1,11 @@ #ifndef SM_MAC_READ_WRITE_AGENT_H #define SM_MAC_READ_WRITE_AGENT_H -#include "../../flexric/src/agent/e2_agent_api.h" +#include "openair2/E2AP/flexric/src/agent/../sm/sm_io.h" +#include "common/ran_context.h" +#include "openair2/LAYER2/NR_MAC_gNB/mac_proto.h" +#include "openair2/E2AP/flexric/src/util/time_now_us.h" + void read_mac_sm(void*); diff --git a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_pdcp.c b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_pdcp.c index 14bb2400f7..e65bd3ac0c 100644 --- a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_pdcp.c +++ b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_pdcp.c @@ -1,14 +1,150 @@ #include "ran_func_pdcp.h" -#include "../../flexric/test/rnd/fill_rnd_data_pdcp.h" +#include "openair2/E2AP/flexric/test/rnd/fill_rnd_data_pdcp.h" #include +static +const int mod_id = 0; + +static + uint32_t num_act_rb(NR_UEs_t* UE_info) +{ + assert(UE_info!= NULL); + + uint32_t act_rb = 0; + UE_iterator(UE_info->list, UE) { + uint16_t const rnti = UE->rnti; + for(int rb_id = 1; rb_id < 6; ++rb_id ){ + nr_rlc_statistics_t rlc = {0}; + const int srb_flag = 0; + const bool rc = nr_rlc_get_statistics(rnti, srb_flag, rb_id, &rlc); + if(rc) ++act_rb; + } + } + return act_rb; +} + void read_pdcp_sm(void* data) { assert(data != NULL); //assert(data->type == PDCP_STATS_V0); pdcp_ind_data_t* pdcp = (pdcp_ind_data_t*)data; - fill_pdcp_ind_data(pdcp); + //fill_pdcp_ind_data(pdcp); + // TODO: Need to improve, not good + if (NODE_IS_CU(RC.nrrrc[mod_id]->node_type)) { + + uint32_t act_rb = 0; + struct rrc_gNB_ue_context_s *ue_context_p1 = NULL; + RB_FOREACH(ue_context_p1, rrc_nr_ue_tree_s, &RC.nrrrc[mod_id]->rrc_ue_head) { + uint16_t const rnti = ue_context_p1->ue_context.rnti; + for(int rb_id = 1; rb_id < 6; ++rb_id ){ + nr_pdcp_statistics_t rb_pdcp = {0}; + const int srb_flag = 0; + const bool rc = nr_pdcp_get_statistics(rnti, srb_flag, rb_id, &rb_pdcp); + if(rc) ++act_rb; + } + } + + pdcp->msg.tstamp = time_now_us(); + + pdcp->msg.len = act_rb; + if (pdcp->msg.len > 0) { + pdcp->msg.rb = calloc(pdcp->msg.len, sizeof(pdcp_radio_bearer_stats_t)); + assert(pdcp->msg.rb != NULL && "Memory exhausted!"); + } + + size_t i = 0; + struct rrc_gNB_ue_context_s *ue_context_p2 = NULL; + RB_FOREACH(ue_context_p2, rrc_nr_ue_tree_s, &RC.nrrrc[mod_id]->rrc_ue_head) { + // TODO: Need to handel multiple UEs + uint16_t const rnti = ue_context_p2->ue_context.rnti; + + for (size_t rb_id = 1; rb_id < 6; ++rb_id) { + nr_pdcp_statistics_t rb_pdcp = {0}; + + const int srb_flag = 0; + const bool rc = nr_pdcp_get_statistics(rnti, srb_flag, rb_id, &rb_pdcp); + + if (!rc) continue; + + pdcp_radio_bearer_stats_t *rd = &pdcp->msg.rb[i]; + + rd->txpdu_pkts = rb_pdcp.txpdu_pkts; /* aggregated number of tx packets */ + rd->txpdu_bytes = rb_pdcp.txpdu_bytes; /* aggregated bytes of tx packets */ + rd->txpdu_sn = rb_pdcp.txpdu_sn; /* current sequence number of last tx packet (or TX_NEXT) */ + rd->rxpdu_pkts = rb_pdcp.rxpdu_pkts; /* aggregated number of rx packets */ + rd->rxpdu_bytes = rb_pdcp.rxpdu_bytes; /* aggregated bytes of rx packets */ + rd->rxpdu_sn = rb_pdcp.rxpdu_sn; /* current sequence number of last rx packet (or RX_NEXT) */ + rd->rxpdu_oo_pkts = rb_pdcp.rxpdu_oo_pkts; /* aggregated number of out-of-order rx pkts (or RX_REORD) */ + rd->rxpdu_oo_bytes = rb_pdcp.rxpdu_oo_bytes; /* aggregated amount of out-of-order rx bytes */ + rd->rxpdu_dd_pkts = rb_pdcp.rxpdu_dd_pkts; /* aggregated number of duplicated discarded packets (or dropped packets because of other reasons such as integrity failure) (or RX_DELIV) */ + rd->rxpdu_dd_bytes = rb_pdcp.rxpdu_dd_bytes; /* aggregated amount of discarded packets' bytes */ + rd->rxpdu_ro_count = rb_pdcp.rxpdu_ro_count; /* this state variable indicates the COUNT value following the COUNT value associated with the PDCP Data PDU which triggered t-Reordering. (RX_REORD) */ + rd->txsdu_pkts = rb_pdcp.txsdu_pkts; /* number of SDUs delivered */ + rd->txsdu_bytes = rb_pdcp.txsdu_bytes; /* number of bytes of SDUs delivered */ + rd->rxsdu_pkts = rb_pdcp.rxsdu_pkts; /* number of SDUs received */ + rd->rxsdu_bytes = rb_pdcp.rxsdu_bytes; /* number of bytes of SDUs received */ + rd->rnti = rnti; + rd->mode = rb_pdcp.mode; /* 0: PDCP AM, 1: PDCP UM, 2: PDCP TM */ + rd->rbid = rb_id; + + ++i; + } + } + } else { + + //assert(0!=0 && "Calling PDCP"); + // for the moment and while we don't have a split base station, we use the + // MAC structures to obtain the RNTIs which we use to query the PDCP + NR_UEs_t *UE_info = &RC.nrmac[mod_id]->UE_info; + uint32_t const act_rb = num_act_rb(UE_info); + + pdcp->msg.tstamp = time_now_us(); + + pdcp->msg.len = act_rb; + if (pdcp->msg.len > 0) { + pdcp->msg.rb = calloc(pdcp->msg.len, sizeof(pdcp_radio_bearer_stats_t)); + assert(pdcp->msg.rb != NULL && "Memory exhausted!"); + } + + size_t i = 0; + UE_iterator(UE_info->list, UE) + { + + const int rnti = UE->rnti; + for (size_t rb_id = 1; rb_id < 6; ++rb_id) { + nr_pdcp_statistics_t rb_pdcp = {0}; + + const int srb_flag = 0; + const bool rc = nr_pdcp_get_statistics(rnti, srb_flag, rb_id, &rb_pdcp); + + if (!rc) continue; + + pdcp_radio_bearer_stats_t *rd = &pdcp->msg.rb[i]; + + rd->txpdu_pkts = rb_pdcp.txpdu_pkts; /* aggregated number of tx packets */ + rd->txpdu_bytes = rb_pdcp.txpdu_bytes; /* aggregated bytes of tx packets */ + rd->txpdu_sn = rb_pdcp.txpdu_sn; /* current sequence number of last tx packet (or TX_NEXT) */ + rd->rxpdu_pkts = rb_pdcp.rxpdu_pkts; /* aggregated number of rx packets */ + rd->rxpdu_bytes = rb_pdcp.rxpdu_bytes; /* aggregated bytes of rx packets */ + rd->rxpdu_sn = rb_pdcp.rxpdu_sn; /* current sequence number of last rx packet (or RX_NEXT) */ + rd->rxpdu_oo_pkts = rb_pdcp.rxpdu_oo_pkts; /* aggregated number of out-of-order rx pkts (or RX_REORD) */ + rd->rxpdu_oo_bytes = rb_pdcp.rxpdu_oo_bytes; /* aggregated amount of out-of-order rx bytes */ + rd->rxpdu_dd_pkts = rb_pdcp.rxpdu_dd_pkts; /* aggregated number of duplicated discarded packets (or dropped packets because of other reasons such as integrity failure) (or RX_DELIV) */ + rd->rxpdu_dd_bytes = rb_pdcp.rxpdu_dd_bytes; /* aggregated amount of discarded packets' bytes */ + rd->rxpdu_ro_count = rb_pdcp.rxpdu_ro_count; /* this state variable indicates the COUNT value following the COUNT value associated with the PDCP Data PDU which triggered t-Reordering. (RX_REORD) */ + rd->txsdu_pkts = rb_pdcp.txsdu_pkts; /* number of SDUs delivered */ + rd->txsdu_bytes = rb_pdcp.txsdu_bytes; /* number of bytes of SDUs delivered */ + rd->rxsdu_pkts = rb_pdcp.rxsdu_pkts; /* number of SDUs received */ + rd->rxsdu_bytes = rb_pdcp.rxsdu_bytes; /* number of bytes of SDUs received */ + rd->rnti = rnti; + rd->mode = rb_pdcp.mode; /* 0: PDCP AM, 1: PDCP UM, 2: PDCP TM */ + rd->rbid = rb_id; + + ++i; + } + } + } } void read_pdcp_setup_sm(void* data) diff --git a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_pdcp.h b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_pdcp.h index 12dcaaf16b..ef16f3c123 100644 --- a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_pdcp.h +++ b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_pdcp.h @@ -1,7 +1,13 @@ #ifndef RAN_FUNC_SM_PDCP_READ_WRITE_AGENT_H #define RAN_FUNC_SM_PDCP_READ_WRITE_AGENT_H -#include "../../flexric/src/agent/e2_agent_api.h" +#include "openair2/E2AP/flexric/src/agent/../sm/sm_io.h" +#include "common/ran_context.h" +#include "openair2/RRC/NR/rrc_gNB_UE_context.h" +#include "openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h" +#include "openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h" +#include "openair2/LAYER2/NR_MAC_gNB/mac_proto.h" +#include "openair2/E2AP/flexric/src/util/time_now_us.h" void read_pdcp_sm(void*); diff --git a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_rlc.c b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_rlc.c index 9a2a661175..82c1a0b869 100644 --- a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_rlc.c +++ b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_rlc.c @@ -1,5 +1,43 @@ +#include #include "ran_func_rlc.h" -#include "../../flexric/test/rnd/fill_rnd_data_rlc.h" +#include "openair2/E2AP/flexric/test/rnd/fill_rnd_data_rlc.h" + +static +const int mod_id = 0; + +static +uint32_t num_act_rb(NR_UEs_t* const UE_info) +{ + assert(UE_info!= NULL); + + uint32_t act_rb = 0; + UE_iterator(UE_info->list, UE) { + uint16_t const rnti = UE->rnti; + for(int rb_id = 1; rb_id < 6; ++rb_id ){ + nr_rlc_statistics_t rlc = {0}; + const int srb_flag = 0; + const bool rc = nr_rlc_get_statistics(rnti, srb_flag, rb_id, &rlc); + if(rc) ++act_rb; + } + } + return act_rb; +} + +static +void active_avg_to_tx(NR_UEs_t* const UE_info) +{ + assert(UE_info!= NULL); + + UE_iterator(UE_info->list, UE) { + uint16_t const rnti = UE->rnti; + for(int rb_id = 1; rb_id < 6; ++rb_id ){ + nr_rlc_statistics_t rlc = {0}; + const int srb_flag = 0; + const bool rc = nr_rlc_get_statistics(rnti, srb_flag, rb_id, &rlc); + if(rc) nr_rlc_activate_avg_time_to_tx(rnti, rb_id+3, 1); // rb_id (DRB ID) mapping to logical channel + } + } +} void read_rlc_sm(void* data) { @@ -7,7 +45,94 @@ void read_rlc_sm(void* data) // assert(data->type == RLC_STATS_V0); rlc_ind_data_t* rlc = (rlc_ind_data_t*)data; - fill_rlc_ind_data(rlc); + //fill_rlc_ind_data(rlc); + // use MAC structures to get RNTIs + NR_UEs_t *UE_info = &RC.nrmac[mod_id]->UE_info; + uint32_t const act_rb = num_act_rb(UE_info); + + //assert(0!=0 && "Read RLC called"); + + // activate the rlc to calculate the average tx time + active_avg_to_tx(UE_info); + + rlc->msg.len = act_rb; + if(rlc->msg.len > 0){ + rlc->msg.rb = calloc(rlc->msg.len, sizeof(rlc_radio_bearer_stats_t)); + assert(rlc->msg.rb != NULL && "Memory exhausted"); + } + + rlc->msg.tstamp = time_now_us(); + + uint32_t i = 0; + UE_iterator(UE_info->list, UE) { + uint16_t const rnti = UE->rnti; + //for every LC ID + for(int rb_id = 1; rb_id < 6; ++rb_id ){ + + nr_rlc_statistics_t rb_rlc = {0}; + const int srb_flag = 0; + const bool rc = nr_rlc_get_statistics(rnti, srb_flag, rb_id, &rb_rlc); + if(!rc) continue; + rlc_radio_bearer_stats_t* sm_rb = &rlc->msg.rb[i]; + + /* TX */ + sm_rb->txpdu_pkts = rb_rlc.txpdu_pkts; + sm_rb->txpdu_bytes = rb_rlc.txpdu_bytes; /* aggregated amount of transmitted bytes in RLC PDUs */ + + /* TODO? */ + /* (TO BE DISCUSSED) HOL delay of the packet to be transmitted (RLC delay + MAC delay) */ + // sm_rb->txpdu_wt_ms += rb_rlc.txsdu_avg_time_to_tx; + + sm_rb->txpdu_dd_pkts = rb_rlc.txpdu_dd_pkts; /* aggregated number of dropped or discarded tx packets by RLC */ + sm_rb->txpdu_dd_bytes = rb_rlc.txpdu_dd_bytes; /* aggregated amount of bytes dropped or discarded tx packets by RLC */ + sm_rb->txpdu_retx_pkts = rb_rlc.txpdu_retx_pkts; /* aggregated number of tx pdus/pkts to be re-transmitted (only applicable to RLC AM) */ + sm_rb->txpdu_retx_bytes = rb_rlc.txpdu_retx_bytes; /* aggregated amount of bytes to be re-transmitted (only applicable to RLC AM) */ + sm_rb->txpdu_segmented = rb_rlc.txpdu_segmented; /* aggregated number of segmentations */ + sm_rb->txpdu_status_pkts = rb_rlc.txpdu_status_pkts; /* aggregated number of tx status pdus/pkts (only applicable to RLC AM) */ + sm_rb->txpdu_status_bytes = rb_rlc.txpdu_status_bytes; /* aggregated amount of tx status bytes (only applicable to RLC AM) */ + sm_rb->txbuf_occ_bytes = rb_rlc.txbuf_occ_bytes; /* (IMPLEMENTED) transmitting bytes currently in buffer */ + sm_rb->txbuf_occ_pkts = rb_rlc.txbuf_occ_pkts; /* current tx buffer occupancy in terms of number of packets (average: NOT IMPLEMENTED) */ + + /* RX */ + sm_rb->rxpdu_pkts = rb_rlc.rxpdu_pkts; /* aggregated number of received RLC PDUs */ + sm_rb->rxpdu_bytes = rb_rlc.rxpdu_bytes; /* amount of bytes received by the RLC */ + sm_rb->rxpdu_dup_pkts = rb_rlc.rxpdu_dup_pkts; /* aggregated number of duplicate packets */ + sm_rb->rxpdu_dup_bytes = rb_rlc.rxpdu_dup_bytes; /* aggregated amount of duplicated bytes */ + sm_rb->rxpdu_dd_pkts = rb_rlc.rxpdu_dd_pkts; /* aggregated number of rx packets dropped or discarded by RLC */ + sm_rb->rxpdu_dd_bytes = rb_rlc.rxpdu_dd_bytes; /* aggregated amount of rx bytes dropped or discarded by RLC */ + sm_rb->rxpdu_ow_pkts = rb_rlc.rxpdu_ow_pkts; /* aggregated number of out of window received RLC pdu */ + sm_rb->rxpdu_ow_bytes = rb_rlc.rxpdu_ow_bytes; /* aggregated number of out of window bytes received RLC pdu */ + sm_rb->rxpdu_status_pkts = rb_rlc.rxpdu_status_pkts; /* aggregated number of rx status pdus/pkts (only applicable to RLC AM) */ + sm_rb->rxpdu_status_bytes = rb_rlc.rxpdu_status_bytes; /* aggregated amount of rx status bytes (only applicable to RLC AM) */ + + sm_rb->rxbuf_occ_bytes = rb_rlc.rxbuf_occ_bytes; /* (IMPLEMENTED) received bytes currently in buffer */ + sm_rb->rxbuf_occ_pkts = rb_rlc.rxbuf_occ_pkts; /* current rx buffer occupancy in terms of number of packets (average: NOT IMPLEMENTED) */ + + /* TX */ + sm_rb->txsdu_pkts = rb_rlc.txsdu_pkts; /* number of SDUs delivered */ + sm_rb->txsdu_bytes = rb_rlc.txsdu_bytes; /* (UPDATED) number of SDUs bytes successfully transmitted so far (counter) */ + + sm_rb->txsdu_avg_time_to_tx = rb_rlc.txsdu_avg_time_to_tx; /* (100ms-windowed) per-packet sojourn (SDU to PDU) in microseconds */ + sm_rb->txsdu_wt_us = rb_rlc.txsdu_wt_us; /* HOL delay of the current radio bearer */ + + /* RX */ + sm_rb->rxsdu_pkts = rb_rlc.rxsdu_pkts; /* number of SDUs received */ + sm_rb->rxsdu_bytes = rb_rlc.rxsdu_bytes; /* (UPDATED) number of SDUs bytes arrived so far (counter) */ + sm_rb->rxsdu_dd_pkts = rb_rlc.rxsdu_dd_pkts; /* number of dropped or discarded SDUs */ + sm_rb->rxsdu_dd_bytes = rb_rlc.rxsdu_dd_bytes; /* number of bytes of SDUs dropped or discarded */ + + sm_rb->mode = rb_rlc.mode; /* 0: RLC AM, 1: RLC UM, 2: RLC TM */ + sm_rb->rnti = rnti; + sm_rb->rbid = rb_id; + + LOG_D(RLC, "[E2-AGENT] SDU Goodput & HOL-waittime/200ms-sojourn: %lu/%lu (bytes) & %u/%f (microsecs);\n\ + Current Buffer: received %u, transmitting %u (bytes).\n", + sm_rb->txsdu_bytes, sm_rb->rxsdu_bytes, sm_rb->txsdu_wt_us, sm_rb->txsdu_avg_time_to_tx, + sm_rb->rxbuf_occ_bytes, sm_rb->txbuf_occ_bytes); + + ++i; + } + } } void read_rlc_setup_sm(void* data) diff --git a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_rlc.h b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_rlc.h index c442cd7825..0241401793 100644 --- a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_rlc.h +++ b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_rlc.h @@ -1,7 +1,12 @@ #ifndef RAN_FUNC_SM_RLC_READ_WRITE_AGENT_H #define RAN_FUNC_SM_RLC_READ_WRITE_AGENT_H -#include "../../flexric/src/agent/e2_agent_api.h" +#include "openair2/E2AP/flexric/src/agent/../sm/sm_io.h" +#include "common/ran_context.h" +#include "openair2/LAYER2/NR_MAC_gNB/mac_proto.h" +#include "openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h" +#include "openair2/E2AP/flexric/src/util/time_now_us.h" + void read_rlc_sm(void*); diff --git a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_slice.h b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_slice.h index 0c26d63120..6cbe466bbc 100644 --- a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_slice.h +++ b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_slice.h @@ -1,7 +1,7 @@ #ifndef RAN_FUNC_SM_SLICE_READ_WRITE_AGENT_H #define RAN_FUNC_SM_SLICE_READ_WRITE_AGENT_H -#include "../../flexric/src/agent/e2_agent_api.h" +#include "openair2/E2AP/flexric/src/agent/../sm/sm_io.h" void read_slice_sm(void*); diff --git a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_tc.h b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_tc.h index 526d564b1b..ab5cc4ba6e 100644 --- a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_tc.h +++ b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_tc.h @@ -1,7 +1,7 @@ #ifndef RAN_FUNC_SM_TC_READ_WRITE_AGENT_H #define RAN_FUNC_SM_TC_READ_WRITE_AGENT_H -#include "../../flexric/src/agent/e2_agent_api.h" +#include "openair2/E2AP/flexric/src/agent/../sm/sm_io.h" void read_tc_sm(void*); From 011a5a3b56a870bbd4ce27a09c5aaffb9d98a483 Mon Sep 17 00:00:00 2001 From: Teodora Vladic Date: Mon, 31 Jul 2023 14:45:21 +0200 Subject: [PATCH 5/9] Add O-RAN KPM E2 Service Model - Add O-RAN KPM SM with DRB.RlcSduDelayDl, DRB.UEThpDl and DRB.UEThpUl measurements - Add L1M.PHR1.BinX KPM meas - Add comments for all KPM meas; deleted few outputs (but added in xApp) - Add DRB.PdcpSduVolumeDL and DRB.PdcpSduVolumeUL KPM meas - Delete unnecessary rnd functions in ran_func_kpm.c -> path set to flexric submodule --- .../E2AP/RAN_FUNCTION/O-RAN/CMakeLists.txt | 2 + .../E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c | 391 +++++++++++++++--- .../E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.h | 2 +- .../E2AP/RAN_FUNCTION/O-RAN/ran_func_rc.h | 2 +- 4 files changed, 339 insertions(+), 58 deletions(-) diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/CMakeLists.txt b/openair2/E2AP/RAN_FUNCTION/O-RAN/CMakeLists.txt index 49624dc302..ee5ccf01d0 100644 --- a/openair2/E2AP/RAN_FUNCTION/O-RAN/CMakeLists.txt +++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/CMakeLists.txt @@ -5,3 +5,5 @@ add_library(e2_ran_func_oran STATIC ../../flexric/test/rnd/fill_rnd_data_kpm.c ../../flexric/test/rnd/fill_rnd_data_rc.c ) + +target_link_libraries(e2_ran_func_oran PUBLIC asn1_nr_rrc nr_rrc asn1_nr_rrc_hdrs) diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c index daedef70e7..a3a6d75033 100644 --- a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c +++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c @@ -1,74 +1,279 @@ #include "ran_func_kpm.h" -#include "../../flexric/test/rnd/fill_rnd_data_kpm.h" -#include "../../flexric/src/util/time_now_us.h" +#include "openair2/E2AP/flexric/test/rnd/fill_rnd_data_kpm.h" +#include "openair2/E2AP/flexric/src/util/time_now_us.h" +#include "openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h" +#include "openair2/RRC/NR/rrc_gNB_UE_context.h" +#include "openair3/NGAP/ngap_gNB_ue_context.h" +#include "openair2/LAYER2/nr_pdcp/nr_pdcp_entity.h" +#include "openair2/LAYER2/nr_rlc/nr_rlc_entity.h" +#include "openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h" +#include "openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h" #include #include +static +size_t get_number_connected_ues(void) +{ + size_t num_ues = 0; + + for (int i = 0; i < RC.nb_nr_macrlc_inst; i++) { + UE_iterator(RC.nrmac[i]->UE_info.list, ue) + { + if (ue) num_ues += 1; + } + } + + return num_ues; +} + +static +NR_UE_info_t * connected_ues_list(void) +{ + size_t num_ues = get_number_connected_ues(); + + NR_UE_info_t * ue_list = calloc(num_ues, sizeof(NR_UE_info_t)); + assert(ue_list != NULL && "Memory exhausted"); + + size_t j = 0; + + for (int i = 0; i < RC.nb_nr_macrlc_inst; i++) { + UE_iterator(RC.nrmac[i]->UE_info.list, ue) + { + if (ue) + { + ue_list[j] = *ue; + j++; + } + } + } + + return ue_list; +} + +static +nr_rlc_statistics_t get_rlc_stats_per_drb(NR_UE_info_t * const UE, int const rb_id) +{ + assert(UE != NULL); + + nr_rlc_statistics_t rlc = {0}; + const int srb_flag = 0; + + // 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 +nr_pdcp_statistics_t get_pdcp_stats_per_drb(NR_UE_info_t * const UE, int const rb_id) +{ + assert(UE != NULL); + + nr_pdcp_statistics_t pdcp = {0}; + const int srb_flag = 0; + + // Get PDCP stats for specific DRB + const bool rc = nr_pdcp_get_statistics(UE->rnti, srb_flag, rb_id, &pdcp); + assert(rc == true && "Cannot get PDCP stats\n"); + + return pdcp; +} + static -gnb_e2sm_t fill_gnb_data(void) +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 = 112358132134; // % 2^40; + // fill with openair3/NGAP/ngap_gNB_ue_context.h:61 + gnb.amf_ue_ngap_id = ue_context_p->ue_context.gNB_ue_ngap_id; // Mandatory //GUAMI 6.2.3.17 - gnb.guami.plmn_id = (e2sm_plmn_t) {.mcc = 505, .mnc = 1, .mnc_digit_len = 2}; + 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 = (rand() % 2^8) + 0; - gnb.guami.amf_set_id = (rand() % 2^10) + 0; - gnb.guami.amf_ptr = (rand() % 2^6) + 0; + 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; return gnb; } + static -ue_id_e2sm_t fill_ue_id_data(void) +ue_id_e2sm_t fill_ue_id_data(rrc_gNB_ue_context_t * ue_context_p) { ue_id_e2sm_t ue_id_data = {0}; ue_id_data.type = GNB_UE_ID_E2SM; - ue_id_data.gnb = fill_gnb_data(); + ue_id_data.gnb = fill_gnb_data(ue_context_p); return ue_id_data; } + + +uint32_t last_dl_rlc_pdu_total_bytes[MAX_MOBILES_PER_GNB] = {0}; +uint32_t last_ul_rlc_pdu_total_bytes[MAX_MOBILES_PER_GNB] = {0}; +uint32_t last_dl_total_prbs[MAX_MOBILES_PER_GNB] = {0}; +uint32_t last_ul_total_prbs[MAX_MOBILES_PER_GNB] = {0}; +uint32_t last_dl_pdcp_sdu_total_bytes[MAX_MOBILES_PER_GNB] = {0}; +uint32_t last_ul_pdcp_sdu_total_bytes[MAX_MOBILES_PER_GNB] = {0}; + static -kpm_ind_msg_format_1_t fill_kpm_ind_msg_frm_1(void) +kpm_ind_msg_format_1_t fill_kpm_ind_msg_frm_1(NR_UE_info_t* const UE, size_t const ue_idx, kpm_act_def_format_1_t const * act_def_fr_1) { kpm_ind_msg_format_1_t msg_frm_1 = {0}; - - // Measurement Data - uint32_t num_drbs = 2; - msg_frm_1.meas_data_lst_len = num_drbs; // (rand() % 65535) + 1; + + // 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 */ + 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 = 1; // (rand() % 65535) + 1; - 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++){ - msg_frm_1.meas_data_lst[i].meas_record_lst[j].value = REAL_MEAS_VALUE; // rand()%END_MEAS_VALUE; - msg_frm_1.meas_data_lst[i].meas_record_lst[j].real_val = (rand() % 256) + 0.1; + for (size_t i = 0; imeas_record_len = act_def_fr_1->meas_info_lst_len; // record data list length corresponds to info list length from action definition + + meas_data->meas_record_lst = calloc(meas_data->meas_record_len, sizeof(meas_record_lst_t)); + assert(meas_data->meas_record_lst != NULL && "Memory exhausted"); + + for (size_t j = 0; j < meas_data->meas_record_len; j++) // each meas record corresponds to one meas type + { + meas_record_lst_t* meas_record = &meas_data->meas_record_lst[j]; + + // Measurement Type as requested in Action Definition + meas_type_t const meas_info_type = act_def_fr_1->meas_info_lst[j].meas_type; + + switch (meas_info_type.type) + { + case NAME_MEAS_TYPE: + { + char meas_info_name_str[meas_info_type.name.len + 1]; + memcpy(meas_info_name_str, meas_info_type.name.buf, meas_info_type.name.len); + meas_info_name_str[meas_info_type.name.len] = '\0'; + + 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_dl_rlc_pdu_total_bytes[ue_idx])*8/act_def_fr_1->gran_period_ms; // [kbps] + last_dl_rlc_pdu_total_bytes[ue_idx] = 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_ul_rlc_pdu_total_bytes[ue_idx])*8/act_def_fr_1->gran_period_ms; // [kbps] + last_ul_rlc_pdu_total_bytes[ue_idx] = 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_dl_total_prbs[ue_idx]; // [PRBs] + last_dl_total_prbs[ue_idx] = 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_ul_total_prbs[ue_idx]; // [PRBs] + last_ul_total_prbs[ue_idx] = 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) */ + } + else 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_dl_pdcp_sdu_total_bytes[ue_idx])*8/1000; // [kb] + last_dl_pdcp_sdu_total_bytes[ue_idx] = 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_ul_pdcp_sdu_total_bytes[ue_idx])*8/1000; // [kb] + last_ul_pdcp_sdu_total_bytes[ue_idx] = pdcp.txsdu_bytes; + /* note: this measurement is calculated as per spec */ + } + + break; + } + + case ID_MEAS_TYPE: + assert(false && "ID Measurement Type not yet implemented"); + break; + + default: + assert(false && "Measurement Type not recognized"); + break; + } + } } // Measurement Information - OPTIONAL - msg_frm_1.meas_info_lst_len = num_drbs; + 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" ); + // Get measInfo from action definition for (size_t i = 0; i < msg_frm_1.meas_info_lst_len; i++) { // Measurement Type - msg_frm_1.meas_info_lst[i].meas_type.type = ID_MEAS_TYPE; - // DRB ID - msg_frm_1.meas_info_lst[i].meas_type.id = i; + msg_frm_1.meas_info_lst[i].meas_type.type = act_def_fr_1->meas_info_lst[i].meas_type.type; + // Measurement Name + if (act_def_fr_1->meas_info_lst[i].meas_type.type == NAME_MEAS_TYPE) { + msg_frm_1.meas_info_lst[i].meas_type.name.buf = calloc(act_def_fr_1->meas_info_lst[i].meas_type.name.len, sizeof(uint8_t)); + memcpy(msg_frm_1.meas_info_lst[i].meas_type.name.buf, act_def_fr_1->meas_info_lst[i].meas_type.name.buf, act_def_fr_1->meas_info_lst[i].meas_type.name.len); + msg_frm_1.meas_info_lst[i].meas_type.name.len = act_def_fr_1->meas_info_lst[i].meas_type.name.len; + } else { + msg_frm_1.meas_info_lst[i].meas_type.id = act_def_fr_1->meas_info_lst[i].meas_type.id; + } + // Label Information msg_frm_1.meas_info_lst[i].label_info_lst_len = 1; @@ -84,22 +289,89 @@ kpm_ind_msg_format_1_t fill_kpm_ind_msg_frm_1(void) return msg_frm_1; } -static -kpm_ind_msg_format_3_t fill_kpm_ind_msg_frm_3_sta(void) + +typedef struct { + NR_UE_info_t *ue_list; + size_t num_ues; +} matched_ues_t; + +static +matched_ues_t filter_ues_by_s_nssai_criteria(test_cond_e const * condition, int64_t const value, NR_UE_info_t * ue_list, size_t const num_connected_ues) +{ + matched_ues_t matched_ues = {.num_ues = 0, .ue_list = calloc(num_connected_ues, sizeof(NR_UE_info_t))}; + assert(matched_ues.ue_list != NULL && "Memory exhausted"); + + assert(RC.nb_nr_inst == 1 && "Number of RRC instances greater than 1 not yet implemented"); + + // Check if each UE satisfies the given S-NSSAI criteria + for (size_t i = 0; iue_context.gNB_ue_ngap_id); + + switch (*condition) + { + case EQUAL_TEST_COND: + assert(ngap_ue_context_list->gNB_instance[0].s_nssai[0][0].sST == value && "Please, check the condition for S-NSSAI. At the moment, OAI supports eMBB"); + matched_ues.ue_list[matched_ues.num_ues] = ue_list[i]; + matched_ues.num_ues++; + break; + + default: + assert(false && "Condition not yet implemented"); + } + } + + return matched_ues; +} + +static +kpm_ind_msg_format_3_t fill_kpm_ind_msg_frm_3(const kpm_act_def_format_4_t * act_def_fr_4) { kpm_ind_msg_format_3_t msg_frm_3 = {0}; + + // Get the number of connected UEs and its info (RNTI) + msg_frm_3.ue_meas_report_lst_len = get_number_connected_ues(); // (rand() % 65535) + 1; + assert(msg_frm_3.ue_meas_report_lst_len != 0 && "Number of UEs to report must be greater than 0"); - uint32_t num_ues = 1; - msg_frm_3.ue_meas_report_lst_len = num_ues; // (rand() % 65535) + 1; + NR_UE_info_t * ue_list = connected_ues_list(); - 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++) - { - msg_frm_3.meas_report_per_ue[i].ue_meas_report_lst = fill_ue_id_data(); - msg_frm_3.meas_report_per_ue[i].ind_msg_format_1 = fill_kpm_ind_msg_frm_1(); - } + // Filter the UE by the test condition criteria + matched_ues_t matched_ues = {0}; + + for (size_t j = 0; jmatching_cond_lst_len; j++) + { + switch (act_def_fr_4->matching_cond_lst[j].test_info_lst.test_cond_type) + { + case S_NSSAI_TEST_COND_TYPE: + assert(act_def_fr_4->matching_cond_lst[j].test_info_lst.S_NSSAI == TRUE_TEST_COND_TYPE && "Must be true"); + + matched_ues = filter_ues_by_s_nssai_criteria(act_def_fr_4->matching_cond_lst[j].test_info_lst.test_cond, *act_def_fr_4->matching_cond_lst[j].test_info_lst.int_value, ue_list, msg_frm_3.ue_meas_report_lst_len); + break; + + default: + assert(false && "Test condition type not yet implemented"); + } + + } + + // Fill UE Measurement Reports + assert(matched_ues.num_ues >= 1 && "The number of filtered UEs must be at least equal to 1"); + msg_frm_3.meas_report_per_ue = calloc(matched_ues.num_ues, sizeof(meas_report_per_ue_t)); + assert(msg_frm_3.meas_report_per_ue != NULL && "Memory exhausted"); + + for (size_t i = 0; iaction_def_format_1); + } + + return msg_frm_3; } @@ -132,7 +404,7 @@ kpm_ric_ind_hdr_format_1_t fill_kpm_ind_hdr_frm_1(void) } static -kpm_ind_hdr_t fill_kpm_ind_hdr_sta(void) +kpm_ind_hdr_t fill_kpm_ind_hdr(void) { kpm_ind_hdr_t hdr = {0}; @@ -142,32 +414,39 @@ kpm_ind_hdr_t fill_kpm_ind_hdr_sta(void) return hdr; } + void read_kpm_sm(void* data) { assert(data != NULL); //assert(data->type == KPM_STATS_V3_0); - kpm_rd_ind_data_t* kpm = (kpm_rd_ind_data_t*)data; + kpm_rd_ind_data_t* const kpm = (kpm_rd_ind_data_t*)data; assert(kpm->act_def!= NULL && "Cannot be NULL"); - if(kpm->act_def->type == FORMAT_4_ACTION_DEFINITION){ - if(kpm->act_def->frm_4.matching_cond_lst[0].test_info_lst.test_cond_type == CQI_TEST_COND_TYPE - && *kpm->act_def->frm_4.matching_cond_lst[0].test_info_lst.test_cond == GREATERTHAN_TEST_COND){ - printf("Matching condition: UEs with CQI greater than %ld \n", *kpm->act_def->frm_4.matching_cond_lst[0].test_info_lst.int_value ); - } - - printf("Parameter to report: %s \n", kpm->act_def->frm_4.action_def_format_1.meas_info_lst->meas_type.name.buf); - - kpm->ind.hdr = fill_kpm_ind_hdr_sta(); - // 7.8 Supported RIC Styles and E2SM IE Formats - // Format 4 corresponds to indication message 3 + // 7.8 Supported RIC Styles and E2SM IE Formats + // Action Definition Format 4 corresponds to Indication Message Format 3 + switch (kpm->act_def->type) + { + case FORMAT_4_ACTION_DEFINITION: + { + kpm->ind.hdr = fill_kpm_ind_hdr(); + kpm->ind.msg.type = FORMAT_3_INDICATION_MESSAGE; - kpm->ind.msg.frm_3 = fill_kpm_ind_msg_frm_3_sta(); - } else { - kpm->ind.hdr = fill_kpm_ind_hdr(); - kpm->ind.msg = fill_kpm_ind_msg(); + kpm->ind.msg.frm_3 = fill_kpm_ind_msg_frm_3(&kpm->act_def->frm_4); + + break; } + + default: + { + kpm->ind.hdr = fill_rnd_kpm_ind_hdr(); + kpm->ind.msg = fill_rnd_kpm_ind_msg(); + + break; + } + } + } void read_kpm_setup_sm(void* e2ap) @@ -176,7 +455,7 @@ void read_kpm_setup_sm(void* e2ap) // 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_kpm_ran_func_def(); + kpm->ran_func_def = fill_rnd_kpm_ran_func_def(); } sm_ag_if_ans_t write_ctrl_kpm_sm(void const* src) diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.h b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.h index cec10e06ad..a8505a1671 100644 --- a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.h +++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.h @@ -1,7 +1,7 @@ #ifndef RAN_FUNC_SM_KPM_READ_WRITE_AGENT_H #define RAN_FUNC_SM_KPM_READ_WRITE_AGENT_H -#include "../../flexric/src/agent/e2_agent_api.h" +#include "openair2/E2AP/flexric/src/agent/../sm/sm_io.h" void read_kpm_sm(void*); diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc.h b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc.h index 64f11c8068..fa8638650f 100644 --- a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc.h +++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc.h @@ -1,7 +1,7 @@ #ifndef RAN_FUNC_SM_RAN_CTRL_READ_WRITE_AGENT_H #define RAN_FUNC_SM_RAN_CTRL_READ_WRITE_AGENT_H -#include "../../flexric/src/agent/e2_agent_api.h" +#include "openair2/E2AP/flexric/src/agent/../sm/sm_io.h" void read_rc_sm(void *); From c1864711cd7f4bf12539432a583d86d7ea09d24a Mon Sep 17 00:00:00 2001 From: Teodora Vladic Date: Wed, 23 Aug 2023 16:53:46 +0200 Subject: [PATCH 6/9] Remove unnecessary include --- openair2/E2AP/RAN_FUNCTION/init_ran_func.c | 1 - 1 file changed, 1 deletion(-) diff --git a/openair2/E2AP/RAN_FUNCTION/init_ran_func.c b/openair2/E2AP/RAN_FUNCTION/init_ran_func.c index bc60d139de..ecd1dc6ec7 100644 --- a/openair2/E2AP/RAN_FUNCTION/init_ran_func.c +++ b/openair2/E2AP/RAN_FUNCTION/init_ran_func.c @@ -1,5 +1,4 @@ #include "init_ran_func.h" -#include "../flexric/src/agent/e2_agent_api.h" #include "CUSTOMIZED/ran_func_mac.h" #include "CUSTOMIZED/ran_func_rlc.h" #include "CUSTOMIZED/ran_func_pdcp.h" From 505a00c3b3392c536d4f31a41e476e522a7eca1e Mon Sep 17 00:00:00 2001 From: Teodora Vladic Date: Tue, 8 Aug 2023 17:59:38 +0200 Subject: [PATCH 7/9] Update README file in E2AP folder --- openair2/E2AP/README.md | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/openair2/E2AP/README.md b/openair2/E2AP/README.md index 095d802877..610ec0c53d 100644 --- a/openair2/E2AP/README.md +++ b/openair2/E2AP/README.md @@ -11,7 +11,7 @@ Please see [NOTICE](NOTICE.md) file for third party software that is included in # Overview -This tutorial describes the steps of deployment 5G OAI RAN, with integrated E2 agent, with FlexRIC, O-RAN compliant nearRT-RIC. +This tutorial describes the steps of deployment 5G OAI RAN, with integrated E2 agent, and FlexRIC, O-RAN compliant nearRT-RIC. # 1. Installation @@ -82,7 +82,7 @@ git submodule update ```bash git clone https://gitlab.eurecom.fr/mosaic5g/flexric flexric cd flexric/ -git checkout d3ff879135d036632d7938c2085dbf4577759225 +git checkout 793daf62b6f7d5b56062ec9f3cf70e874b6538e3 ``` ### 2.2.2 Build FlexRIC @@ -95,9 +95,14 @@ mkdir build && cd build && cmake .. && make -j8 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`. +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`. - * Note: currently, only xApp KPM v03.00 and RC v01.03 (xapp_kpm_rc) is supported to communicate with the integrated E2 agent in OAI. If you are interested in custom SMs (MAC, RLC, PDCP, GTP, TC and SLICE), please follow the instructions at https://gitlab.eurecom.fr/mosaic5g/flexric. +Available SMs in this version are: +* KPM v03.00 (xapp_kpm_moni) +* GTP (xapp_gtp_moni) +* MAC + RLC + PDCP (xapp_mac_rlc_pdcp_moni) + +If you are interested in TC and SLICE SMs, please follow the instructions at https://gitlab.eurecom.fr/mosaic5g/flexric. # 3. Start the process @@ -119,8 +124,24 @@ cd flexric ./build/examples/ric/nearRT-RIC ``` -* start the KPM+RC xApp +* start the KPM xApp ```bash cd flexric -./build/examples/xApp/c/kpm_rc/xapp_kpm_rc +./build/examples/xApp/c/monitor/xapp_kpm_moni ``` + +* start the GTP xApp +```bash +cd flexric +./build/examples/xApp/c/monitor/xapp_gtp_moni +``` + +* start the (MAC + RLC + PDCP) xApp +```bash +cd flexric +./build/examples/xApp/c/monitor/xapp_mac_rlc_pdcp_moni +``` + +# Optional - Multiple UEs + +If you are interested in having multiple UEs in rfsim mode, please, follow the instructions at https://gitlab.eurecom.fr/oaiworkshop/summerworkshop2023/-/tree/main/ran#multiple-ues. From 6c73828cd5ee0791d504d85c178af97a25f57ac0 Mon Sep 17 00:00:00 2001 From: Teodora Date: Tue, 12 Sep 2023 12:48:55 +0200 Subject: [PATCH 8/9] Add option to collect data from CU and DU for KPM --- .../E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c | 778 ++++++++++++------ openair2/E2AP/README.md | 16 +- openair2/E2AP/flexric | 2 +- 3 files changed, 552 insertions(+), 244 deletions(-) diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c index a3a6d75033..d9931f2d47 100644 --- a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c +++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c @@ -8,55 +8,109 @@ #include "openair2/LAYER2/nr_rlc/nr_rlc_entity.h" #include "openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h" #include "openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h" +#include "openair2/F1AP/f1ap_ids.h" #include #include -static -size_t get_number_connected_ues(void) -{ - size_t num_ues = 0; - for (int i = 0; i < RC.nb_nr_macrlc_inst; i++) { - UE_iterator(RC.nrmac[i]->UE_info.list, ue) - { - if (ue) num_ues += 1; - } - } +typedef struct { + NR_UE_info_t *ue_list; + size_t num_ues; +} matched_ues_mac_t; - return num_ues; -} static -NR_UE_info_t * connected_ues_list(void) +matched_ues_mac_t filter_ues_by_s_nssai_in_du_or_monolithic(test_cond_e const * condition, int64_t const value) { - size_t num_ues = get_number_connected_ues(); + matched_ues_mac_t matched_ues = {.num_ues = 0, .ue_list = calloc(MAX_MOBILES_PER_GNB, sizeof(NR_UE_info_t)) }; + assert(matched_ues.ue_list != NULL && "Memory exhausted"); - NR_UE_info_t * ue_list = calloc(num_ues, sizeof(NR_UE_info_t)); - assert(ue_list != NULL && "Memory exhausted"); + /* IMPORTANT: in the case of gNB-DU, it is not possible to filter UEs by S-NSSAI as the ngap context is stored in gNB-CU + instead, we take all connected UEs*/ - size_t j = 0; - - for (int i = 0; i < RC.nb_nr_macrlc_inst; i++) { - UE_iterator(RC.nrmac[i]->UE_info.list, ue) + // Take MAC info + UE_iterator(RC.nrmac[0]->UE_info.list, ue) + { + if (ue) { - if (ue) + ngap_gNB_ue_context_t *ngap_ue_context_list = NULL; + + // Filter connected UEs by S-NSSAI test condition to get list of matched UEs + // note: not possible to filter + switch (*condition) { - ue_list[j] = *ue; - j++; + case EQUAL_TEST_COND: + { + if (NODE_IS_MONOLITHIC(RC.nrrrc[0]->node_type)) + { + rrc_gNB_ue_context_t *rrc_ue_context_list = rrc_gNB_get_ue_context_by_rnti(RC.nrrrc[0], ue->rnti); + ngap_ue_context_list = ngap_get_ue_context(rrc_ue_context_list->ue_context.rrc_ue_id); + assert(ngap_ue_context_list->gNB_instance[0].s_nssai[0][0].sST == value && "Please, check the condition for S-NSSAI. At the moment, OAI supports eMBB"); + } + matched_ues.ue_list[matched_ues.num_ues] = *ue; + matched_ues.num_ues++; + break; + } + + + default: + assert(false && "Condition not yet implemented"); } + } } - return ue_list; + assert(matched_ues.num_ues >= 1 && "The number of filtered UEs must be at least equal to 1"); + + return matched_ues; +} + + +typedef struct { + f1_ue_data_t * rrc_ue_id_list; // list of matched UEs on RRC level containing only rrc_ue_id (gNB CU UE ID) + size_t num_ues; + +} matched_ues_rrc_t; + +static +matched_ues_rrc_t filter_ues_by_s_nssai_in_cu(test_cond_e const * condition, int64_t const value) +{ + matched_ues_rrc_t matched_ues = {.num_ues = 0, .rrc_ue_id_list = calloc(MAX_MOBILES_PER_GNB, sizeof(f1_ue_data_t)) }; + assert(matched_ues.rrc_ue_id_list != NULL && "Memory exhausted"); + + + struct rrc_gNB_ue_context_s *ue_context_p1 = NULL; + RB_FOREACH(ue_context_p1, rrc_nr_ue_tree_s, &RC.nrrrc[0]->rrc_ue_head) { + + ngap_gNB_ue_context_t *ngap_ue_context_list = ngap_get_ue_context(ue_context_p1->ue_context.rrc_ue_id); + + // Filter connected UEs by S-NSSAI test condition to get list of matched UEs + switch (*condition) + { + case EQUAL_TEST_COND: + assert(ngap_ue_context_list->gNB_instance[0].s_nssai[0][0].sST == value && "Please, check the condition for S-NSSAI. At the moment, OAI supports eMBB"); + matched_ues.rrc_ue_id_list[matched_ues.num_ues].secondary_ue = ue_context_p1->ue_context.rrc_ue_id; + matched_ues.num_ues++; + break; + + default: + assert(false && "Condition not yet implemented"); + } + } + + assert(matched_ues.num_ues >= 1 && "The number of filtered UEs must be at least equal to 1"); + + return matched_ues; } static -nr_rlc_statistics_t get_rlc_stats_per_drb(NR_UE_info_t * const UE, int const rb_id) +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); @@ -69,15 +123,14 @@ nr_rlc_statistics_t get_rlc_stats_per_drb(NR_UE_info_t * const UE, int const rb_ } static -nr_pdcp_statistics_t get_pdcp_stats_per_drb(NR_UE_info_t * const UE, int const rb_id) +nr_pdcp_statistics_t get_pdcp_stats_per_drb(const uint32_t rrc_ue_id) { - assert(UE != NULL); - nr_pdcp_statistics_t pdcp = {0}; const int srb_flag = 0; + const int rb_id = 1; // at the moment, only 1 DRB is supported // Get PDCP stats for specific DRB - const bool rc = nr_pdcp_get_statistics(UE->rnti, srb_flag, rb_id, &pdcp); + 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; @@ -91,8 +144,7 @@ gnb_e2sm_t fill_gnb_data(rrc_gNB_ue_context_t * ue_context_p) // 6.2.3.16 // Mandatory // AMF UE NGAP ID - // fill with openair3/NGAP/ngap_gNB_ue_context.h:61 - gnb.amf_ue_ngap_id = ue_context_p->ue_context.gNB_ue_ngap_id; + gnb.amf_ue_ngap_id = ue_context_p->ue_context.rrc_ue_id; // Mandatory //GUAMI 6.2.3.17 @@ -109,18 +161,43 @@ gnb_e2sm_t fill_gnb_data(rrc_gNB_ue_context_t * ue_context_p) return gnb; } - -static -ue_id_e2sm_t fill_ue_id_data(rrc_gNB_ue_context_t * ue_context_p) +static +gnb_du_e2sm_t fill_gnb_du_data(const f1_ue_data_t * rrc_ue_id) { - ue_id_e2sm_t ue_id_data = {0}; + gnb_du_e2sm_t gnb_du = {0}; - ue_id_data.type = GNB_UE_ID_E2SM; - ue_id_data.gnb = fill_gnb_data(ue_context_p); + // 6.2.3.21 + // gNB CU UE F1AP + // Mandatory + gnb_du.gnb_cu_ue_f1ap = rrc_ue_id->secondary_ue; - return ue_id_data; + + // 6.2.3.25 + // RAN UE ID + // Optional + gnb_du.ran_ue_id = NULL; + + return gnb_du; } +static +gnb_cu_up_e2sm_t fill_gnb_cu_up_data(const uint32_t rrc_ue_id) +{ + gnb_cu_up_e2sm_t gnb_cu_up = {0}; + + // 6.2.3.20 + // gNB CU CP UE E1AP + // Mandatory + gnb_cu_up.gnb_cu_cp_ue_e1ap = rrc_ue_id; + + // 6.2.3.25 + // RAN UE ID + // Optional + gnb_cu_up.ran_ue_id = NULL; + + + return gnb_cu_up; +} uint32_t last_dl_rlc_pdu_total_bytes[MAX_MOBILES_PER_GNB] = {0}; @@ -130,8 +207,148 @@ uint32_t last_ul_total_prbs[MAX_MOBILES_PER_GNB] = {0}; uint32_t last_dl_pdcp_sdu_total_bytes[MAX_MOBILES_PER_GNB] = {0}; uint32_t last_ul_pdcp_sdu_total_bytes[MAX_MOBILES_PER_GNB] = {0}; -static -kpm_ind_msg_format_1_t fill_kpm_ind_msg_frm_1(NR_UE_info_t* const UE, size_t const ue_idx, kpm_act_def_format_1_t const * act_def_fr_1) + +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) +{ + 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_dl_rlc_pdu_total_bytes[ue_idx])*8/gran_period_ms; // [kbps] + last_dl_rlc_pdu_total_bytes[ue_idx] = 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_ul_rlc_pdu_total_bytes[ue_idx])*8/gran_period_ms; // [kbps] + last_ul_rlc_pdu_total_bytes[ue_idx] = 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_dl_total_prbs[ue_idx]; // [PRBs] + last_dl_total_prbs[ue_idx] = 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_ul_total_prbs[ue_idx]; // [PRBs] + last_ul_total_prbs[ue_idx] = 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) */ + } + else + { + assert(false && "Measurement Name not yet implemented"); + } + + + return meas_record; +} + +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_dl_pdcp_sdu_total_bytes[ue_idx])*8/1000; // [kb] + last_dl_pdcp_sdu_total_bytes[ue_idx] = 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_ul_pdcp_sdu_total_bytes[ue_idx])*8/1000; // [kb] + last_ul_pdcp_sdu_total_bytes[ue_idx] = pdcp.txsdu_bytes; + /* note: this measurement is calculated as per spec */ + } + else + { + assert(false && "Measurement Name not yet implemented"); + } + + + return meas_record; +} + +static +meas_info_format_1_lst_t * fill_kpm_meas_info_frm_1(const size_t len, const kpm_act_def_format_1_t * act_def_fr_1) +{ + meas_info_format_1_lst_t * meas_info_lst = calloc(len, sizeof(meas_info_format_1_lst_t)); + assert(meas_info_lst != NULL && "Memory exhausted" ); + + // Get measInfo from action definition + for (size_t i = 0; i < len; i++) + { + // Measurement Type + meas_info_lst[i].meas_type.type = act_def_fr_1->meas_info_lst[i].meas_type.type; + // Measurement Name + if (act_def_fr_1->meas_info_lst[i].meas_type.type == NAME_MEAS_TYPE) { + meas_info_lst[i].meas_type.name.buf = calloc(act_def_fr_1->meas_info_lst[i].meas_type.name.len, sizeof(uint8_t)); + memcpy(meas_info_lst[i].meas_type.name.buf, act_def_fr_1->meas_info_lst[i].meas_type.name.buf, act_def_fr_1->meas_info_lst[i].meas_type.name.len); + meas_info_lst[i].meas_type.name.len = act_def_fr_1->meas_info_lst[i].meas_type.name.len; + } else { + meas_info_lst[i].meas_type.id = act_def_fr_1->meas_info_lst[i].meas_type.id; + } + + + // Label Information + meas_info_lst[i].label_info_lst_len = 1; + meas_info_lst[i].label_info_lst = calloc(meas_info_lst[i].label_info_lst_len, sizeof(label_info_lst_t)); + assert(meas_info_lst[i].label_info_lst != NULL && "Memory exhausted" ); + + for (size_t j = 0; j < meas_info_lst[i].label_info_lst_len; j++) { + meas_info_lst[i].label_info_lst[j].noLabel = malloc(sizeof(enum_value_e)); + *meas_info_lst[i].label_info_lst[j].noLabel = TRUE_ENUM_VALUE; + } + } + + return meas_info_lst; +} + +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) { kpm_ind_msg_format_1_t msg_frm_1 = {0}; @@ -142,105 +359,26 @@ kpm_ind_msg_format_1_t fill_kpm_ind_msg_frm_1(NR_UE_info_t* const UE, size_t con 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; imeas_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_len = act_def_fr_1->meas_info_lst_len; // record data list length corresponds to info list length from action definition - meas_data->meas_record_lst = calloc(meas_data->meas_record_len, sizeof(meas_record_lst_t)); - assert(meas_data->meas_record_lst != NULL && "Memory exhausted"); + 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 < meas_data->meas_record_len; j++) // each meas record corresponds to one meas type + 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 { - meas_record_lst_t* meas_record = &meas_data->meas_record_lst[j]; - // Measurement Type as requested in Action Definition - meas_type_t const meas_info_type = act_def_fr_1->meas_info_lst[j].meas_type; - - switch (meas_info_type.type) + switch (act_def_fr_1->meas_info_lst[j].meas_type.type) { case NAME_MEAS_TYPE: { - char meas_info_name_str[meas_info_type.name.len + 1]; - memcpy(meas_info_name_str, meas_info_type.name.buf, meas_info_type.name.len); - meas_info_name_str[meas_info_type.name.len] = '\0'; - - 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_dl_rlc_pdu_total_bytes[ue_idx])*8/act_def_fr_1->gran_period_ms; // [kbps] - last_dl_rlc_pdu_total_bytes[ue_idx] = 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_ul_rlc_pdu_total_bytes[ue_idx])*8/act_def_fr_1->gran_period_ms; // [kbps] - last_ul_rlc_pdu_total_bytes[ue_idx] = 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_dl_total_prbs[ue_idx]; // [PRBs] - last_dl_total_prbs[ue_idx] = 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_ul_total_prbs[ue_idx]; // [PRBs] - last_ul_total_prbs[ue_idx] = 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) */ - } - else 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_dl_pdcp_sdu_total_bytes[ue_idx])*8/1000; // [kb] - last_dl_pdcp_sdu_total_bytes[ue_idx] = 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_ul_pdcp_sdu_total_bytes[ue_idx])*8/1000; // [kb] - last_ul_pdcp_sdu_total_bytes[ue_idx] = pdcp.txsdu_bytes; - /* note: this measurement is calculated as per spec */ - } + 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_mac_rlc_data(UE, ue_idx, meas_info_name_str, act_def_fr_1->gran_period_ms); break; } @@ -258,124 +396,214 @@ kpm_ind_msg_format_1_t fill_kpm_ind_msg_frm_1(NR_UE_info_t* const UE, size_t con // 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" ); - - // Get measInfo from action definition - for (size_t i = 0; i < msg_frm_1.meas_info_lst_len; i++) { - // Measurement Type - msg_frm_1.meas_info_lst[i].meas_type.type = act_def_fr_1->meas_info_lst[i].meas_type.type; - // Measurement Name - if (act_def_fr_1->meas_info_lst[i].meas_type.type == NAME_MEAS_TYPE) { - msg_frm_1.meas_info_lst[i].meas_type.name.buf = calloc(act_def_fr_1->meas_info_lst[i].meas_type.name.len, sizeof(uint8_t)); - memcpy(msg_frm_1.meas_info_lst[i].meas_type.name.buf, act_def_fr_1->meas_info_lst[i].meas_type.name.buf, act_def_fr_1->meas_info_lst[i].meas_type.name.len); - msg_frm_1.meas_info_lst[i].meas_type.name.len = act_def_fr_1->meas_info_lst[i].meas_type.name.len; - } else { - msg_frm_1.meas_info_lst[i].meas_type.id = act_def_fr_1->meas_info_lst[i].meas_type.id; - } - - - // Label Information - msg_frm_1.meas_info_lst[i].label_info_lst_len = 1; - msg_frm_1.meas_info_lst[i].label_info_lst = calloc(msg_frm_1.meas_info_lst[i].label_info_lst_len, sizeof(label_info_lst_t)); - assert(msg_frm_1.meas_info_lst[i].label_info_lst != NULL && "Memory exhausted" ); - - for (size_t j = 0; j < msg_frm_1.meas_info_lst[i].label_info_lst_len; j++) { - msg_frm_1.meas_info_lst[i].label_info_lst[j].noLabel = malloc(sizeof(enum_value_e)); - *msg_frm_1.meas_info_lst[i].label_info_lst[j].noLabel = TRUE_ENUM_VALUE; - } - } + msg_frm_1.meas_info_lst = fill_kpm_meas_info_frm_1(msg_frm_1.meas_info_lst_len, act_def_fr_1); return msg_frm_1; } - -typedef struct { - NR_UE_info_t *ue_list; - size_t num_ues; -} matched_ues_t; - static -matched_ues_t filter_ues_by_s_nssai_criteria(test_cond_e const * condition, int64_t const value, NR_UE_info_t * ue_list, size_t const num_connected_ues) +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) { - matched_ues_t matched_ues = {.num_ues = 0, .ue_list = calloc(num_connected_ues, sizeof(NR_UE_info_t))}; - assert(matched_ues.ue_list != NULL && "Memory exhausted"); - - assert(RC.nb_nr_inst == 1 && "Number of RRC instances greater than 1 not yet implemented"); - - // Check if each UE satisfies the given S-NSSAI criteria - for (size_t i = 0; iue_context.gNB_ue_ngap_id); + assert(act_def_fr_1 != NULL); - switch (*condition) - { - case EQUAL_TEST_COND: - assert(ngap_ue_context_list->gNB_instance[0].s_nssai[0][0].sST == value && "Please, check the condition for S-NSSAI. At the moment, OAI supports eMBB"); - matched_ues.ue_list[matched_ues.num_ues] = ue_list[i]; - matched_ues.num_ues++; - break; - - default: - assert(false && "Condition not yet implemented"); - } + + 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; imatching_cond_lst_len; j++) - { - switch (act_def_fr_4->matching_cond_lst[j].test_info_lst.test_cond_type) - { - case S_NSSAI_TEST_COND_TYPE: - assert(act_def_fr_4->matching_cond_lst[j].test_info_lst.S_NSSAI == TRUE_TEST_COND_TYPE && "Must be true"); - - matched_ues = filter_ues_by_s_nssai_criteria(act_def_fr_4->matching_cond_lst[j].test_info_lst.test_cond, *act_def_fr_4->matching_cond_lst[j].test_info_lst.int_value, ue_list, msg_frm_3.ue_meas_report_lst_len); - break; - - default: - assert(false && "Test condition type not yet implemented"); - } - - } - - // Fill UE Measurement Reports - assert(matched_ues.num_ues >= 1 && "The number of filtered UEs must be at least equal to 1"); - msg_frm_3.meas_report_per_ue = calloc(matched_ues.num_ues, sizeof(meas_report_per_ue_t)); - assert(msg_frm_3.meas_report_per_ue != NULL && "Memory exhausted"); - - for (size_t i = 0; iaction_def_format_1); - } - - return msg_frm_3; } +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) +{ + 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 */ + + 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; imeas_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"); + break; + + default: + assert(false && "Measurement Type not recognized"); + break; + } + + } + } + + // Measurement Information - OPTIONAL + msg_frm_1.meas_info_lst_len = act_def_fr_1->meas_info_lst_len; + msg_frm_1.meas_info_lst = fill_kpm_meas_info_frm_1(msg_frm_1.meas_info_lst_len, act_def_fr_1); + + return msg_frm_1; +} + +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 + + 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; igran_period_ms) + please, check their values in xApp */ + + 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; imeas_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'; + + 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"); + break; + + default: + assert(false && "Measurement Type not recognized"); + break; + } + } + } + + // Measurement Information - OPTIONAL + msg_frm_1.meas_info_lst_len = act_def_fr_1->meas_info_lst_len; + msg_frm_1.meas_info_lst = fill_kpm_meas_info_frm_1(msg_frm_1.meas_info_lst_len, act_def_fr_1); + + + return msg_frm_1; +} + +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) +{ + 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; iue_context.rrc_ue_id, act_def_fr_1); + } + + + return msg_frm_3; +} + + static kpm_ric_ind_hdr_format_1_t fill_kpm_ind_hdr_frm_1(void) { @@ -384,16 +612,45 @@ kpm_ric_ind_hdr_format_1_t fill_kpm_ind_hdr_frm_1(void) hdr_frm_1.collectStartTime = time_now_us(); hdr_frm_1.fileformat_version = NULL; + + // Check E2 Node NG-RAN Type + if (NODE_IS_DU(RC.nrrrc[0]->node_type)) + { + hdr_frm_1.sender_name = calloc(1, sizeof(byte_array_t)); + hdr_frm_1.sender_name->buf = calloc(strlen("My OAI-DU") + 1, sizeof(char)); + memcpy(hdr_frm_1.sender_name->buf, "My OAI-DU", strlen("My OAI-DU")); + hdr_frm_1.sender_name->len = strlen("My OAI-DU"); - hdr_frm_1.sender_name = calloc(1, sizeof(byte_array_t)); - hdr_frm_1.sender_name->buf = calloc(strlen("My OAI-MONO") + 1, sizeof(char)); - memcpy(hdr_frm_1.sender_name->buf, "My OAI-MONO", strlen("My OAI-MONO")); - hdr_frm_1.sender_name->len = strlen("My OAI-MONO"); + 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 (NODE_IS_CU(RC.nrrrc[0]->node_type)) + { + hdr_frm_1.sender_name = calloc(1, sizeof(byte_array_t)); + hdr_frm_1.sender_name->buf = calloc(strlen("My OAI-CU") + 1, sizeof(char)); + memcpy(hdr_frm_1.sender_name->buf, "My OAI-CU", strlen("My OAI-CU")); + hdr_frm_1.sender_name->len = strlen("My 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 (NODE_IS_MONOLITHIC(RC.nrrrc[0]->node_type)) + { + hdr_frm_1.sender_name = calloc(1, sizeof(byte_array_t)); + hdr_frm_1.sender_name->buf = calloc(strlen("My OAI-MONO") + 1, sizeof(char)); + memcpy(hdr_frm_1.sender_name->buf, "My OAI-MONO", strlen("My OAI-MONO")); + hdr_frm_1.sender_name->len = strlen("My 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"); + } - 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"); hdr_frm_1.vendor_name = calloc(1, sizeof(byte_array_t)); hdr_frm_1.vendor_name->buf = calloc(strlen("OAI") + 1, sizeof(char)); @@ -433,7 +690,43 @@ void read_kpm_sm(void* data) kpm->ind.hdr = fill_kpm_ind_hdr(); kpm->ind.msg.type = FORMAT_3_INDICATION_MESSAGE; - kpm->ind.msg.frm_3 = fill_kpm_ind_msg_frm_3(&kpm->act_def->frm_4); + + // Filter the UE by the test condition criteria + for (size_t i = 0; iact_def->frm_4.matching_cond_lst_len; i++) + { + switch (kpm->act_def->frm_4.matching_cond_lst[i].test_info_lst.test_cond_type) + { + case S_NSSAI_TEST_COND_TYPE: + assert(kpm->act_def->frm_4.matching_cond_lst[i].test_info_lst.S_NSSAI == TRUE_TEST_COND_TYPE && "Must be true"); + + // Check E2 Node NG-RAN Type + if (NODE_IS_DU(RC.nrrrc[0]->node_type)) + { + matched_ues_mac_t matched_ues = filter_ues_by_s_nssai_in_du_or_monolithic(kpm->act_def->frm_4.matching_cond_lst[i].test_info_lst.test_cond, *kpm->act_def->frm_4.matching_cond_lst[i].test_info_lst.int_value); + kpm->ind.msg.frm_3 = fill_kpm_ind_msg_frm_3_in_du(matched_ues, &kpm->act_def->frm_4.action_def_format_1); + } + else if (NODE_IS_CU(RC.nrrrc[0]->node_type)) + { + matched_ues_rrc_t matched_ues = filter_ues_by_s_nssai_in_cu(kpm->act_def->frm_4.matching_cond_lst[i].test_info_lst.test_cond, *kpm->act_def->frm_4.matching_cond_lst[i].test_info_lst.int_value); + kpm->ind.msg.frm_3 = fill_kpm_ind_msg_frm_3_in_cu(matched_ues, &kpm->act_def->frm_4.action_def_format_1); + } + else if (NODE_IS_MONOLITHIC(RC.nrrrc[0]->node_type)) + { + matched_ues_mac_t matched_ues = filter_ues_by_s_nssai_in_du_or_monolithic(kpm->act_def->frm_4.matching_cond_lst[i].test_info_lst.test_cond, *kpm->act_def->frm_4.matching_cond_lst[i].test_info_lst.int_value); + kpm->ind.msg.frm_3 = fill_kpm_ind_msg_frm_3_in_monolithic(matched_ues, &kpm->act_def->frm_4.action_def_format_1); + } + else + { + assert(false && "NG-RAN Type not yet implemented"); + } + + break; + + default: + assert(false && "Test condition type not yet implemented"); + } + + } break; } @@ -445,6 +738,7 @@ void read_kpm_sm(void* data) break; } + } } diff --git a/openair2/E2AP/README.md b/openair2/E2AP/README.md index 610ec0c53d..d1e20bb1e3 100644 --- a/openair2/E2AP/README.md +++ b/openair2/E2AP/README.md @@ -106,10 +106,24 @@ If you are interested in TC and SLICE SMs, please follow the instructions at htt # 3. Start the process +In order to configure E2 agent, please, add the following block in the configuration file: +```bash +e2_agent = { + near_ric_ip_addr = "127.0.0.1"; + sm_dir = "/usr/local/lib/flexric/" +} +``` + * start the gNB ```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 --gNBs.[0].min_rxtxtime 6 --rfsim --sa --rfsimulator.serveraddr server +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 --rfsim --sa -E +sudo ./nr-softmodem -O --rfsim --sa -E ``` * start the nrUE diff --git a/openair2/E2AP/flexric b/openair2/E2AP/flexric index ad8ea30ffb..793daf62b6 160000 --- a/openair2/E2AP/flexric +++ b/openair2/E2AP/flexric @@ -1 +1 @@ -Subproject commit ad8ea30ffb67ed2d7b958f04fbc639c92311f79b +Subproject commit 793daf62b6f7d5b56062ec9f3cf70e874b6538e3 From fec1396fb02a1862397d06ac2fdc6ff59a2954da Mon Sep 17 00:00:00 2001 From: Teodora Date: Thu, 14 Sep 2023 12:59:08 +0200 Subject: [PATCH 9/9] Fix CU as E2 node - when CU is used, UE ID data is filled as GNB_UE_ID_E2SM with CU UE ID value included --- .../E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c | 39 +++++++++---------- openair2/E2AP/README.md | 2 +- openair2/E2AP/flexric | 2 +- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c index d9931f2d47..37054ad8fb 100644 --- a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c +++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c @@ -158,6 +158,21 @@ gnb_e2sm_t fill_gnb_data(rrc_gNB_ue_context_t * ue_context_p) 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; + } + } + + return gnb; } @@ -180,25 +195,6 @@ gnb_du_e2sm_t fill_gnb_du_data(const f1_ue_data_t * rrc_ue_id) return gnb_du; } -static -gnb_cu_up_e2sm_t fill_gnb_cu_up_data(const uint32_t rrc_ue_id) -{ - gnb_cu_up_e2sm_t gnb_cu_up = {0}; - - // 6.2.3.20 - // gNB CU CP UE E1AP - // Mandatory - gnb_cu_up.gnb_cu_cp_ue_e1ap = rrc_ue_id; - - // 6.2.3.25 - // RAN UE ID - // Optional - gnb_cu_up.ran_ue_id = NULL; - - - return gnb_cu_up; -} - uint32_t last_dl_rlc_pdu_total_bytes[MAX_MOBILES_PER_GNB] = {0}; uint32_t last_ul_rlc_pdu_total_bytes[MAX_MOBILES_PER_GNB] = {0}; @@ -501,8 +497,9 @@ kpm_ind_msg_format_3_t fill_kpm_ind_msg_frm_3_in_cu(const matched_ues_rrc_t matc for (size_t i = 0; i