[O-RAN] 基于FlexRIC开发Service Model及xApp
FlexRIC
![image](https://i-blog.csdnimg.cn/blog_migrate/e322b747c823fdf8af6f076f5984c021.jpeg)
主要包含E2 Agent、RIC和xApp三个部分,其中SM(Service Model)以动态库的形式供E2 Agent和xApp使用。SM对RAN负责调用RAN function来采集数据、发送控制指令,对xApp则提供对应的控制接口和数据。
说明
O-RAN架构目前仍然处于高速发展完善当中,不同版本之间的变动较大,导致版本之间存在兼容性问题,本文档基于OAIC仓库的Commit: 2b717d49200d05c9e14a297d6375b3a4eacaff65
以及FlexRIC仓库的Commit: 93961d2480b2c7a5721e03435cb34cf9446d1ad1
版本基础上进行开发。鉴于FlexRIC官方的开发文档较为陈旧不适用于当前版本,故有了这篇开发文档。本文档介绍了使用c语言开发xApp以及开发service model所需的修改内容。
FlexRIC部署
安装pcre 2
到GitHub中下载压缩包https://github.com/PCRE2Project/pcre2/releases,执行命令安装
unzip pcre2-10.42.zip
cd pcre2-10.42/
./configure
sudo make
sudo make install
pcre2-config --version
安装SWIG
git clone https://github.com/swig/swig.git
cd swig
git checkout release-4.1
./autogen.sh
./configure --prefix=/usr/
make -j8
sudo make install
安装第三方依赖库
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt update
sudo apt install libsctp-dev python3.8 cmake-curses-gui libpcre2-dev python3-dev ccache
配置默认python版本
# 查看当前系统中的python版本
ls /usr/bin/python*
# 查看系统默认python版本
python3 --version
# 基于update-alternatives
# 列出所有版本,若为空则需要安装
update-alternatives --list python
# 安装
sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.8 1
sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 2
# 配置默认版本
sudo update-alternatives --config python3
安装FlexRIC
git clone https://gitlab.eurecom.fr/mosaic5g/flexric.git
cd flexric
git checkout 93961d2480b2c7a5721e03435cb34cf9446d1ad1 # 切勿随意切换分支,分支版本与OAI版本需要相对应
mkdir build && cd build
cmake .. -DE2AP_VERSION=E2AP_V2 -DKPM_VERSION=KPM_V3
make -j8
sudo make install
运行带FlexRIC的ORAN平台
编译带E2 Agent的OAI
# Build OAI gNB
cd ~/openairinterface5g
source oaienv
cd cmake_targets
sudo ./build_oai -w SIMU --nrUE --gNB --build-e2 --cmake-opt -DE2AP_VERSION=E2AP_V2 --cmake-opt -DKPM_VERSION=KPM_V3 -C
配置基站运行文件
为了配置E2代理,需要在配置文件中添加对应配置信息,此处在gnb.sa.band78.fr1.106PRB.usrpb210.conf
文件最下方添加一下配置信息(官方默认已经配置了可以不用管)
e2_agent = {
near_ric_ip_addr = "127.0.0.1";
sm_dir = "/usr/local/lib/flexric/"
}
运行
启动核心网
cd ~/openxg-5gcs-release/docker-compose
# 配置网络、启动数据库
docker-compose -f docker-mysql.yml up -d
# 启动核心网
docker-compose -f docker-3-network-element.yml up -d
启动基站
cd ~/openairinterface5g
source oaienv
cd cmake_targets/ran_build/build
sudo -E ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf --gNBs.[0].min_rxtxtime 6 --rfsim --sa
启动UE
cd ~/openairinterface5g
source oaienv
cd cmake_targets/ran_build/build
sudo -E ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --rfsim --sa --uicc0.imsi 466920000000001 --rfsimulator.serveraddr 127.0.0.1
启动nearRT-RIC
cd openairinterface5g/openair2/E2AP/flexric/
./build/examples/ric/nearRT-RIC
正确启动后运行界面如下所示
启动xApp
cd openairinterface5g/openair2/E2AP/flexric/
# KPM
./build/examples/xApp/c/monitor/xapp_kpm_moni
# GTP
./build/examples/xApp/c/monitor/xapp_gtp_moni
# (MAC + RLC + PDCP)
./build/examples/xApp/c/monitor/xapp_mac_rlc_pdcp_moni
添加新的Service Model
生成SM
首先利用FlexRIC中的Python脚本生成新的Service Model原始文件,本文档生成的Servicel Model为new_sm
cd flexric/src/sm
python3 gen_sm.py --name <NAME>
添加CMakeLists
添加两处CMakeLists使得SM能够正确识别编译
# /flexric/src/sm/CMakeLists.txt
add_subdirectory(kpm_sm)
...
add_subdirectory(new_sm)
# /flexric/CMakeLists.txt
#######
## Service Models
#######
...
# NEW Service Model
set(SM_ENCODING_NEW "PLAIN" CACHE STRING "The NEW SM encoding to use")
set_property(CACHE SM_ENCODING_NEW PROPERTY STRINGS "PLAIN" "ASN" "FLATBUFFERS")
message(STATUS "Selected NEW SM_ENCODING: ${SM_ENCODING_NEW}")
###
# Install the Service models
###
...
install(TARGETS new_sm DESTINATION ${CMAKE_INSTALL_LIBDIR}/flexric)
修改生成服务模型的服务ID号,FlexRIC中所有服务模型都有一个唯一的ID,需要为新创建的模型分配一个不重复的ID
// /flexric/src/sm/new_sm/new_sm_id.h
static
const uint16_t SM_NEW_ID = 149;
修改new_sm
定义Information element(IE)也就是 SM 中交换的资料与格式
flexric/src/sm/new_sm/ie$ tree
.
├── e2sm_new_v00.asn # ASN.1格式
├── e2sm_new_v00.fbs # fb (flatBuffer) 格式
├── new_data_ie.c # plain格式
└── new_data_ie.h # plain格式
在new_data_ie.h
定义自己的回传数据,封装结构以及操作接口,此处定义了四个回传数据并定义了相关数据结构及接口。
// /flexric/src/sm/new_sm/ie/new_data_ie.h
//
// RIC Indication Message
/
// We want to get 4 data coming from ngu_tunnel from RAN
typedef struct
{
uint64_t data1;
uint64_t data2;
uint64_t data3;
uint64_t data4;
} new_ng_u_tunnel_stats_t;
typedef struct {
new_ng_u_tunnel_stats_t* ngut;
uint32_t len; // define Length for our ngut array
int64_t tstamp; // Time stamp to trace latency of flexRIC
} new_ind_msg_t; // Serivce model indication message
void free_new_ind_msg(new_ind_msg_t* src);
new_ind_msg_t cp_new_ind_msg(new_ind_msg_t const* src);
bool eq_new_ind_msg(new_ind_msg_t* m0, new_ind_msg_t* m1);
在new_data_ie.c
中实现对应的操作接口
// /flexric/src/sm/new_sm/ie/new_data_ie.c
//
// RIC Indication Message
/
void free_new_ind_msg(new_ind_msg_t* src)
{
assert(src != NULL);
if(src->len > 0){
assert(src->ngut != NULL);
free(src->ngut);
}
}
new_ind_msg_t cp_new_ind_msg(new_ind_msg_t const* src)
{
assert(src != NULL);
new_ind_msg_t cp = {.len = src->len, .tstamp = src->tstamp};
if(cp.len > 0){
cp.ngut = calloc(cp.len, sizeof(new_ng_u_tunnel_stats_t));
assert(cp.ngut != NULL && "memory exhausted");
memcpy(cp.ngut, src->ngut, sizeof(new_ng_u_tunnel_stats_t)*cp.len);
}
return cp;
}
bool eq_new_ind_msg(new_ind_msg_t* m0, new_ind_msg_t* m1)
{
assert(m0 != NULL);
assert(m1 != NULL);
if(m0->tstamp != m1->tstamp ||
m0->len != m1->len)
return false;
for(uint32_t i =0; i < m0->len; ++i){
new_ng_u_tunnel_stats_t const* ngut0 = &m0->ngut[i];
new_ng_u_tunnel_stats_t const* ngut1 = &m1->ngut[i];
if(ngut0->data1 != ngut1->data1 || ngut0->data2 != ngut1->data2 ||
ngut0->data3 != ngut1->data3 || ngut0->data4 != ngut1->data4)
return false;
}
return true;
}
修改SM的Encode/Decode
编解码部分同样涉及ASN.1、fb(flatBuffer)、plain
三种格式,在本次示例中使用的为plain
格式,因此涉及的需要修改的文件有以下四个。
├── dec
│ ├── new_dec_asn.c
│ ├── new_dec_asn.h
│ ├── new_dec_fb.c
│ ├── new_dec_fb.h
│ ├── new_dec_generic.h
│ ├── new_dec_plain.c # 需修改
│ └── new_dec_plain.h # 需修改
├── enc
│ ├── new_enc_asn.c
│ ├── new_enc_asn.h
│ ├── new_enc_fb.c
│ ├── new_enc_fb.h
│ ├── new_enc_generic.h
│ ├── new_enc_plain.c # 需修改
│ └── new_enc_plain.h # 需修改
其中.h
头文件都为方法定义,由于与我们定义的数据类型名称能够对其因此不需要修改,其中需要修改的.c
文件内容如下
编码文件new_enc_plain.c
:
// /flexric/src/sm/new_sm/enc/new_enc_plain.c
byte_array_t new_enc_ind_msg_plain(new_ind_msg_t const* ind_msg)
{
assert(ind_msg != NULL);
byte_array_t ba = {0};
size_t const sz = sizeof(ind_msg->len) +
sizeof(new_ng_u_tunnel_stats_t)*ind_msg->len +
sizeof(ind_msg->tstamp);
// printf("Size of the byte array = %lu\n", sz);
ba.buf = malloc(sz);
assert(ba.buf != NULL && "Memory exhausted");
memcpy(ba.buf, &ind_msg->len, sizeof(ind_msg->len));
void* it = ba.buf + sizeof(ind_msg->len);
for(uint32_t i = 0; i < ind_msg->len ; ++i){
memcpy(it, &ind_msg->ngut[i], sizeof(ind_msg->ngut[i]));
it += sizeof(ind_msg->ngut[i]);
}
memcpy(it, &ind_msg->tstamp, sizeof(ind_msg->tstamp));
it += sizeof(ind_msg->tstamp);
// memcpy(it, &ind_msg->slot, sizeof(ind_msg->slot));
// it += sizeof(ind_msg->slot);
assert(it == ba.buf + sz && "Mismatch of data layout");
ba.len = sz;
return ba;
}
解码文件new_dec_plain.c
:
// /flexric/src/sm/new_sm/dec/new_dec_plain.c
new_ind_msg_t new_dec_ind_msg_plain(size_t len, uint8_t const ind_msg[len])
{
assert(next_pow2(len) >= sizeof(new_ind_msg_t) - sizeof(new_ng_u_tunnel_stats_t*) && "Less bytes than the case where there are not active Radio bearers! Next pow2 trick used for aligned struct");
new_ind_msg_t ret = {0};
memcpy(&ret.len, ind_msg, sizeof(ret.len));
if(ret.len > 0){
ret.ngut = calloc(ret.len, sizeof(new_ng_u_tunnel_stats_t) );
assert(ret.ngut != NULL && "memory exhausted");
}
void const* it = ind_msg + sizeof(ret.len);
for(uint32_t i = 0; i < ret.len; ++i){
memcpy(&ret.ngut[i], it, sizeof(ret.ngut[i]) );
it += sizeof(ret.ngut[i]);
}
memcpy(&ret.tstamp, it, sizeof(ret.tstamp));
it += sizeof(ret.tstamp);
// memcpy(&ret.slot, it, sizeof(ret.slot));
// it += sizeof(ret.slot);
assert(it == &ind_msg[len] && "Mismatch of data layout");
return ret;
}
测试Servicr Model需要修改的文件
修改monitor、slice、tc的CMakelists
# /flexric/examples/xApp/c/monitor/CMakeLists.txt
add_executable(xapp_mac_rlc_pdcp_moni
xapp_mac_rlc_pdcp_moni.c
...
../../../../src/sm/new_sm/ie/new_data_ie.c
)
# /flexric/examples/xApp/c/slice/CMakeLists.txt
add_executable(xapp_slice_moni_ctrl
xapp_slice_moni_ctrl.c
...
../../../../src/sm/new_sm/ie/new_data_ie.c
)
# /flexric/examples/xApp/c/tc/CMakeLists.txt
add_executable(xapp_tc_all
xapp_tc_all.c
...
../../../../src/sm/new_sm/ie/new_data_ie.c
)
add_executable(xapp_tc_codel
xapp_tc_codel.c
...
../../../../src/sm/new_sm/ie/new_data_ie.c
)
add_executable(xapp_tc_partition
xapp_tc_partition.c
...
../../../../src/sm/new_sm/ie/new_data_ie.c
)
add_executable(xapp_tc_ecn
xapp_tc_ecn.c
...
../../../../src/sm/new_sm/ie/new_data_ie.c
)
add_executable(xapp_tc_osi_codel
xapp_tc_osi_codel.c
...
../../../../src/sm/new_sm/ie/new_data_ie.c
)
在agent_if/中添加新的服务模型
在sm_ag_if_ans.h中定义服务模型
// /flexric/src/sm/agent_if/ans/sm_ag_if_ans.h
#include "../../mac_sm/ie/mac_data_ie.h"
...
#include "../../new_sm/ie/new_data_ie.h" // for new sm
typedef enum{
MAC_AGENT_IF_CTRL_ANS_V0,
...
NEW_AGENT_IF_CTRL_ANS_V0,
SM_AGENT_IF_CTRL_ANS_V0_END,
} sm_ag_if_ans_ctrl_e;
typedef struct{
sm_ag_if_ans_ctrl_e type;
union {
mac_ctrl_out_t mac;
...
new_ctrl_out_t sm_new;
};
} sm_ag_if_ans_ctrl_t;
在read中定义服务模型
// /flexric/src/sm/agent_if/read/sm_ag_if_rd.h
#include "../../mac_sm/ie/mac_data_ie.h"
...
#include "../../new_sm/ie/new_data_ie.h"
typedef enum{
MAC_STATS_V0 = 0,
...
NEW_STATS_V0,
SM_AGENT_IF_READ_V0_END,
} sm_ag_if_rd_ind_e;
typedef struct{
sm_ag_if_rd_ind_e type;
// Do not change the order!
union {
mac_ind_data_t mac;
...
new_ind_data_t sm_new;
};
} sm_ag_if_rd_ind_t;
typedef enum{
MAC_AGENT_IF_E2_SETUP_ANS_V0 = 0,
...
NEW_AGENT_IF_E2_SETUP_ANS_V0,
SM_AGENT_IF_E2_SETUP_ANS_V0_END,
} sm_ag_if_rd_e2setup_e;
typedef struct{
sm_ag_if_rd_e2setup_e type;
union {
mac_e2_setup_data_t mac;
...
new_e2_setup_data_t sm_new;
};
} sm_ag_if_rd_e2setup_t;
// /flexric/src/sm/agent_if/read/sm_ag_if_rd.c
#include "sm_ag_if_rd.h"
...
#include "../../new_sm/ie/new_data_ie.h"
void free_sm_ag_if_rd_ind(sm_ag_if_rd_ind_t* d)
{
assert(d != NULL);
if(d->type == MAC_STATS_V0){
...
} else if(d->type == NEW_STATS_V0){
free_new_ind_data(&d->sm_new);
} else {
assert(0!=0 && "Unforeseen case");
}
}
sm_ag_if_rd_ind_t cp_sm_ag_if_rd_ind(sm_ag_if_rd_ind_t const* d)
{
assert(d != NULL);
sm_ag_if_rd_ind_t ans = {.type = d->type};
if(ans.type == MAC_STATS_V0){
...
} else if(ans.type == NEW_STATS_V0) {
ans.sm_new = cp_new_ind_data(&d->sm_new);
} else {
assert("Unknown type or not implemented");
}
return ans;
}
在write中定义服务模型
// /flexric/src/sm/agent_if/write/sm_ag_if_wr.h
#include "subscribe_timer.h"
...
#include "../../new_sm/ie/new_data_ie.h"
typedef enum{
MAC_CTRL_REQ_V0 = 0,
...
NEW_CTRL_REQ_V0 = 7,
SM_AGENT_IF_WRITE_CTRL_V0_END,
} sm_ag_if_ctrl_e;
typedef struct{
sm_ag_if_ctrl_e type;
union{
mac_ctrl_req_data_t mac_ctrl;
...
new_ctrl_req_data_t new_ctrl;
};
} sm_ag_if_wr_ctrl_t;
typedef enum{
MAC_SUBS_V0 = 0,
...
NEW_SUBS_V0,
SM_AGENT_IF_WRITE_SUBS_V0_END,
} sm_ag_if_subs_e;
typedef struct{
sm_ag_if_subs_e type;
union{
mac_sub_data_t mac;
...
new_sub_data_t new_sm;
};
} sm_ag_if_wr_subs_t;
修改test/中的服务模型
更新 agent-ric-xapp 中的 CMakeFile.txt
# /flexric/test/agent-ric-xapp/CMakeLists.txt
add_executable(test_ag_ric_xapp
test_ag_ric_xapp.c
...
../../src/sm/new_sm/ie/new_data_ie.c
../../src/util/alg_ds/alg/defer.c
../../
)
在rnd中添加随机数据生成
// /flexric/test/rnd/fill_rnd_data_new.h
#ifndef FILL_RND_DATA_NEW_H
#define FILL_RND_DATA_NEW_H
#include "../../src/sm/new_sm/ie/new_data_ie.h"
void fill_new_ind_data(new_ind_data_t* ind);
#endif
// /flexric/test/rnd/fill_rnd_data_new.c
#include "fill_rnd_data_new.h"
#include "../../src/util/time_now_us.h"
#include <assert.h>
#include <stdlib.h>
#include <time.h>
void fill_new_ind_data(new_ind_data_t* ind){
assert(ind != NULL);
srand(time(0));
int const mod = 1024;
// Get indication message
new_ind_msg_t* ind_msg = &ind->msg;
// Set time now
ind_msg->tstamp = time_now_us();
// Set random number of messages
ind_msg->len = rand()%4;
if(ind_msg->len > 0 ){
ind_msg->ngut = calloc(ind_msg->len, sizeof(new_ng_u_tunnel_stats_t) );
assert(ind_msg->ngut != NULL);
}
for(uint32_t i = 0; i < ind_msg->len; ++i){
new_ng_u_tunnel_stats_t* ngut = &ind_msg->ngut[i];
// Fill dummy data in your data structure
ngut->data1 = abs(rand()%mod);
ngut->data2 = abs(rand()%mod);
ngut->data3 = abs(rand()%mod);
ngut->data4 = abs(rand()%mod);
}
}
修改new_sm的主函数匹配其他修改
// /flexric/test/sm/new_sm/main.c
// E2 -> RIC
static
void check_indication(sm_agent_t* ag, sm_ric_t* ric)
{
...
new_ind_data_t* data = &msg.sm_new;
if(msg.sm_new.msg.ngut != NULL){
assert(msg.sm_new.msg.len != 0);
}
}
修改CMakeLists.txt
# /flexric/test/sm/CMakeLists.txt
add_subdirectory(mac_sm)
...
add_subdirectory(new_sm)
enable_testing()
修改源文件中new_sm下的测试文件以匹配外部定义
// /flexric/src/sm/new_sm/test/main.c
...
#include "../../../../test/rnd/fill_rnd_data_new.h"
static
void read_RAN(sm_ag_if_rd_t* read)
{
assert(read != NULL);
assert(read->ind.type == NEW_STATS_V0);
fill_new_ind_data(&read->ind.sm_new);
cp.hdr = cp_new_ind_hdr(&read->ind.sm_new.hdr);
cp.msg = cp_new_ind_msg(&read->ind.sm_new.msg);
}
// E2 -> RIC
static
void check_indication(sm_agent_t* ag, sm_ric_t* ric)
{
assert(ag != NULL);
assert(ric != NULL);
...
sm_ag_if_rd_ind_t msg = ric->proc.on_indication(ric, &sm_data);
assert(msg.type == NEW_STATS_V0);
new_ind_data_t* data = &msg.sm_new;
if(msg.sm_new.msg.ngut != NULL){
assert(msg.sm_new.msg.len != 0);
}
}
开发xApp C binding需要修改文件
修改 xApp/
// /flexric/src/xApp/db/sqlite3/sqlite3_wrapper.c
// 依据IE中的数据创建表
static
void create_new_sm_table(sqlite3* db)
{
assert(db != NULL);
// ToDo: PRIMARY KEY UNIQUE
char* sql_new = "DROP TABLE IF EXISTS NEW_NGUT;"
"CREATE TABLE NEW_NGUT(tstamp INT CHECK(tstamp > 0),"
"ngran_node INT CHECK(ngran_node >= 0 AND ngran_node < 9),"
"mcc INT,"
"mnc INT,"
"mnc_digit_len INT,"
"nb_id INT,"
"cu_du_id TEXT,"
"data1 INT ,"
"data2 INT ,"
"data3 INT ,"
"data4 INT "
");";
create_table(db, sql_new);
}
// 转换
static
int to_sql_string_new_NGUT(global_e2_node_id_t const* id,new_ng_u_tunnel_stats_t* new, int64_t tstamp, char* out, size_t out_len)
{
assert(new != NULL);
assert(out != NULL);
const size_t max = 1024;
assert(out_len >= max);
char* c_null = NULL;
char c_cu_du_id[26];
if (id->cu_du_id) {
int rc = snprintf(c_cu_du_id, 26, "%lu", *id->cu_du_id);
assert(rc < (int) max && "Not enough space in the char array to write all the data");
}
int const rc = snprintf(out, max,
"INSERT INTO NEW_NGUT VALUES("
"%ld," //tstamp
"%d," //ngran_node
"%d," //mcc
"%d," //mnc
"%d," //mnc_digit_len
"%d," //nb_id
"'%s'," //cu_du_id
"%lu," //data1
"%lu," //data2
"%lu," //data3
"%lu" //data4
");"
, tstamp
, id->type
, id->plmn.mcc
, id->plmn.mnc
, id->plmn.mnc_digit_len
, id->nb_id.nb_id
, id->cu_du_id ? c_cu_du_id : c_null
, new->data1
, new->data2
, new->data3
, new->data4
);
assert(rc < (int)max && "Not enough space in the char array to write all the data");
return rc;
}
// 写入
static
void write_new_stats(sqlite3* db, global_e2_node_id_t const* id, new_ind_data_t const* ind)
{
assert(db != NULL);
assert(ind != NULL);
new_ind_msg_t const* ind_msg_new = &ind->msg;
char buffer[2048] = {0};
int pos = 0;
for(size_t i = 0; i < ind_msg_new->len; ++i){
pos += to_sql_string_new_NGUT(id, &ind_msg_new->ngut[i], ind_msg_new->tstamp, buffer + pos, 2048 - pos);
}
insert_db(db, buffer);
}
// 添加初始化
void init_db_sqlite3(sqlite3** db, char const* db_filename)
{
...
//
// NEW
//
create_new_sm_table(*db);
}
// 写表
void write_db_sqlite3(sqlite3* db, global_e2_node_id_t const* id, sm_ag_if_rd_t const* ag_rd)
{
...
assert(rd->type == MAC_STATS_V0 || rd->type == RLC_STATS_V0
...
rd->type == NEW_STATS_V0);
if(rd->type == MAC_STATS_V0){
...
} else if(rd->type == NEW_STATS_V0){
write_new_stats(db, id, &rd->sm_new);
} else {
assert(0!=0 && "Unknown statistics type received ");
}
}
// /flexric/src/xApp/e42_xapp.c
#include "../sm/mac_sm/mac_sm_id.h"
...
#include "../sm/new_sm/new_sm_id.h"
static
bool valid_ran_func_id(uint16_t ran_func_id)\
{
if(ran_func_id == SM_SLICE_ID
...
|| ran_func_id == SM_NEW_ID
)
return true;
return false;
}
// /flexric/src/xApp/e42_xapp_api.c
static inline
bool valid_sm_id(global_e2_node_id_t* id, uint32_t sm_id)
{
assert(id != NULL);
// Only for testing purposes
assert( sm_id == 2
...
|| sm_id == 149);
return true;
}
// /flexric/src/xApp/msg_handler_xapp.c
#include "../sm/rlc_sm/rlc_sm_id.h"
...
#include "../sm/new_sm/new_sm_id.h"
// E2 -> RIC
e2ap_msg_t e2ap_handle_indication_xapp(e42_xapp_t* xapp, const e2ap_msg_t* msg)
{
msg_dispatch_t msg_disp = {.rd.type = INDICATION_MSG_AGENT_IF_ANS_V0 };
msg_disp.rd.ind = sm->proc.on_indication(sm, &ind_data);
assert(msg_disp.rd.ind.type == MAC_STATS_V0 || msg_disp.rd.ind.type == RLC_STATS_V0
...
|| msg_disp.rd.ind.type == NEW_STATS_V0);
}
# /flexric/src/xApp/CMakeLists.txt
set(XAPP_SRC
../util/ngran_types.c
...
../sm/new_sm/ie/new_data_ie.c
)
修改 ric/
// /flexric/src/ric/iApps/influx.c
#include "../../sm/mac_sm/ie/mac_data_ie.h" // for mac_ind_msg_t
...
#include "../../sm/new_sm/ie/new_data_ie.h" // for new_ind_msg_t
void notify_influx_listener(sm_ag_if_rd_ind_t const* data)
{
assert(data != NULL);
assert(data->type == MAC_STATS_V0 || data->type == RLC_STATS_V0 || data->type == PDCP_STATS_V0
...
|| data->type == NEW_STATS_V0);
if(data->type == MAC_STATS_V0){
...
} else if (data->type == NEW_STATS_V0){
new_ind_msg_t const* new = &data->sm_new.msg;
for(uint32_t i = 0; i < new->len; ++i){
char stats[1024] = {0};
to_string_new_ngu(&new->ngut[i], new->tstamp, stats, 1024);
int const rc = sendto(sockfd, stats, strlen(stats), MSG_CONFIRM, (const struct sockaddr *) &servaddr, sizeof(servaddr));
assert(rc != -1);
}
} else {
assert(0 != 0 || "invalid data type ");
}
}
// /flexric/src/ric/iApps/redis.c
void notify_redis_listener(sm_ag_if_rd_ind_t const* data)
{
assert(data != NULL);
assert(data->type == MAC_STATS_V0 || data->type == RLC_STATS_V0 || data->type == PDCP_STATS_V0
...
|| data->type == NEW_STATS_V0 );
}
// /flexric/src/ric/iApps/stdout.c
#include "stdout.h"
...
#include "../../sm/new_sm//ie/new_data_ie.h" // for new_ind_msg_t
static
void print_new_stats(new_ind_msg_t const* new_msg)
{
assert(new_msg != NULL);
pthread_once(&init_fp_once, init_fp);
assert(fp != NULL);
for(uint32_t i = 0; i < new_msg->len; ++i){
char stats[1024] = {0};
to_string_new_ngu(&new_msg->ngut[i], new_msg->tstamp , stats , 1024);
int const rc = fputs(stats , fp);
// Edit: The C99 standard §7.19.1.3 states:
// The macros are [...]
// EOF which expands to an integer constant expression,
// with type int and a negative value, that is returned by
// several functions to indicate end-of-file, that is, no more input from a stream;
assert(rc > -1);
}
}
void notify_stdout_listener(sm_ag_if_rd_ind_t const* data)
{
assert(data != NULL);
if(data->type == MAC_STATS_V0)
...
else if (data->type == NEW_STATS_V0){
print_new_stats(&data->sm_new.msg);
} else {
assert(0 != 0 && "Unknown data type");
}
}
/// /flexric/src/ric/iApps/string_parser.h
#include "../../sm/mac_sm/ie/mac_data_ie.h"
...
#include "../../sm/new_sm/ie/new_data_ie.h"
void to_string_new_ngu(new_ng_u_tunnel_stats_t* new, int64_t tstamp , char* out, size_t out_len);
// /flexric/src/ric/iApps/string_parser.c
#include "ric/iApps/../../sm/mac_sm/ie/mac_data_ie.h" // for mac_ue_stats...
...
#include "ric/iApps/../../sm/new_sm/ie/new_data_ie.h" // for new_ind_msg_t
void to_string_new_ngu(new_ng_u_tunnel_stats_t* new, int64_t tstamp , char* out, size_t out_len)
{
assert(new != NULL);
assert(out != NULL);
const size_t max = 1024;
assert(out_len >= max);
int const rc = snprintf(out, max,
// Your serivce model IE data
"new_stats: tstamp=%ld,"
"data1=%ld"
"data2=%ld"
"data3=%ld"
"data4=%ld"
"\n"
,tstamp
,new->data1
,new->data2
,new->data3
,new->data4
);
assert(rc < (int)max && "Not enough space in the char array to write all the data");
}
// /flexric/src/ric/msg_handler_ric.c
// E2 -> RIC
e2ap_msg_t e2ap_handle_indication_ric(near_ric_t* ric, const e2ap_msg_t* msg)
{
sm_ag_if_rd_ind_t d = sm->proc.on_indication(sm, &data);
defer({ sm->alloc.free_ind_data(&d); } );
assert(d.type == MAC_STATS_V0 || d.type == RLC_STATS_V0
...
|| d.type == NEW_STATS_V0 );
}
修改源文件中new_sm中的new_sm_ric.c文件以匹配外部更改
// /flexric/src/sm/new_sm/new_sm_ric.c
static
sm_ag_if_rd_ind_t on_indication_new_sm_ric(sm_ric_t const* sm_ric, sm_ind_data_t const* data)
{
assert(sm_ric != NULL);
...
// Header
rd_if.sm_new.msg = new_dec_ind_msg(&sm->enc, data->len_msg, data->ind_msg);
// Message
rd_if.sm_new.hdr = new_dec_ind_hdr(&sm->enc, data->len_hdr, data->ind_hdr);
// call_process_id
assert(data->call_process_id == NULL && "not implemented");
rd_if.sm_new.proc_id = NULL;
return rd_if;
}
static
sm_ag_if_ans_ctrl_t ric_on_control_out_new_sm_ric(sm_ric_t const* sm_ric,const sm_ctrl_out_data_t * out)
{
assert(sm_ric != NULL);
...
ag_if.sm_new = new_dec_ctrl_out(&sm->enc, out->len_out, out->ctrl_out);
assert(ag_if.sm_new.ans == NEW_CTRL_OUT_OK);
}
static
void free_ind_data_new_sm_ric(void* msg)
{
assert(msg != NULL);
...
new_ind_data_t* ind = &rd_ind->sm_new;
}
修改 example/
修改CMakeLists.txt添加自己的xApp的可执行文件行
# /flexric/examples/xApp/c/monitor/CMakeLists.txt
add_executable(xapp_new_moni
xapp_new_moni.c
../../../../src/util/alg_ds/alg/defer.c
../../../../src/sm/mac_sm/ie/mac_data_ie.c
../../../../src/sm/rlc_sm/ie/rlc_data_ie.c
../../../../src/sm/pdcp_sm/ie/pdcp_data_ie.c
../../../../src/sm/slice_sm/ie/slice_data_ie.c
../../../../src/sm/tc_sm/ie/tc_data_ie.c
../../../../src/sm/gtp_sm/ie/gtp_data_ie.c
../../../../src/sm/new_sm/ie/new_data_ie.c
)
target_link_libraries(xapp_new_moni
PUBLIC
e42_xapp
-pthread
-lsctp
-ldl
)
添加测试
// /flexric/examples/emulator/agent/sm_new.h
#ifndef SM_NEW_READ_WRITE_AGENT_H
#define SM_NEW_READ_WRITE_AGENT_H
#include "../../../src/agent/e2_agent_api.h"
void read_new_sm(void*);
void read_new_setup_sm(void*);
sm_ag_if_ans_t write_ctrl_new_sm(void const*);
#endif
// /flexric/examples/emulator/agent/sm_new.c
#include "sm_new.h"
#include "../../../test/rnd/fill_rnd_data_new.h"
#include <assert.h>
void read_new_sm(void* data){
assert(data != NULL);
new_ind_data_t* new_ind = (new_ind_data_t *)(data);
fill_new_ind_data(new_ind);
}
void read_new_setup_sm(void* data){
assert(data != NULL);
assert(0 != 0 && "Not supported");
}
sm_ag_if_ans_t write_ctrl_new_sm(void const* src){
assert(src != NULL);
assert(0 !=0 && "Not supported");
}
// /flexric/examples/emulator/agent/test_agent.c
#include "sm_mac.h"
...
#include "sm_new.h"
static
void init_read_ind_tbl(read_ind_fp (*read_ind_tbl)[SM_AGENT_IF_READ_V0_END])
{
(*read_ind_tbl)[MAC_STATS_V0] = read_mac_sm;
.
(*read_ind_tbl)[NEW_STATS_V0] = read_new_sm;
}
static
void init_read_setup_tbl(read_e2_setup_fp (*read_setup_tbl)[SM_AGENT_IF_E2_SETUP_ANS_V0_END])
{
(*read_setup_tbl)[MAC_AGENT_IF_E2_SETUP_ANS_V0] = read_mac_setup_sm;
...
(*read_setup_tbl)[NEW_AGENT_IF_E2_SETUP_ANS_V0] = read_new_setup_sm;
}
static
void init_write_ctrl( write_ctrl_fp (*write_ctrl_tbl)[SM_AGENT_IF_WRITE_CTRL_V0_END])
{
(*write_ctrl_tbl)[MAC_CTRL_REQ_V0] = write_ctrl_mac_sm;
...
(*write_ctrl_tbl)[NEW_CTRL_REQ_V0] = write_ctrl_new_sm;
}
static
void init_write_subs(write_subs_fp (*write_subs_tbl)[SM_AGENT_IF_WRITE_SUBS_V0_END])
{
(*write_subs_tbl)[MAC_SUBS_V0] = NULL;
...
(*write_subs_tbl)[NEW_SUBS_V0] = NULL;
}
# /flexric/examples/emulator/agent/CMakeLists.txt
add_library(test_agent_obj OBJECT
../../../test/rnd/fill_rnd_data_gtp.c
...
../../../test/rnd/fill_rnd_data_new.c
../../../src/util/time_now_us.c
read_setup_ran.c
...
sm_new.c
)
// /flexric/examples/ric/near_ric.c
const uint16_t MAC_ran_func_id = 142;
...
const uint16_t NEW_ran_func_id = 149; // new sm
创建xApp
// /flexric/examples/xApp/c/monitor/xapp_new_moni.c
#include "../../../../src/xApp/e42_xapp_api.h"
#include "../../../../src/util/alg_ds/alg/defer.h"
#include "../../../../src/util/time_now_us.h"
#include "../../../../src/sm/new_sm/new_sm_id.h"
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
static
void sm_cb_new(sm_ag_if_rd_t const* rd)
{
assert(rd != NULL);
assert(rd->ind.type == NEW_STATS_V0);
int64_t now = time_now_us();
printf("NEW ind_msg latency = %ld \n", now - rd->ind.sm_new.msg.tstamp);
}
int main(int argc, char *argv[])
{
// init the config of xApp
fr_args_t args = init_fr_args(argc, argv);
// init xApp
init_xapp_api(&args);
sleep(1);
// Get the state of connection E2 nodes
e2_node_arr_t nodes = e2_nodes_xapp_api();
defer({ free_e2_node_arr(&nodes); });
assert(nodes.len > 0);
printf("Connected E2 nodes = %d\n", nodes.len);
// new indication
// Set the interval time of indication message
const char* i_2 = "1_ms";
sm_ans_xapp_t* new_handle = NULL;
// if there is more than one E2 node connected to the RIC
if(nodes.len > 0){
// Create an array of with length of nodes
new_handle = calloc( nodes.len, sizeof(sm_ans_xapp_t));
assert(new_handle != NULL);
}
for (int i = 0; i < nodes.len; i++) {
e2_node_connected_t* n = &nodes.n[i];
for (size_t j = 0; j < n->len_rf; j++)
printf("Registered node %d ran func id = %d \n ", i, n->ack_rf[j].id);
// generate subscription request
new_handle[i] = report_sm_xapp_api(&nodes.n[i].id, SM_NEW_ID, (void*)i_2, sm_cb_new);
assert(new_handle[i].success == true);
}
sleep(10);
for(int i = 0; i < nodes.len; ++i){
// Remove the handle previously returned
rm_report_sm_xapp_api(new_handle[i].u.handle);
}
if(nodes.len > 0){
free(new_handle);
}
//Stop the xApp
while(try_stop_xapp_api() == false)
usleep(1000);
printf("Test xApp run SUCCESSFULLY\n");
}
测试创建的xApp
1、运行agent
./build/examples/emulator/agent/emu_agent_gnb
2、运行 nearRT-RIC
./build/examples/ric/nearRT-RIC
3、运行添加的xApp
./build/examples/xApp/c/monitor/xapp_new_moni
可能遇到的问题
问题:官方文档中安装python-dev
显示无法找到软件包
在 Ubuntu 22.04 中,python-dev 包已经不再直接提供,因为它已经过时了。从 Ubuntu 20.04 开始,默认的 Python 版本是 Python 3,所以 python-dev 被替换为了 python3-dev。
问题:编译flexRIC时出现internal compiler error: Segmentation fault
报错。
Ubuntu22.04默认的gcc及g++版本为11.4,编译时会因为编译器内部原因而无法编译通过,因此需要切换gcc版本来完成编译。
# 查看当前gcc版本
gcc --version
# 安装其他版本,此处选择10.5版本
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt-get install -y gcc-10 g++-10
# 查看已经安装的版本
ls /usr/bin/gcc*
# gcc版本管理,其中1和2是编号(优先级)
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 1 --slave /usr/bin/g++ g++ /usr/bin/g++-11
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 2 --slave /usr/bin/g++ g++ /usr/bin/g++-10
# 选择10.5版本
sudo update-alternatives --config gcc
# 查看当前gcc版本是否切换成功
gcc --version
问题:切换Python版本后执行apt update
提示ModuleNotFoundError: No module named ‘apt_pkg‘
这是因为切换Python版本后对应的apt包名称并没有改变,导致系统找不到。直接复制一个apt包即可。
sudo apt-get install python3-apt --reinstall
cd /usr/lib/python3/dist-packages
sudo cp apt_pkg.cpython-311m-x86_64-linux-gnu.so apt_pkg.so # 注意此处apt_pkg.cpython-3x,最好利用tab自动补全一下