[O-RAN] 基于FlexRIC开发Service Model及xApp

[O-RAN] 基于FlexRIC开发Service Model及xApp

FlexRIC

image

主要包含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

正确启动后运行界面如下所示

image-20240122155129652

启动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自动补全一下

  • 38
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值