colosseum-near-rt-ric/setup/xapp-bs-connector/src/xapp.cc
2021-12-09 09:44:00 -05:00

540 lines
16 KiB
C++

/*
==================================================================================
Copyright (c) 2019-2020 AT&T Intellectual Property.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================================
*/
/*
* xapp.cc
*
* Mar, 2020 (Shraboni Jana)
*/
#include "xapp.hpp"
#define BUFFER_SIZE 1024
std::map<string, int> agentIp_socket;
std::map<std::string, std::string> agentIp_gnbId;
std::vector<std::string> drl_agent_ip{AGENT_0};
Xapp::Xapp(XappSettings &config, XappRmr &rmr){
rmr_ref = &rmr;
config_ref = &config;
xapp_mutex = NULL;
subhandler_ref = NULL;
return;
}
Xapp::~Xapp(void){
//Joining the threads
int threadcnt = xapp_rcv_thread.size();
for(int i=0; i<threadcnt; i++){
if(xapp_rcv_thread[i].joinable())
xapp_rcv_thread[i].join();
}
xapp_rcv_thread.clear();
if(xapp_mutex!=NULL){
xapp_mutex->~mutex();
delete xapp_mutex;
}
// close sockets
close_control_socket_agent();
// join threads
for (int i = 0; i < control_thr_rx.size(); ++i) {
if(control_thr_rx[i] && control_thr_rx[i]->joinable()) {
control_thr_rx[i]->join();
}
}
if(ext_control_thr_rx && ext_control_thr_rx->joinable()) {
ext_control_thr_rx->join();
}
};
//Stop the xapp. Note- To be run only from unit test scripts.
void Xapp::stop(void){
// Get the mutex lock
std::lock_guard<std::mutex> guard(*xapp_mutex);
rmr_ref->set_listen(false);
rmr_ref->~XappRmr();
//Detaching the threads....not sure if this is the right way to stop the receiver threads.
//Hence function should be called only in Unit Tests
int threadcnt = xapp_rcv_thread.size();
for(int i=0; i<threadcnt; i++){
xapp_rcv_thread[i].detach();
}
sleep(10);
}
void Xapp::startup(SubscriptionHandler &sub_ref) {
subhandler_ref = &sub_ref;
if (GNB_ID == "") {
// get list of gnbs from ric
std::cout << "Getting gNB list from RIC" << std::endl;
set_rnib_gnblist();
}
else {
// only insert target gnb
std::cout << "Querying target gNB" << std::endl;
rnib_gnblist.push_back(GNB_ID);
}
// open external control socket in thread and wait for message
ext_control_thr_rx = std::unique_ptr<std::thread>(new std::thread{&Xapp::handle_external_control_message, this, SOCKET_PORT_EXT});
for (int i = 0; i < drl_agent_ip.size(); ++i) {
// open control socket with agent
if (open_control_socket_agent(const_cast<char*>(drl_agent_ip[i].c_str()), 4200) == 0) {
// start receive thread
std::unique_ptr<std::thread> tmp_thr = std::unique_ptr<std::thread>(new std::thread{&Xapp::handle_rx_msg_agent, this, drl_agent_ip[i]});
control_thr_rx.push_back(std::move(tmp_thr));
}
}
// send test message
// send_socket("Hello, Server!", AGENT_1);
//send subscriptions.
startup_subscribe_requests();
//read A1 policies
// startup_get_policies();
// send control
// send_ric_control_request();
return;
}
void Xapp::Run(){
rmr_ref->set_listen(true);
if(xapp_mutex == NULL){
xapp_mutex = new std::mutex();
}
std::lock_guard<std::mutex> guard(*xapp_mutex);
for(size_t j=0; j < _callbacks.size(); j++){
std::thread th_recv([&](){ rmr_ref->xapp_rmr_receive(std::move(_callbacks[j]), rmr_ref);});
xapp_rcv_thread.push_back(std::move(th_recv));
}
return;
}
//Starting a seperate single receiver
void Xapp::start_xapp_receiver(XappMsgHandler& mp_handler){
//start a receiver thread. Can be multiple receiver threads for more than 1 listening port.
rmr_ref->set_listen(true);
if(xapp_mutex == NULL){
xapp_mutex = new std::mutex();
}
mdclog_write(MDCLOG_INFO,"Receiver Thread file= %s, line=%d",__FILE__,__LINE__);
std::lock_guard<std::mutex> guard(*xapp_mutex);
std::thread th_recv([&](){ rmr_ref->xapp_rmr_receive(std::move(mp_handler), rmr_ref);});
xapp_rcv_thread.push_back(std::move(th_recv));
return;
}
void Xapp::shutdown(){
return;
}
// handle received message from DRL agent
void Xapp::handle_rx_msg(void) {
std::cout << "handle_rx_msg" << std::endl;
const size_t max_size = 256;
char buf[max_size] = {0};
// listen to control from agent
while (true) {
// iterate through map
std::map<std::string, int>::iterator it;
for (it = agentIp_socket.begin(); it != agentIp_socket.end(); ++it) {
std::string agent_ip = it->first;
int control_sckfd = it->second;
int rcv_size = recv(control_sckfd, buf, max_size, 0);
if (rcv_size > 0) {
std::cout << "Message from agent " << agent_ip << std::endl;
std::cout << buf << std::endl;
// get gnb_id from agent IP
std::map<std::string, std::string>::iterator it_gnb;
it_gnb = agentIp_gnbId.find(agent_ip);
// send RIC control request
if (it_gnb != agentIp_gnbId.end()) {
send_ric_control_request(buf, it_gnb->second);
}
else {
std::cout << "ERROR: No gNB ID found for agent " << agent_ip << std::endl;
}
memset(buf, 0, max_size);
}
}
}
}
// handle received message from a specific DRL agent
void Xapp::handle_rx_msg_agent(std::string agent_ip) {
std::cout << "Opening RX thread with agent " << agent_ip << std::endl;
const size_t max_size = 256;
char buf[max_size] = {0};
// listen to control from agent
while (true) {
// get control_sckfd from agent IP
std::map<std::string, int>::iterator it;
it = agentIp_socket.find(agent_ip);
if (it != agentIp_socket.end()) {
int control_sckfd = it->second;
int rcv_size = recv(control_sckfd, buf, max_size, 0);
if (rcv_size > 0) {
std::cout << "Message from agent " << agent_ip << std::endl;
std::cout << buf << std::endl;
// get gnb_id from agent IP
std::map<std::string, std::string>::iterator it_gnb;
it_gnb = agentIp_gnbId.find(agent_ip);
// send RIC control request
if (it_gnb != agentIp_gnbId.end()) {
send_ric_control_request(buf, it_gnb->second);
}
else {
std::cout << "ERROR: No gNB ID found for agent " << agent_ip << std::endl;
}
memset(buf, 0, max_size);
}
}
}
}
// handle external control socket message
void Xapp::handle_external_control_message(int port) {
// Create a socket (IPv4, TCP)
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
std::cout << "Failed to create socket. errno: " << errno << std::endl;
return;
}
// Listen on given port on any address
sockaddr_in sockaddr;
sockaddr.sin_family = AF_INET;
sockaddr.sin_addr.s_addr = INADDR_ANY;
sockaddr.sin_port = htons(port);
if (bind(sockfd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) {
std::cout << "Failed to bind to port. Errno: " << errno << std::endl;
return;
}
// Start listening. Hold at most 10 connections in the queue
if (listen(sockfd, 10) < 0) {
std::cout << "Failed to listen on socket. Errno: " << errno << std::endl;
return;
}
std::cout << "Opened control socket server on port " << port << std::endl;
while (true) {
auto addrlen = sizeof(sockaddr);
int connection = accept(sockfd, (struct sockaddr*)&sockaddr, (socklen_t*)&addrlen);
if (connection < 0) {
continue;
}
// Read from the connection
const size_t max_size = 256;
char buffer[max_size] = {0};
auto bytes_read = read(connection, buffer, 100);
if (bytes_read > 0) {
std::cout << "External control socket. Message received: " << buffer << std::endl;
// TODO: check if message is termination, send to DU and shutdown xApp
if (strcmp(buffer, XAPP_TERMINATE) == 0) {
terminate_du_reporting();
}
}
memset(buffer, 0, max_size);
close(connection);
}
close(sockfd);
return;
}
// terminate all DU reportings and shutdown xApp
void Xapp::terminate_du_reporting(void) {
std::map<std::string, int>::iterator it;
for (it = agentIp_socket.begin(); it != agentIp_socket.end(); ++it) {
std::string agent_ip = it->first;
int control_sckfd = it->second;
// get gnb_id from agent IP
std::map<std::string, std::string>::iterator it_gnb;
it_gnb = agentIp_gnbId.find(agent_ip);
std::cout << "Terminating reporting gNB " << it_gnb->second << std::endl;
send_ric_control_request(XAPP_TERMINATE, it_gnb->second);
}
// stop xapp docker container with SIGTERM (15)
if (DEBUG) {
std::cout << "Debug mode, only echoing" << std::endl;
system("echo kill -s 15 1");
}
else {
system("kill -s 15 1");
}
}
void Xapp::send_ric_control_request(char* payload, std::string gnb_id) {
std::cout << "Sending RIC Control Request" << std::endl;
bool res;
size_t data_size = ASN_BUFF_MAX_SIZE;
unsigned char data[data_size];
unsigned char meid[RMR_MAX_MEID];
std::string xapp_id = config_ref->operator [](XappSettings::SettingName::XAPP_ID);
mdclog_write(MDCLOG_INFO, "Preparing to send control in file= %s, line=%d", __FILE__, __LINE__);
auto gnblist = get_rnib_gnblist();
int sz = gnblist.size();
if(sz <= 0) {
mdclog_write(MDCLOG_INFO, "Subscriptions cannot be sent as GNBList in RNIB is NULL");
return;
}
// give the message to subscription handler, along with the transmitter.
strcpy((char*)meid, gnb_id.c_str());
std::cout << "RIC Control Request, gNB " << gnb_id << std::endl;
// helpers
// set fields randomly
ric_control_helper din {};
//= {
// 1,
// 1,
// 0,
// 1,
// -1,
// 0,
// 0,
// 1,
// "test", // control_msg
// 5, // control_msg_size
// "testh", // control header
// 6,
// "testp", // call process id
// 6
//};
const char* msg = payload;
din.control_msg_size = strlen(msg) + 1;
mdclog_write(MDCLOG_INFO, "Size of msg %d", din.control_msg_size);
din.control_msg = (uint8_t*) calloc(din.control_msg_size, sizeof(uint8_t));
std::memcpy(din.control_msg, msg, din.control_msg_size);
ric_control_helper dout {};
// control request object
ric_control_request ctrl_req {};
ric_control_request ctrl_recv {};
unsigned char buf[BUFFER_SIZE];
size_t buf_size = BUFFER_SIZE;
res = ctrl_req.encode_e2ap_control_request(&buf[0], &buf_size, din);
xapp_rmr_header rmr_header;
rmr_header.message_type = RIC_CONTROL_REQ;
rmr_header.payload_length = buf_size; //data_size
strcpy((char*)rmr_header.meid, gnb_id.c_str());
mdclog_write(MDCLOG_INFO, "Sending CTRL REQ in file= %s, line=%d for MEID %s", __FILE__, __LINE__, meid);
int result = rmr_ref->xapp_rmr_send(&rmr_header, (void*)buf);
if(result) {
mdclog_write(MDCLOG_INFO, "CTRL REQ SUCCESSFUL in file= %s, line=%d for MEID %s",__FILE__,__LINE__, meid);
}
}
void Xapp::startup_subscribe_requests(void ){
bool res;
size_t data_size = ASN_BUFF_MAX_SIZE;
unsigned char data[data_size];
unsigned char meid[RMR_MAX_MEID];
std::string xapp_id = config_ref->operator [](XappSettings::SettingName::XAPP_ID);
mdclog_write(MDCLOG_INFO,"Preparing to send subscription in file= %s, line=%d", __FILE__, __LINE__);
auto gnblist = get_rnib_gnblist();
int sz = gnblist.size();
if(sz <= 0)
mdclog_write(MDCLOG_INFO,"Subscriptions cannot be sent as GNBList in RNIB is NULL");
for(int i = 0; i<sz; i++){
std::cout << "Sending subscriptions to: " << gnblist[i] << std::endl;
// give the message to subscription handler, along with the transmitter.
strcpy((char*)meid,gnblist[i].c_str());
// char *strMsg = "Subscription Request from HelloWorld XApp\0";
// strncpy((char *)data,strMsg,strlen(strMsg));
// data_size = strlen(strMsg);
subscription_helper din;
subscription_helper dout;
subscription_request sub_req;
subscription_request sub_recv;
unsigned char buf[BUFFER_SIZE];
size_t buf_size = BUFFER_SIZE;
bool res;
//Random Data for request
int request_id = XAPP_REQ_ID;
int function_id = 0;
// DU report timer in ms
std::string event_def = "250";
din.set_request(request_id);
din.set_function_id(function_id);
din.set_event_def(event_def.c_str(), event_def.length());
std::string act_def = "HelloWorld Action Definition";
din.add_action(1,1,(void*)act_def.c_str(), act_def.length(), 0);
res = sub_req.encode_e2ap_subscription(&buf[0], &buf_size, din);
xapp_rmr_header rmr_header;
rmr_header.message_type = RIC_SUB_REQ;
rmr_header.payload_length = buf_size; //data_size
strcpy((char*)rmr_header.meid,gnblist[i].c_str());
mdclog_write(MDCLOG_INFO,"Sending subscription in file= %s, line=%d for MEID %s",__FILE__,__LINE__, meid);
auto transmitter = std::bind(&XappRmr::xapp_rmr_send,rmr_ref, &rmr_header, (void*)buf );//(void*)data);
int result = subhandler_ref->manage_subscription_request(meid, transmitter);
if(result){
mdclog_write(MDCLOG_INFO,"Subscription SUCCESSFUL in file= %s, line=%d for MEID %s",__FILE__,__LINE__, meid);
}
}
}
void Xapp::startup_get_policies(void){
int policy_id = HELLOWORLD_POLICY_ID;
std::string policy_query = "{\"policy_type_id\":" + std::to_string(policy_id) + "}";
unsigned char * message = (unsigned char *)calloc(policy_query.length(), sizeof(unsigned char));
memcpy(message, policy_query.c_str(), policy_query.length());
xapp_rmr_header header;
header.payload_length = policy_query.length();
header.message_type = A1_POLICY_QUERY;
mdclog_write(MDCLOG_INFO, "Sending request for policy id %d\n", policy_id);
rmr_ref->xapp_rmr_send(&header, (void *)message);
free(message);
}
void Xapp::set_rnib_gnblist(void) {
openSdl();
void *result = getListGnbIds();
if(strlen((char*)result) < 1){
mdclog_write(MDCLOG_ERR, "ERROR: no data from getListGnbIds\n");
return;
}
mdclog_write(MDCLOG_INFO, "GNB List in R-NIB %s\n", (char*)result);
// remove non-unicode characters that make rapodjson fail the parsing
std::string result_clean((char*) result);
while (result_clean.back() != '}') {
result_clean.pop_back();
}
Document doc;
ParseResult parseJson = doc.Parse(result_clean.c_str());
if (!parseJson) {
//std::cerr << "JSON parse error: %s (%u)\n", GetParseErrorFunc(parseJson.Code());
std::cerr << "JSON parse error: " << GetParseErrorFunc(parseJson.Code()) << std::endl;
return;
}
if(!doc.HasMember("gnb_list")){
mdclog_write(MDCLOG_INFO, "JSON Has No GNB List Object");
return;
}
assert(doc.HasMember("gnb_list"));
const Value& gnblist = doc["gnb_list"];
if (gnblist.IsNull())
return;
if(!gnblist.IsArray()){
mdclog_write(MDCLOG_INFO, "GNB List is not an array");
return;
}
assert(gnblist.IsArray());
for (SizeType i = 0; i < gnblist.Size(); i++) { // Uses SizeType instead of size_t
assert(gnblist[i].IsObject());
const Value& gnbobj = gnblist[i];
assert(gnbobj.HasMember("inventory_name"));
assert(gnbobj["inventory_name"].IsString());
std::string name = gnbobj["inventory_name"].GetString();
rnib_gnblist.push_back(name);
}
closeSdl();
return;
}