blob: e4a06e7fd373e9723de251d6a38f8f2740df5495 [file] [log] [blame]
Ron Shachame7dfeb82020-04-24 14:46:48 -04001// vi: ts=4 sw=4 noet:
2/*
3==================================================================================
Ron Shachame7dfeb82020-04-24 14:46:48 -04004 Copyright (c) 2020 AT&T Intellectual Property.
5
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
17==================================================================================
18*/
19
20/*
21 Mnemonic: ts_xapp.cpp
Alexandre Huffb86721b2021-05-28 13:32:02 -030022 Abstract: Traffic Steering xApp
23 1. Receives A1 Policy
24 2. Receives anomaly detection
Ron Shachame7dfeb82020-04-24 14:46:48 -040025 3. Requests prediction for UE throughput on current and neighbor cells
26 4. Receives prediction
27 5. Optionally exercises Traffic Steering action over E2
28
Alexandre Huffb86721b2021-05-28 13:32:02 -030029 Date: 22 April 2020
Ron Shachame7dfeb82020-04-24 14:46:48 -040030 Author: Ron Shacham
E. Scott Danielsd8461662021-01-22 08:16:05 -050031
Alexandre Huffb86721b2021-05-28 13:32:02 -030032 Modified: 21 May 2021 (Alexandre Huff)
33 Update for traffic steering use case in release D.
Alexandre Huff05b94382021-12-09 00:29:05 -030034 07 Dec 2021 (Alexandre Huff)
35 Update for traffic steering use case in release E.
Ron Shachame7dfeb82020-04-24 14:46:48 -040036*/
37
38#include <stdio.h>
39#include <string.h>
40#include <unistd.h>
41
Ron Shachame187d502020-05-08 12:14:44 -040042#include <thread>
Ron Shachame7dfeb82020-04-24 14:46:48 -040043#include <iostream>
44#include <memory>
45
Ron Shachame7dfeb82020-04-24 14:46:48 -040046#include <set>
47#include <map>
48#include <vector>
49#include <string>
Ron Shachame187d502020-05-08 12:14:44 -040050#include <unordered_map>
51
52#include <rapidjson/document.h>
53#include <rapidjson/writer.h>
54#include <rapidjson/stringbuffer.h>
55#include <rapidjson/schema.h>
56#include <rapidjson/reader.h>
Alexandre Huffb86721b2021-05-28 13:32:02 -030057#include <rapidjson/prettywriter.h>
Ron Shachame187d502020-05-08 12:14:44 -040058
Alexandre Huffb86721b2021-05-28 13:32:02 -030059#include <rmr/RIC_message_types.h>
Alexandre Huff11a46a42022-04-26 09:42:11 -030060#include <ricxfcpp/xapp.hpp>
61#include <ricxfcpp/config.hpp>
Ron Shachame7dfeb82020-04-24 14:46:48 -040062
Alexandre Huff05b94382021-12-09 00:29:05 -030063/*
64 FIXME unfortunately this RMR flag has to be disabled
65 due to name resolution conflicts.
66 RC xApp defines the same name for gRPC control messages.
67*/
68#undef RIC_CONTROL_ACK
Alexandre Huffb86721b2021-05-28 13:32:02 -030069
Alexandre Huff05b94382021-12-09 00:29:05 -030070#include <grpc/grpc.h>
71#include <grpcpp/channel.h>
72#include <grpcpp/client_context.h>
73#include <grpcpp/create_channel.h>
74#include <grpcpp/security/credentials.h>
Alexandre Huff11a46a42022-04-26 09:42:11 -030075#include "protobuf/api.grpc.pb.h"
76
77#include "utils/restclient.hpp"
Alexandre Huffb86721b2021-05-28 13:32:02 -030078
79
Ron Shachame187d502020-05-08 12:14:44 -040080using namespace rapidjson;
81using namespace std;
Alexandre Huffb86721b2021-05-28 13:32:02 -030082using namespace xapp;
83
Ron Shachame7dfeb82020-04-24 14:46:48 -040084using Namespace = std::string;
85using Key = std::string;
86using Data = std::vector<uint8_t>;
87using DataMap = std::map<Key, Data>;
88using Keys = std::set<Key>;
89
90
91// ----------------------------------------------------------
Alexandre Huffb86721b2021-05-28 13:32:02 -030092std::unique_ptr<Xapp> xfw;
Alexandre Huff05b94382021-12-09 00:29:05 -030093std::unique_ptr<api::MsgComm::Stub> rc_stub;
Ron Shachame187d502020-05-08 12:14:44 -040094
Alexandre Huff1c844fb2022-05-10 17:00:23 -030095int downlink_threshold = 0; // A1 policy type 20008 (in percentage)
Ron Shachame187d502020-05-08 12:14:44 -040096
Alexandre Huff05b94382021-12-09 00:29:05 -030097// scoped enum to identify which API is used to send control messages
98enum class TsControlApi { REST, gRPC };
99TsControlApi ts_control_api; // api to send control messages
100string ts_control_ep; // api target endpoint
101
Alexandre Huff11a46a42022-04-26 09:42:11 -0300102typedef struct nodeb {
103 string ran_name;
104 struct {
105 string plmn_id;
106 string nb_id;
107 } global_nb_id;
108} nodeb_t;
109
110unordered_map<string, shared_ptr<nodeb_t>> cell_map; // maps each cell to its nodeb
111
Alexandre Huffb86721b2021-05-28 13:32:02 -0300112/* struct UEData {
Ron Shachame187d502020-05-08 12:14:44 -0400113 string serving_cell;
114 int serving_cell_rsrp;
Alexandre Huffb86721b2021-05-28 13:32:02 -0300115}; */
Ron Shachame187d502020-05-08 12:14:44 -0400116
Ron Shacham085267b2020-05-20 22:09:23 -0400117struct PolicyHandler : public BaseReaderHandler<UTF8<>, PolicyHandler> {
Alexandre Huff1c844fb2022-05-10 17:00:23 -0300118 /*
119 Assuming we receive the following payload from A1 Mediator
120 {"operation": "CREATE", "policy_type_id": 20008, "policy_instance_id": "tsapolicy145", "payload": {"threshold": 5}}
121 */
Ron Shacham085267b2020-05-20 22:09:23 -0400122 unordered_map<string, string> cell_pred;
123 std::string ue_id;
124 bool ue_id_found = false;
125 string curr_key = "";
126 string curr_value = "";
127 int policy_type_id;
128 int policy_instance_id;
129 int threshold;
130 std::string operation;
131 bool found_threshold = false;
132
Ron Shachamee5e4342020-06-11 12:34:46 -0400133 bool Null() { return true; }
134 bool Bool(bool b) { return true; }
Ron Shacham085267b2020-05-20 22:09:23 -0400135 bool Int(int i) {
Ron Shachamee5e4342020-06-11 12:34:46 -0400136
Ron Shacham085267b2020-05-20 22:09:23 -0400137 if (curr_key.compare("policy_type_id") == 0) {
138 policy_type_id = i;
139 } else if (curr_key.compare("policy_instance_id") == 0) {
140 policy_instance_id = i;
141 } else if (curr_key.compare("threshold") == 0) {
142 found_threshold = true;
143 threshold = i;
144 }
145
146 return true;
147 }
148 bool Uint(unsigned u) {
Ron Shachamee5e4342020-06-11 12:34:46 -0400149
Ron Shacham085267b2020-05-20 22:09:23 -0400150 if (curr_key.compare("policy_type_id") == 0) {
151 policy_type_id = u;
152 } else if (curr_key.compare("policy_instance_id") == 0) {
153 policy_instance_id = u;
154 } else if (curr_key.compare("threshold") == 0) {
155 found_threshold = true;
156 threshold = u;
157 }
158
159 return true;
Alexandre Huffb86721b2021-05-28 13:32:02 -0300160 }
Ron Shachamee5e4342020-06-11 12:34:46 -0400161 bool Int64(int64_t i) { return true; }
162 bool Uint64(uint64_t u) { return true; }
163 bool Double(double d) { return true; }
Ron Shacham085267b2020-05-20 22:09:23 -0400164 bool String(const char* str, SizeType length, bool copy) {
Alexandre Huffb86721b2021-05-28 13:32:02 -0300165
Ron Shacham085267b2020-05-20 22:09:23 -0400166 if (curr_key.compare("operation") != 0) {
167 operation = str;
168 }
169
170 return true;
171 }
172 bool StartObject() {
Ron Shachamee5e4342020-06-11 12:34:46 -0400173
Ron Shacham085267b2020-05-20 22:09:23 -0400174 return true;
175 }
176 bool Key(const char* str, SizeType length, bool copy) {
Ron Shachamee5e4342020-06-11 12:34:46 -0400177
Ron Shacham085267b2020-05-20 22:09:23 -0400178 curr_key = str;
179
180 return true;
181 }
Ron Shachamee5e4342020-06-11 12:34:46 -0400182 bool EndObject(SizeType memberCount) { return true; }
183 bool StartArray() { return true; }
184 bool EndArray(SizeType elementCount) { return true; }
Ron Shacham085267b2020-05-20 22:09:23 -0400185
186};
187
Ron Shachame187d502020-05-08 12:14:44 -0400188struct PredictionHandler : public BaseReaderHandler<UTF8<>, PredictionHandler> {
Ron Shacham3be133f2020-05-26 13:43:44 -0400189 unordered_map<string, int> cell_pred_down;
190 unordered_map<string, int> cell_pred_up;
Ron Shachame187d502020-05-08 12:14:44 -0400191 std::string ue_id;
192 bool ue_id_found = false;
193 string curr_key = "";
194 string curr_value = "";
Alexandre Huffb86721b2021-05-28 13:32:02 -0300195 string serving_cell_id;
Ron Shacham3be133f2020-05-26 13:43:44 -0400196 bool down_val = true;
Ron Shachamee5e4342020-06-11 12:34:46 -0400197 bool Null() { return true; }
198 bool Bool(bool b) { return true; }
199 bool Int(int i) { return true; }
Alexandre Huffb86721b2021-05-28 13:32:02 -0300200 bool Uint(unsigned u) {
201 // Currently, we assume the first cell in the prediction message is the serving cell
202 if ( serving_cell_id.empty() ) {
203 serving_cell_id = curr_key;
204 }
Ron Shachamee5e4342020-06-11 12:34:46 -0400205
Ron Shacham3be133f2020-05-26 13:43:44 -0400206 if (down_val) {
207 cell_pred_down[curr_key] = u;
Ron Shacham3be133f2020-05-26 13:43:44 -0400208 down_val = false;
209 } else {
210 cell_pred_up[curr_key] = u;
Ron Shacham3be133f2020-05-26 13:43:44 -0400211 down_val = true;
212 }
213
Alexandre Huffb86721b2021-05-28 13:32:02 -0300214 return true;
Ron Shacham3be133f2020-05-26 13:43:44 -0400215
216 }
Ron Shachamee5e4342020-06-11 12:34:46 -0400217 bool Int64(int64_t i) { return true; }
218 bool Uint64(uint64_t u) { return true; }
219 bool Double(double d) { return true; }
Ron Shachame187d502020-05-08 12:14:44 -0400220 bool String(const char* str, SizeType length, bool copy) {
Ron Shachame187d502020-05-08 12:14:44 -0400221
222 return true;
223 }
Ron Shachamee5e4342020-06-11 12:34:46 -0400224 bool StartObject() { return true; }
Ron Shachame187d502020-05-08 12:14:44 -0400225 bool Key(const char* str, SizeType length, bool copy) {
Ron Shachame187d502020-05-08 12:14:44 -0400226 if (!ue_id_found) {
Ron Shachamee5e4342020-06-11 12:34:46 -0400227
Ron Shachame187d502020-05-08 12:14:44 -0400228 ue_id = str;
229 ue_id_found = true;
230 } else {
231 curr_key = str;
232 }
233 return true;
234 }
Ron Shachamee5e4342020-06-11 12:34:46 -0400235 bool EndObject(SizeType memberCount) { return true; }
236 bool StartArray() { return true; }
237 bool EndArray(SizeType elementCount) { return true; }
Ron Shachame187d502020-05-08 12:14:44 -0400238};
239
Alexandre Huffb86721b2021-05-28 13:32:02 -0300240struct AnomalyHandler : public BaseReaderHandler<UTF8<>, AnomalyHandler> {
241 /*
242 Assuming we receive the following payload from AD
243 [{"du-id": 1010, "ue-id": "Train passenger 2", "measTimeStampRf": 1620835470108, "Degradation": "RSRP RSSINR"}]
244 */
245 vector<string> prediction_ues;
246 string curr_key = "";
Ron Shachame187d502020-05-08 12:14:44 -0400247
Alexandre Huffb86721b2021-05-28 13:32:02 -0300248 bool Key(const Ch* str, SizeType len, bool copy) {
249 curr_key = str;
250 return true;
251 }
252
253 bool String(const Ch* str, SizeType len, bool copy) {
254 // We are only interested in the "ue-id"
255 if ( curr_key.compare( "ue-id") == 0 ) {
256 prediction_ues.push_back( str );
257 }
258 return true;
259 }
260};
261
Alexandre Huff11a46a42022-04-26 09:42:11 -0300262struct NodebListHandler : public BaseReaderHandler<UTF8<>, NodebListHandler> {
263 vector<string> nodeb_list;
264 string curr_key = "";
265
266 bool Key(const Ch* str, SizeType length, bool copy) {
267 curr_key = str;
268 return true;
269 }
270
271 bool String(const Ch* str, SizeType length, bool copy) {
272 if( curr_key.compare( "inventoryName" ) == 0 ) {
273 nodeb_list.push_back( str );
274 }
275 return true;
276 }
277};
278
279struct NodebHandler : public BaseReaderHandler<UTF8<>, NodebHandler> {
280 string curr_key = "";
281 shared_ptr<nodeb_t> nodeb = make_shared<nodeb_t>();
282
283 bool Key(const Ch* str, SizeType length, bool copy) {
284 curr_key = str;
285 return true;
286 }
287
288 bool String(const Ch* str, SizeType length, bool copy) {
289 if( curr_key.compare( "ranName" ) == 0 ) {
290 nodeb->ran_name = str;
291 } else if( curr_key.compare( "plmnId" ) == 0 ) {
292 nodeb->global_nb_id.plmn_id = str;
293 } else if( curr_key.compare( "nbId" ) == 0 ) {
294 nodeb->global_nb_id.nb_id = str;
295 } else if( curr_key.compare( "cellId" ) == 0 ) {
296 cell_map[str] = nodeb;
297 }
298 return true;
299 }
300
301};
302
Alexandre Huffb86721b2021-05-28 13:32:02 -0300303
304/* struct UEDataHandler : public BaseReaderHandler<UTF8<>, UEDataHandler> {
Ron Shachame187d502020-05-08 12:14:44 -0400305 unordered_map<string, string> cell_pred;
306 std::string serving_cell_id;
307 int serving_cell_rsrp;
308 int serving_cell_rsrq;
309 int serving_cell_sinr;
310 bool in_serving_array = false;
311 int rf_meas_index = 0;
312
Alexandre Huffb86721b2021-05-28 13:32:02 -0300313 bool in_serving_report_object = false;
Ron Shacham1de06da2020-12-08 13:20:20 -0500314
Ron Shachame187d502020-05-08 12:14:44 -0400315 string curr_key = "";
316 string curr_value = "";
Ron Shachamee5e4342020-06-11 12:34:46 -0400317 bool Null() { return true; }
318 bool Bool(bool b) { return true; }
Ron Shachame187d502020-05-08 12:14:44 -0400319 bool Int(int i) {
Ron Shachamee5e4342020-06-11 12:34:46 -0400320
Ron Shachame187d502020-05-08 12:14:44 -0400321 return true;
322 }
Alexandre Huffb86721b2021-05-28 13:32:02 -0300323
Ron Shacham1de06da2020-12-08 13:20:20 -0500324 bool Uint(unsigned i) {
325
326 if (in_serving_report_object) {
327 if (curr_key.compare("rsrp") == 0) {
328 serving_cell_rsrp = i;
329 } else if (curr_key.compare("rsrq") == 0) {
330 serving_cell_rsrq = i;
331 } else if (curr_key.compare("rssinr") == 0) {
332 serving_cell_sinr = i;
333 }
Alexandre Huffb86721b2021-05-28 13:32:02 -0300334 }
335
Ron Shachamee5e4342020-06-11 12:34:46 -0400336 return true; }
Ron Shacham1de06da2020-12-08 13:20:20 -0500337 bool Int64(int64_t i) {
338
339 return true; }
340 bool Uint64(uint64_t i) {
341
342 return true; }
Ron Shachamee5e4342020-06-11 12:34:46 -0400343 bool Double(double d) { return true; }
Ron Shachame187d502020-05-08 12:14:44 -0400344 bool String(const char* str, SizeType length, bool copy) {
Alexandre Huffb86721b2021-05-28 13:32:02 -0300345
Ron Shachame187d502020-05-08 12:14:44 -0400346 if (curr_key.compare("ServingCellID") == 0) {
347 serving_cell_id = str;
Alexandre Huffb86721b2021-05-28 13:32:02 -0300348 }
Ron Shachame187d502020-05-08 12:14:44 -0400349
350 return true;
351 }
Ron Shacham1de06da2020-12-08 13:20:20 -0500352 bool StartObject() {
353 if (curr_key.compare("ServingCellRF") == 0) {
354 in_serving_report_object = true;
355 }
356
357 return true; }
Ron Shachame187d502020-05-08 12:14:44 -0400358 bool Key(const char* str, SizeType length, bool copy) {
Alexandre Huffb86721b2021-05-28 13:32:02 -0300359
Ron Shachame187d502020-05-08 12:14:44 -0400360 curr_key = str;
361 return true;
362 }
Ron Shacham1de06da2020-12-08 13:20:20 -0500363 bool EndObject(SizeType memberCount) {
364 if (curr_key.compare("ServingCellRF") == 0) {
365 in_serving_report_object = false;
366 }
367 return true; }
Ron Shachame187d502020-05-08 12:14:44 -0400368 bool StartArray() {
Ron Shachamee5e4342020-06-11 12:34:46 -0400369
Ron Shachame187d502020-05-08 12:14:44 -0400370 if (curr_key.compare("ServingCellRF") == 0) {
371 in_serving_array = true;
372 }
Alexandre Huffb86721b2021-05-28 13:32:02 -0300373
Ron Shachame187d502020-05-08 12:14:44 -0400374 return true;
375 }
376 bool EndArray(SizeType elementCount) {
Ron Shachamee5e4342020-06-11 12:34:46 -0400377
Ron Shachame187d502020-05-08 12:14:44 -0400378 if (curr_key.compare("servingCellRF") == 0) {
379 in_serving_array = false;
380 rf_meas_index = 0;
381 }
382
383 return true; }
Alexandre Huffb86721b2021-05-28 13:32:02 -0300384}; */
Ron Shachame187d502020-05-08 12:14:44 -0400385
386
Alexandre Huffb86721b2021-05-28 13:32:02 -0300387/* unordered_map<string, UEData> get_sdl_ue_data() {
Ron Shachame187d502020-05-08 12:14:44 -0400388
389 fprintf(stderr, "In get_sdl_ue_data()\n");
390
391 unordered_map<string, string> ue_data;
392
393 unordered_map<string, UEData> return_ue_data_map;
Alexandre Huffb86721b2021-05-28 13:32:02 -0300394
Ron Shacham1de06da2020-12-08 13:20:20 -0500395 std::string prefix3="";
Ron Shachame187d502020-05-08 12:14:44 -0400396 Keys K2 = sdl->findKeys(nsu, prefix3);
397 DataMap Dk2 = sdl->get(nsu, K2);
Alexandre Huffb86721b2021-05-28 13:32:02 -0300398
Ron Shachame187d502020-05-08 12:14:44 -0400399 string ue_json;
400 string ue_id;
Alexandre Huffb86721b2021-05-28 13:32:02 -0300401
Ron Shachame187d502020-05-08 12:14:44 -0400402 for(auto si=K2.begin();si!=K2.end();++si){
403 std::vector<uint8_t> val_v = Dk2[(*si)]; // 4 lines to unpack a string
404 char val[val_v.size()+1]; // from Data
405 int i;
Ron Shachamee5e4342020-06-11 12:34:46 -0400406
Ron Shachame187d502020-05-08 12:14:44 -0400407 for(i=0;i<val_v.size();++i) val[i] = (char)(val_v[i]);
408 val[i]='\0';
409 ue_id.assign((std::string)*si);
Alexandre Huffb86721b2021-05-28 13:32:02 -0300410
Ron Shachame187d502020-05-08 12:14:44 -0400411 ue_json.assign(val);
412 ue_data[ue_id] = ue_json;
413 }
Alexandre Huffb86721b2021-05-28 13:32:02 -0300414
Ron Shachame187d502020-05-08 12:14:44 -0400415 for (auto map_iter = ue_data.begin(); map_iter != ue_data.end(); map_iter++) {
416 UEDataHandler handler;
417 Reader reader;
418 StringStream ss(map_iter->second.c_str());
419 reader.Parse(ss,handler);
420
421 string ueID = map_iter->first;
422 string serving_cell_id = handler.serving_cell_id;
423 int serv_rsrp = handler.serving_cell_rsrp;
Alexandre Huffb86721b2021-05-28 13:32:02 -0300424
Ron Shachame187d502020-05-08 12:14:44 -0400425 return_ue_data_map[ueID] = {serving_cell_id, serv_rsrp};
Alexandre Huffb86721b2021-05-28 13:32:02 -0300426
427 }
Ron Shachamee5e4342020-06-11 12:34:46 -0400428
Ron Shachame187d502020-05-08 12:14:44 -0400429 return return_ue_data_map;
Alexandre Huffb86721b2021-05-28 13:32:02 -0300430} */
Ron Shachame7dfeb82020-04-24 14:46:48 -0400431
432void policy_callback( Message& mbuf, int mtype, int subid, int len, Msg_component payload, void* data ) {
Alexandre Huffc6e27b02021-06-24 13:22:08 -0300433 string arg ((const char*)payload.get(), len); // RMR payload might not have a nil terminanted char
Ron Shachame7dfeb82020-04-24 14:46:48 -0400434
Alexandre Huff1c844fb2022-05-10 17:00:23 -0300435 cout << "[INFO] Policy Callback got a message, type=" << mtype << ", length=" << len << "\n";
Alexandre Huffc6e27b02021-06-24 13:22:08 -0300436 cout << "[INFO] Payload is " << arg << endl;
Ron Shacham1de06da2020-12-08 13:20:20 -0500437
Ron Shacham085267b2020-05-20 22:09:23 -0400438 PolicyHandler handler;
439 Reader reader;
Alexandre Huffc6e27b02021-06-24 13:22:08 -0300440 StringStream ss(arg.c_str());
Ron Shacham085267b2020-05-20 22:09:23 -0400441 reader.Parse(ss,handler);
442
Ron Shachame187d502020-05-08 12:14:44 -0400443 //Set the threshold value
Ron Shacham085267b2020-05-20 22:09:23 -0400444 if (handler.found_threshold) {
Alexandre Huff1c844fb2022-05-10 17:00:23 -0300445 cout << "[INFO] Setting Threshold for A1-P value: " << handler.threshold << "%\n";
446 downlink_threshold = handler.threshold;
Ron Shacham085267b2020-05-20 22:09:23 -0400447 }
Ron Shacham3be133f2020-05-26 13:43:44 -0400448
Alexandre Huffb86721b2021-05-28 13:32:02 -0300449}
Ron Shachame187d502020-05-08 12:14:44 -0400450
Alexandre Huffb86721b2021-05-28 13:32:02 -0300451// sends a handover message through REST
Alexandre Huff11a46a42022-04-26 09:42:11 -0300452void send_rest_control_request( string ue_id, string serving_cell_id, string target_cell_id ) {
453 time_t now;
454 string str_now;
455 static unsigned int seq_number = 0; // static counter, not thread-safe
Ron Shachame7dfeb82020-04-24 14:46:48 -0400456
Alexandre Huff11a46a42022-04-26 09:42:11 -0300457 // building a handoff control message
458 now = time( nullptr );
459 str_now = ctime( &now );
460 str_now.pop_back(); // removing the \n character
Ron Shachame187d502020-05-08 12:14:44 -0400461
Alexandre Huff11a46a42022-04-26 09:42:11 -0300462 seq_number++; // static counter, not thread-safe
Alexandre Huffb86721b2021-05-28 13:32:02 -0300463
Alexandre Huff11a46a42022-04-26 09:42:11 -0300464 rapidjson::StringBuffer s;
465 rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(s);
466 writer.StartObject();
467 writer.Key( "command" );
468 writer.String( "HandOff" );
469 writer.Key( "seqNo" );
470 writer.Int( seq_number );
471 writer.Key( "ue" );
472 writer.String( ue_id.c_str() );
473 writer.Key( "fromCell" );
474 writer.String( serving_cell_id.c_str() );
475 writer.Key( "toCell" );
476 writer.String( target_cell_id.c_str() );
477 writer.Key( "timestamp" );
478 writer.String( str_now.c_str() );
479 writer.Key( "reason" );
480 writer.String( "HandOff Control Request from TS xApp" );
481 writer.Key( "ttl" );
482 writer.Int( 10 );
483 writer.EndObject();
484 // creates a message like
485 /* {
486 "command": "HandOff",
487 "seqNo": 1,
488 "ue": "ueid-here",
489 "fromCell": "CID1",
490 "toCell": "CID3",
491 "timestamp": "Sat May 22 10:35:33 2021",
492 "reason": "HandOff Control Request from TS xApp",
493 "ttl": 10
494 } */
495
496 string msg = s.GetString();
Alexandre Huffb86721b2021-05-28 13:32:02 -0300497
Alexandre Huff05b94382021-12-09 00:29:05 -0300498 cout << "[INFO] Sending a HandOff CONTROL message to \"" << ts_control_ep << "\"\n";
Alexandre Huffb86721b2021-05-28 13:32:02 -0300499 cout << "[INFO] HandOff request is " << msg << endl;
500
501 // sending request
Alexandre Huff11a46a42022-04-26 09:42:11 -0300502 restclient::RestClient client( ts_control_ep );
503 restclient::response_t resp = client.do_post( "", msg ); // we already have the full path in ts_control_ep
Alexandre Huffb86721b2021-05-28 13:32:02 -0300504
Alexandre Huff11a46a42022-04-26 09:42:11 -0300505 if( resp.status_code == 200 ) {
Alexandre Huffb86721b2021-05-28 13:32:02 -0300506 // ============== DO SOMETHING USEFUL HERE ===============
507 // Currently, we only print out the HandOff reply
508 rapidjson::Document document;
Alexandre Huff11a46a42022-04-26 09:42:11 -0300509 document.Parse( resp.body.c_str() );
Alexandre Huffb86721b2021-05-28 13:32:02 -0300510 rapidjson::StringBuffer s;
511 rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(s);
512 document.Accept( writer );
513 cout << "[INFO] HandOff reply is " << s.GetString() << endl;
514
Alexandre Huff11a46a42022-04-26 09:42:11 -0300515 } else {
516 cout << "[ERROR] Unexpected HTTP code " << resp.status_code << " from " << \
517 client.getBaseUrl() << \
518 "\n[ERROR] HTTP payload is " << resp.body.c_str() << endl;
Ron Shachame187d502020-05-08 12:14:44 -0400519 }
520
Ron Shachame187d502020-05-08 12:14:44 -0400521}
Ron Shachame7dfeb82020-04-24 14:46:48 -0400522
Alexandre Huff05b94382021-12-09 00:29:05 -0300523// sends a handover message to RC xApp through gRPC
Alexandre Huff11a46a42022-04-26 09:42:11 -0300524void send_grpc_control_request( string ue_id, string target_cell_id ) {
Alexandre Huff05b94382021-12-09 00:29:05 -0300525 grpc::ClientContext context;
Alexandre Huff11a46a42022-04-26 09:42:11 -0300526
Alexandre Huff05b94382021-12-09 00:29:05 -0300527 api::RicControlGrpcRsp response;
Alexandre Huff11a46a42022-04-26 09:42:11 -0300528 shared_ptr<api::RicControlGrpcReq> request = make_shared<api::RicControlGrpcReq>();
Alexandre Huff05b94382021-12-09 00:29:05 -0300529
Alexandre Huff11a46a42022-04-26 09:42:11 -0300530 api::RICE2APHeader *apHeader = request->mutable_rice2apheaderdata();
531 apHeader->set_ranfuncid( 300 );
532 apHeader->set_ricrequestorid( 1001 );
Alexandre Huff05b94382021-12-09 00:29:05 -0300533
Alexandre Huff11a46a42022-04-26 09:42:11 -0300534 api::RICControlHeader *ctrlHeader = request->mutable_riccontrolheaderdata();
535 ctrlHeader->set_controlstyle( 3 );
536 ctrlHeader->set_controlactionid( 1 );
537 ctrlHeader->set_ueid( ue_id );
Alexandre Huff05b94382021-12-09 00:29:05 -0300538
Alexandre Huff11a46a42022-04-26 09:42:11 -0300539 api::RICControlMessage *ctrlMsg = request->mutable_riccontrolmessagedata();
540 ctrlMsg->set_riccontrolcelltypeval( api::RIC_CONTROL_CELL_UNKWON );
541 ctrlMsg->set_targetcellid( target_cell_id );
Alexandre Huff05b94382021-12-09 00:29:05 -0300542
Alexandre Huff11a46a42022-04-26 09:42:11 -0300543 auto data = cell_map.find( target_cell_id );
544 if( data != cell_map.end() ) {
545 request->set_e2nodeid( data->second->global_nb_id.nb_id );
546 request->set_plmnid( data->second->global_nb_id.plmn_id );
547 request->set_ranname( data->second->ran_name );
548 } else {
549 request->set_e2nodeid( "unknown_e2nodeid" );
550 request->set_plmnid( "unknown_plmnid" );
551 request->set_ranname( "unknown_ranname" );
552 }
553 request->set_riccontrolackreqval( api::RIC_CONTROL_ACK_UNKWON ); // not yet used in api.proto
554
555 grpc::Status status = rc_stub->SendRICControlReqServiceGrpc( &context, *request, &response );
556
557 if( status.ok() ) {
558 if( response.rspcode() == 0 ) {
Alexandre Huff05b94382021-12-09 00:29:05 -0300559 cout << "[INFO] Control Request succeeded with code=0, description=" << response.description() << endl;
560 } else {
561 cout << "[ERROR] Control Request failed with code=" << response.rspcode()
562 << ", description=" << response.description() << endl;
563 }
564
565 } else {
566 cout << "[ERROR] failed to send a RIC Control Request message to RC xApp, error_code="
567 << status.error_code() << ", error_msg=" << status.error_message() << endl;
568 }
569
Alexandre Huff05b94382021-12-09 00:29:05 -0300570}
571
Ron Shachame7dfeb82020-04-24 14:46:48 -0400572void prediction_callback( Message& mbuf, int mtype, int subid, int len, Msg_component payload, void* data ) {
Alexandre Huffc6e27b02021-06-24 13:22:08 -0300573 string json ((char *)payload.get(), len); // RMR payload might not have a nil terminanted char
574
Alexandre Huffb86721b2021-05-28 13:32:02 -0300575 cout << "[INFO] Prediction Callback got a message, type=" << mtype << ", length=" << len << "\n";
Alexandre Huffc6e27b02021-06-24 13:22:08 -0300576 cout << "[INFO] Payload is " << json << endl;
Ron Shachame7dfeb82020-04-24 14:46:48 -0400577
Ron Shachame187d502020-05-08 12:14:44 -0400578 PredictionHandler handler;
Ron Shacham3be133f2020-05-26 13:43:44 -0400579 try {
Ron Shacham3be133f2020-05-26 13:43:44 -0400580 Reader reader;
Alexandre Huffc6e27b02021-06-24 13:22:08 -0300581 StringStream ss(json.c_str());
Ron Shacham3be133f2020-05-26 13:43:44 -0400582 reader.Parse(ss,handler);
583 } catch (...) {
Alexandre Huffb86721b2021-05-28 13:32:02 -0300584 cout << "[ERROR] Got an exception on stringstream read parse\n";
Ron Shacham3be133f2020-05-26 13:43:44 -0400585 }
Alexandre Huffb86721b2021-05-28 13:32:02 -0300586
587 // We are only considering download throughput
Ron Shacham3be133f2020-05-26 13:43:44 -0400588 unordered_map<string, int> throughput_map = handler.cell_pred_down;
Ron Shachame187d502020-05-08 12:14:44 -0400589
Alexandre Huffb86721b2021-05-28 13:32:02 -0300590 // Decision about CONTROL message
591 // (1) Identify UE Id in Prediction message
592 // (2) Iterate through Prediction message.
593 // If one of the cells has a higher throughput prediction than serving cell, send a CONTROL request
594 // We assume the first cell in the prediction message is the serving cell
Ron Shachame187d502020-05-08 12:14:44 -0400595
Alexandre Huffb86721b2021-05-28 13:32:02 -0300596 int serving_cell_throughput = 0;
597 int highest_throughput = 0;
598 string highest_throughput_cell_id;
Ron Shachame187d502020-05-08 12:14:44 -0400599
Alexandre Huffb86721b2021-05-28 13:32:02 -0300600 // Getting the current serving cell throughput prediction
601 auto cell = throughput_map.find( handler.serving_cell_id );
602 serving_cell_throughput = cell->second;
Ron Shachame187d502020-05-08 12:14:44 -0400603
Alexandre Huffb86721b2021-05-28 13:32:02 -0300604 // Iterating to identify the highest throughput prediction
Ron Shachame187d502020-05-08 12:14:44 -0400605 for (auto map_iter = throughput_map.begin(); map_iter != throughput_map.end(); map_iter++) {
Ron Shachamee5e4342020-06-11 12:34:46 -0400606
Alexandre Huffb86721b2021-05-28 13:32:02 -0300607 string curr_cellid = map_iter->first;
Ron Shacham3be133f2020-05-26 13:43:44 -0400608 int curr_throughput = map_iter->second;
Ron Shachame187d502020-05-08 12:14:44 -0400609
Alexandre Huffb86721b2021-05-28 13:32:02 -0300610 if ( highest_throughput < curr_throughput ) {
Ron Shachame187d502020-05-08 12:14:44 -0400611 highest_throughput = curr_throughput;
612 highest_throughput_cell_id = curr_cellid;
613 }
Alexandre Huffb86721b2021-05-28 13:32:02 -0300614
Ron Shachame187d502020-05-08 12:14:44 -0400615 }
616
Alexandre Huff1c844fb2022-05-10 17:00:23 -0300617 float thresh = 0;
618 if( downlink_threshold > 0 ) { // we also take into account the threshold in A1 policy type 20008
619 thresh = serving_cell_throughput * (downlink_threshold / 100.0);
620 }
621
622 if ( highest_throughput > ( serving_cell_throughput + thresh ) ) {
Alexandre Huffb86721b2021-05-28 13:32:02 -0300623
624 // sending a control request message
Alexandre Huff05b94382021-12-09 00:29:05 -0300625 if ( ts_control_api == TsControlApi::REST ) {
Alexandre Huff11a46a42022-04-26 09:42:11 -0300626 send_rest_control_request( handler.ue_id, handler.serving_cell_id, highest_throughput_cell_id );
Alexandre Huff05b94382021-12-09 00:29:05 -0300627 } else {
Alexandre Huff11a46a42022-04-26 09:42:11 -0300628 send_grpc_control_request( handler.ue_id, highest_throughput_cell_id );
Alexandre Huff05b94382021-12-09 00:29:05 -0300629 }
Alexandre Huffb86721b2021-05-28 13:32:02 -0300630
631 } else {
632 cout << "[INFO] The current serving cell \"" << handler.serving_cell_id << "\" is the best one" << endl;
Ron Shachame187d502020-05-08 12:14:44 -0400633 }
Ron Shacham3be133f2020-05-26 13:43:44 -0400634
Ron Shachame187d502020-05-08 12:14:44 -0400635}
636
Alexandre Huffb86721b2021-05-28 13:32:02 -0300637void send_prediction_request( vector<string> ues_to_predict ) {
Alexandre Huffb86721b2021-05-28 13:32:02 -0300638 std::unique_ptr<Message> msg;
Alexandre Huff11a46a42022-04-26 09:42:11 -0300639 Msg_component payload; // special type of unique pointer to the payload
Ron Shachame187d502020-05-08 12:14:44 -0400640
Alexandre Huffb86721b2021-05-28 13:32:02 -0300641 int sz;
642 int i;
643 size_t plen;
644 Msg_component send_payload;
Ron Shachame187d502020-05-08 12:14:44 -0400645
Alexandre Huffb86721b2021-05-28 13:32:02 -0300646 msg = xfw->Alloc_msg( 2048 );
Ron Shachame187d502020-05-08 12:14:44 -0400647
Alexandre Huffb86721b2021-05-28 13:32:02 -0300648 sz = msg->Get_available_size(); // we'll reuse a message if we received one back; ensure it's big enough
649 if( sz < 2048 ) {
650 fprintf( stderr, "[ERROR] message returned did not have enough size: %d [%d]\n", sz, i );
651 exit( 1 );
Ron Shachame187d502020-05-08 12:14:44 -0400652 }
Alexandre Huffb86721b2021-05-28 13:32:02 -0300653
654 string ues_list = "[";
655
656 for (int i = 0; i < ues_to_predict.size(); i++) {
657 if (i == ues_to_predict.size() - 1) {
658 ues_list = ues_list + "\"" + ues_to_predict.at(i) + "\"]";
659 } else {
660 ues_list = ues_list + "\"" + ues_to_predict.at(i) + "\"" + ",";
661 }
662 }
663
664 string message_body = "{\"UEPredictionSet\": " + ues_list + "}";
665
Alexandre Huffb86721b2021-05-28 13:32:02 -0300666 send_payload = msg->Get_payload(); // direct access to payload
Alexandre Huffc6e27b02021-06-24 13:22:08 -0300667 snprintf( (char *) send_payload.get(), 2048, "%s", message_body.c_str() );
Alexandre Huffb86721b2021-05-28 13:32:02 -0300668
Alexandre Huffc6e27b02021-06-24 13:22:08 -0300669 plen = strlen( (char *)send_payload.get() );
Alexandre Huffb86721b2021-05-28 13:32:02 -0300670
671 cout << "[INFO] Prediction Request length=" << plen << ", payload=" << send_payload.get() << endl;
672
673 // payload updated in place, nothing to copy from, so payload parm is nil
674 if ( ! msg->Send_msg( TS_UE_LIST, Message::NO_SUBID, plen, NULL )) { // msg type 30000
675 fprintf( stderr, "[ERROR] send failed: %d\n", msg->Get_state() );
676 }
677
Ron Shachame7dfeb82020-04-24 14:46:48 -0400678}
679
Deepanshu Karnwal008ad982020-11-18 12:35:12 +0530680/* This function works with Anomaly Detection(AD) xApp. It is invoked when anomalous UEs are send by AD xApp.
Alexandre Huffb86721b2021-05-28 13:32:02 -0300681 * It parses the payload received from AD xApp, sends an ACK with same UEID as payload to AD xApp, and
682 * sends a prediction request to the QP Driver xApp.
Deepanshu Karnwal008ad982020-11-18 12:35:12 +0530683 */
Alexandre Huff11a46a42022-04-26 09:42:11 -0300684void ad_callback( Message& mbuf, int mtype, int subid, int len, Msg_component payload, void* data ) {
Alexandre Huffc6e27b02021-06-24 13:22:08 -0300685 string json ((char *)payload.get(), len); // RMR payload might not have a nil terminanted char
Alexandre Huffb86721b2021-05-28 13:32:02 -0300686
687 cout << "[INFO] AD Callback got a message, type=" << mtype << ", length=" << len << "\n";
688 cout << "[INFO] Payload is " << json << "\n";
689
690 AnomalyHandler handler;
691 Reader reader;
Alexandre Huffc6e27b02021-06-24 13:22:08 -0300692 StringStream ss(json.c_str());
Alexandre Huffb86721b2021-05-28 13:32:02 -0300693 reader.Parse(ss,handler);
694
695 // just sending ACK to the AD xApp
696 mbuf.Send_response( TS_ANOMALY_ACK, Message::NO_SUBID, len, nullptr ); // msg type 30004
697
Alexandre Huffb86721b2021-05-28 13:32:02 -0300698 send_prediction_request(handler.prediction_ues);
Deepanshu Karnwal008ad982020-11-18 12:35:12 +0530699}
Ron Shachame7dfeb82020-04-24 14:46:48 -0400700
Alexandre Huff11a46a42022-04-26 09:42:11 -0300701vector<string> get_nodeb_list( restclient::RestClient& client ) {
Ron Shachame7dfeb82020-04-24 14:46:48 -0400702
Alexandre Huff11a46a42022-04-26 09:42:11 -0300703 restclient::response_t response = client.do_get( "/v1/nodeb/states" );
704
705 NodebListHandler handler;
706 if( response.status_code == 200 ) {
707 Reader reader;
708 StringStream ss( response.body.c_str() );
709 reader.Parse( ss, handler );
710
711 cout << "[INFO] nodeb list is " << response.body.c_str() << endl;
712
713 } else {
714 if( response.body.empty() ) {
715 cout << "[ERROR] Unexpected HTTP code " << response.status_code << " from " << client.getBaseUrl() << endl;
716 } else {
717 cout << "[ERROR] Unexpected HTTP code " << response.status_code << " from " << client.getBaseUrl() <<
718 ". HTTP payload is " << response.body.c_str() << endl;
719 }
720 }
721
722 return handler.nodeb_list;
723}
724
725bool build_cell_mapping() {
726 string base_url;
727 char *data = getenv( "SERVICE_E2MGR_HTTP_BASE_URL" );
728 if ( data == NULL ) {
729 base_url = "http://service-ricplt-e2mgr-http.ricplt:3800";
730 } else {
731 base_url = string( data );
732 }
733
734 restclient::RestClient client( base_url );
735
736 vector<string> nb_list = get_nodeb_list( client );
737
738 for( string nb : nb_list ) {
739 string full_path = string("/v1/nodeb/") + nb;
740 restclient::response_t response = client.do_get( full_path );
741 if( response.status_code != 200 ) {
742 if( response.body.empty() ) {
743 cout << "[ERROR] Unexpected HTTP code " << response.status_code << " from " << \
744 client.getBaseUrl() + full_path << endl;
745 } else {
746 cout << "[ERROR] Unexpected HTTP code " << response.status_code << " from " << \
747 client.getBaseUrl() + full_path << ". HTTP payload is " << response.body.c_str() << endl;
748 }
749 return false;
750 }
751
752 try {
753 NodebHandler handler;
754 Reader reader;
755 StringStream ss( response.body.c_str() );
756 reader.Parse( ss, handler );
757 } catch (...) {
758 cout << "[ERROR] Got an exception on parsing nodeb (stringstream read parse)\n";
759 return false;
760 }
761 }
762
763 return true;
764}
765
766extern int main( int argc, char** argv ) {
Ron Shachame7dfeb82020-04-24 14:46:48 -0400767 int nthreads = 1;
E. Scott Danielsf7b96952020-04-29 10:07:53 -0400768 char* port = (char *) "4560";
Alexandre Huff05b94382021-12-09 00:29:05 -0300769 shared_ptr<grpc::Channel> channel;
Ron Shachame7dfeb82020-04-24 14:46:48 -0400770
Alexandre Huff05b94382021-12-09 00:29:05 -0300771 Config *config = new Config();
772 string api = config->Get_control_str("ts_control_api");
773 ts_control_ep = config->Get_control_str("ts_control_ep");
774 if ( api.empty() ) {
775 cout << "[ERROR] a control api (rest/grpc) is required in xApp descriptor\n";
776 exit(1);
Alexandre Huffb86721b2021-05-28 13:32:02 -0300777 }
Alexandre Huff05b94382021-12-09 00:29:05 -0300778 if ( api.compare("rest") == 0 ) {
779 ts_control_api = TsControlApi::REST;
780 } else {
781 ts_control_api = TsControlApi::gRPC;
Alexandre Huff05b94382021-12-09 00:29:05 -0300782
Alexandre Huff11a46a42022-04-26 09:42:11 -0300783 if( !build_cell_mapping() ) {
784 cout << "[ERROR] unable to map cells to nodeb\n";
785 }
786
787 channel = grpc::CreateChannel(ts_control_ep, grpc::InsecureChannelCredentials());
788 rc_stub = api::MsgComm::NewStub(channel, grpc::StubOptions());
789 }
Ron Shachame7dfeb82020-04-24 14:46:48 -0400790
Alexandre Huffb86721b2021-05-28 13:32:02 -0300791 fprintf( stderr, "[TS xApp] listening on port %s\n", port );
792 xfw = std::unique_ptr<Xapp>( new Xapp( port, true ) );
Ron Shacham085267b2020-05-20 22:09:23 -0400793
Alexandre Huffb86721b2021-05-28 13:32:02 -0300794 xfw->Add_msg_cb( A1_POLICY_REQ, policy_callback, NULL ); // msg type 20010
795 xfw->Add_msg_cb( TS_QOE_PREDICTION, prediction_callback, NULL ); // msg type 30002
796 xfw->Add_msg_cb( TS_ANOMALY_UPDATE, ad_callback, NULL ); /*Register a callback function for msg type 30003*/
Ron Shachame7dfeb82020-04-24 14:46:48 -0400797
798 xfw->Run( nthreads );
Alexandre Huffb86721b2021-05-28 13:32:02 -0300799
Ron Shachame7dfeb82020-04-24 14:46:48 -0400800}