Files
openairinterface5g/common/utils/websrv/websrv_scope.c
T

329 lines
13 KiB
C

/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (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.openairinterface.org/?page_id=698
*
* 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.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
/*! \file common/utils/websrv/websrv_scope.c
* \brief: implementation of web API specific for oai softscope
* \author Francois TABURET
* \date 2022
* \version 0.1
* \company NOKIA BellLabs France
* \email: francois.taburet@nokia-bell-labs.com
* \note
* \warning
*/
#include <libgen.h>
#include <jansson.h>
#include <ulfius.h>
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
#include "common/config/config_userapi.h"
#include "common/utils/LOG/log.h"
#include "common/utils/websrv/websrv.h"
#include "executables/softmodem-common.h"
#include "common/ran_context.h"
#include "common/utils/websrv/websrv_noforms.h"
#include "openair1/PHY/TOOLS/phy_scope.h"
#include "openair1/PHY/TOOLS/phy_scope_interface.h"
#include "common/utils/load_module_shlib.h"
extern PHY_VARS_NR_UE ***PHY_vars_UE_g;
static websrv_scope_params_t scope_params = {0, 1000, NULL, NULL, 65535};
static websrv_params_t *websrvparams_ptr;
void websrv_websocket_send_scopemessage(char msg_type, char *msg_data, struct _websocket_manager *websocket_manager)
{
websrv_websocket_send_message(WEBSOCK_SRC_SCOPE, msg_type, msg_data, websocket_manager);
}
void websrv_scope_senddata(int numd, int dsize, websrv_scopedata_msg_t *msg)
{
msg->header.src = WEBSOCK_SRC_SCOPE;
int diff = scope_params.num_datamsg_sent;
char strbuff[128];
diff = __atomic_sub_fetch(&diff, scope_params.num_datamsg_ack, __ATOMIC_SEQ_CST);
if ((diff < scope_params.num_datamsg_max) || !(scope_params.statusmask & SCOPE_STATUSMASK_DATAACK)) {
int st = ulfius_websocket_send_message(websrvparams_ptr->wm, U_WEBSOCKET_OPCODE_BINARY, (numd * dsize) + WEBSOCK_HEADSIZE, (char *)msg);
if (st != U_OK)
LOG_I(UTIL, "Error sending scope IQs, status %i\n", st);
else {
scope_params.num_datamsg_sent++;
if (diff > 0 && ((scope_params.num_datamsg_sent % 10) == 0)) {
snprintf(strbuff, sizeof(strbuff), "%lu|%d", scope_params.num_datamsg_skipped, diff);
websrv_websocket_send_scopemessage(SCOPEMSG_TYPE_DATAFLOW, strbuff, websrvparams_ptr->wm);
}
}
} else {
LOG_W(UTIL, "[websrv] %i messages with no ACK\n", diff);
scope_params.num_datamsg_skipped++;
snprintf(strbuff, sizeof(strbuff), "%lu|%d", scope_params.num_datamsg_skipped, diff);
websrv_websocket_send_scopemessage(SCOPEMSG_TYPE_DATAFLOW, strbuff, websrvparams_ptr->wm);
}
};
void websrv_websocket_process_scopemessage(char msg_type, char *msg_data, struct _websocket_manager *websocket_manager)
{
LOG_I(UTIL, "[websrv] processing scope message type %i\n", msg_type);
switch (msg_type) {
case SCOPEMSG_TYPE_DATAACK:
scope_params.num_datamsg_ack++;
break;
default:
LOG_W(UTIL, "[websrv] Unprocessed scope message type: %c /n", msg_type);
break;
}
}
int websrv_scope_manager(uint64_t lcount, websrv_params_t *websrvparams)
{
time_t linuxtime;
struct tm loctime;
char strtime[64];
if (scope_params.statusmask & SCOPE_STATUSMASK_STARTED) {
if (lcount % 10 == 0) {
linuxtime = time(NULL);
localtime_r(&linuxtime, &loctime);
snprintf(strtime, sizeof(strtime), "%d/%d/%d %d:%d:%d", loctime.tm_mday, loctime.tm_mon, loctime.tm_year + 1900, loctime.tm_hour, loctime.tm_min, loctime.tm_sec);
websrv_websocket_send_scopemessage(SCOPEMSG_TYPE_TIME, strtime, websrvparams_ptr->wm);
}
if ((lcount % scope_params.refrate) == 0) {
if (IS_SOFTMODEM_GNB && (scope_params.statusmask & SCOPE_STATUSMASK_STARTED)) {
phy_scope_gNB(scope_params.scopeform, scope_params.scopedata, scope_params.selectedTarget + 1 /* ue id, +1 as used in loop < limit */);
}
if (IS_SOFTMODEM_5GUE && (scope_params.statusmask & SCOPE_STATUSMASK_STARTED)) {
phy_scope_nrUE(((scopeData_t *)((PHY_VARS_NR_UE *)(scope_params.scopedata))->scopeData)->liveData,
scope_params.scopeform,
(PHY_VARS_NR_UE *)scope_params.scopedata,
scope_params.selectedTarget /* gNB id */,
0);
}
}
}
return 0;
}
/* free scope resources, as websrv scope interface can be stopped and started */
void websrv_scope_stop(void)
{
IS_SOFTMODEM_DOSCOPE = false;
scope_params.statusmask &= ~SCOPE_STATUSMASK_STARTED;
OAI_phy_scope_t *sp = (OAI_phy_scope_t *)scope_params.scopeform;
if (sp != NULL)
for (int i = 0; sp->graph[i].graph != NULL; i++) {
websrv_free_xyplot(sp->graph[i].graph);
}
free(sp);
scope_params.scopeform = NULL;
}
char *websrv_scope_initdata(void)
{
scope_params.num_datamsg_max = 200;
if (IS_SOFTMODEM_GNB) {
scopeParms_t p;
p.ru = RC.ru[0];
p.gNB = RC.gNB[0];
gNBinitScope(&p);
scope_params.scopedata = p.gNB->scopeData;
scope_params.scopeform = create_phy_scope_gnb();
scope_params.statusmask |= SCOPE_STATUSMASK_AVAILABLE;
return "gNB";
} else if (IS_SOFTMODEM_5GUE) {
scope_params.scopedata = PHY_vars_UE_g[0][0];
nrUEinitScope(PHY_vars_UE_g[0][0]);
scope_params.scopeform = create_phy_scope_nrue(scope_params.selectedTarget);
scope_params.statusmask |= SCOPE_STATUSMASK_AVAILABLE;
return "5GUE";
} else {
LOG_I(UTIL, "[websrv] SoftScope web interface not implemented for this softmodem\n");
}
return "none";
} /* websrv_scope_init */
/* callback to process control commands received from frontend */
int websrv_scope_callback_set_params(const struct _u_request *request, struct _u_response *response, void *user_data)
{
websrv_params_t *websrvparams = (websrv_params_t *)user_data;
websrv_dump_request("scope set params ", request, websrvparams->dbglvl);
json_error_t jserr;
json_t *jsbody = ulfius_get_json_body_request(request, &jserr);
int httpstatus = 404;
char errmsg[128];
if (jsbody == NULL) {
LOG_W(UTIL, "cannot find json body in %s %s\n", request->http_url, jserr.text);
httpstatus = 400;
} else {
errmsg[0] = 0;
websrv_printjson("websrv_scope_callback_set_params: ", jsbody, websrvparams->dbglvl);
json_t *J = json_object_get(jsbody, "name");
const char *vname = json_string_value(J);
J = json_object_get(jsbody, "value");
const char *vval = json_string_value(J);
if (strcmp(vname, "startstop") == 0) {
if (strcmp(vval, "start") == 0) {
if (scope_params.statusmask & SCOPE_STATUSMASK_AVAILABLE) {
scope_params.num_datamsg_sent = 0;
scope_params.num_datamsg_ack = 0;
scope_params.num_datamsg_skipped = 0;
websrv_scope_initdata();
scope_params.statusmask |= SCOPE_STATUSMASK_STARTED;
scope_params.selectedTarget = 1; // 1 UE to be received from GUI (for xNB scope's
IS_SOFTMODEM_DOSCOPE = true; // to trigger data copy in scope buffers
}
httpstatus = 200;
} else if (strcmp(vval, "stop") == 0) {
scope_params.statusmask &= ~SCOPE_STATUSMASK_STARTED;
IS_SOFTMODEM_DOSCOPE = false;
httpstatus = 200;
} else {
LOG_W(UTIL, "invalid startstop command value: %s\n", vval);
httpstatus = 400;
}
} else if (strcmp(vname, "refrate") == 0) {
scope_params.refrate = (uint32_t)strtol(vval, NULL, 10);
httpstatus = 200;
} else if (strcmp(vname, "enabled") == 0) {
J = json_object_get(jsbody, "graphid");
const int gid = json_integer_value(J);
OAI_phy_scope_t *sp = (OAI_phy_scope_t *)scope_params.scopeform;
if (sp == NULL) {
httpstatus = 500;
} else {
sp->graph[gid].enabled = (strcmp(vval, "true") == 0) ? true : false;
httpstatus = 200;
}
} else if (strcmp(vname, "xmin") == 0) {
scope_params.xmin = strtol(vval, NULL, 10);
httpstatus = 200;
} else if (strcmp(vname, "xmax") == 0) {
scope_params.xmax = strtol(vval, NULL, 10);
httpstatus = 200;
} else if (strcmp(vname, "ymin") == 0) {
scope_params.ymin = strtol(vval, NULL, 10);
httpstatus = 200;
} else if (strcmp(vname, "ymax") == 0) {
scope_params.ymax = strtol(vval, NULL, 10);
httpstatus = 200;
} else if (strcmp(vname, "llrxmax") == 0) {
scope_params.llrxmax = strtol(vval, NULL, 10);
httpstatus = 200;
} else if (strcmp(vname, "llrxmin") == 0) {
scope_params.llrxmin = strtol(vval, NULL, 10);
httpstatus = 200;
} else if (strcmp(vname, "TargetSelect") == 0) {
scope_params.selectedTarget = strtol(vval, NULL, 10);
if (IS_SOFTMODEM_GNB && scope_params.selectedTarget > NUMBER_OF_UE_MAX) {
snprintf(errmsg, sizeof(errmsg) - 1, "max UE index is %d for this gNB", NUMBER_OF_UE_MAX);
httpstatus = 500;
scope_params.selectedTarget = 1;
} else if (IS_SOFTMODEM_5GUE && scope_params.selectedTarget > 0) {
snprintf(errmsg, sizeof(errmsg) - 1, "UE currently supports only one gNB");
httpstatus = 500;
scope_params.selectedTarget = 0;
} else {
httpstatus = 200;
}
} else if (strcmp(vname, "DataAck") == 0) {
if (strcasecmp(vval, "true") == 0) {
scope_params.statusmask |= SCOPE_STATUSMASK_DATAACK;
} else {
scope_params.statusmask &= (~SCOPE_STATUSMASK_DATAACK);
}
httpstatus = 200;
} else if (strcmp(vname, "llrythresh") == 0) {
scope_params.llr_ythresh = strtol(vval, NULL, 10);
httpstatus = 200;
} else {
httpstatus = 500;
snprintf(errmsg, sizeof(errmsg) - 1, "Unkown scope command: %s\n", vname);
}
} // jsbody not null
if (httpstatus == 200) {
ulfius_set_empty_body_response(response, httpstatus);
} else {
websrv_params_t *websrvparams = (websrv_params_t *)user_data;
websrv_string_response(errmsg, response, httpstatus, websrvparams->dbglvl);
}
return U_CALLBACK_COMPLETE;
}
int websrv_scope_callback_get_desc(const struct _u_request *request, struct _u_response *response, void *user_data)
{
websrv_params_t *websrvparams = (websrv_params_t *)user_data;
websrv_dump_request("scope get desc ", request, websrvparams->dbglvl);
json_t *jgraph = json_array();
char gtype[20];
char stitle[64];
websrv_scope_stop(); // in case it's not the first connection
if (IS_SOFTMODEM_DOSCOPE | IS_SOFTMODEM_ENB | IS_SOFTMODEM_4GUE) {
strcpy(stitle, "none");
} else {
strcpy(stitle, websrv_scope_initdata());
}
OAI_phy_scope_t *sp = (OAI_phy_scope_t *)scope_params.scopeform;
if (sp != NULL && (scope_params.statusmask & SCOPE_STATUSMASK_AVAILABLE))
for (int i = 0; sp->graph[i].graph != NULL; i++) {
json_t *agraph = NULL;
switch (sp->graph[i].chartid) {
case SCOPEMSG_DATAID_IQ:
strcpy(gtype, "IQs");
agraph = json_pack("{s:s,s:s,s:i,s:i,s:i,s:i}", "title", sp->graph[i].graph->label, "type", gtype, "id", sp->graph[i].datasetid, "srvidx", i, "w", sp->graph[i].w, "h", sp->graph[i].h);
break;
case SCOPEMSG_DATAID_LLR:
strcpy(gtype, "LLR");
agraph = json_pack("{s:s,s:s,s:i,s:i,s:i,s:i}", "title", sp->graph[i].graph->label, "type", gtype, "id", sp->graph[i].datasetid, "srvidx", i, "w", sp->graph[i].w, "h", sp->graph[i].h);
break;
case SCOPEMSG_DATAID_WF:
strcpy(gtype, "WF");
agraph = json_pack("{s:s,s:s,s:i,s:i,s:i,s:i}", "title", sp->graph[i].graph->label, "type", gtype, "id", sp->graph[i].datasetid, "srvidx", i, "w", sp->graph[i].w, "h", sp->graph[i].h);
break;
case SCOPEMSG_DATAID_TRESP:
strcpy(gtype, "TRESP");
agraph = json_pack("{s:s,s:s,s:i,s:i,s:i,s:i}", "title", sp->graph[i].graph->label, "type", gtype, "id", sp->graph[i].datasetid, "srvidx", i, "w", sp->graph[i].w, "h", sp->graph[i].h);
break;
default:
break;
}
if (agraph != NULL)
json_array_append_new(jgraph, agraph);
}
json_t *jbody = json_pack("{s:s,s:o}", "title", stitle, "graphs", jgraph);
websrv_jbody(response, jbody, 200, websrvparams->dbglvl);
return U_CALLBACK_COMPLETE;
}
void websrv_init_scope(websrv_params_t *websrvparams)
{
int (*callback_functions_scope[3])(const struct _u_request *request, struct _u_response *response, void *user_data) = {
websrv_callback_okset_softmodem_cmdvar, websrv_scope_callback_set_params, websrv_scope_callback_get_desc};
char *http_methods[3] = {"OPTIONS", "POST", "GET"};
websrvparams_ptr = websrvparams;
websrv_add_endpoint(http_methods, 3, "oaisoftmodem", "scopectrl", callback_functions_scope, websrvparams);
}
websrv_scope_params_t *websrv_scope_getparams(void)
{
return &scope_params;
}