Commit 21b3484e authored by frtabu's avatar frtabu

Initial web server implementation

parent 0cacf29d
......@@ -103,5 +103,85 @@ SpacesInParentheses: false
SpacesInSquareBrackets: false
TabWidth: 2
UseTab: Never
---
Language: JavaScript
# BasedOnStyle: Google
AccessModifierOffset: -1
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignOperands: true
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
BinPackArguments: false
BinPackParameters: false
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakStringLiterals: true
ColumnLimit: 200
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DisableFormat: false
FixNamespaceComments: true
IndentCaseLabels: true
IndentPPDirectives: None
IndentWidth: 2
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
ReflowComments: true
SortUsingDeclarations: false
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
DeriveLineEnding: true
TabWidth: 2
JavaScriptQuotes: Double
UseTab: Never
...
......@@ -3185,5 +3185,6 @@ make_driver(ue_ip ${OPENAIR2_DIR}/NETWORK_DRIVER/UE_IP ${ue_ip_src})
include (${OPENAIR_DIR}/common/utils/telnetsrv/telnetsrv_CMakeLists.txt)
include(${OPENAIR1_DIR}/PHY/CODING/nrLDPC_decoder/nrLDPC_tools/CMakeLists.txt)
include (${OPENAIR_DIR}/common/utils/websrv/websrv_CMakeLists.txt)
add_subdirectory(openair2)
......@@ -56,6 +56,13 @@ CMAKE_CMD="$CMAKE"
BUILD_ECLIPSE=0
NR="False"
OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope"
ulfiusdep=$(basename ./`find /usr/lib* -name libulfius.so`)
jssondep=$(basename ./`find /usr/lib* -name libjansson.so`)
if [ "$ulfiusdep" == "libulfius.so" -a "$jssondep" == "libjansson.so" ] ; then
OPTIONAL_LIBRARIES+=" websrv"
else
echo_warning " websrv optional build not included in build-lib option as dependencies requirements not met "
fi
RU=0
CMAKE_C_FLAGS=()
CMAKE_CXX_FLAGS=()
......@@ -650,9 +657,27 @@ function main() {
if [ ! -z "$BUILD_OPTLIB" ] ; then
for oklib in $BUILD_OPTLIB ; do
compilations $BUILD_DIR $oklib
if [ "$oklib" == "websrv" ] ; then
BUILD_WEBSRVFRONT=1
echo_info "Build of websrv frontend enabled"
fi
done
fi
############################################
# Optional targets (others than libraries) #
############################################
if [ "$BUILD_WEBSRVFRONT" = "1" ] ; then
npmv=`command npm -v`
npms=$?
nodev=`command node -v`
nodes=$?
if [ "$npms" == "0" -a "$nodes" == "0" ] ; then
compilations $BUILD_DIR websrvfront
else
echo_warning "build of websrv front-end skipped, dependencies not met : " $npmv " " $nodev
fi
fi
####################################################
# Build RF device and transport protocol libraries #
####################################################
......
......@@ -18,6 +18,7 @@ The debug level is a mask:
* bit 2: print memory allocation/free performed by the config module
* bit 3: print command line processing messages
* bit 4: disable execution abort when parameters checking fails
* bit 5: write a config file reflecting the parameters after reading the configuration file and processing the command line
As a oai user, you may have to use bit 1 (dbgl1) , to check your configuration and get the full name of a parameter you would like to modify on the command line. Other bits are for developers usage, (dbgl7 will print all debug messages).
......@@ -27,7 +28,10 @@ $ ./lte-softmodem -O libconfig:<config>:dbgl1
```bash
$ ./lte-uesoftmodem -O cmdlineonly:dbgl1
```
bit 5 (dbgl32) can be useful to detect unused parameters from a config file or to detect parameters set to their default values, and not present in the config file
To get help on supported parameters you can use specific options:
* ---help: print help for command line only parameters and for parameters not defined in a specific section
* ---help_< prefix > : print help for parameters defined under the section < prefix >
......
......@@ -88,10 +88,26 @@ int load_config_sharedlib(configmodule_interface_t *cfgptr) {
st = -1;
}
if (cfgptr->rtflags & CONFIG_SAVERUNCFG) {
sprintf(fname, "config_%s_set", cfgptr->cfgmode);
cfgptr->set = dlsym(lib_handle, fname);
if (cfgptr->set == NULL) {
printf("[CONFIG] %s %d no function %s for config mode %s\n", __FILE__, __LINE__, fname, cfgptr->cfgmode);
st = -1;
}
sprintf(fname, "config_%s_write_parsedcfg", cfgptr->cfgmode);
cfgptr->write_parsedcfg = dlsym(lib_handle, fname);
if (cfgptr->write_parsedcfg == NULL) {
printf("[CONFIG] %s %d no function %s for config mode %s\n", __FILE__, __LINE__, fname, cfgptr->cfgmode);
}
}
sprintf (fname,"config_%s_end",cfgptr->cfgmode);
cfgptr->end = dlsym(lib_handle,fname);
if (cfgptr->getlist == NULL ) {
if (cfgptr->end == NULL) {
printf("[CONFIG] %s %d no function %s for config mode %s\n",
__FILE__, __LINE__,fname, cfgptr->cfgmode);
}
......@@ -193,6 +209,7 @@ configmodule_interface_t *load_configmodule(int argc,
uint32_t tmpflags=0;
int i;
int OoptIdx=-1;
int OWoptIdx = -1;
printf("CMDLINE: ");
for (int i=0; i<argc; i++)
......@@ -209,6 +226,10 @@ configmodule_interface_t *load_configmodule(int argc,
OoptIdx=i;
}
char *OWopt = strstr(argv[i], "OW");
if (OWopt == argv[i] + 2) {
OWoptIdx = i;
}
if ( strstr(argv[i], "help_config") != NULL ) {
config_printhelp(Config_Params,CONFIG_PARAMLENGTH(Config_Params),CONFIG_SECTIONNAME);
exit(0);
......@@ -258,6 +279,11 @@ configmodule_interface_t *load_configmodule(int argc,
/* argv[0] is the exec name, always Ok */
cfgptr->argv_info[0] |= CONFIG_CMDLINEOPT_PROCESSED;
/* When reuested _(_--OW or rtflag is 5), a file with config parameters, as defined after all processing, will be created */
if (OWoptIdx >= 0) {
cfgptr->argv_info[OWoptIdx] |= CONFIG_CMDLINEOPT_PROCESSED;
cfgptr->rtflags |= CONFIG_SAVERUNCFG;
}
/* when OoptIdx is >0, -O option has been detected at position OoptIdx
* we must memorize arv[OoptIdx is Ok */
if (OoptIdx >= 0) {
......@@ -294,14 +320,18 @@ configmodule_interface_t *load_configmodule(int argc,
printf("%s ",cfgptr->cfgP[i]);
}
printf(", debug flags: 0x%08x\n",cfgptr->rtflags);
if (cfgptr->rtflags & CONFIG_PRINTPARAMS) {
cfgptr->status = malloc(sizeof(configmodule_status_t));
}
if (strstr(cfgparam,CONFIG_CMDLINEONLY) == NULL) {
i=load_config_sharedlib(cfgptr);
if (i == 0) {
printf("[CONFIG] config module %s loaded\n",cfgmode);
Config_Params[CONFIGPARAM_DEBUGFLAGS_IDX].uptr=&(cfgptr->rtflags);
int idx = config_paramidx_fromname(Config_Params, CONFIG_PARAMLENGTH(Config_Params), CONFIGP_DEBUGFLAGS);
Config_Params[idx].uptr = &(cfgptr->rtflags);
idx = config_paramidx_fromname(Config_Params, CONFIG_PARAMLENGTH(Config_Params), CONFIGP_TMPDIR);
Config_Params[idx].strptr = &(cfgptr->tmpdir);
config_get(Config_Params,CONFIG_PARAMLENGTH(Config_Params), CONFIG_SECTIONNAME );
} else {
fprintf(stderr,"[CONFIG] %s %d config module \"%s\" couldn't be loaded\n", __FILE__, __LINE__,cfgmode);
......@@ -314,6 +344,8 @@ configmodule_interface_t *load_configmodule(int argc,
cfgptr->end = (configmodule_endfunc_t)nooptfunc;
}
printf("[CONFIG] debug flags: 0x%08x\n", cfgptr->rtflags);
if (modeparams != NULL) free(modeparams);
if (cfgmode != NULL) free(cfgmode);
......@@ -326,10 +358,28 @@ configmodule_interface_t *load_configmodule(int argc,
return cfgptr;
}
/* Possibly write config file, with parameters which have been read and after command line parsing */
void write_parsedcfg(void)
{
if (cfgptr->status && (cfgptr->rtflags & CONFIG_SAVERUNCFG)) {
printf_params("[CONFIG] Runtime params creation status: %i null values, %i errors, %i empty list or array, %i successfull \n",
cfgptr->status->num_err_nullvalue,
cfgptr->status->num_err_write,
cfgptr->status->emptyla,
cfgptr->status->num_write);
}
if (cfgptr != NULL) {
if (cfgptr->write_parsedcfg != NULL) {
printf("[CONFIG] calling config module write_parsedcfg function...\n");
cfgptr->write_parsedcfg();
}
}
}
/* free memory allocated when reading parameters */
/* config module could be initialized again after this call */
void end_configmodule(void) {
write_parsedcfg();
if (cfgptr != NULL) {
if (cfgptr->end != NULL) {
printf ("[CONFIG] calling config module end function...\n");
......
......@@ -54,6 +54,7 @@
#define CONFIG_DEBUGPTR (1<<1) // print memory allocation/free debug messages
#define CONFIG_DEBUGCMDLINE (1<<2) // print command line processing messages
#define CONFIG_NOABORTONCHKF (1<<4) // disable abort execution when parameter checking function fails
#define CONFIG_SAVERUNCFG (1 << 5) // create config file with running values, as after cfg file + command line processing
#define CONFIG_NOEXITONHELP (1<<19) // do not exit after printing help
#define CONFIG_HELP (1<<20) // print help message
#define CONFIG_ABORT (1<<21) // config failed,abort execution
......@@ -61,8 +62,21 @@
typedef int(*configmodule_initfunc_t)(char *cfgP[],int numP);
typedef int(*configmodule_getfunc_t)(paramdef_t *,int numparams, char *prefix);
typedef int(*configmodule_getlistfunc_t)(paramlist_def_t *, paramdef_t *,int numparams, char *prefix);
typedef int (*configmodule_setfunc_t)(paramdef_t *cfgoptions, int numoptions, char *prefix);
typedef void(*configmodule_endfunc_t)(void);
typedef struct configmodule_status {
int num_paramgroups;
char **paramgroups_names;
int num_err_nullvalue;
int emptyla;
int num_err_read;
int num_err_write;
int num_read;
int num_write;
char *debug_cfgname;
} configmodule_status_t;
typedef struct oneBlock_s{
void *ptrs;
int sz;
......@@ -80,11 +94,15 @@ typedef struct configmodule_interface {
configmodule_initfunc_t init;
configmodule_getfunc_t get;
configmodule_getlistfunc_t getlist;
configmodule_setfunc_t set;
configmodule_endfunc_t end;
pthread_mutex_t memBlocks_mutex;
configmodule_endfunc_t write_parsedcfg;
uint32_t numptrs;
uint32_t rtflags;
oneBlock_t oneBlock[CONFIG_MAX_ALLOCATEDPTRS];
char *tmpdir;
configmodule_status_t *status; // allocated in debug mode only
} configmodule_interface_t;
#ifdef CONFIG_LOADCONFIG_MAIN
......@@ -97,16 +115,19 @@ static char config_helpstr [] = "\n lte-softmodem -O [config mode]<:dbgl[debugfl
incp parameter can be used to define the include path used for config files (@include directive)\n \
defaults is set to the path of the main config file.\n";
#define CONFIG_SECTIONNAME "config"
#define CONFIGPARAM_DEBUGFLAGS_IDX 0
#define CONFIG_HELP_TMPDIR "<Repository to create temporary file>"
#define CONFIG_SECTIONNAME "config"
#define CONFIGP_DEBUGFLAGS "debugflags"
#define CONFIGP_TMPDIR "tmpdir"
static paramdef_t Config_Params[] = {
/*-----------------------------------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------------------------------------------*/
/* config parameters for config module */
/* optname helpstr paramflags XXXptr defXXXval type numelt */
/*-----------------------------------------------------------------------------------------------------------------------*/
{"debugflags", config_helpstr, 0, uptr:NULL, defintval:0, TYPE_MASK, 0},
/*--------------------------------------------------------------------------------------------------------------------------*/
{CONFIGP_DEBUGFLAGS, config_helpstr, 0, uptr : NULL, defintval : 0, TYPE_MASK, 0},
{CONFIGP_TMPDIR, CONFIG_HELP_TMPDIR, PARAMFLAG_NOFREE, strptr : NULL, defstrval : "/tmp", TYPE_STRING, 0},
};
#else
......@@ -126,6 +147,8 @@ extern configmodule_interface_t *load_configmodule(int argc, char **argv, uint32
* a new config module init
*/
extern void end_configmodule(void);
extern void write_parsedcfg(void);
/* free all config module memory, to be used at end of program as
* it will free parameters values even those specified with the PARAMFLAG_NOFREE flag */
extern void free_configmodule(void);
......
......@@ -46,6 +46,7 @@
#define PARAMFLAG_NOFREE (1 << 3) // don't free parameter in end function
#define PARAMFLAG_BOOL (1 << 4) // integer param can be 0 or 1
#define PARAMFLAG_CMDLINE_NOPREFIXENABLED (1 << 5) // on the command line, allow a parameter to be specified without the prefix
#define PARAMFLAG_CMDLINEONLY (1 << 6) // this parameter cannot be specified in config file
/* Flags used by config modules to return info to calling modules and/or to for internal usage*/
#define PARAMFLAG_MALLOCINCONFIG (1 << 15) // parameter allocated in config module
......
......@@ -275,6 +275,9 @@ int config_get(paramdef_t *params, int numparams, char *prefix) {
if (ret >= 0) {
config_process_cmdline(params, numparams, prefix);
if (cfgif->rtflags & CONFIG_SAVERUNCFG) {
config_get_if()->set(params, numparams, prefix);
}
config_execcheck(params, numparams, prefix);
}
......@@ -312,6 +315,9 @@ int config_getlist(paramlist_def_t *ParamList, paramdef_t *params, int numparams
// TODO config_process_cmdline?
sprintf(cfgpath, "%s.[%i]", newprefix, i);
config_process_cmdline(ParamList->paramarray[i],numparams,cfgpath);
if (config_get_if()->rtflags & CONFIG_SAVERUNCFG) {
config_get_if()->set(ParamList->paramarray[i], numparams, cfgpath);
}
config_execcheck(ParamList->paramarray[i], numparams, cfgpath);
}
......
......@@ -35,8 +35,11 @@
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <libgen.h>
#include <time.h>
#include <libgen.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "config_libconfig.h"
#include "config_libconfig_private.h"
......@@ -91,9 +94,219 @@ int read_intarray(paramdef_t *cfgoptions,config_setting_t *setting, char *cfgpat
return cfgoptions->numelt;
}
int config_libconfig_setparams(paramdef_t *cfgoptions, int numoptions, config_setting_t *asetting, char *prefix)
{
int status;
int errors = 0;
int notused = 0;
int emptyla = 0;
#define LIBCONFIG_NOTUSED_PARAMS "Not used? (NULL value ptr)"
char *secprefix = (prefix == NULL) ? "" : prefix;
for (int i = 0; i < numoptions; i++) {
if (cfgoptions[i].paramflags & PARAMFLAG_CMDLINEONLY) {
continue;
printf_params("[LIBCONFIG] setting %s.%s skipped (command line only) \n", secprefix, cfgoptions[i].optname);
}
status = CONFIG_FALSE;
config_setting_t *psetting;
char *spath = malloc(((prefix == NULL) ? 0 : strlen(prefix)) + strlen(cfgoptions[i].optname) + 10);
sprintf(spath, "%s%s%s", secprefix, (prefix == NULL) ? "" : ".", cfgoptions[i].optname);
psetting = config_lookup(&(libconfig_privdata.runtcfg), spath);
free(spath);
if (psetting != NULL) {
printf_params("[LIBCONFIG] setting %s.%s already created \n", secprefix, cfgoptions[i].optname);
continue;
}
switch (cfgoptions[i].type) {
case TYPE_STRING:
psetting = config_setting_add(asetting, cfgoptions[i].optname, CONFIG_TYPE_STRING);
if (psetting != NULL) {
char *value;
if (cfgoptions[i].strptr == NULL) {
notused++;
value = LIBCONFIG_NOTUSED_PARAMS;
} else {
value = *(cfgoptions[i].strptr);
}
status = config_setting_set_string(psetting, value);
}
break;
case TYPE_STRINGLIST:
psetting = config_setting_add(asetting, cfgoptions[i].optname, CONFIG_TYPE_LIST);
if (psetting != NULL) {
if (cfgoptions[i].numelt <= 0) {
emptyla++;
printf_params("[LIBCONFIG], no element in list %s\n", cfgoptions[i].optname);
status = CONFIG_TRUE;
}
for (int j = 0; j < cfgoptions[i].numelt; j++) {
config_setting_t *elemsetting = config_setting_set_string_elem(psetting, -1, cfgoptions[i].strptr[j]);
if (elemsetting == NULL) {
fprintf(stderr, "[LIBCONFIG] Error: Creating list %s element %i value %s\n", cfgoptions[i].optname, j, cfgoptions[i].strptr[j]);
break;
} else
status = CONFIG_TRUE;
}
}
break;
case TYPE_UINT8:
case TYPE_INT8:
case TYPE_UINT16:
case TYPE_INT16:
case TYPE_UINT32:
case TYPE_INT32:
case TYPE_MASK:
psetting = config_setting_add(asetting, cfgoptions[i].optname, CONFIG_TYPE_INT);
if (psetting != NULL)
status = config_setting_set_int(psetting, (int)*(cfgoptions[i].iptr));
break;
case TYPE_UINT64:
case TYPE_INT64:
psetting = config_setting_add(asetting, cfgoptions[i].optname, CONFIG_TYPE_INT64);
if (psetting != NULL)
status = config_setting_set_int64(psetting, (int64_t) * (cfgoptions[i].i64ptr));
break;
case TYPE_UINTARRAY:
case TYPE_INTARRAY:
psetting = config_setting_add(asetting, cfgoptions[i].optname, CONFIG_TYPE_ARRAY);
if (psetting != NULL) {
if (cfgoptions[i].numelt <= 0) {
emptyla++;
printf_params("[LIBCONFIG], no element in array %s.%s\n", secprefix, cfgoptions[i].optname);
status = CONFIG_TRUE;
}
for (int j = 0; j < cfgoptions[i].numelt; j++) {
config_setting_t *elemsetting = config_setting_set_int_elem(psetting, -1, (int)(cfgoptions[i].iptr[j]));
if (elemsetting == NULL) {
fprintf(stderr, "[LIBCONFIG] Error: Creating array %s.%s, at index %i value %i\n", secprefix, cfgoptions[i].optname, j, (int)(cfgoptions[i].iptr[j]));
break;
} else
status = CONFIG_TRUE;
}
}
break;
case TYPE_DOUBLE:
psetting = config_setting_add(asetting, cfgoptions[i].optname, CONFIG_TYPE_FLOAT);
if (psetting != NULL)
status = config_setting_set_float(psetting, (double)*(cfgoptions[i].dblptr));
break;
case TYPE_IPV4ADDR:
psetting = config_setting_add(asetting, cfgoptions[i].optname, CONFIG_TYPE_STRING);
if (psetting != NULL) {
char ipstr[INET_ADDRSTRLEN];
if (inet_ntop(AF_INET, cfgoptions[i].uptr, ipstr, INET_ADDRSTRLEN) == NULL) {
notused++;
sprintf(ipstr, "undef");
} else {
status = config_setting_set_string(psetting, ipstr);
}
}
break;
case TYPE_LIST:
break;
default:
fprintf(stderr, "[LIBCONFIG] %s.%s type %i not supported\n", secprefix, cfgoptions[i].optname, cfgoptions[i].type);
status = CONFIG_FALSE;
break;
} /* switch on param type */
if (status != CONFIG_TRUE) {
errors++;
fprintf(stderr, "[LIBCONFIG] Error creating setting %i: %s.%s type %i\n", i, secprefix, cfgoptions[i].optname, cfgoptions[i].type);
}
}
printf_params("[LIBCONFIG], in group \"%s\" %i settings \n", secprefix, numoptions);
if (notused > 0)
printf_params("[LIBCONFIG], ..... %i settings with NULL value pointer\n", notused);
if (errors > 0)
fprintf(stderr, "[LIBCONFIG] .....%i settings creation errors \n", errors);
if (emptyla > 0)
fprintf(stderr, "[LIBCONFIG] .....%i empty lists or arrays settings \n", emptyla);
if (cfgptr->status) {
cfgptr->status->num_err_nullvalue += notused;
cfgptr->status->num_err_write += errors;
cfgptr->status->num_write += numoptions;
cfgptr->status->emptyla += emptyla;
}
return errors;
}
int config_libconfig_set(paramdef_t *cfgoptions, int numoptions, char *prefix)
{
char *tokctx1 = NULL;
int listidx = -1;
char *prefixbck = NULL;
char *prefix_elem1 = NULL;
config_setting_t *asetting = config_root_setting(&(libconfig_privdata.runtcfg));
if (prefix != NULL) {
prefixbck = strdup(prefix);
prefix_elem1 = strtok_r(prefixbck, ".", &tokctx1);
}
printf_params("[LIBCONFIG], processing prefix %s\n", (prefix == NULL) ? "NULL" : prefix);
/* parse the prefix , possibly creating groups, lists and list elements */
while (prefix_elem1 != NULL) {
int n1 = strlen(prefix_elem1);
char *prefix_elem2 = prefix_elem1 + n1 + 1;
printf_params("[LIBCONFIG], processing elem1 %s elem2 %s\n", prefix_elem1, prefix_elem2);
/* prefix (the path to the parameter name) may contain groups and elements from a list, which are specified with [] */
if (prefix_elem2[0] != '[') { // not a list
config_setting_t *tmpset = config_setting_lookup(asetting, prefix_elem1);
if (tmpset == NULL)
asetting = config_setting_add(asetting, prefix_elem1, CONFIG_TYPE_GROUP);
else
asetting = tmpset;
printf_params("[LIBCONFIG], creating or looking for group %s: %s\n", prefix_elem1, (asetting == NULL) ? "NOK" : "OK");
} else { // a list
listidx = (int)strtol(prefix_elem2 + 1, NULL, 10);
if (errno == EINVAL || errno == ERANGE) {
printf_params("[LIBCONFIG], Error %s looking for list index in %s \n", strerror(errno), prefix_elem2);
break;
}
config_setting_t *tmpset = config_setting_lookup(asetting, prefix_elem1);
if (tmpset == NULL)
asetting = config_setting_add(asetting, prefix_elem1, CONFIG_TYPE_LIST);
else
asetting = tmpset;
printf_params("[LIBCONFIG], creating or looking for list %s: %s\n", prefix_elem1, (asetting == NULL) ? "NOK" : "OK");
if (asetting != NULL) {
tmpset = config_setting_get_elem(asetting, listidx);
if (tmpset == NULL)
asetting = config_setting_add(asetting, NULL, CONFIG_TYPE_GROUP);
else
asetting = tmpset;
printf_params("[LIBCONFIG], creating or looking for list element %i: %s\n", listidx, (asetting == NULL) ? "NOK" : "OK");
}
prefix_elem1 = strtok_r(NULL, ".", &tokctx1); // skip the [x] elements we already took care of it
if (prefix_elem1 == NULL) {
fprintf(stderr, "[LICONFIG] End of parsing %s \n", prefix);
break;
}
}
if (asetting == NULL) {
fprintf(stderr, "[LIBCONFIG] Error creating setting %s %s %i\n", prefix_elem1, prefix_elem2, listidx);
}
prefix_elem1 = strtok_r(NULL, ".", &tokctx1);
}
free(prefixbck);
if (asetting != NULL) {
config_libconfig_setparams(cfgoptions, numoptions, asetting, prefix);
printf_params("[LIBCONFIG] %i settings added in group %s\n", numoptions, (prefix == NULL) ? "" : prefix);
return 0;
} else {
fprintf(stderr, "[LIBCONFIG] Error parsing %s params, skipped...\n", (prefix == NULL) ? "" : prefix);
return -1;
}
}
int config_libconfig_get(paramdef_t *cfgoptions,int numoptions, char *prefix ) {
config_setting_t *setting;
......@@ -354,14 +567,47 @@ int config_libconfig_init(char *cfgP[], int numP) {
free(tmppath);
return -1;
}
/* possibly init a libconfig struct for saving really used params */
if (cfgptr->rtflags & CONFIG_SAVERUNCFG) {
config_init(&(libconfig_privdata.runtcfg));
// config_set_options (&(libconfig_privdata.runtcfg), CONFIG_OPTION_ALLOW_OVERRIDES, CONFIG_TRUE);
}
free(tmppath);
return 0;
}
void config_libconfig_write_parsedcfg(void)
{
if (cfgptr->rtflags & CONFIG_SAVERUNCFG) {
char *fname = strdup(libconfig_privdata.configfile);
int newcfgflen = strlen(libconfig_privdata.configfile) + strlen(cfgptr->tmpdir) + 20;
cfgptr->status->debug_cfgname = realloc(cfgptr->status->debug_cfgname, newcfgflen);
time_t t = time(NULL);
struct tm tm = *localtime(&t);
snprintf(cfgptr->status->debug_cfgname, newcfgflen, "%s/%s-run%d_%02d_%02d_%02d%02d", cfgptr->tmpdir, basename(fname), tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min);
if (config_write_file(&(libconfig_privdata.runtcfg), cfgptr->status->debug_cfgname) != CONFIG_TRUE) {
fprintf(stderr,
"[LIBCONFIG] %s %d file %s - line %d: %s\n",
__FILE__,
__LINE__,
cfgptr->status->debug_cfgname,
config_error_line(&(libconfig_privdata.runtcfg)),
config_error_text(&(libconfig_privdata.runtcfg)));
} else {
printf("[LIBCONFIG] file %s created successfully\n", cfgptr->status->debug_cfgname);
}
free(fname);
} else {
printf("[LIBCONFIG] Cannot create config file after parsing: CONFIG_SAVERUNCFG flag not specified\n");
}
}
void config_libconfig_end(void ) {
config_destroy(&(libconfig_privdata.cfg));
if (cfgptr->rtflags & CONFIG_SAVERUNCFG) {
config_destroy(&(libconfig_privdata.runtcfg));
}
if ( libconfig_privdata.configfile != NULL ) {
free(libconfig_privdata.configfile);
libconfig_privdata.configfile=NULL;
......
......@@ -46,6 +46,7 @@ extern "C"
typedef struct libconfig_privatedata {
char *configfile;
config_t cfg;
config_t runtcfg;
} libconfig_privatedata_t;
#ifdef __cplusplus
......
......@@ -19,8 +19,8 @@ in `telnetsrv.h`
*/
static int coding_setmod_cmd(char *buff, int debug, telnet_printfunc_t prnt);
static telnetshell_cmddef_t coding_cmdarray[] = {
{"mode","[sse,avx2,stdc,none]",coding_setmod_cmd},
{"","",NULL},
{"mode", "[sse,avx2,stdc,none]", coding_setmod_cmd, {NULL}, 0, NULL},
{"", "", NULL, {NULL}, 0, NULL},
};
/*
......@@ -109,8 +109,24 @@ The telnet server is dynamically loaded, to use the `add_telnetcmd` function, t
| Fields | type |Description |
|:-----------|:------:|:-----------------------|
| `cmdname` | `char[TELNET_CMD_MAXSIZE]` | command name, as tested by the telnet server to check it should call the `cmdfunc` function |
| `helpstr` | `char[TELNET_HELPSTR_SIZE]` | character string to print when the elp`command is received from the telnet client |
| `cmdfunc` | `cmdfunc_t` | pointer to the function implementing the `cmdname` sub command. |
| `cmdname` | `char[TELNET_CMD_MAXSIZE]` | command name, as tested by the telnet server or web server to check it should call the `cmdfunc` or `webfunc` function |
| `helpstr` | `char[TELNET_HELPSTR_SIZE]` | character string to print when the help`command is received from the telnet client |
| `cmdfunc` | `cmdfunc_t` | pointer to the function implementing the `cmdname` telnet or web server sub command. |
| `webfunc` | `webfunc_t` or `webfunc_getdata_t` | pointer to the function implementing the `cmdname` web server sub command. |
| `cmdflags` | unsigned int bit mask | possible bits are defined in the next table |
| `qptr` | pointer to a thread pool queue | when `TELNETSRV_CMDFLAG_PUSHINTPOOLQ` bit is set in `cmdflags` , contains the queue where the command is pushed|
### `cmdflags ` bit definitions
These flags possibly modify the way the `telnetshell_cmddef_t` structure is used by the telnet server and/or the web server
| Fields | Description |
|:------------------------------------:|:-----------------------|
| TELNETSRV_CMDFLAG_PUSHINTPOOLQ | function defined in `cmdfunc` field is pushed in a thread pool queue instead of beeing executed synchronously. The queue is created by the telnet server code in the `add_telnetcmd` function|
| TELNETSRV_CMDFLAG_GETWEBTBLDATA | web server will use the `webfunc_getdata_t` version of the `webfunc` union. command results are written in a `webdatadef_t` structure instead of being printed |
| TELNETSRV_CMDFLAG_TELNETONLY | this command definition is only for the telnet server, web server is ignoring it |
| TELNETSRV_CMDFLAG_WEBSRVONLY | this command definition is only for the web server, telnet server is ignoring it |
| TELNETSRV_CMDFLAG_CONFEXEC | Currently only used by the web server, command execution has to be confirmed by user |
| TELNETSRV_CMDFLAG_NEEDPARAM | Currently only used by the web server, user must provide a parameter before command execution |
| TELNETSRV_CMDFLAG_WEBSRV_SETRETURNTBL | Currently only used by the web server, when a parameter modification requires a new page |
| TELNETSRV_CMDFLAG_AUTOUPDATE | Currently only used by the web server, the command can be rescheduled for result update |
[oai telnet server home](telnetsrv.md)
......@@ -71,47 +71,49 @@ paramdef_t telnetoptions[] = {
/* configuration parameters for telnet utility */
/* optname helpstr paramflags XXXptr defXXXval type numelt */
/*-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
{"listenaddr", "<listen ip address>\n", 0, uptr:&telnetparams.listenaddr, defstrval:"0.0.0.0", TYPE_IPV4ADDR, 0 },
{"listenport", "<local port>\n", 0, uptr:&(telnetparams.listenport), defuintval:9090, TYPE_UINT, 0 },
{"listenstdin", "enable input from stdin\n", PARAMFLAG_BOOL, uptr:&(telnetparams.listenstdin), defuintval:0, TYPE_UINT, 0 },
{"priority", "<scheduling policy (0-99)\n", 0, iptr:&telnetparams.priority, defuintval:0, TYPE_INT, 0 },
{"debug", "<debug level>\n", 0, uptr:NULL, defuintval:0, TYPE_UINT, 0 },
{"loopcount", "<loop command iterations>\n", 0, uptr:&(telnetparams.loopcount), defuintval:10, TYPE_UINT, 0 },
{"loopdelay", "<loop command delay (ms)>\n", 0, uptr:&(telnetparams.loopdelay), defuintval:5000, TYPE_UINT, 0 },
{"histfile", "<history file name>\n", PARAMFLAG_NOFREE, strptr:&(telnetparams.histfile), defstrval:"oaitelnet.history", TYPE_STRING, 0 },
{"histsize", "<history sizes>\n", 0, iptr:&(telnetparams.histsize), defuintval:50, TYPE_INT, 0 },
{"phypbsize", "<phy dump buff size (bytes)>\n",0, uptr:&(telnetparams.phyprntbuff_size),defuintval:65000, TYPE_UINT, 0 },
{TELNETSRV_OPTNAME_STATICMOD, "<static modules selection>\n", 0, strlistptr:NULL, defstrlistval:telnet_defstatmod,TYPE_STRINGLIST,(sizeof(telnet_defstatmod)/sizeof(char *))},
{TELNETSRV_OPTNAME_SHRMOD, "<dynamic modules selection>\n", 0, strlistptr:NULL, defstrlistval:NULL,TYPE_STRINGLIST,0 }
};
{"listenaddr", "<listen ip address>\n", 0, uptr : &telnetparams.listenaddr, defstrval : "0.0.0.0", TYPE_IPV4ADDR, 0},
{"listenport", "<local port>\n", 0, uptr : &(telnetparams.listenport), defuintval : 9090, TYPE_UINT, 0},
{"listenstdin", "enable input from stdin\n", PARAMFLAG_BOOL, uptr : &(telnetparams.listenstdin), defuintval : 0, TYPE_UINT, 0},
{"priority", "<scheduling policy (0-99)\n", 0, iptr : &telnetparams.priority, defuintval : 0, TYPE_INT, 0},
{"debug", "<debug level>\n", 0, uptr : NULL, defuintval : 0, TYPE_UINT, 0},
{"loopcount", "<loop command iterations>\n", 0, uptr : &(telnetparams.loopcount), defuintval : 10, TYPE_UINT, 0},
{"loopdelay", "<loop command delay (ms)>\n", 0, uptr : &(telnetparams.loopdelay), defuintval : 5000, TYPE_UINT, 0},
{"histfile", "<history file name>\n", PARAMFLAG_NOFREE, strptr : &(telnetparams.histfile), defstrval : "oaitelnet.history", TYPE_STRING, 0},
{"histsize", "<history sizes>\n", 0, iptr : &(telnetparams.histsize), defuintval : 50, TYPE_INT, 0},
{"logfile", "log file when redirecting", PARAMFLAG_NOFREE, strptr : &(telnetparams.logfile), defstrval : "oaisoftmodem.log", TYPE_STRING, 0},
{"phypbsize", "<phy dump buff size (bytes)>\n", 0, uptr : &(telnetparams.phyprntbuff_size), defuintval : 65000, TYPE_UINT, 0},
{TELNETSRV_OPTNAME_STATICMOD, "<static modules selection>\n", 0, strlistptr : NULL, defstrlistval : telnet_defstatmod, TYPE_STRINGLIST, (sizeof(telnet_defstatmod) / sizeof(char *))},
{TELNETSRV_OPTNAME_SHRMOD, "<dynamic modules selection>\n", 0, strlistptr : NULL, defstrlistval : NULL, TYPE_STRINGLIST, 0}};
int get_phybsize(void) {
return telnetparams.phyprntbuff_size;
};
int add_telnetcmd(char *modulename,telnetshell_vardef_t *var, telnetshell_cmddef_t *cmd );
int setoutput(char *buff, int debug, telnet_printfunc_t prnt);
int wsetoutput(char *buff, int debug, telnet_printfunc_t prnt, ...);
int setparam(char *buff, int debug, telnet_printfunc_t prnt);
int wsetparam(char *buff, int debug, telnet_printfunc_t prnt, ...);
int history_cmd(char *buff, int debug, telnet_printfunc_t prnt);
telnetshell_vardef_t telnet_vardef[] = {
{"debug",TELNET_VARTYPE_INT32,&telnetparams.telnetdbg},
{"prio",TELNET_VARTYPE_INT32,&telnetparams.priority},
{"loopc",TELNET_VARTYPE_INT32,&telnetparams.loopcount},
{"loopd",TELNET_VARTYPE_INT32,&telnetparams.loopdelay},
{"phypb",TELNET_VARTYPE_INT32,&telnetparams.phyprntbuff_size},
{"hsize",TELNET_VARTYPE_INT32,&telnetparams.histsize},
{"hfile",TELNET_VARTYPE_STRING,&telnetparams.histfile},
{"",0,NULL}
};
telnetshell_vardef_t telnet_vardef[] = {{"debug", TELNET_VARTYPE_INT32, 0, &telnetparams.telnetdbg},
{"prio", TELNET_VARTYPE_INT32, 0, &telnetparams.priority},
{"loopc", TELNET_VARTYPE_INT32, 0, &telnetparams.loopcount},
{"loopd", TELNET_VARTYPE_INT32, 0, &telnetparams.loopdelay},
{"phypb", TELNET_VARTYPE_INT32, 0, &telnetparams.phyprntbuff_size},
{"hsize", TELNET_VARTYPE_INT32, 0, &telnetparams.histsize},
{"hfile", TELNET_VARTYPE_STRING, TELNET_CHECKVAL_RDONLY, &telnetparams.histfile},
{"logfile", TELNET_VARTYPE_STRING, 0, &telnetparams.logfile},
{"", 0, 0, NULL}};
telnetshell_cmddef_t telnet_cmdarray[] = {
{"redirlog","[here,file,off]",setoutput},
{"param","[prio]",setparam},
{"history","[list,reset]",history_cmd},
{"","",NULL},
{"redirlog", "[here,file,off]", setoutput, {NULL}, TELNETSRV_CMDFLAG_TELNETONLY, NULL},
{"redirlog file", "", setoutput, {NULL}, TELNETSRV_CMDFLAG_WEBSRVONLY, NULL},
{"redirlog off", "", setoutput, {NULL}, TELNETSRV_CMDFLAG_WEBSRVONLY, NULL},
{"param", "[prio]", setparam, {wsetparam}, 0, NULL},
{"history", "[list,reset]", history_cmd, {NULL}, TELNETSRV_CMDFLAG_TELNETONLY, NULL},
{"", "", NULL, {NULL}, 0, NULL},
};
void client_printf(const char *message, ...) {
va_list va_args;
va_start(va_args, message);
......@@ -127,36 +129,39 @@ void client_printf(const char *message, ...) {
return ;
}
#define NICE_MAX 19
#define NICE_MIN -20
void set_sched(pthread_t tid, int pid, int priority) {
int rt;
struct sched_param schedp;
int policy;
char strpolicy[10];
int niceval = 0;
//sched_get_priority_max(SCHED_FIFO)
if (priority < NICE_MIN) {
if (priority < -100 && priority > -200) { // RR priority 1 to 99 (high) mapped to oai priority -101 to -199 (high)
policy = SCHED_RR;
sprintf(strpolicy, "%s", "RR");
schedp.sched_priority = -(priority + 100);
} else if (priority < 0 && priority > -100) {
policy=SCHED_FIFO;
sprintf(strpolicy,"%s","fifo");
schedp.sched_priority= NICE_MIN - priority ;
if ( (schedp.sched_priority < sched_get_priority_min(SCHED_FIFO)) ||
(schedp.sched_priority > sched_get_priority_max(SCHED_FIFO)) ) {
client_printf("Error: %i invalid prio, should be %i to %i, \n",
priority, NICE_MIN -sched_get_priority_min(SCHED_FIFO),
NICE_MIN - sched_get_priority_max(SCHED_FIFO) );
}
} else if (priority > NICE_MAX) {
schedp.sched_priority = -priority; // fifo priority 1 to 99 (high) mapped to oai priority -1 to -99 (high)
} else if (priority >= 0 && priority <= (NICE_MAX - NICE_MIN)) { // other (normal) nice value -20 to 19 mapped to oai priority 0 to 39
policy = SCHED_OTHER;
sprintf(strpolicy, "%s", "other");
schedp.sched_priority = 0;
niceval = priority + NICE_MIN;
} else if (priority > NICE_MAX - NICE_MIN && priority <= (2 * (NICE_MAX - NICE_MIN) + 1)) { // batch (normal) nice value -20 to 19 mapped to oai priority 40 to 79
policy = SCHED_BATCH;
sprintf(strpolicy, "%s", "batch");
niceval = priority + NICE_MIN - (NICE_MAX - NICE_MIN + 1);
schedp.sched_priority = 0;
} else if (priority > (2 * (NICE_MAX - NICE_MIN) + 1)) { // idle mapped to oai priority >79
policy=SCHED_IDLE;
sprintf(strpolicy,"%s","idle");
schedp.sched_priority=0;
} else {
policy=SCHED_OTHER;
sprintf(strpolicy,"%s","other");
schedp.sched_priority=0;
client_printf("Error: %i invalid priority \n", priority);
return;
}
if( tid != 0) {
rt = pthread_setschedparam(tid, policy, &schedp);
} else if(pid > 0) {
......@@ -172,15 +177,14 @@ void set_sched(pthread_t tid, int pid, int priority) {
} else {
client_printf("policy set to %s, priority %i\n",strpolicy,schedp.sched_priority);
if ( policy==SCHED_OTHER) {
if (policy == SCHED_OTHER || policy == SCHED_BATCH) {
rt = getpriority(PRIO_PROCESS,tid);
if (rt != -1) {
rt = setpriority(PRIO_PROCESS,tid,priority);
rt = setpriority(PRIO_PROCESS, tid, niceval);
if (rt < 0) {
client_printf("Error %i: %s trying to set nice value of thread %u to %i\n",
errno,strerror(errno),tid,priority);
client_printf("Error %i: %s trying to set nice value of thread %u to %i\n", errno, strerror(errno), tid, niceval);
}
} else {
client_printf("Error %i: %s trying to get nice value of thread %u \n",
......@@ -189,17 +193,17 @@ void set_sched(pthread_t tid, int pid, int priority) {
}
}
if ( policy == SCHED_OTHER) {
if (policy == SCHED_OTHER || policy == SCHED_BATCH) {
if ( tid > 0 && tid != pthread_self()) {
client_printf("setting nice value using a thread id not implemented....\n");
} else if (pid > 0) {
errno=0;
rt = setpriority(PRIO_PROCESS,pid,priority);
rt = setpriority(PRIO_PROCESS, pid, niceval);
if (rt != 0) {
client_printf("Error %i: %s calling setpriority, \n",errno,strerror(errno));
} else {
client_printf("nice value set to %i\n",priority);
client_printf("nice value set to %i\n", niceval);
}
}
}
......@@ -236,20 +240,30 @@ void redirstd(char *newfname,telnet_printfunc_t prnt ) {
fd=freopen(newfname, "w", stdout);
if (fd == NULL) {
prnt("ERROR: stdout redir to %s error %s",strerror(errno));
prnt("ERROR: stdout redir to %s error %s\n", strerror(errno));
} else {
prnt("stdout redirected to %s\n", newfname);
}
fd=freopen(newfname, "w", stderr);
if (fd == NULL) {
prnt("ERROR: stderr redir to %s error %s",strerror(errno));
prnt("ERROR: stderr redir to %s error %s\n", strerror(errno));
} else {
prnt("stderr redirected to %s\n", newfname);
}
}
int wsetoutput(char *buffer, int debug, telnet_printfunc_t prnt, ...)
{
return 0;
}
int setoutput(char *buff, int debug, telnet_printfunc_t prnt) {
char cmds[TELNET_MAX_MSGLENGTH/TELNET_CMD_MAXSIZE][TELNET_CMD_MAXSIZE];
char *logfname;
char stdout_str[64];
#define LOGFILE "logfile.log"
memset(cmds,0,sizeof(cmds));
sscanf(buff,"%9s %32s %9s %9s %9s", cmds[0],cmds[1],cmds[2],cmds[3],cmds[4] );
......@@ -265,10 +279,10 @@ int setoutput(char *buff, int debug, telnet_printfunc_t prnt) {
if (strncasecmp(cmds[0],"file",4) == 0) {
if (cmds[1][0] == 0)
logfname=LOGFILE;
logfname = telnetparams.logfile;
else
logfname=cmds[1];
prnt("Log output redirected to (%s)\n", logfname);
fflush(stdout);
redirstd(logfname,prnt);
}
......@@ -281,6 +295,11 @@ int setoutput(char *buff, int debug, telnet_printfunc_t prnt) {
return CMDSTATUS_FOUND;
} /* setoutput */
int wsetparam(char *buff, int debug, telnet_printfunc_t prnt, ...)
{
return 0;
}
int setparam(char *buff, int debug, telnet_printfunc_t prnt) {
char cmds[TELNET_MAX_MSGLENGTH/TELNET_CMD_MAXSIZE][TELNET_CMD_MAXSIZE];
memset(cmds,0,sizeof(cmds));
......@@ -344,94 +363,120 @@ int history_cmd(char *buff, int debug, telnet_printfunc_t prnt) {
/*
generic commands available for all modules loaded by the server
*/
int setgetvar(int moduleindex,char getorset,char *params) {
int n,i;
char varname[TELNET_CMD_MAXSIZE];
char *varval=NULL;
memset(varname,0,sizeof(varname));
n = sscanf(params,"%9s %ms",varname,&varval);
for ( i=0 ; telnetparams.CmdParsers[moduleindex].var[i].varvalptr != NULL ; i++) {
if ( strncasecmp(telnetparams.CmdParsers[moduleindex].var[i].varname,varname,strlen(telnetparams.CmdParsers[moduleindex].var[i].varname)) == 0) {
if (n > 0 && (getorset == 'g' || getorset == 'G')) {
client_printf("%s, %s = ", telnetparams.CmdParsers[moduleindex].module,
telnetparams.CmdParsers[moduleindex].var[i].varname );
switch(telnetparams.CmdParsers[moduleindex].var[i].vartype) {
char *telnet_getvarvalue(telnetshell_vardef_t *var, int varindex)
{
char *val;
switch (var[varindex].vartype) {
case TELNET_VARTYPE_INT32:
client_printf("%i\n",*(int32_t *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
val = malloc(64);
snprintf(val, 64, "%i", *(int32_t *)(var[varindex].varvalptr));
break;
case TELNET_VARTYPE_INT64:
client_printf("%lli\n",*(int64_t *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
val = malloc(128);
snprintf(val, 128, "%lli", (long long)*(int64_t *)(var[varindex].varvalptr));
break;
case TELNET_VARTYPE_INT16:
client_printf("%hi\n",*(short *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
val = malloc(16);
snprintf(val, 16, "%hi", *(short *)(var[varindex].varvalptr));
break;
case TELNET_VARTYPE_INT8:
client_printf("%i\n",(int)(*(int8_t *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr)));
val = malloc(16);
snprintf(val, 16, "%i", (int)(*(int8_t *)(var[varindex].varvalptr)));
break;
case TELNET_VARTYPE_UINT:
client_printf("%u\n",*(unsigned int *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
val = malloc(64);
snprintf(val, 64, "%u", *(unsigned int *)(var[varindex].varvalptr));
break;
case TELNET_VARTYPE_DOUBLE:
client_printf("%g\n",*(double *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
val = malloc(32);
snprintf(val, 32, "%g\n", *(double *)(var[varindex].varvalptr));
break;
case TELNET_VARTYPE_STRING:
client_printf("\"%s\"\n",*(char **)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
val = strdup(*(char **)(var[varindex].varvalptr));
break;
default:
client_printf("unknown type\n");
val = malloc(64);
snprintf(val, 64, "ERR:var %i unknown type", varindex);
break;
}
}
if (n > 1 && (getorset == 's' || getorset == 'S')) {
client_printf("%s, %s set to \n", telnetparams.CmdParsers[moduleindex].module,
telnetparams.CmdParsers[moduleindex].var[i].varname);
return val;
}
switch(telnetparams.CmdParsers[moduleindex].var[i].vartype) {
int telnet_setvarvalue(telnetshell_vardef_t *var, char *strval, telnet_printfunc_t prnt)
{
int st = 0;
switch (var->vartype) {
case TELNET_VARTYPE_INT32:
*(int *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr) = (int)strtol(varval,NULL,0);
client_printf("%i\n",*(int *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
*(int *)(var->varvalptr) = (int)strtol(strval, NULL, 0);
if (prnt != NULL)
prnt("%i\n", *(int *)(var->varvalptr));
break;
case TELNET_VARTYPE_INT16:
*(short *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr) = (short)strtol(varval,NULL,0);
client_printf("%hi\n",*(short *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
*(short *)(var->varvalptr) = (short)strtol(strval, NULL, 0);
if (prnt != NULL)
prnt("%hi\n", *(short *)(var->varvalptr));
break;
case TELNET_VARTYPE_INT8:
*(char *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr) = (char)strtol(varval,NULL,0);
client_printf("%i\n",*(int *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
*(char *)(var->varvalptr) = (char)strtol(strval, NULL, 0);
if (prnt != NULL)
prnt("%i\n", *(int *)(var->varvalptr));
break;
case TELNET_VARTYPE_UINT:
*(unsigned int *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr) = (unsigned int)strtol(varval,NULL,0);
client_printf("%u\n",*(unsigned int *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
*(unsigned int *)(var->varvalptr) = (unsigned int)strtol(strval, NULL, 0);
if (prnt != NULL)
prnt("%u\n", *(unsigned int *)(var->varvalptr));
break;
case TELNET_VARTYPE_DOUBLE:
*(double *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr) = strtod(varval,NULL);
client_printf("%g\n",*(double *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
*(double *)(var->varvalptr) = strtod(strval, NULL);
if (prnt != NULL)
prnt("%g\n", *(double *)(var->varvalptr));
break;
case TELNET_VARTYPE_STRING:
sprintf(*(char **)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr),"%s", varval);
client_printf("\"%s\"\n",*(char **)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
sprintf(*(char **)(var->varvalptr), "%s", strval);
if (prnt != NULL)
prnt("\"%s\"\n", *(char **)(var->varvalptr));
break;
default:
client_printf("unknown type\n");
if (prnt != NULL)
prnt("unknown type\n");
st = -1;
break;
}
return st;
}
int setgetvar(int moduleindex, char getorset, char *params)
{
int n, i;
char varname[TELNET_CMD_MAXSIZE];
char *varval = NULL;
memset(varname, 0, sizeof(varname));
n = sscanf(params, "%9s %ms", varname, &varval);
for (i = 0; telnetparams.CmdParsers[moduleindex].var[i].varvalptr != NULL; i++) {
if (strncasecmp(telnetparams.CmdParsers[moduleindex].var[i].varname, varname, strlen(telnetparams.CmdParsers[moduleindex].var[i].varname)) == 0) {
if (n > 0 && (getorset == 'g' || getorset == 'G')) {
client_printf("%s, %s = ", telnetparams.CmdParsers[moduleindex].module, telnetparams.CmdParsers[moduleindex].var[i].varname);
char *strval = telnet_getvarvalue(telnetparams.CmdParsers[moduleindex].var, i);
client_printf("%s\n", strval);
free(strval);
}
if (n > 1 && (getorset == 's' || getorset == 'S')) {
client_printf("%s, %s set to \n", telnetparams.CmdParsers[moduleindex].module, telnetparams.CmdParsers[moduleindex].var[i].varname);
telnet_setvarvalue(&(telnetparams.CmdParsers[moduleindex].var[i]), varval, client_printf);
}
}
}
......@@ -442,6 +487,14 @@ int setgetvar(int moduleindex,char getorset,char *params) {
return CMDSTATUS_VARNOTFOUND;
}
void telnetsrv_freetbldata(webdatadef_t *wdata)
{
for (int i = 0; i < wdata->numlines; i++)
for (int j = 0; j < wdata->numcols; j++)
if (wdata->columns[j].coltype & TELNET_VAR_NEEDFREE)
free(wdata->lines[i].val[j]);
}
/*----------------------------------------------------------------------------------------------------*/
char *get_time(char *buff,int bufflen) {
struct tm tmstruct;
......@@ -449,17 +502,28 @@ char *get_time(char *buff,int bufflen) {
strftime (buff, bufflen, "%Y-%m-%d %H:%M:%S.000", localtime_r(&now,&tmstruct));
return buff;
}
void telnet_pushcmd(telnetshell_cmddef_t *cmd, char *cmdbuff, telnet_printfunc_t prnt)
{
notifiedFIFO_elt_t *msg = newNotifiedFIFO_elt(sizeof(telnetsrv_qmsg_t), 0, NULL, NULL);
telnetsrv_qmsg_t *cmddata = NotifiedFifoData(msg);
cmddata->cmdfunc = (qcmdfunc_t)cmd->cmdfunc;
cmddata->prnt = prnt;
cmddata->debug = telnetparams.telnetdbg;
if (cmdbuff != NULL)
cmddata->cmdbuff = strdup(cmdbuff);
pushNotifiedFIFO(cmd->qptr, msg);
}
int process_command(char *buf) {
int process_command(char *buf, int iteration)
{
int i,j,k;
char modulename[TELNET_CMD_MAXSIZE];
char cmd[TELNET_CMD_MAXSIZE];
char *cmdb=NULL;
char *bufbck;
int rt;
memset(modulename,0,sizeof(modulename));
memset(cmd,0,sizeof(cmd));
extern char **environ;
if (strncasecmp(buf,"ex",2) == 0)
return CMDSTATUS_EXIT;
......@@ -474,6 +538,8 @@ int process_command(char *buf) {
}
for(j=0; telnetparams.CmdParsers[i].cmd[j].cmdfunc != NULL ; j++) {
if (telnetparams.CmdParsers[i].cmd[j].cmdflags & TELNETSRV_CMDFLAG_WEBSRVONLY)
continue;
client_printf(" %s %s %s\n",
telnetparams.CmdParsers[i].module,telnetparams.CmdParsers[i].cmd[j].cmdname,
telnetparams.CmdParsers[i].cmd[j].helpstr);
......@@ -483,13 +549,11 @@ int process_command(char *buf) {
return CMDSTATUS_FOUND;
}
bufbck=strdup(buf);
rt=CMDSTATUS_NOTFOUND;
j = sscanf(buf,"%19s %19s %m[^\t\n]",modulename,cmd,&cmdb);
if (telnetparams.telnetdbg > 0)
printf("process_command: %i words, module=%s cmd=%s, parameters= %s\n",j,modulename,cmd,cmdb);
printf("process_command: %i words, module=%s cmd=%s, parameters= %s\n", j, modulename, cmd, (cmdb == NULL) ? "" : cmdb);
for (i=0; j>=2 && telnetparams.CmdParsers[i].var != NULL && telnetparams.CmdParsers[i].cmd != NULL; i++) {
if ( (strncasecmp(telnetparams.CmdParsers[i].module,modulename,strlen(telnetparams.CmdParsers[i].module)) == 0)) {
......@@ -504,14 +568,10 @@ int process_command(char *buf) {
} else {
for (k=0 ; telnetparams.CmdParsers[i].cmd[k].cmdfunc != NULL ; k++) {
if (strncasecmp(cmd, telnetparams.CmdParsers[i].cmd[k].cmdname,sizeof(telnetparams.CmdParsers[i].cmd[k].cmdname)) == 0) {
if (telnetparams.CmdParsers[i].cmd[k].cmdflags & TELNETSRV_CMDFLAG_WEBSRVONLY)
continue;
if (telnetparams.CmdParsers[i].cmd[k].qptr != NULL) {
notifiedFIFO_elt_t *msg =newNotifiedFIFO_elt(sizeof(telnetsrv_qmsg_t),0,NULL,NULL);
telnetsrv_qmsg_t *cmddata=NotifiedFifoData(msg);
cmddata->cmdfunc=(qcmdfunc_t)telnetparams.CmdParsers[i].cmd[k].cmdfunc;
cmddata->prnt=client_printf;
cmddata->debug=telnetparams.telnetdbg;
cmddata->cmdbuff=cmdb ? strdup(cmdb) : NULL;
pushNotifiedFIFO(telnetparams.CmdParsers[i].cmd[k].qptr, msg);
telnet_pushcmd(&(telnetparams.CmdParsers[i].cmd[k]), (cmdb != NULL) ? strdup(cmdb) : NULL, client_printf);
} else {
telnetparams.CmdParsers[i].cmd[k].cmdfunc(cmdb, telnetparams.telnetdbg, client_printf);
}
......@@ -533,7 +593,7 @@ int process_command(char *buf) {
char tbuff[64];
client_printf(CSI "1J" CSI "1;10H " STDFMT "%s %i/%i\n",
get_time(tbuff,sizeof(tbuff)),lc,telnetparams.loopcount );
process_command(bufbck+strlen("loop")+1);
process_command(buf + strlen("loop") + 1, lc);
errno=0;
int rs = read(telnetparams.new_socket,dummybuff,sizeof(dummybuff));
......@@ -554,7 +614,6 @@ int process_command(char *buf) {
} /* loop */
} /* for i */
free(cmdb);
free(bufbck);
return rt;
}
......@@ -645,7 +704,7 @@ void run_telnetsrv(void) {
}
if (strlen(buf) > 2 ) {
status=process_command(buf);
status = process_command(buf, 0);
} else
status=CMDSTATUS_NOCMD;
......@@ -847,44 +906,35 @@ int telnetsrv_autoinit(void) {
*/
int add_telnetcmd(char *modulename, telnetshell_vardef_t *var, telnetshell_cmddef_t *cmd)
{
notifiedFIFO_t *afifo = NULL;
if( modulename == NULL || var == NULL || cmd == NULL) {
fprintf(stderr,"[TELNETSRV] Telnet server, add_telnetcmd: invalid parameters\n");
return -1;
}
int i;
for (i=0; i<TELNET_MAXCMD ; i++) {
cmdparser_t *CmdParser = &telnetparams.CmdParsers[i];
if (CmdParser->var == NULL) {
strncpy(CmdParser->module, modulename, sizeof(CmdParser->module) - 1);
CmdParser->cmd = cmd;
CmdParser->var = var;
printf("[TELNETSRV] Telnet server: module %i = %s added to shell\n",
i, CmdParser->module);
break;
}
}
if (i == TELNET_MAXCMD) {
fprintf(stderr, "[TELNETSRV] TELNET_MAXCMD reached, cannot add new module %s\n", modulename);
return -1;
}
notifiedFIFO_t *afifo = NULL;
for (int k = 0; cmd[k].cmdfunc != NULL ; k++) {
if (cmd[k].cmdflags & TELNETSRV_CMDFLAG_PUSHINTPOOLQ) {
for (int i = 0; i < TELNET_MAXCMD; i++) {
if (telnetparams.CmdParsers[i].var == NULL) {
strncpy(telnetparams.CmdParsers[i].module, modulename, sizeof(telnetparams.CmdParsers[i].module) - 1);
telnetparams.CmdParsers[i].cmd = cmd;
telnetparams.CmdParsers[i].var = var;
for (int j = 0; cmd[j].cmdfunc != NULL; j++) {
if (cmd[j].cmdflags & TELNETSRV_CMDFLAG_PUSHINTPOOLQ) {
if (afifo == NULL) {
afifo = malloc(sizeof(notifiedFIFO_t));
initNotifiedFIFO(afifo);
}
cmd[k].qptr = afifo;
cmd[j].qptr = afifo;
}
}
printf("[TELNETSRV] Telnet server: module %i = %s added to shell\n", i, telnetparams.CmdParsers[i].module);
break;
}
}
return 0;
}
/* function which will be called by the shared lib loader, to check shared lib version
against main exec version. version mismatch not considered as fatal (interfaces not supposed to change)
*/
......@@ -903,10 +953,17 @@ int telnetsrv_checkbuildver(char *mainexec_buildversion, char **shlib_buildvers
}
int telnetsrv_getfarray(loader_shlibfunc_t **farray) {
*farray=malloc(sizeof(loader_shlibfunc_t)*2);
*farray = malloc(sizeof(loader_shlibfunc_t) * 3);
(*farray)[0].fname=TELNET_ADDCMD_FNAME;
(*farray)[0].fptr=(int (*)(void) )add_telnetcmd;
(*farray)[1].fname=TELNET_POLLCMDQ_FNAME;
(*farray)[1].fptr=(int (*)(void) )poll_telnetcmdq;
return ( 2);
(*farray)[1].fptr = (int (*)(void))poll_telnetcmdq;
(*farray)[2].fname = TELNET_PUSHCMD_FNAME;
(*farray)[2].fptr = (int (*)(void))telnet_pushcmd;
return (3);
}
/* for webserver interface, needs access to some telnet server paramaters */
telnetsrv_params_t *get_telnetsrv_params(void)
{
return &telnetparams;
}
......@@ -48,19 +48,54 @@
#define CMDSTATUS_VARNOTFOUND 3
#define CMDSTATUS_NOTFOUND 4
/* definitions to store 2 dim table, used to store command results before */
/* displaying them either on console or web page */
#define TELNET_MAXLINE_NUM 100
#define TELNET_MAXCOL_NUM 10
typedef struct col {
char coltitle[TELNET_CMD_MAXSIZE];
unsigned int coltype;
} acol_t;
typedef struct line {
char *val[TELNET_MAXCOL_NUM];
} aline_t;
typedef struct webdatadef {
char tblname[TELNET_HELPSTR_SIZE];
int numlines;
int numcols;
acol_t columns[TELNET_MAXCOL_NUM];
aline_t lines[TELNET_MAXLINE_NUM];
} webdatadef_t;
/*----------------------------------------------------------------------------*/
/* structure to be used when adding a module to the telnet server */
/* This is the second parameter of the add_telnetcmd function, which can be used */
/* to add a set of new command to the telnet server shell */
typedef void(*telnet_printfunc_t)(const char* format, ...);
typedef int(*cmdfunc_t)(char*, int, telnet_printfunc_t prnt);
typedef int (*webfunc_t)(char *cmdbuff, int debug, telnet_printfunc_t prnt, ...);
typedef int (*webfunc_getdata_t)(char *cmdbuff, int debug, void *data, telnet_printfunc_t prnt);
typedef int(*qcmdfunc_t)(char*, int, telnet_printfunc_t prnt,void *arg);
#define TELNETSRV_CMDFLAG_PUSHINTPOOLQ (1<<0) // ask the telnet server to push the command in a thread pool queue
#define TELNETSRV_CMDFLAG_PUSHINTPOOLQ (1 << 0) // ask the telnet server to push the command in a thread pool queue
#define TELNETSRV_CMDFLAG_GETWEBDATA (1 << 1) // When called from web server, use the getdata variant of the function
#define TELNETSRV_CMDFLAG_TELNETONLY (1 << 2) // Command only for telnet client connections
#define TELNETSRV_CMDFLAG_WEBSRVONLY (1 << 3) // Command only for web server connections
#define TELNETSRV_CMDFLAG_CONFEXEC (1 << 4) // Ask for confirm before exec
#define TELNETSRV_CMDFLAG_GETWEBTBLDATA (1 << 8) // When called from web server, use the get table data variant of the function
#define TELNETSRV_CMDFLAG_NEEDPARAM (1 << 10) // websrv: The command needs a parameter
#define TELNETSRV_CMDFLAG_WEBSRV_SETRETURNTBL (1 << 11) // websrv: set callback returns a new table
#define TELNETSRV_CMDFLAG_AUTOUPDATE (1 << 12) // command can be re-submitted automatically for result update
typedef struct cmddef {
char cmdname[TELNET_CMD_MAXSIZE];
char helpstr[TELNET_HELPSTR_SIZE];
cmdfunc_t cmdfunc;
union {
webfunc_t webfunc;
webfunc_getdata_t webfunc_getdata;
};
unsigned int cmdflags;
void *qptr;
} telnetshell_cmddef_t;
......@@ -78,6 +113,8 @@ typedef struct telnetsrv_qmsg {
/*structure to be used when adding a module to the telnet server */
/* This is the first parameter of the add_telnetcmd function, which can be used */
/* to add a set of new variables which can be got/set from the telnet server shell */
/* var type: bits 0-3, type value (1 to 15)
* type modifier, bits 4-31 */
#define TELNET_VARTYPE_INT32 1
#define TELNET_VARTYPE_INT16 2
#define TELNET_VARTYPE_INT64 3
......@@ -85,9 +122,16 @@ typedef struct telnetsrv_qmsg {
#define TELNET_VARTYPE_DOUBLE 5
#define TELNET_VARTYPE_INT8 6
#define TELNET_VARTYPE_UINT 7
#define TELNET_CHECKVAL_RDONLY 16
#define TELNET_CHECKVAL_BOOL 32
#define TELNET_VAR_NEEDFREE 64
#define TELNET_CHECKVAL_LOGLVL 128
#define TELNET_CHECKVAL_SIMALGO 256
typedef struct variabledef {
char varname[TELNET_CMD_MAXSIZE];
char vartype;
char checkval;
void *varvalptr;
} telnetshell_vardef_t;
......@@ -113,6 +157,7 @@ typedef struct {
int priority; // server running priority
char *histfile; // command history
int histsize; // command history length
char *logfile; // filename to use when redirecting output to file
int new_socket; // socket of the client connection
int logfilefd; // file id of the log file when log output is redirected to a file
int saved_stdout; // file id of the previous stdout, used to be able to restore original stdout
......@@ -145,14 +190,25 @@ VT escape sequence definition, for smarter display....
#define STDFMT "\x1b[0m"
/*---------------------------------------------------------------------------------------------*/
#define NICE_MAX 19
#define NICE_MIN -20
#define TELNET_ADDCMD_FNAME "add_telnetcmd"
#define TELNET_POLLCMDQ_FNAME "poll_telnetcmdq"
#define TELNET_PUSHCMD_FNAME "telnet_pushcmd"
typedef int(*add_telnetcmd_func_t)(char *, telnetshell_vardef_t *, telnetshell_cmddef_t *);
typedef void(*poll_telnetcmdq_func_t)(void *qid,void *arg);
typedef void (*push_telnetcmd_func_t)(telnetshell_cmddef_t *cmd, char *cmdbuff, telnet_printfunc_t prnt);
#ifdef TELNETSERVERCODE
int add_telnetcmd(char *modulename, telnetshell_vardef_t *var, telnetshell_cmddef_t *cmd);
void set_sched(pthread_t tid, int pid,int priority);
void set_affinity(pthread_t tid, int pid, int coreid);
extern int get_phybsize(void);
#endif
#ifdef WEBSERVERCODE
extern void telnet_pushcmd(telnetshell_cmddef_t *cmd, char *cmdbuff, telnet_printfunc_t prnt);
extern telnetsrv_params_t *get_telnetsrv_params(void);
extern char *telnet_getvarvalue(telnetshell_vardef_t *var, int varindex);
extern int telnet_setvarvalue(telnetshell_vardef_t *var, char *strval, telnet_printfunc_t prnt);
extern void telnetsrv_freetbldata(webdatadef_t *wdata);
#endif
#endif
......@@ -30,6 +30,6 @@ endforeach()
install(TARGETS telnetsrv DESTINATION bin)
if (EXISTS "${OPENAIR_BUILD_DIR}/ran_build/build" AND IS_DIRECTORY "${OPENAIR_BUILD_DIR}/ran_build/build")
install(TARGETS telnetsrv DESTINATION ${OPENAIR_BUILD_DIR}/ran_build/build)
endif (EXISTS "${OPENAIR_BUILD_DIR}/ran_build/build" AND IS_DIRECTORY "${OPENAIR_BUILD_DIR}/ran_build/build")
if (EXISTS "${OPENAIR_CMAKE}/ran_build/build" AND IS_DIRECTORY "${OPENAIR_CMAKE}/ran_build/build")
install(TARGETS telnetsrv DESTINATION ${OPENAIR_CMAKE}/ran_build/build)
endif (EXISTS "${OPENAIR_CMAKE}/ran_build/build" AND IS_DIRECTORY "${OPENAIR_CMAKE}/ran_build/build")
......@@ -42,14 +42,17 @@
int loader_show_cmd(char *buff, int debug, telnet_printfunc_t prnt);
telnetshell_cmddef_t loader_cmdarray[] = {
{"show","[params,modules]",loader_show_cmd},
{"","",NULL},
{"show", "[params,modules]", loader_show_cmd, {(webfunc_t)loader_show_cmd}, 0, NULL},
{"", "", NULL, {NULL}, 0, NULL},
};
/*-------------------------------------------------------------------------------------*/
int loader_show_cmd(char *buff, int debug, telnet_printfunc_t prnt)
{
if (buff == NULL) {
prnt("ERROR wrong loader SHOW command...\n");
return 0;
}
if (debug > 0)
prnt( "loader_show_cmd received %s\n",buff);
......@@ -58,21 +61,19 @@ int loader_show_cmd(char *buff, int debug, telnet_printfunc_t prnt)
prnt( " Main executable build version: \"%s\"\n", loader_data.mainexec_buildversion);
prnt( " Default shared lib path: \"%s\"\n", loader_data.shlibpath);
prnt( " Max number of shared lib : %i\n", loader_data.maxshlibs);
}
else if (strcasestr(buff,"modules") != NULL) {
prnt( "%i shared lib have been dynamicaly loaded by the oai loader\n", loader_data.numshlibs);
for (int i=0 ; i<loader_data.numshlibs ; i++) {
prnt( " Module %i: %s\n", i,loader_data.shlibs[i].name);
prnt( " Shared library build version: \"%s\"\n",((loader_data.shlibs[i].shlib_buildversion == NULL )?"":loader_data.shlibs[i].shlib_buildversion));
prnt( " Shared library path: \"%s\"\n", loader_data.shlibs[i].thisshlib_path);
prnt( " %i function pointers registered:\n", loader_data.shlibs[i].numfunc);
for(int j=0 ; j<loader_data.shlibs[i].numfunc;j++) {
prnt( " function %i %s at %p\n",j,
loader_data.shlibs[i].funcarray[j].fname, loader_data.shlibs[i].funcarray[j].fptr);
} else if (strcasestr(buff, "modules") != NULL || buff[0] == 0 || strcasestr(buff, "show") != NULL) {
prnt("%i shared lib have been dynamicaly loaded by the oai loader\n", loader_data.numshlibs);
for (int i = 0; i < loader_data.numshlibs; i++) {
prnt(" Module %i: %s\n", i, loader_data.shlibs[i].name);
prnt(" Shared library build version: \"%s\"\n", ((loader_data.shlibs[i].shlib_buildversion == NULL) ? "" : loader_data.shlibs[i].shlib_buildversion));
prnt(" Shared library path: \"%s\"\n", loader_data.shlibs[i].thisshlib_path);
prnt(" %i function pointers registered:\n", loader_data.shlibs[i].numfunc);
for (int j = 0; j < loader_data.shlibs[i].numfunc; j++) {
prnt(" function %i %s at %p\n", j, loader_data.shlibs[i].funcarray[j].fname, loader_data.shlibs[i].funcarray[j].fptr);
}
}
} else {
prnt("%s: wrong loader command...\n",buff);
prnt("%s: wrong loader command...\n", buff);
}
return 0;
}
......
......@@ -37,14 +37,11 @@
#include "common/utils/load_module_shlib.h"
telnetshell_vardef_t loader_globalvardef[] = {
{"mainversion",TELNET_VARTYPE_STRING,&(loader_data.mainexec_buildversion)},
{"defpath",TELNET_VARTYPE_STRING,&(loader_data.shlibpath)},
{"maxshlibs",TELNET_VARTYPE_INT32,&(loader_data.maxshlibs)},
{"numshlibs",TELNET_VARTYPE_INT32,&(loader_data.numshlibs)},
{"",0,NULL}
};
telnetshell_vardef_t loader_globalvardef[] = {{"mainversion", TELNET_VARTYPE_STRING, TELNET_CHECKVAL_RDONLY, &(loader_data.mainexec_buildversion)},
{"defpath", TELNET_VARTYPE_STRING, TELNET_CHECKVAL_RDONLY, &(loader_data.shlibpath)},
{"maxshlibs", TELNET_VARTYPE_INT32, TELNET_CHECKVAL_RDONLY, &(loader_data.maxshlibs)},
{"numshlibs", TELNET_VARTYPE_INT32, TELNET_CHECKVAL_RDONLY, &(loader_data.numshlibs)},
{"", 0, 0, NULL}};
telnetshell_vardef_t *loader_modulesvardef;
extern void add_loader_cmds(void);
......
......@@ -91,9 +91,7 @@ telnetshell_cmddef_t measur_cmdarray[] = {
{"","",NULL}
};
telnetshell_vardef_t measur_vardef[] = {
{"",0,NULL}
};
telnetshell_vardef_t measur_vardef[] = {{"", 0, 0, NULL}};
/* function to be implemented in any telnetsrv_xxx_measurements.c sources
to allow common measurment code to access measurments data */
extern int get_measurgroups(telnet_measurgroupdef_t **measurgroups);
......
......@@ -44,17 +44,7 @@
#define TELNETVAR_PHYCC0 0
#define TELNETVAR_PHYCC1 1
telnetshell_vardef_t phy_vardef[] = {
//{"iqmax",TELNET_VARTYPE_INT16,NULL},
//{"iqmin",TELNET_VARTYPE_INT16,NULL},
//{"loglvl",TELNET_VARTYPE_INT32,NULL},
//{"sndslp",TELNET_VARTYPE_INT32,NULL},
//{"rxrescale",TELNET_VARTYPE_INT32,NULL},
//{"txshift",TELNET_VARTYPE_INT32,NULL},
//{"rachemin",TELNET_VARTYPE_INT32,NULL},
//{"rachdmax",TELNET_VARTYPE_INT32,NULL},
{"",0,NULL}
};
telnetshell_vardef_t phy_vardef[] = {{"", 0, 0, NULL}};
#else
......
......@@ -57,84 +57,119 @@
#include "openair1/PHY/phy_extern.h"
#include "telnetsrv_proccmd.h"
void decode_procstat(char *record, int debug, telnet_printfunc_t prnt)
void decode_procstat(char *record, int debug, telnet_printfunc_t prnt, webdatadef_t *tdata)
{
char prntline[160];
char *procfile_fiels;
char *procfile_fields;
char *strtokptr;
char *lptr;
int fieldcnt;
char toksep[2];
fieldcnt=0;
procfile_fiels =strtok_r(record," ",&strtokptr);
lptr= prntline;
fieldcnt = 0;
procfile_fields = strtok_r(record, " ", &strtokptr);
lptr = prntline;
/*http://man7.org/linux/man-pages/man5/proc.5.html gives the structure of the stat file */
while( procfile_fiels != NULL && fieldcnt < 42) {
int priority = 0;
int nice = 0;
while (procfile_fields != NULL && fieldcnt < 42) {
long int policy;
if (strlen(procfile_fiels) == 0)
if (strlen(procfile_fields) == 0)
continue;
fieldcnt++;
sprintf(toksep," ");
switch(fieldcnt) {
sprintf(toksep, " ");
switch (fieldcnt) {
case 1: /* id */
lptr+=sprintf(lptr,"%9.9s ",procfile_fiels);
sprintf(toksep,")");
if (tdata != NULL) {
tdata->lines[tdata->numlines].val[0] = strdup(procfile_fields);
}
lptr += sprintf(lptr, "%9.9s ", procfile_fields);
sprintf(toksep, ")");
break;
case 2: /* name */
lptr+=sprintf(lptr,"%20.20s ",procfile_fiels+1);
if (tdata != NULL) {
tdata->lines[tdata->numlines].val[1] = strdup(procfile_fields);
}
lptr += sprintf(lptr, "%20.20s ", procfile_fields + 1);
break;
case 3: //thread state
lptr+=sprintf(lptr," %c ",procfile_fiels[0]);
case 3: // thread state
lptr += sprintf(lptr, " %c ", procfile_fields[0]);
break;
case 14: //time in user mode
case 15: //time in kernel mode
lptr+=sprintf(lptr,"%9.9s ",procfile_fiels);
case 14: // time in user mode
case 15: // time in kernel mode
lptr += sprintf(lptr, "%9.9s ", procfile_fields);
break;
case 18: //priority
case 19: //nice
lptr+=sprintf(lptr,"%3.3s ",procfile_fiels);
case 18: // priority column index 2 in tdata, -2 to -100 (1, min to 99, highest prio)
priority = strtol(procfile_fields, NULL, 0);
case 19: // nice column index 3 in tdata 0 to 39 (-20, highest prio, to 19)
if (tdata != NULL) {
tdata->lines[tdata->numlines].val[fieldcnt - 16] = strdup(procfile_fields);
}
lptr += sprintf(lptr, "%3.3s ", procfile_fields);
nice = strtol(procfile_fields, NULL, 0);
break;
case 23: //vsize
lptr+=sprintf(lptr,"%9.9s ",procfile_fiels);
case 23: // vsize
lptr += sprintf(lptr, "%9.9s ", procfile_fields);
break;
case 39: //processor
lptr+=sprintf(lptr," %2.2s ",procfile_fiels);
case 39: // processor
if (tdata != NULL) {
tdata->lines[tdata->numlines].val[4] = strdup(procfile_fields);
}
lptr += sprintf(lptr, " %2.2s ", procfile_fields);
break;
case 41: //policy
lptr+=sprintf(lptr,"%3.3s ",procfile_fiels);
policy=strtol(procfile_fiels,NULL,0);
switch(policy) {
case 41: // policy
lptr += sprintf(lptr, "%3.3s ", procfile_fields);
policy = strtol(procfile_fields, NULL, 0);
char strschedp[64];
switch (policy) {
case SCHED_FIFO:
lptr+=sprintf(lptr,"%s ","rt: fifo");
snprintf(strschedp, sizeof(strschedp), "%s ", "rt:fifo");
priority = priority + 1; // in /proc file system priority 1 to 99 mapped to -2 to -100
break;
case SCHED_OTHER:
lptr+=sprintf(lptr,"%s ","other");
snprintf(strschedp, sizeof(strschedp), "%s ", "other");
priority = nice - NICE_MIN; // linux nice is -20 to 19
break;
case SCHED_IDLE:
lptr+=sprintf(lptr,"%s ","idle");
snprintf(strschedp, sizeof(strschedp), "%s ", "idle");
priority = 2 * (NICE_MAX - NICE_MIN + 1);
break;
case SCHED_BATCH:
lptr+=sprintf(lptr,"%s ","batch");
snprintf(strschedp, sizeof(strschedp), "%s ", "batch");
priority = (NICE_MAX - NICE_MIN + 1) + nice - NICE_MIN;
break;
case SCHED_RR:
lptr+=sprintf(lptr,"%s ","rt: rr");
snprintf(strschedp, sizeof(strschedp), "%s ", "rt:rr");
priority = priority - 99;
break;
#ifdef SCHED_DEADLINE
case SCHED_DEADLINE:
snprintf(strschedp, sizeof(strschedp), "%s ", "rt:deadline");
break;
#endif
default:
lptr+=sprintf(lptr,"%s ","????");
snprintf(strschedp, sizeof(strschedp), "%s ", "????");
break;
}
lptr += sprintf(lptr, "%s ", strschedp);
if (tdata != NULL) {
tdata->lines[tdata->numlines].val[5] = strdup(strschedp);
tdata->lines[tdata->numlines].val[6] = malloc(10);
snprintf(tdata->lines[tdata->numlines].val[6], 9, "%i", priority);
}
break;
default:
break;
}/* switch on fieldcnr */
procfile_fiels =strtok_r(NULL,toksep,&strtokptr);
} /* while on proc_fields != NULL */
prnt("%s\n",prntline);
} /* switch on fieldcnr */
procfile_fields = strtok_r(NULL, toksep, &strtokptr);
} /* while on proc_fields != NULL */
prnt("%s\n", prntline);
if (tdata != NULL) {
tdata->numlines++;
}
} /*decode_procstat */
void read_statfile(char *fname,int debug, telnet_printfunc_t prnt)
void read_statfile(char *fname, int debug, telnet_printfunc_t prnt, webdatadef_t *tdata)
{
FILE *procfile;
char arecord[1024];
......@@ -152,44 +187,205 @@ char arecord[1024];
return;
}
fclose(procfile);
decode_procstat(arecord, debug, prnt);
decode_procstat(arecord, debug, prnt, tdata);
}
void print_threads(char *buf, int debug, telnet_printfunc_t prnt)
int nullprnt(char *fmt, ...)
{
return 0;
}
void proccmd_get_threaddata(char *buf, int debug, telnet_printfunc_t fprnt, webdatadef_t *tdata)
{
char aname[256];
DIR *proc_dir;
struct dirent *entry;
telnet_printfunc_t prnt = (fprnt != NULL) ? fprnt : (telnet_printfunc_t)nullprnt;
if (tdata != NULL) {
tdata->numcols = 7;
snprintf(tdata->columns[0].coltitle, sizeof(tdata->columns[0].coltitle), "thread id");
tdata->columns[0].coltype = TELNET_VARTYPE_STRING | TELNET_CHECKVAL_RDONLY | TELNET_VAR_NEEDFREE;
snprintf(tdata->columns[1].coltitle, sizeof(tdata->columns[1].coltitle), "thread name");
tdata->columns[1].coltype = TELNET_VARTYPE_STRING | TELNET_CHECKVAL_RDONLY | TELNET_VAR_NEEDFREE;
snprintf(tdata->columns[2].coltitle, sizeof(tdata->columns[2].coltitle), "priority");
tdata->columns[2].coltype = TELNET_VARTYPE_STRING | TELNET_CHECKVAL_RDONLY | TELNET_VAR_NEEDFREE;
snprintf(tdata->columns[3].coltitle, sizeof(tdata->columns[3].coltitle), "nice");
tdata->columns[3].coltype = TELNET_VARTYPE_STRING | TELNET_CHECKVAL_RDONLY | TELNET_VAR_NEEDFREE;
snprintf(tdata->columns[4].coltitle, sizeof(tdata->columns[4].coltitle), "core");
tdata->columns[4].coltype = TELNET_VARTYPE_STRING | TELNET_VAR_NEEDFREE;
snprintf(tdata->columns[5].coltitle, sizeof(tdata->columns[5].coltitle), "sched policy");
tdata->columns[5].coltype = TELNET_VARTYPE_STRING | TELNET_CHECKVAL_RDONLY | TELNET_VAR_NEEDFREE;
snprintf(tdata->columns[5].coltitle, sizeof(tdata->columns[5].coltitle), "sched policy");
tdata->columns[6].coltype = TELNET_VARTYPE_STRING | TELNET_VAR_NEEDFREE;
snprintf(tdata->columns[6].coltitle, sizeof(tdata->columns[6].coltitle), "oai priority");
tdata->numlines = 0;
}
unsigned int eax = 11, ebx = 0, ecx = 1, edx = 0;
asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "0"(eax), "2"(ecx) :);
prnt(" id name state USRmod KRNmod prio nice vsize proc pol \n\n");
snprintf(aname, sizeof(aname), "/proc/%d/stat", getpid());
read_statfile(aname,debug,prnt);
prnt("\n");
snprintf(aname, sizeof(aname), "/proc/%d/task", getpid());
proc_dir = opendir(aname);
if (proc_dir == NULL)
{
prnt("Error: Couldn't open %s %i %s\n",aname,errno,strerror(errno));
prnt("System has %d cores %d threads %d Actual threads", eax, ebx, edx);
prnt("\n id name state USRmod KRNmod prio nice vsize proc pol \n\n");
snprintf(aname, sizeof(aname), "/proc/%d/stat", getpid());
read_statfile(aname, debug, prnt, NULL);
prnt("\n");
snprintf(aname, sizeof(aname), "/proc/%d/task", getpid());
proc_dir = opendir(aname);
if (proc_dir == NULL) {
prnt("Error: Couldn't open %s %i %s\n", aname, errno, strerror(errno));
return;
}
}
while ((entry = readdir(proc_dir)) != NULL)
{
if(entry->d_name[0] == '.')
continue;
snprintf(aname, sizeof(aname), "/proc/%d/task/%.*s/stat", getpid(),(int)(sizeof(aname)-24),entry->d_name);
read_statfile(aname,debug,prnt);
} /* while entry != NULL */
closedir(proc_dir);
while ((entry = readdir(proc_dir)) != NULL) {
if (entry->d_name[0] != '.') {
snprintf(aname, sizeof(aname), "/proc/%d/task/%.*s/stat", getpid(), (int)(sizeof(aname) - 24), entry->d_name);
read_statfile(aname, debug, prnt, tdata);
}
} /* while entry != NULL */
closedir(proc_dir);
} /* print_threads */
void print_threads(char *buf, int debug, telnet_printfunc_t prnt)
{
proccmd_get_threaddata(buf, debug, prnt, NULL);
}
int proccmd_websrv_getdata(char *cmdbuff, int debug, void *data, telnet_printfunc_t prnt)
{
webdatadef_t *logsdata = (webdatadef_t *)data;
if (strncmp(cmdbuff, "set", 3) == 0) {
telnet_printfunc_t printfunc = (prnt != NULL) ? prnt : (telnet_printfunc_t)printf;
if (strcasestr(cmdbuff, "loglvl") != NULL) {
int level = map_str_to_int(log_level_names, logsdata->lines[0].val[1]);
int enabled = (strcmp(logsdata->lines[0].val[2], "true") == 0) ? 1 : 0;
int loginfile = (strcmp(logsdata->lines[0].val[3], "true") == 0) ? 1 : 0;
set_log(logsdata->numlines, level);
if (enabled == 0)
set_log(logsdata->numlines, OAILOG_DISABLE);
if (loginfile == 1) {
set_component_filelog(logsdata->numlines);
} else {
close_component_filelog(logsdata->numlines);
}
printfunc("%s log level %s is %s, output to %s\n",
logsdata->lines[0].val[0],
logsdata->lines[0].val[1],
enabled ? "enabled" : "disabled",
loginfile ? g_log->log_component[logsdata->numlines].filelog_name : "stdout");
}
if (strcasestr(cmdbuff, "logopt") != NULL) {
int optbit = map_str_to_int(log_options, logsdata->lines[0].val[0]);
if (optbit < 0) {
printfunc("option %s unknown\n", logsdata->lines[0].val[0]);
} else {
if (strcmp(logsdata->lines[0].val[1], "true") == 0) {
SET_LOG_OPTION(optbit);
} else {
CLEAR_LOG_OPTION(optbit);
}
printfunc("%s log option %s\n", logsdata->lines[0].val[0], (strcmp(logsdata->lines[0].val[1], "true") == 0) ? "enabled" : "disabled");
}
}
if (strcasestr(cmdbuff, "dbgopt") != NULL) {
int optbit = map_str_to_int(log_maskmap, logsdata->lines[0].val[0]);
if (optbit < 0) {
printfunc("debug option %s unknown\n", logsdata->lines[0].val[0]);
} else {
if (strcmp(logsdata->lines[0].val[1], "true") == 0)
SET_LOG_DEBUG(optbit);
else
CLEAR_LOG_DEBUG(optbit);
if (strcmp(logsdata->lines[0].val[2], "true") == 0)
SET_LOG_DUMP(optbit);
else
CLEAR_LOG_DUMP(optbit);
printfunc("%s debug %s dump %s\n",
logsdata->lines[0].val[0],
(strcmp(logsdata->lines[0].val[1], "true") == 0) ? "enabled" : "disabled",
(strcmp(logsdata->lines[0].val[2], "true") == 0) ? "enabled" : "disabled");
}
}
if (strcasestr(cmdbuff, "threadsched") != NULL) {
unsigned int tid = strtoll(logsdata->lines[0].val[0], NULL, 0);
unsigned int core = strtoll(logsdata->lines[0].val[4], NULL, 0);
int priority = strtoll(logsdata->lines[0].val[6], NULL, 0);
printfunc("Thread %s id %u set affinity to core %u\n", logsdata->lines[0].val[0], tid, core);
set_affinity(0, (pid_t)tid, core);
set_sched(0, (pid_t)tid, priority);
}
} else { /* end of set, => show */
if (strcasestr(cmdbuff, "loglvl") != NULL) {
logsdata->numcols = 4;
logsdata->numlines = 0;
snprintf(logsdata->columns[0].coltitle, TELNET_CMD_MAXSIZE, "component");
logsdata->columns[0].coltype = TELNET_VARTYPE_STRING | TELNET_CHECKVAL_RDONLY;
snprintf(logsdata->columns[1].coltitle, TELNET_CMD_MAXSIZE, "level");
logsdata->columns[1].coltype = TELNET_VARTYPE_STRING | TELNET_CHECKVAL_LOGLVL;
snprintf(logsdata->columns[2].coltitle, TELNET_CMD_MAXSIZE, "enabled");
logsdata->columns[2].coltype = TELNET_CHECKVAL_BOOL;
snprintf(logsdata->columns[3].coltitle, TELNET_CMD_MAXSIZE, "in file");
logsdata->columns[3].coltype = TELNET_CHECKVAL_BOOL;
for (int i = MIN_LOG_COMPONENTS; i < MAX_LOG_COMPONENTS; i++) {
if (g_log->log_component[i].name != NULL) {
logsdata->numlines++;
logsdata->lines[i].val[0] = (char *)(g_log->log_component[i].name);
logsdata->lines[i].val[1] = map_int_to_str(log_level_names, (g_log->log_component[i].level >= 0) ? g_log->log_component[i].level : g_log->log_component[i].savedlevel);
logsdata->lines[i].val[2] = (g_log->log_component[i].level >= 0) ? "true" : "false";
logsdata->lines[i].val[3] = (g_log->log_component[i].filelog > 0) ? "true" : "false";
}
}
}
if (strcasestr(cmdbuff, "dbgopt") != NULL) {
webdatadef_t *logsdata = (webdatadef_t *)data;
logsdata->numcols = 3;
logsdata->numlines = 0;
snprintf(logsdata->columns[0].coltitle, TELNET_CMD_MAXSIZE, "module");
logsdata->columns[0].coltype = TELNET_VARTYPE_STRING | TELNET_CHECKVAL_RDONLY;
snprintf(logsdata->columns[1].coltitle, TELNET_CMD_MAXSIZE, "debug");
logsdata->columns[1].coltype = TELNET_CHECKVAL_BOOL;
snprintf(logsdata->columns[2].coltitle, TELNET_CMD_MAXSIZE, "dump");
logsdata->columns[2].coltype = TELNET_CHECKVAL_BOOL;
for (int i = 0; log_maskmap[i].name != NULL; i++) {
logsdata->numlines++;
logsdata->lines[i].val[0] = log_maskmap[i].name;
logsdata->lines[i].val[1] = (g_log->debug_mask & log_maskmap[i].value) ? "true" : "false";
logsdata->lines[i].val[2] = (g_log->dump_mask & log_maskmap[i].value) ? "true" : "false";
}
}
if (strcasestr(cmdbuff, "logopt") != NULL) {
webdatadef_t *logsdata = (webdatadef_t *)data;
logsdata->numcols = 2;
logsdata->numlines = 0;
snprintf(logsdata->columns[0].coltitle, TELNET_CMD_MAXSIZE, "option");
logsdata->columns[0].coltype = TELNET_VARTYPE_STRING | TELNET_CHECKVAL_RDONLY;
snprintf(logsdata->columns[1].coltitle, TELNET_CMD_MAXSIZE, "enabled");
logsdata->columns[1].coltype = TELNET_CHECKVAL_BOOL;
for (int i = 0; log_options[i].name != NULL; i++) {
logsdata->numlines++;
logsdata->lines[i].val[0] = log_options[i].name;
logsdata->lines[i].val[1] = (g_log->flag & log_options[i].value) ? "true" : "false";
}
}
if (strcasestr(cmdbuff, "threadsched") != NULL) {
proccmd_get_threaddata(cmdbuff, debug, prnt, (webdatadef_t *)data);
}
} // show
return 0;
}
int proccmd_show(char *buf, int debug, telnet_printfunc_t prnt)
{
if (buf == NULL) {
prnt("ERROR wrong softmodem SHOW command...\n");
return 0;
}
if (debug > 0)
prnt(" proccmd_show received %s\n",buf);
if (strcasestr(buf,"thread") != NULL) {
......@@ -265,6 +461,10 @@ int bv1,bv2;
int res;
char sv1[64];
if (buf == NULL) {
prnt("ERROR wrong thread command...\n");
return 0;
}
bv1=0;
bv2=0;
sv1[0]=0;
......@@ -279,7 +479,7 @@ char sv1[64];
prnt(" proccmd_thread: %i params = %i,%s,%i\n",res,bv1,sv1,bv2);
if(res != 3)
{
prnt("softmodem thread needs 3 params, %i received\n",res);
print_threads(buf, debug, prnt);
return 0;
}
......@@ -302,11 +502,20 @@ int proccmd_exit(char *buf, int debug, telnet_printfunc_t prnt)
{
if (debug > 0)
prnt("process module received %s\n",buf);
end_configmodule();
exit_fun("telnet server received exit command\n");
return 0;
}
int proccmd_restart(char *buf, int debug, telnet_printfunc_t prnt)
{
if (debug > 0)
prnt("process module received %s\n", buf);
end_configmodule();
configmodule_interface_t *cfg = config_get_if();
execvpe(cfg->argv[0], cfg->argv, environ);
return 0;
}
int proccmd_log(char *buf, int debug, telnet_printfunc_t prnt)
{
......@@ -391,7 +600,7 @@ int s = sscanf(buf,"%ms %i-%i\n",&logsubcmd, &idx1,&idx2);
SET_LOG_DUMP(optbit);
else
CLEAR_LOG_DUMP(optbit);
proccmd_show("dbgopt",debug,prnt);
proccmd_show("dump", debug, prnt);
}
}
if (logparam != NULL) free(logparam);
......@@ -431,7 +640,8 @@ int s = sscanf(buf,"%ms %i-%i\n",&logsubcmd, &idx1,&idx2);
prnt("%s%s unknown log sub command \n",logparam, tmpstr);
}
} else {
prnt("%s unknown log sub command \n",logsubcmd);
level = map_str_to_int(log_level_names, tmpstr);
prnt("%s unknown log sub command \n", logsubcmd);
}
if (logparam != NULL) free(logparam);
if (tmpstr != NULL) free(tmpstr);
......
......@@ -44,11 +44,12 @@
extern int proccmd_show(char *buf, int debug, telnet_printfunc_t prnt);
extern int proccmd_thread(char *buf, int debug, telnet_printfunc_t prnt);
extern int proccmd_exit(char *buf, int debug, telnet_printfunc_t prnt);
extern int proccmd_restart(char *buf, int debug, telnet_printfunc_t prnt);
extern int proccmd_log(char *buf, int debug, telnet_printfunc_t prnt);
telnetshell_vardef_t proc_vardef[] = {
{"",0,NULL}
};
#define PROCCMD_LOG_HELP_STRING " log sub commands: \n\
extern int proccmd_websrv_getdata(char *cmdbuff, int debug, void *data, telnet_printfunc_t prnt);
telnetshell_vardef_t proc_vardef[] = {{"", 0, 0, NULL}};
#define PROCCMD_LOG_HELP_STRING \
" log sub commands: \n\
show: display current log configuration \n\
online, noonline: enable or disable console logs \n\
enable, disable id1-id2: enable or disable logs for components index id1 to id2 \n\
......@@ -62,22 +63,30 @@ telnetshell_vardef_t proc_vardef[] = {
dump_<mod> debug_<mod > disable or enable the debug file generation or debug code\
for \"mod\" module. use the show command to get the available modules\n"
#define PROCCMD_THREAD_HELP_STRING " thread sub commands: \n\
#define PROCCMD_THREAD_HELP_STRING \
" thread sub commands: \n\
<thread id> aff <core> : set affinity of thread <thread id> to core <core> \n\
<thread id> prio <prio> : set scheduling parameters for thread <thread id> \n\
if prio < -20: linux scheduling policy set to FIFO, \n\
with priority = -20 - prio \n\
if prio > 19: linux scheduling policy set to OTHER, \n\
with priority (nice value) = prio \n\
-100 < prio < 0: linux scheduling policy set to FIFO, priority -prio, \n\
-200 < prio < -100: linux scheduling policy set to RR, priority -(prio+100), \n\
0 <= prio < 40: linux scheduling policy set to NORMAL, nice value prio-20 \n\
40 <= prio < 80: linux scheduling policy set to BATCH, nice value prio-60 \n\
prio >=80: linux scheduling policy set to IDLE \n\
use \"softmodem show thread\" to get <thread id> \n"
telnetshell_cmddef_t proc_cmdarray[] = {
{"show","loglvl|thread|config", proccmd_show},
{"log","(enter help for details)", proccmd_log},
{"thread","(enter help for details)", proccmd_thread},
{"exit","", proccmd_exit},
{"","",NULL},
{"show", "loglvl|thread|config", proccmd_show, {(webfunc_t)proccmd_websrv_getdata}, TELNETSRV_CMDFLAG_TELNETONLY, NULL},
{"log", "(enter help for details)", proccmd_log, {NULL}, TELNETSRV_CMDFLAG_TELNETONLY, NULL},
{"show loglvl", "", proccmd_log, {(webfunc_t)proccmd_websrv_getdata}, TELNETSRV_CMDFLAG_WEBSRVONLY | TELNETSRV_CMDFLAG_GETWEBTBLDATA, NULL},
{"show logopt", "", proccmd_log, {(webfunc_t)proccmd_websrv_getdata}, TELNETSRV_CMDFLAG_WEBSRVONLY | TELNETSRV_CMDFLAG_GETWEBTBLDATA, NULL},
{"show dbgopt", "", proccmd_log, {(webfunc_t)proccmd_websrv_getdata}, TELNETSRV_CMDFLAG_WEBSRVONLY | TELNETSRV_CMDFLAG_GETWEBTBLDATA, NULL},
{"show config", "", proccmd_show, {(webfunc_t)proccmd_show}, TELNETSRV_CMDFLAG_WEBSRVONLY, NULL},
{"show thread", "", proccmd_thread, {(webfunc_t)proccmd_show}, TELNETSRV_CMDFLAG_WEBSRVONLY | TELNETSRV_CMDFLAG_AUTOUPDATE, NULL},
{"thread", "(enter help for details)", proccmd_thread, {NULL}, TELNETSRV_CMDFLAG_TELNETONLY, NULL},
{"show threadsched", "", proccmd_show, {(webfunc_t)proccmd_websrv_getdata}, TELNETSRV_CMDFLAG_WEBSRVONLY | TELNETSRV_CMDFLAG_GETWEBTBLDATA, NULL},
{"exit", "", proccmd_exit, {NULL}, TELNETSRV_CMDFLAG_CONFEXEC, NULL},
{"restart", "", proccmd_restart, {NULL}, TELNETSRV_CMDFLAG_CONFEXEC, NULL},
{"", "", NULL},
};
#else
extern void add_proccmd_cmds(void);
......
The oai web server is an optional monitoring and debugging tool. Purpose is to give access to oai softmodems functionalities via a web interface. In this first release interface to the telnet server commands and to the softscope are delivered.
* [Using the web server](webserverusage.md)
* [enhancing the webserver](webserverdevcmd.md)
* [web server architecture ](webserverarch.md)
[oai Wikis home](https://gitlab.eurecom.fr/oai/openairinterface5g/wikis/home)
# web server interface principles
The web server interface is implemented in two parts: a back-end, included in the softmodem process and a front-end which is a browser application.
The oai web server back-end is implemented in a shared library to be loaded by the [oai shared library loader](loader) when `--websrv` option is specified on the command line. `libwebsrv.so ` code is common to all oai softmodem executables, the current release has been tested with the nr UE and the gNB.
The front-end is an [angular](https://angular.io/docs) application. After being built and installed it is delivered to browsers by the back-end at connection time.
Front-end and back-end communicate via http request - http response transactions, including `json` body. these `json` interfaces are defined in the [frontend/src/app/api/XXXX.api.ts](https://gitlab.eurecom.fr/oai/openairinterface5g/tree/develop/common/utils/websrv/src/frontend/src/app/api/) files. Back-end decapsulates the http requests it receives, looking for the `json`body to determine the requested information or command. Then the back-end builds a http response, with a `json`body encapsulating the requested information or the requested command result.
When unsolicited communication, from back-end to front-end is necessary, a [websocket](https://www.rfc-editor.org/rfc/rfc6455) link is opened. This is the case for the softscope interface.
# web server interface source files
web server source files are located in [common/utils/websrv](https://gitlab.eurecom.fr/oai/openairinterface5g/tree/develop/common/utils/websrv)
1. back-end files are directly located in the `websrv` repository
1. The [frontend/src](https://gitlab.eurecom.fr/oai/openairinterface5g/tree/develop/common/utils/websrv/src/frontend) sub-directory contains the angular front-end source tree.
1. [common/utils/websrv/helpfiles](https://gitlab.eurecom.fr/oai/openairinterface5g/tree/develop/common/utils/websrv/helpfiles) contains files delivered to the front-end to respond to help requests.
[oai web server interface home](websrv.md)
\ No newline at end of file
When all dependencies are met, you can build the all the web server interface components using the build_oai script with the `--build-lib all` option . As the web interface is an optional component, if it's dependencies are not found it won't stop the build. Web interface components (back-end or front-end) which cannot be built are just skipped.
###### build example when missing back-end dependencies
```
./build_oai --build-lib all --nrUE
websrv optional build not included in build-lib option as dependencies requirements not met
Enabling build of all optional shared libraries (telnetsrv enbscope uescope nrscope)
Will compile NR UE
RF HW set to None
2. Setting the OAI PATHS ...
OPENAIR_DIR = /usr/local/oai/oai-develop/openairinterface5g
FreeDiameter prefix not found, install freeDiameter if EPC, HSS
3. building the compilation directives ...
running cmake ../../..
NETTLE VERSION_INSTALLED = 3.5.1
NETTLE_VERSION_MAJOR = 3
NETTLE_VERSION_MINOR = 5
cuda include /usr/include
cuda library
-- CMAKE_BUILD_TYPE is RelWithDebInfo
-- CPUARCH x86_64
-- AVX512 intrinsics are OFF
-- AVX2 intrinsics are ON
-- No T1 Offload support detected
calling protoc_call=/usr/local/oai/oai-develop/openairinterface5g/cmake_targets/tools/generate_protobuf FSPT_C_DIR=/usr/local/oai/oai-develop/openairinterface5g/cmake_targets/ran_build/build/CMakeFiles/FSPT_V2 FSPT_MSG_DIR=/usr/local/oai/oai-develop/openairinterface5g/targets/COMMON/MESSAGES/V2 FSPT_MSG_FILES=/usr/local/oai/oai-develop/openairinterface5g/targets/COMMON/MESSAGES/V2/flexsplit.proto
[libprotobuf WARNING google/protobuf/compiler/parser.cc:546] No syntax specified for the proto file: flexsplit.proto. Please use 'syntax = "proto2";' or 'syntax = "proto3";' to specify a syntax version. (Defaulted to proto2 syntax.)
fspt c dir is : /usr/local/oai/oai-develop/openairinterface5g/cmake_targets/ran_build/build/CMakeFiles/FSPT_V2
gcc -Wall -I. -I.. -I../itti -I../../../openair2/COMMON -Itracer -o _check_vcd check_vcd.c tracer/database.c tracer/utils.c -lm -pthread
./_check_vcd || (rm -f ./_check_vcd ./T_IDs.h ./T_messages.txt.h && false)
rm -f ./_check_vcd
Add enb specific telnet functions in libtelnetsrv_enb.so
No specific telnet functions for gnb
No specific telnet functions for 4Gue
Add 5Gue specific telnet functions in libtelnetsrv_5Gue.so
CMake Warning at common/utils/websrv/websrv_CMakeLists.txt:12 (message):
ulfius library (https://github.com/babelouest/ulfius) not found, install
libulfius-dev (ubuntu) if you need to build websrv back-end
Call Stack (most recent call first):
CMakeLists.txt:3189 (include)
-- websrv backend build skipped, dependencies not found
..........
.............
```
###### build example when missing back-end dependencies
```
./build_oai --build-lib all --nrUE
Enabling build of all optional shared libraries (telnetsrv enbscope uescope nrscope websrv)
Will compile NR UE
RF HW set to None
2. Setting the OAI PATHS ...
OPENAIR_DIR = /usr/local/oai/oai-develop/openairinterface5g
FreeDiameter prefix not found, install freeDiameter if EPC, HSS
3. building the compilation directives ...
running cmake ../../..
NETTLE VERSION_INSTALLED = 3.5.1
NETTLE_VERSION_MAJOR = 3
NETTLE_VERSION_MINOR = 5
cuda include /usr/include
cuda library
-- CMAKE_BUILD_TYPE is RelWithDebInfo
-- CPUARCH x86_64
-- AVX512 intrinsics are OFF
-- AVX2 intrinsics are ON
-- No T1 Offload support detected
calling protoc_call=/usr/local/oai/oai-develop/openairinterface5g/cmake_targets/tools/generate_protobuf FSPT_C_DIR=/usr/local/oai/oai-develop/openairinterface5g/cmake_targets/ran_build/build/CMakeFiles/FSPT_V2 FSPT_MSG_DIR=/usr/local/oai/oai-develop/openairinterface5g/targets/COMMON/MESSAGES/V2 FSPT_MSG_FILES=/usr/local/oai/oai-develop/openairinterface5g/targets/COMMON/MESSAGES/V2/flexsplit.proto
[libprotobuf WARNING google/protobuf/compiler/parser.cc:546] No syntax specified for the proto file: flexsplit.proto. Please use 'syntax = "proto2";' or 'syntax = "proto3";' to specify a syntax version. (Defaulted to proto2 syntax.)
fspt c dir is : /usr/local/oai/oai-develop/openairinterface5g/cmake_targets/ran_build/build/CMakeFiles/FSPT_V2
gcc -Wall -I. -I.. -I../itti -I../../../openair2/COMMON -Itracer -o _check_vcd check_vcd.c tracer/database.c tracer/utils.c -lm -pthread
./_check_vcd || (rm -f ./_check_vcd ./T_IDs.h ./T_messages.txt.h && false)
rm -f ./_check_vcd
Add enb specific telnet functions in libtelnetsrv_enb.so
No specific telnet functions for gnb
No specific telnet functions for 4Gue
Add 5Gue specific telnet functions in libtelnetsrv_5Gue.so
-- websrv backend can be built
-- websrv frontend can be built
-- Configuring done
-- Generating done
-- Build files have been written to: /usr/local/oai/oai-develop/openairinterface5g/cmake_targets/ran_build/build
............
.............
Log file for compilation is being written to: /usr/local/oai/oai-develop/openairinterface5g/cmake_targets/log/websrv.txt
websrv compiled
Build of websrv frontend enabled
Log file for compilation is being written to: /usr/local/oai/oai-develop/openairinterface5g/cmake_targets/log/websrvfront.txt
websrvfront compiled
Compiling rfsimulator
Log file for compilation is being written to: /usr/local/oai/oai-develop/openairinterface5g/cmake_targets/log/rfsimulator.txt
.............
..............
```
# building and installing the front-end
Before building the front-end you need to install the npm node.js installer:
`apt-get install npm` for ubuntu or `dnf install npm`for fedora
then to build and install the frontend:
``` bash
cd \<oai repository\>/openairinterface5g/cmake_targets/ran_build/build
make websrvfront
up to date, audited 1099 packages in 3s
142 packages are looking for funding
run `npm fund` for details
3 moderate severity vulnerabilities
Some issues need review, and may require choosing
a different dependency.
Run `npm audit` for details.
Built target websrvfront_installjsdep
> softmodemngx@2.0.0 build
> ng build
✔ Browser application bundle generation complete.
✔ Copying assets complete.
✔ Index html generation complete.
Initial Chunk Files | Names | Raw Size | Estimated Transfer Size
main.1917944d43411293.js | main | 996.88 kB | 231.03 kB
styles.ca83009174c6585f.css | styles | 74.33 kB | 7.70 kB
polyfills.36a0bc8cdfe2cb81.js | polyfills | 45.10 kB | 13.82 kB
runtime.bf8117e4a18c7212.js | runtime | 1.06 kB | 606 bytes
| Initial Total | 1.09 MB | 253.14 kB
Build at: 2022-10-17T16:52:42.517Z - Hash: 3b6b647cfcb0dac9 - Time: 8651ms
Moving frontend files to:
/usr/local/oai/oai-develop/openairinterface5g/cmake_targets/ran_build/build/websrv
/usr/local/oai/oai-develop/openairinterface5g/targets/bin/websrv
Built target websrvfront
```
# Building and installing the web server back-end
The back-end has two dependencies:
1. the [ulfius library](https://github.com/babelouest/ulfius) and the corresponding include files which are provided by the ubuntu libulfius-dev package: `sudo apt-get install -y libulfius-dev`
2. the [jansson](https://github.com/akheron/jansson-debian) library and the corresponding include files which are provided by the ubuntu libjansson-dev package: `sudo apt-get install -y libjansson-dev`
Dependencies can also be installed on fedora distribution, the jansson package is `jansson-devel`, ulfius has to be installed as explained [here](https://github.com/babelouest/ulfius/blob/master/INSTALL.md#pre-compiled-packages).
By default the embedded web server back-end , which is implemented in a shared library, is not built. It can be built after compiling the softmodem executable using the `build_oai` script:
```bash
cd \<oai repository\>/openairinterface5g
source oaienv
cd cmake_targets
./build_oai --build-lib websrv
```
This will create the `libwebsrv.so` file in the `targets/bin` and `cmake_targets/ran_build/build` sub directories of the oai repository.
When starting the softmodem, you must specify the **_\-\-websrv_** option to load and start the web server. The web server is loaded via the [oai shared library loader](loader).
# Testing the web server interface
## web server parameters
The web server back-end is using the [oai configuration module](Config/Rtusage). web server parameters must be specified in the websrv section.
| name | type | default | description |
|:---:|:---:|:---:|:----|
| `listenaddr` | `ipV4 address, ascii format` | "0.0.0.0" | local address the back-end is listening on |
| `listenport` | `integer` | 8090 | port number the server is listening on |
| debug | `integer` | 0 | When not 0, http requests headers and json objects dump are added to back-end traces |
| fpath | character string | websrv | The path to on-disk http server resources . The default value matches the front-end installation when running the softmodem from the executables repository. |
| cert, key, rootca | `character string` | null | certificates and key used to trigger https protocol (not tested) |
| | | | |
## running the back-end
To trigger the back-end use the `--websrv` option, possibly modifying the parameters as explained in the previous chapter. The two following commands allow starting the oai gNB and the oai 5G UE on the same computer, starting the telnet server and the web interface on both executables.
`./nr-softmodem -O /usr/local/oai/conf/gnb.band78.sa.fr1.106PRB.usrpb210.conf --rfsim --rfsimulator.serveraddr server --telnetsrv --telnetsrv.listenstdin --websrv --rfsimulator.options chanmod`
.`/nr-uesoftmodem -O /usr/local/oai/conf/nrue_sim.conf --sa --numerology 1 -r 106 -C 3649440000 --rfsim --rfsimulator.serveraddr 127.0.0.1 --websrv --telnetsrv --websrv.listenport 8092 --nokrnmod --telnetsrv.listenport 8091`
## Connecting from a browser to the oai softmodem's
Assuming that the previous commands run successfully and that you also run your browser on the same host, you should be able to connect to the gNB and UE web interface using respectively the following url's:
http://127.0.0.1:8090/websrv/index.html
http://127.0.0.1:8092/websrv/index.html
The interface should be intuitive enough, keeping in mind the following restrictions:
- The command tab is not available if the telnet server is not enabled
- The softscope tab is not available if the xforms scope is started `(-d` option)
- Only one connection is supported to a back-end, especially for the scope interface
Some front-end objects, which usage are less intuitive provide a tooltip to help interface usage.
[oai web serverinterface home](websrv.md)
\ No newline at end of file
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# For the full list of supported browsers by the Angular framework, please see:
# https://angular.io/guide/browser-support
# You can see what browsers were selected by your queries by running:
# npx browserslist
last 1 Chrome version
last 1 Firefox version
last 2 Edge major versions
last 2 Safari major versions
last 2 iOS major versions
Firefox ESR
not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false
{
"root": true,
"ignorePatterns": [
"projects/**/*"
],
"overrides": [
{
"files": [
"*.ts"
],
"parserOptions": {
"project": [
"common/utils/websrv/frontend/tsconfig.json"
],
"createDefaultProgram": true
},
"extends": [
"plugin:@angular-eslint/recommended",
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {
"@angular-eslint/directive-selector": [
"error",
{
"type": "attribute",
"prefix": "app",
"style": "camelCase"
}
],
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "app",
"style": "kebab-case"
}
]
}
},
{
"files": [
"*.html"
],
"extends": [
"plugin:@angular-eslint/template/recommended"
],
"rules": {}
}
]
}
\ No newline at end of file
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
/artifacts
/builds
# Only exists if Bazel was run
/bazel-out
# dependencies
/node_modules
# profiling files
chrome-profiler-events*.json
speed-measure-plugin*.json
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# misc
/.angular/cache
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db
config.toml
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"cli": {
"analytics": false,
"schematicCollections": [
"@angular-eslint/schematics"
]
},
"version": 1,
"newProjectRoot": "projects",
"projects": {
"softmodemngx": {
"projectType": "application",
"schematics": {
"@schematics/angular:application": {
"strict": true
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/softmodem-ngx",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/purple-green.css",
"src/styles.css"
],
"scripts": [],
"allowedCommonJsDependencies": [
"@firebase/auth",
"firebase"
]
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "1.5mb",
"maximumError": "2mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"outputHashing": "all"
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"browserTarget": "softmodemngx:build:production"
},
"development": {
"browserTarget": "softmodemngx:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "softmodemngx:build"
}
},
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
"lintFilePatterns": [
"src/**/*.ts",
"src/**/*.html"
]
}
}
}
}
}
}
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const {SpecReporter, StacktraceOption} = require("jasmine-spec-reporter");
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout : 11000,
specs : [ "./src/**/*.e2e-spec.ts" ],
capabilities : {browserName : "chrome", chromeOptions : {args : [ "--headless", "--no-sandbox" ]}},
directConnect : true,
SELENIUM_PROMISE_MANAGER : true,
baseUrl : "http://localhost:4200/",
framework : "jasmine",
jasmineNodeOpts : {showColors : true, defaultTimeoutInterval : 30000, print : function() {}},
onPrepare() {
require("ts-node").register({project : require("path").join(__dirname, "./tsconfig.json")});
jasmine.getEnv().addReporter(new SpecReporter({spec : {displayStacktrace : StacktraceOption.PRETTY}}));
}
};
\ No newline at end of file
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const {SpecReporter, StacktraceOption} = require("jasmine-spec-reporter");
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout : 11000,
specs : [ "./src/**/*.e2e-spec.ts" ],
capabilities : {browserName : "chrome"},
directConnect : true,
SELENIUM_PROMISE_MANAGER : false,
baseUrl : "http://localhost:4200/",
framework : "jasmine",
jasmineNodeOpts : {showColors : true, defaultTimeoutInterval : 30000, print : function() {}},
onPrepare() {
require("ts-node").register({project : require("path").join(__dirname, "./tsconfig.json")});
jasmine.getEnv().addReporter(new SpecReporter({spec : {displayStacktrace : StacktraceOption.PRETTY}}));
}
};
import {browser, logging} from "protractor";
import {AppPage} from "./app.po";
describe("workspace-project App", () => {
let page: AppPage;
beforeEach(() => { page = new AppPage(); });
// it('should display welcome message', async () => {
// await page.navigateTo();
// expect(await page.getTitleText()).toEqual('example app is running!');
// });
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level : logging.Level.SEVERE,
} as logging.Entry));
});
});
import {browser, by, element} from "protractor";
export class AppPage {
async navigateTo(): Promise<unknown>
{
return browser.get(browser.baseUrl);
}
async getTitleText(): Promise<string>
{
return element(by.css("app-root .content span")).getText();
}
}
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es2018",
"types": ["jasmine", "node"]
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "softmodemngx",
"version": "2.0.0",
"scripts": {
"lint": "ng lint",
"build": "ng build",
"dev": "ng serve --configuration=development",
"prod": "ng serve --configuration=production"
},
"private": true,
"dependencies": {
"@angular-slider/ngx-slider": "^2.0.4",
"@angular/animations": "^14.1.0",
"@angular/cdk": "^14.1.0",
"@angular/common": "^14.1.0",
"@angular/compiler": "^14.1.0",
"@angular/core": "^14.1.0",
"@angular/flex-layout": "^14.0.0-beta.40",
"@angular/forms": "^14.1.0",
"@angular/material": "^14.1.0",
"@angular/platform-browser": "^14.1.0",
"@angular/platform-browser-dynamic": "^14.1.0",
"@angular/router": "^14.1.0",
"chart.js": "^3.8.2",
"moment": "^2.29.3",
"ng2-charts": "^4.0.0",
"rxjs": "^6.6.6",
"tslib": "^2.0.0",
"zone.js": "^0.11.4"
},
"devDependencies": {
"@angular-devkit/architect": "^0.1401.0",
"@angular-devkit/build-angular": "^14.1.0",
"@angular-eslint/builder": "14.0.2",
"@angular-eslint/eslint-plugin": "14.0.2",
"@angular-eslint/eslint-plugin-template": "14.0.2",
"@angular-eslint/schematics": "14.0.2",
"@angular-eslint/template-parser": "14.0.2",
"@angular/cli": "^14.1.0",
"@angular/compiler-cli": "^14.1.0",
"@angular/language-service": "^14.1.0",
"@types/node": "^12.20.23",
"@typescript-eslint/eslint-plugin": "^5.29.0",
"@typescript-eslint/parser": "^5.29.0",
"eslint": "^8.18.0",
"fs-extra": "^10.0.0",
"fuzzy": "^0.1.3",
"husky": "^5.1.3",
"inquirer": "^6.2.2",
"inquirer-autocomplete-prompt": "^1.0.1",
"jasmine-core": "~3.7.1",
"jasmine-spec-reporter": "~5.0.0",
"jsonc-parser": "^3.0.0",
"open": "^7.0.3",
"ts-node": "^8.10.2",
"tslint-angular": "^3.0.3",
"typescript": "~4.6.4"
},
"husky": {
"hooks": {
"pre-commit": "pretty-quick --staged && ng lint && ng test",
"pre-push": "ng build --aot true"
}
}
}
import {HttpClient} from "@angular/common/http";
import {Injectable} from "@angular/core";
import {environment} from "src/environments/environment";
export interface IInfo {
name: string;
value: string;
type: IArgType;
modifiable: boolean; // set command ?
}
export interface IModule {
name: string;
}
export enum ILogLvl {
error = "error",
warn = "warn",
analysis = "analysis",
info = "info",
debug = "debug",
trace = "trace"
}
export enum ILogOutput {
stdout = "stdout",
telnet = "telnet",
web = "web",
file = "/tmp/<component>.log",
}
export enum IArgType {
boolean = "boolean",
list = "list",
loglvl = "loglvl",
range = "range",
number = "number",
string = "string",
configfile = "configfile",
simuTypes = "simuTypes",
}
export enum ICommandOptions {
update = "update", // result can be updated, triggers update button on result page when set
help = "help" // help tooltip available on command buttons
}
export interface IVariable {
name: string;
value: string;
type: IArgType;
modifiable: boolean; // set command ?
}
export interface ICommand {
name: string;
confirm?: string;
question?: IQuestion;
param?: IVariable;
options?: ICommandOptions[];
}
export interface ITable {
columns: IColumn[];
rows: string[];
}
export interface IQuestion {
display: string;
pname: string;
type: IArgType;
}
export interface IResp {
display: string[], table?: ITable
}
export interface IParam {
value: string, col: IColumn
}
export interface IColumn { // should use IVariable ?
name: string;
type: IArgType;
modifiable: boolean; // set command ?
help: boolean; // is help available
}
export interface IRow {
params: IParam[], rawIndex: number, cmdName: string,
param?: IVariable // to transmit the initial command parameter, ex: the channel model index when modify a channel model
}
export const route = "oaisoftmodem/";
@Injectable({
providedIn : "root",
})
export class CommandsApi {
constructor(private httpClient: HttpClient)
{
}
public readInfos$ = () => this.httpClient.get<IInfo[]>(environment.backend + route + "variables/");
public setInfo$ = (info: IInfo) => this.httpClient.post<IResp>(environment.backend + route + "variables/", info);
public readModules$ = () => this.httpClient.get<IModule[]>(environment.backend + route + "commands/");
public readVariables$ = (moduleName: string) => this.httpClient.get<IInfo[]>(environment.backend + route + moduleName + "/variables/");
public readCommands$ = (moduleName: string) => this.httpClient.get<ICommand[]>(environment.backend + route + moduleName + "/commands/");
public runCommand$ = (command: ICommand, moduleName: string) => this.httpClient.post<IResp>(environment.backend + route + moduleName + "/commands/", command);
public setCmdVariable$ = (variable: IInfo, moduleName: string) => this.httpClient.post<IResp>(environment.backend + route + moduleName + "/variables/", variable);
public setCmdParams$ = (row: IRow, moduleName: string) => this.httpClient.post<IResp>(environment.backend + route + moduleName + "/set/", row);
}
import {HttpClient} from "@angular/common/http";
import {Injectable} from "@angular/core";
import {Observable} from "rxjs";
import {map} from "rxjs/operators";
import {environment} from "src/environments/environment";
export interface HelpRequest {
module: string;
command: string;
object: string;
}
export interface HelpResp {
text: string;
}
const hlproute = "oaisoftmodem/helpfiles/";
@Injectable({
providedIn : "root",
})
export class HelpApi {
constructor(private httpClient: HttpClient)
{
}
public getHelp$ = (req: HelpRequest) => this.httpClient.get<HelpResp>(environment.backend + hlproute + req.module + "_" + req.command + "_" + req.object + ".html", {observe : "response"});
public getHelpText(module: string, command: string, object: string): Observable<string>
{
return this.getHelp$({module : module, command : command.replace(" ", "_"), object : object.replace(" ", "_")})
.pipe(map(
(response => { return (response.status == 201) ? response.body!.text : ""; }),
)); // pipe
}
}
import {HttpClient} from "@angular/common/http";
import {Injectable} from "@angular/core";
import {environment} from "src/environments/environment";
export enum IScopeGraphType {
IQs = "IQs",
LLR = "LLR",
WF = "WF",
TRESP = "TRESP",
UNSUP = "UNSUP"
}
export interface IGraphDesc {
title: string;
type: IScopeGraphType;
id: number;
srvidx: number;
}
export interface IScopeDesc {
title: string;
graphs: IGraphDesc[];
}
export interface IScopeCmd {
name: string;
graphid?: number; // the graph srvidx
value: string;
}
export interface ISigDesc {
target_id: number;
antenna_id: number;
}
const route = "oaisoftmodem/scopectrl/";
@Injectable({
providedIn : "root",
})
export class ScopeApi {
constructor(private httpClient: HttpClient)
{
}
public getScopeInfos$ = () => this.httpClient.get<IScopeDesc>(environment.backend + route);
public setScopeParams$ = (cmd: IScopeCmd) => this.httpClient.post(environment.backend + route, cmd, {observe : "response"});
}
import {NgModule} from "@angular/core";
import {RouterModule, Routes} from "@angular/router";
import {AppComponent} from "./app.component";
import {CommandsComponent} from "./components/commands/commands.component";
const routes: Routes = [
{path : "", redirectTo : "/websrv", pathMatch : "full"},
{path : "websrv", component : AppComponent},
{path : "**", redirectTo : ""},
];
@NgModule({
imports : [ RouterModule.forRoot(routes, {relativeLinkResolution : "legacy"}) ],
exports : [ RouterModule ],
})
export class AppRoutingModule {
}
.mat-tab-group {
margin-bottom: 10px;
}
.mat-ink-bar{
height: 8px !important;
}
.mat-tab-label-active
{
color: #000000 !important;
}
<mat-tab-group backgroundColor="primary" color="accent">
<mat-tab label="Commands">
<app-commands></app-commands>
</mat-tab>
<mat-tab label={{scopelabel}} disabled={{!isscopeavailable}}>
<app-scope (ScopeEnabled)="onScopeEnabled($event)"></app-scope>
</mat-tab>
</mat-tab-group>
\ No newline at end of file
import {Component} from "@angular/core";
import {MatTabsModule} from "@angular/material/tabs";
@Component({
selector : "app-root",
templateUrl : "./app.component.html",
styleUrls : [ "./app.component.css" ],
})
export class AppComponent {
title = "oai softmodem";
isscopeavailable = false;
scopelabel = "";
constructor()
{
this.scopelabel = "";
this.isscopeavailable = false;
}
onScopeEnabled(enabled: boolean)
{
this.isscopeavailable = enabled;
this.scopelabel = enabled ? "Scope" : "";
}
}
import {DragDropModule} from "@angular/cdk/drag-drop";
import {HttpClientModule} from "@angular/common/http";
import {NgModule} from "@angular/core";
import {FlexLayoutModule} from "@angular/flex-layout";
import {FormsModule, ReactiveFormsModule} from "@angular/forms";
import {MatButtonModule} from "@angular/material/button";
import {MatCardModule} from "@angular/material/card";
import {MatChipsModule} from "@angular/material/chips";
import {MatDialogModule} from "@angular/material/dialog";
import {MatFormFieldModule} from "@angular/material/form-field";
import {MatGridListModule} from "@angular/material/grid-list";
import {MatInputModule} from "@angular/material/input";
import {MatListModule} from "@angular/material/list";
import {MatProgressSpinnerModule} from "@angular/material/progress-spinner";
import {MatSelectModule} from "@angular/material/select";
import {MatSlideToggleModule} from "@angular/material/slide-toggle";
import {MatSliderModule} from "@angular/material/slider";
import {MatSnackBarModule} from "@angular/material/snack-bar";
import {MatTableModule} from "@angular/material/table";
import {MatTabsModule} from "@angular/material/tabs";
import {MatToolbarModule} from "@angular/material/toolbar";
import {MatTooltipModule} from "@angular/material/tooltip";
import {BrowserModule} from "@angular/platform-browser";
import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
import {NgChartsModule} from "ng2-charts";
import {CommandsApi} from "./api/commands.api";
import {ScopeApi} from "./api/scope.api";
import {AppRoutingModule} from "./app-routing.module";
import {AppComponent} from "./app.component";
import {CommandsComponent} from "./components/commands/commands.component";
import {ConfirmDialogComponent} from "./components/confirm/confirm.component";
import {DialogComponent} from "./components/dialog/dialog.component";
import {QuestionDialogComponent} from "./components/question/question.component";
import {ScopeComponent} from "./components/scope/scope.component";
import {InterceptorProviders} from "./interceptors/interceptors";
import {LoadingService} from "./services/loading.service";
import {WebSocketService} from "./services/websocket.service";
@NgModule({
declarations : [ AppComponent, CommandsComponent, ConfirmDialogComponent, QuestionDialogComponent, DialogComponent, ScopeComponent ],
imports : [
BrowserModule, AppRoutingModule, FormsModule, ReactiveFormsModule, BrowserAnimationsModule, HttpClientModule, MatButtonModule, FlexLayoutModule, MatDialogModule, DragDropModule,
MatSliderModule, MatFormFieldModule, MatInputModule, MatChipsModule, MatProgressSpinnerModule, MatToolbarModule, MatTableModule, MatListModule, MatSelectModule, MatSnackBarModule,
MatSlideToggleModule, MatGridListModule, MatCardModule, MatTabsModule, MatTooltipModule, NgChartsModule,
],
providers : [
// services
LoadingService,
WebSocketService,
// api
CommandsApi,
ScopeApi,
// interceptors
InterceptorProviders,
],
bootstrap : [ AppComponent ]
})
export class AppModule {
}
<div class="grid-container" >
<mat-grid-list cols="2" rowHeight="15vh" >
<mat-grid-tile [colspan]="1" [rowspan]="2" >
<mat-card class="dashboard-card">
<mat-card-header >
<mat-card-title>Connection info</mat-card-title>
</mat-card-header>
<mat-card-content class="dashboard-card-content">
<div *ngIf="infos$ | async as infos">
<div class="spaceddiv" *ngFor="let info of infos">
<mat-form-field class="scrollablefield" >
<mat-label>{{ info.nameFC.value }}</mat-label>
<input matInput [formControl]="info.valueFC" [readonly]="!info.modifiableFC.value" />
</mat-form-field>
<button mat-raised-button color="primary" [disabled]="!info.modifiableFC.value"
(click)="onInfoSubmit(info)">
{{ info.btnTxtFC }}
</button>
</div>
</div>
</mat-card-content>
</mat-card>
</mat-grid-tile>
<mat-grid-tile [colspan]="1" [rowspan]="2">
<mat-card class="dashboard-card">
<mat-card-header>
<mat-card-title>Softmodem commands</mat-card-title>
</mat-card-header>
<mat-card-content class="dashboard-card-content">
<div class="spaceddiv" *ngIf="modules$ | async as modules" fxLaypout="row">
<mat-form-field>
<mat-label>Module</mat-label>
<mat-select (selectionChange)="onModuleSelect($event.value)" [(value)]="selectedModule">
<mat-option *ngFor="let module of modules" [value]="module"> {{ module.name }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div *ngIf="cmds$ | async as cmds">
<div *ngIf="cmds.length">
<mat-chip-list>
<mat-chip *ngFor="let cmd of cmds" (click)="onCmdSubmit(cmd)" [matTooltip]="cmd.hlp_cmd" > {{cmd.nameFC.value}}
</mat-chip>
</mat-chip-list>
</div>
</div>
</mat-card-content>
</mat-card>
</mat-grid-tile>
<mat-grid-tile *ngIf="vars$ | async as vars" [colspan]="1" [rowspan]="4">
<mat-card *ngIf="vars.length" class="dashboard-card">
<mat-card-header>
<mat-card-title>{{ selectedModule!.name }} variables</mat-card-title>
</mat-card-header>
<mat-card-content class="dashboard-card-content">
<div class="spaceddiv" *ngFor="let variable of vars">
<mat-form-field class="scrollablefield">
<mat-label>{{ variable.nameFC.value }}</mat-label>
<input matInput [formControl]="variable.valueFC" [readonly]="!variable.modifiableFC.value" />
</mat-form-field>
<button mat-raised-button color="primary" [disabled]="!variable.modifiableFC.value"
(click)="onVarsubmit(variable)">
set
</button>
</div>
</mat-card-content>
</mat-card>
</mat-grid-tile>
<mat-grid-tile *ngIf="(rows$ | async)?.length" [colspan]="1" [rowspan]="4">
<mat-card class="dashboard-card">
<mat-card-header>
<mat-card-title>{{ selectedModule!.name }} {{ selectedCmd!.name }} {{ selectedCmd!.param?.value}}</mat-card-title>
</mat-card-header>
<mat-card-content class="dashboard-card-content">
<div >
<mat-table mat-table [dataSource]="rows$" multiTemplateDataRows class="mat-elevation-z8">
<div class="TableRowdiv" *ngFor="let col of columns; index as colIndex" matColumnDef="{{col.name}}">
<mat-header-cell *matHeaderCellDef fxLayoutAlign="start center">
<div [matTooltip]="hlp_cc[colIndex]">
<h4>{{ col.name }}</h4>
</div>
</mat-header-cell>
<mat-cell *matCellDef="let row">
<div [ngSwitch]="col.type" [formGroup]="row" fxLayoutAlign="start center">
<mat-slide-toggle *ngSwitchCase="IArgType.boolean" [formControl]="row.paramsCtrls[colIndex].valueFC"
[checked]="row.paramsCtrls[colIndex].valueFC.value">
</mat-slide-toggle>
<mat-list-item role="listitem">
<mat-form-field *ngSwitchCase="IArgType.loglvl">
<mat-select [formControl]="row.paramsCtrls[colIndex].valueFC"
[(value)]="row.paramsCtrls[colIndex].valueFC.value">
<mat-option *ngFor="let level of logLvlValues" [value]="level"> {{ level }}
</mat-option>
</mat-select>
</mat-form-field>
</mat-list-item>
<div *ngSwitchDefault>
<mat-form-field>
<input matInput [formControl]="row.paramsCtrls[colIndex].valueFC" [readonly]="!col.modifiable" />
</mat-form-field>
</div>
</div>
</mat-cell>
</div>
<div matColumnDef="button">
<mat-header-cell *matHeaderCellDef> </mat-header-cell>
<mat-cell *matCellDef="let row" fxLayoutAlign="center">
<button class="TableBtn" mat-raised-button color="primary" (click)="onParamSubmit(row)" [disabled]="row.pristine"> set
</button>
</mat-cell>
</div>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
</mat-table>
</div>
</mat-card-content>
</mat-card>
</mat-grid-tile>
</mat-grid-list>
</div>
.spaceddiv {
height: 45px;
width: 40vw;
display: flex;
justify-content: space-between;
margin: 0 auto;
padding: 10px 0;
}
.TableRowdiv {
height: 38px;
display: flex;
/* justify-content: space-between;*/
margin: 2px;
width: 95%;
.item:nth-child(1n){
width: 10%;
}
.item {
width: 4%;
margin: 10px;
}
}
.TableBtn {
height: 25px;
width: 35p;
display:flex;
padding-bottom:8px;
}
.TableText {
display: flex;
width: 8em;
overflow-x: auto;
}
.scrollablefield {
width: 80%;
height: 20px;
.mat-input-element {
overflow-x: auto;
}
}
.mat-card {
height: 95%;
overflow-y: auto;
overflow-x: scroll;
}
.status {
color: green;
font-weight: 800;
}
.ng-invalid .status {
color: red;
}
.grid-container {
margin: 20px;
}
.dashboard-card {
position: absolute;
top: 15px;
left: 15px;
right: 15px;
bottom: 15px;
}
.more-button {
position: absolute;
top: 5px;
right: 10px;
}
.dashboard-card-content {
text-align: center;
}
import {Component} from "@angular/core";
import {ViewEncapsulation} from "@angular/core";
import {UntypedFormArray} from "@angular/forms";
import {BehaviorSubject, forkJoin, Observable, of, timer} from "rxjs";
import {filter, map, switchMap, tap} from "rxjs/operators";
import {CommandsApi, IArgType, IColumn, ICommand, ICommandOptions, IInfo, ILogLvl, IParam, IRow} from "src/app/api/commands.api";
import {HelpApi, HelpRequest, HelpResp} from "src/app/api/help.api";
import {CmdCtrl} from "src/app/controls/cmd.control";
import {InfoCtrl} from "src/app/controls/info.control";
import {ModuleCtrl} from "src/app/controls/module.control";
import {RowCtrl} from "src/app/controls/row.control";
import {VarCtrl} from "src/app/controls/var.control";
import {DialogService} from "src/app/services/dialog.service";
import {DownloadService} from "src/app/services/download.service";
import {LoadingService} from "src/app/services/loading.service";
const CHANNEL_MOD_MODULE = "channelmod"
const PREDEF_CMD = "show predef"
@Component({
selector : "app-commands",
templateUrl : "./commands.component.html",
styleUrls : [ "./commands.component.scss" ],
encapsulation : ViewEncapsulation.None,
}) export class CommandsComponent {
hlp_cc: string[] = [];
hlp_cmd: string[] = [];
IArgType = IArgType;
logLvlValues = Object.values(ILogLvl);
// softmodem
infos$: Observable<VarCtrl[]>;
modules$: Observable<ModuleCtrl[]>;
// module
selectedModule?: ModuleCtrl;
vars$?: Observable<VarCtrl[]>;
cmds$?: Observable<CmdCtrl[]>;
// command
selectedCmd?: ICommand
displayedColumns: string[] = [];
rows$: BehaviorSubject<RowCtrl[]> = new BehaviorSubject<RowCtrl[]>([]);
columns: IColumn[] = [];
constructor(
public commandsApi: CommandsApi,
public helpApi: HelpApi,
public loadingService: LoadingService,
public dialogService: DialogService,
public downloadService: DownloadService,
)
{
this.infos$ = this.commandsApi.readInfos$().pipe(map((infos) => infos.map(info => new InfoCtrl(info))));
this.modules$ = this.commandsApi.readModules$().pipe(
map(imodules => imodules.map(imodule => new ModuleCtrl(imodule))), filter(controls => controls.length > 0), tap(controls => this.onModuleSelect(controls[0])));
}
// get types$() {
// return this.modules$.pipe(
// filter(modules => modules.map(module => module.name).includes(CHANNEL_MOD_MODULE)),
// mergeMap(modules => this.commandsApi.readCommands$(modules[0]!.name)),
// map(icmds => icmds.filter(cmd => cmd.name === PREDEF_CMD)),
// filter(icmds => icmds.length > 0),
// map(icmds => new CmdCtrl(icmds[0])),
// mergeMap(control => this.commandsApi.runCommand$(control.api(), CHANNEL_MOD_MODULE)),
// map(resp => resp.display.map(line => line.match('/\s*[0-9]*\s*(\S*)\n/gm')![0])),
// tap(types => console.log(types.join(', ')))
// );
// }
onInfoSubmit(control: InfoCtrl)
{
let info: IInfo = control.api();
if (info.type === IArgType.configfile) {
this.downloadService.getFile(info.value)
} else {
this.commandsApi.setInfo$(info).subscribe();
}
}
onModuleSelect(module: ModuleCtrl)
{
this.selectedModule = module
this.selectedCmd = undefined
this.cmds$ = this.commandsApi.readCommands$(module.name).pipe(
map(icmds => icmds.map(icmd => new CmdCtrl(icmd))),
map(cmds => {
module.cmdsFA = new UntypedFormArray(cmds)
for (let i = 0; i < cmds.length; i++)
{
cmds[i].get_cmd_help(this.helpApi, module.name)
}
return module.cmdsFA.controls as CmdCtrl[]
})
)
this.vars$ = this.commandsApi.readVariables$(module.name).pipe(
map(ivars => ivars.map(ivar => new VarCtrl(ivar))),
map(vars => {
module.varsFA = new UntypedFormArray(vars)
return module.varsFA.controls as VarCtrl[]
})
)
}
onVarsubmit(control: VarCtrl)
{
this.commandsApi.setCmdVariable$(control.api(), this.selectedModule!.name).pipe(map(resp => this.dialogService.openVarRespDialog(resp))).subscribe();
}
onCmdSubmit(control: CmdCtrl)
{
this.selectedCmd = control.api()
const obsparam$ = forkJoin([ control.confirm ? this.dialogService.openConfirmDialog(control.confirm)
: control.question ? this.dialogService.openQuestionDialog(this.selectedModule! + " " + this.selectedModule!.name, control)
: of(true) ]);
obsparam$
.pipe(switchMap(results => {
if (!results[0])
return of(null);
return this.execCmd$(control);
}))
.subscribe();
}
private execCmd$(control: CmdCtrl)
{
let cmd = control!.api();
if (this.selectedCmd!.param)
this.selectedCmd!.param!.value = cmd.param!.value;
this.commandsApi.runCommand$(cmd, this.selectedModule!.name)
.subscribe(
resp => {
if (resp.display[0])
this.dialogService.updateCmdDialog(control, resp, "cmd " + control.nameFC.value + " response:")
// else return of(resp)
const controls: RowCtrl[] = [];
this.displayedColumns = [];
if (resp.table) {
this.columns = resp.table.columns;
this.displayedColumns = this.columns.map(col => col.name);
this.displayedColumns.push("button");
// possibly load help..
for (let i = 0; i < this.columns.length; i = i + 1) {
if (this.columns[i].help) {
this.helpApi.getHelp$({module : this.selectedModule!.name, command : control!.api().name.replace(" ", "_"), object : this.columns[i].name.replace(" ", "_")})
.subscribe(
response => {
if (response.status == 201)
this.hlp_cc[i] = response.body!.text;
},
err => { this.hlp_cc[i] = ""; },
);
} else {
this.hlp_cc[i] = "";
}
}
for (let rawIndex = 0; rawIndex < resp.table.rows.length; rawIndex++) {
let params: IParam[] = [];
for (let i = 0; i < this.columns.length; i = i + 1) {
params.push({value : resp.table.rows[rawIndex][i], col : this.columns[i]})
}
const irow: IRow = {params : params, rawIndex : rawIndex, cmdName : this.selectedCmd!.name}
controls[rawIndex] = new RowCtrl(irow)
}
}
this.rows$.next(controls)
},
err => console.error("execCmd error: " + err),
() => {
console.log("execCmd completed: ");
if (control.isResUpdatable()) {
if (!(control.ResUpdTimerSubscriber) || control.ResUpdTimerSubscriber.closed) {
if (!control.ResUpdTimer)
control.ResUpdTimer = timer(1000, 1000);
control.ResUpdTimerSubscriber = control.ResUpdTimer.subscribe(iteration => {
console.log("Update timer fired" + iteration);
if (control.updbtnname === "Stop update")
this.execCmd$(control);
});
}
}
},
) // map resp
return of(null);
}
onParamSubmit(control: RowCtrl)
{
if (this.selectedCmd!.param)
control.set_cmdparam(this.selectedCmd!.param);
this.commandsApi.setCmdParams$(control.api(), this.selectedModule!.name).subscribe(() => this.execCmd$(new CmdCtrl(this.selectedCmd!)));
}
}
<h3>{{ data.title }}</h3>
<div fxLayoutGap="10px" mat-dialog-actions fxLayout="row" fxLayoutAlign="center start">
<button mat-button [mat-dialog-close]="true" cdkFocusInitial>Ok</button>
<button mat-button (click)="onNoClick()">Cancel</button>
</div>
\ No newline at end of file
/* eslint-disable @typescript-eslint/naming-convention */
import {Component, Inject} from "@angular/core";
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
@Component({selector : "app-confirm", templateUrl : "./confirm.component.html", styleUrls : [ "./confirm.component.css" ]})
export class ConfirmDialogComponent {
constructor(public dialogRef: MatDialogRef<void>, @Inject(MAT_DIALOG_DATA) public data: any)
{
}
onNoClick()
{
this.dialogRef.close();
}
}
/* icon that can be used to drag a dialog */
::ng-deep.dialogdrag-handle {
top: 1px;
left: 1px;
color: #ccc;
cursor: move;
width: 24px;
height: 24px;
}
/* make dialog for command response using the display field scrollable */
::ng-deep.cmdRespDialog {
resize: both;
overflow: scroll;
}
/* make only the dialog for command response using the display field movable */
/* (hide the close button and the drag icon) */
::ng-deep.varRespDialog #dialogclose {
display:none ;
}
::ng-deep.varRespDialog #dialogdrag {
display:none;
}
::ng-deep.errRespDialog #dialogclose {
display:none ;
}
::ng-deep.errRespDialog #dialogdrag {
display:none;
}
<div id="dialogdrag" cdkDrag cdkDragRootElement=".cdk-overlay-pane">
<div class="dialogdrag-handle" cdkDragHandle>
<svg width="24px" fill="currentColor" viewBox="0 0 24 24">
<path
d="M10 9h4V6h3l-5-5-5 5h3v3zm-1 1H6V7l-5 5 5 5v-3h3v-4zm14 2l-5-5v3h-3v4h3v3l5-5zm-9 3h-4v3H7l5 5 5-5h-3v-3z">
</path>
<path d="M0 0h24v24H0z" fill="none"></path>
</svg>
</div>
</div>
<h3> {{ data.title }}</h3>
<pre [innerHTML]="data.body"></pre>
<mat-dialog-actions fxLayoutAlign="end">
<button id="dialogclose" mat-button mat-dialog-close>Close</button>
<button id="dialogupdate" mat-button (click)="onUpdate(data.control)"
*ngIf="data.updatable">{{data.control.updbtnname}}</button>
</mat-dialog-actions>
\ No newline at end of file
import {Component, ElementRef, Inject, OnInit, ViewChild} from "@angular/core";
import {MAT_DIALOG_DATA} from "@angular/material/dialog";
import {CmdCtrl} from "src/app/controls/cmd.control";
import {DialogService} from "src/app/services/dialog.service";
@Component({
selector : "app-dialog",
templateUrl : "./dialog.component.html",
styleUrls : [ "./dialog.component.css" ],
})
export class DialogComponent {
constructor(@Inject(MAT_DIALOG_DATA) public data: any)
{
}
onUpdate(control: CmdCtrl)
{
if (control.updbtnname === "Stop update") {
control.stopUpdate();
} else {
control.startUpdate();
}
}
}
<h3>{{ data.title }}</h3>
<div mat-dialog-content >
<mat-form-field appearance="fill">
<label for="answer">{{data.control.question?.display}}</label>
<input id="answer" type="text" style="width:10vw" [formControl]="data.control.answerFC">
</mat-form-field>
</div>
<div fxLayoutGap=" 10px" mat-dialog-actions fxLayout="row" fxLayoutAlign="center start">
<button mat-button [mat-dialog-close]="true" cdkFocusInitial>Ok</button>
<button mat-button [mat-dialog-close]="false">Cancel</button>
</div>
/* eslint-disable @typescript-eslint/naming-convention */
import {Component, Inject} from "@angular/core";
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
import {CmdCtrl} from "src/app/controls/cmd.control";
export interface QuestionDialogData {
title: string;
control: CmdCtrl;
}
@Component({selector : "app-question", templateUrl : "./question.component.html", styleUrls : [ "./question.component.css" ]})
export class QuestionDialogComponent {
constructor(
public dialogRef: MatDialogRef<QuestionDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: QuestionDialogData,
)
{
}
onNoClick()
{
this.dialogRef.close();
}
}
mat-form-field {
width: 80%;
}
.status {
color: green;
font-weight: 800;
}
.ng-invalid .status {
color: red;
}
.grid-container {
margin: 20px;
}
.dashboard-card {
position: absolute;
top: 15px;
left: 15px;
right: 15px;
bottom: 15px;
}
.more-button {
position: absolute;
top: 5px;
right: 10px;
}
.dashboard-card-content {
text-align: center;
}
.scope-slider {
width: 90%;
}
.mat-mini-fab.mat-started {
background-color: #00a825;
}
<div class="grid-container">
<mat-grid-list cols="11" rowHeight="100px">
<!-- top fullwidth area for scope status display -->
<mat-grid-tile [colspan]="11" [rowspan]="1">
<mat-card class="dashboard-card">
<mat-card-header>
<mat-card-title>{{ scopetitle }} {{ scopesubtitle }}</mat-card-title>
</mat-card-header>
<mat-card-content class="dashboard-card-content">
<div FxLayoutAlign="row">
<label>{{ scopetime }} {{ scopestatus }} &nbsp; msg skipped: {{ skippedmsg }} &nbsp; msg buffered: {{ bufferedmsg }} &nbsp;</label>
</div>
</mat-card-content>
</mat-card>
</mat-grid-tile>
<!-- left full heigth, narrow width area, for global scope controls -->
<mat-grid-tile [colspan]="1" [rowspan]="8">
<mat-card class="dashboard-card">
<mat-card-header>
<mat-card-title>Scope control</mat-card-title>
</mat-card-header>
<mat-card-content class="dashboard-card-content">
<button mat-mini-fab color={{startstop_color}} (click)="startorstop()">{{startstop}}</button>
<br><br>
<div class="scope-slider-div">
<label id="scope-slider-refrate-labelid" class="scope-slider-label">refresh rate</label>
<label class="scope-slider-value-label"> {{rfrate}} </label>
</div>
<mat-slider class="scope-slider" min=".1" max="10" step="0.1" [value]="rfrate"
aria-labelledby="scope-slider-refrate-labelid" vertical="true" thumbLabel="true"
[(ngModel)]="rfrate" (change)="OnRefrateChange()" >
</mat-slider>
<br><br>
<mat-list-item class="sigselect" role="listitem">
<mat-form-field class="select-form">
<mat-select placeholder="sig select" name="SigSelect" class="SigSelect" [value]="selected_sig.target_id" (selectionChange)="SigChanged($event.value)" >
<mat-option *ngFor="let targetid of sig_list" [value]="targetid.target_id" >
{{targetid.target_id}}
</mat-option>
</mat-select>
</mat-form-field>
</mat-list-item>
<br><br>
<mat-slide-toggle [matTooltip]="help_ack" [checked]="data_ACK" (change)="onDataACKchange()" [(ngModel)]="data_ACK">ack
</mat-slide-toggle>
</mat-card-content>
</mat-card>
</mat-grid-tile>
<!-- area for watter fall data display -->
<mat-grid-tile [colspan]="7" [rowspan]="3">
<mat-card class="dashboard-card">
<mat-card-content class="dashboard-card-content">
<div fxLayout="column">
<!-- area for the chart -->
<div fxFlex="1 1 90% " style="width:100%">
<canvas baseChart id="wf"
[datasets]="WFDatasets"
[options]="WFOptions"
[type]="'scatter'">
</canvas>
</div>
<br><br>
<!-- area for the control used to select WF data -->
<div fxFlex="0 1 10%" style="width:100%">
<div class="form-group" fxFlex="50%"><mat-list-item class="WFselect" role="listitem">
<mat-form-field class="select-form">
<mat-select placeholder="select data" name="WFSelect" class="WFSelect" [value]="selected_WF" (selectionChange)="WFChanged($event.value)" >
<mat-option *ngFor="let graph of WFgraph_list" [value]="graph.title" >
{{graph.title}}
</mat-option>
<mat-option>none</mat-option>
</mat-select>
</mat-form-field>
</mat-list-item></div>
<div class="form-group" fxFlex="25%"><button mat-raised-button color="primary" (click)="RefreshWF()">Refresh</button> </div>
</div>
</div></mat-card-content>
</mat-card>
</mat-grid-tile>
<!-- right area for Time Response data -->
<mat-grid-tile [colspan]="3" [rowspan]="3">
<mat-card class="dashboard-card">
<mat-card-content class="dashboard-card-content">
<div fxLayout="column">
<!-- area for the chart -->
<div fxFlex="1 1 80% " style="width:100%">
<canvas baseChart id="tresp"
[datasets]="TRespDatasets"
[options]="TRespOptions"
[labels]="TRespLabels"
[type]="'line'">
</canvas>
</div>
<br><br>
<!-- area for the control used to enable -->
<div fxFlex="0 1 20%" style="width:60%">
<div class="form-group" fxFlex="50%"><mat-slide-toggle [checked]="enable_TResp" (change)="onEnableTResp()" [(ngModel)]="enable_TResp">enable:
</mat-slide-toggle> </div>
<div class="form-group" fxFlex="25%"><button mat-raised-button color="primary" (click)="RefreshTResp()">Refresh</button> </div>
</div>
</div>
</mat-card-content>
</mat-card>
</mat-grid-tile>
<!-- area for LLR data display -->
<mat-grid-tile [colspan]="5" [rowspan]="4">
<mat-card class="dashboard-card">
<mat-card-content class="dashboard-card-content">
<div fxLayout="column">
<div fxFlex="1 1 80% ">
<canvas baseChart id="llr" width="90%"
[datasets]=" LLRDatasets"
[options]="LLROptions"
[type]="'scatter'">
</canvas>
</div>
<div fxFlex="0 1 10%" style="width:100%">
<div class="form-group" fxFlex="50%"> <mat-list-item class="llrchannelselect" role="listitem">
<mat-form-field class="select-form">
<mat-select placeholder="Channels(llr view)" name="llrChannelsSelect" class="ChannelsSelect" multiple [value]="selected_llrchannels" (selectionChange)="llrchannelsChanged($event.value)" >
<mat-option *ngFor="let graph of llrgraph_list" [value]="graph.title" >
{{graph.title}}
</mat-option>
</mat-select>
</mat-form-field>
</mat-list-item> </div>
<div class="form-group" fxFlex="50%">
<div class="scope-slider-div">
<label id="scope-slider-ythresh-labelid" class="scope-slider-label">y threshold </label>
<label class="scope-slider-value-label"> {{llrythresh}} </label>
</div>
<mat-slider class="scope-slider" min="0" max="100" step="1" [value]="llrythresh"
aria-labelledby="scope-slider-ythresh-labelid" thumbLabel="true"
[(ngModel)]="llrythresh" (change)="OnYthreshChange()" >
</mat-slider>
</div>
</div>
<div fxFlex="0 1 10%">
<div class="form-group" fxFlex="25%"> <mat-form-field appearance="fill">
<mat-label>xmin</mat-label>
<input matInput type="number" (change)="OnLLRxminChange()" [value]="llrxmin" [(ngModel)]="llrxmin" min="llrmin" [max]="llrmax">
</mat-form-field></div>
<div class="form-group" fxFlex="25%"><mat-form-field appearance="fill">
<mat-label>xmax</mat-label>
<input matInput type="number" (change)="OnLLRxmaxChange()" [value]="llrxmax" [(ngModel)]="llrxmax" min="llrmin" [max]="llrmax">
</mat-form-field></div>
<div class="form-group" fxFlex="25%"><button mat-raised-button color="primary" (click)="RefreshLLR()">Refresh</button> </div>
</div>
</div>
</mat-card-content>
</mat-card>
</mat-grid-tile> <!-- end of LLR area -->
<!-- area for iq constellation data display -->
<mat-grid-tile [colspan]="5" [rowspan]="6">
<mat-card class="dashboard-card">
<mat-card-content>
<div fxLayout="column">
<!-- area for the chart -->
<div fxFlex="1 1 80% " style="width:60%">
<canvas baseChart id="iqc"
[datasets]="IQDatasets"
[options]="IQOptions"
[type]="'scatter'">
</canvas>
</div>
<br><br>
<!-- area for the control used to select channel's -->
<div fxFlex="0 1 10%" style="width:60%">
<div class="form-group" fxFlex="50%"> <mat-list-item class="channelselect" role="listitem">
<mat-form-field class="select-form">
<mat-select placeholder="Channels(IQ view)" name="ChannelsSelect" class="ChannelsSelect" multiple [value]="selected_channels" (selectionChange)="channelsChanged($event.value)" >
<mat-option *ngFor="let graph of iqgraph_list" [value]="graph.title" >
{{graph.title}}
</mat-option>
</mat-select>
</mat-form-field>
</mat-list-item></div>
<div class="form-group" fxFlex="25%"><button mat-raised-button color="primary" (click)="RefreshIQV()">Refresh</button> </div>
</div>
<!-- area for the 4 controls used to select x and y limits for data -->
<div fxFlex="0 1 5%">
<div class="form-group" fxFlex="25%">
<mat-form-field appearance="fill">
<mat-label>xmin</mat-label>
<input matInput type="number" (change)="OnIQxminChange()" [value]="iqxmin" [(ngModel)]="iqxmin" min="iqmin" [max]="iqmax">
</mat-form-field>
</div>
<div class="form-group" fxFlex="25%">
<mat-form-field appearance="fill">
<mat-label>xmax</mat-label>
<input matInput type="number" (change)="OnIQxmaxChange()" [value]="iqxmax" [(ngModel)]="iqxmax" min="iqmin" [max]="iqmax">
</mat-form-field>
</div>
<div class="form-group" fxFlex="25%">
<mat-form-field appearance="fill">
<mat-label>ymin</mat-label>
<input matInput type="number" (change)="OnIQyminChange()" [value]="iqymin" [(ngModel)]="iqymin" min="iqmin" [max]="iqmax">
</mat-form-field>
</div>
<div class="form-group" fxFlex="25%">
<mat-form-field appearance="fill">
<mat-label>ymax</mat-label>
<input matInput type="number" (change)="OnIQymaxChange()" [value]="iqymax" [(ngModel)]="iqymax" min="iqmin" [max]="iqmax">
</mat-form-field>
</div>
</div>
</div>
</mat-card-content>
</mat-card>
</mat-grid-tile> <!-- end of iq area -->
</mat-grid-list>
</div>
import {Component, EventEmitter, OnDestroy, OnInit, Output, QueryList, ViewChildren} from "@angular/core";
import {Chart, ChartConfiguration} from "chart.js";
import {BaseChartDirective} from "ng2-charts";
import {Subscription} from "rxjs";
import {HelpApi} from "src/app/api/help.api";
import {IGraphDesc, IScopeDesc, IScopeGraphType, ISigDesc, ScopeApi} from "src/app/api/scope.api";
import {arraybuf_data_offset, Message, WebSocketService, webSockSrc} from "src/app/services/websocket.service";
export interface RxScopeMessage {
msgtype: number;
chartid: number;
dataid: number;
segnum: number;
update: boolean;
content: ArrayBuffer;
}
const deserialize = (fullbuff: ArrayBuffer):
RxScopeMessage => {
const header = new DataView(fullbuff, 0, arraybuf_data_offset);
return {
// source: src.getUint8(0), //header
msgtype : header.getUint8(1), // header
chartid : header.getUint8(3), // header
dataid : header.getUint8(4), // header
segnum : header.getUint8(2), // header
update : (header.getUint8(5) == 1) ? true : false, // header
content : fullbuff.slice(arraybuf_data_offset) // data
};
}
const serialize = (msg: TxScopeMessage):
ArrayBuffer => {
const byteArray = new TextEncoder().encode(msg.content);
let arr = new Uint8Array(byteArray.byteLength + arraybuf_data_offset);
arr.set(byteArray, arraybuf_data_offset) // data
let buffview = new DataView(arr.buffer);
buffview.setUint8(1, msg.msgtype); // header
return buffview.buffer;
}
export interface TxScopeMessage {
msgtype: number;
content: string;
}
/*------------------------------------*/
/* constants that must match backend (websrv.h or phy_scope.h) */
const SCOPEMSG_TYPE_TIME = 3;
const SCOPEMSG_TYPE_DATA = 10;
const SCOPEMSG_TYPE_DATAACK = 11;
const SCOPEMSG_TYPE_DATAFLOW = 12;
const SCOPEMSG_DATA_IQ = 1;
const SCOPEMSG_DATA_LLR = 2;
const SCOPEMSG_DATA_WF = 3;
const SCOPEMSG_DATA_TRESP = 4;
/*---------------------------------------*/
@Component({
selector : "app-scope",
templateUrl : "./scope.component.html",
styleUrls : [ "./scope.component.css" ],
})
export class ScopeComponent implements OnInit, OnDestroy {
// data for scope status area
scopetitle = "";
scopesubtitle = "";
scopetime = "";
scopestatus = "stopped";
skippedmsg = "0";
bufferedmsg = "0";
// data for scope control area
startstop = "start";
startstop_color = "warn";
rfrate = 2;
data_ACK = false;
// data for Time Response chart
TRespgraph: IGraphDesc = {title : "", type: IScopeGraphType.TRESP, id: -1, srvidx: -1};
enable_TResp = false;
// data for scope iq constellation area
iqgraph_list: IGraphDesc[] = [];
selected_channels = [ "" ];
iqmax = 32767;
iqmin = -32767;
iqxmin = this.iqmin;
iqymin = this.iqmin;
iqxmax = this.iqmax;
iqymax = this.iqmax;
// data for scope LLR area
llrgraph_list: IGraphDesc[] = [];
selected_llrchannels = [ "" ];
sig_list: ISigDesc[] = [
{target_id : 0, antenna_id: 0},
{target_id : 1, antenna_id: 0},
{target_id : 2, antenna_id: 0},
{target_id : 3, antenna_id: 0},
];
selected_sig: ISigDesc = {target_id : 0, antenna_id: 0};
llrythresh = 5;
llrmin = 0;
llrmax = 200000;
llrxmin = this.llrmin;
llrxmax = this.llrmax;
// data for scope WatterFall area
WFgraph_list: IGraphDesc[] = [];
selected_WF = "";
llrchart?: Chart;
iqcchart?: Chart;
wfchart?: Chart;
trespchart?: Chart;
nwf: number[] = [ 0, 0, 0, 0 ];
// websocket service object and related subscription for message reception
wsSubscription?: Subscription;
@Output() ScopeEnabled = new EventEmitter<boolean>();
@ViewChildren(BaseChartDirective) charts?: QueryList<BaseChartDirective>;
public TRespDatasets: ChartConfiguration<"line">[ "data" ]["datasets"] = [
{
data : [],
label: "",
pointRadius: 1,
showLine: true,
animation: false,
fill: false,
pointStyle: "circle",
backgroundColor: "rgba(255,0,0,0)",
borderColor: "red",
pointBorderColor: "red",
},
];
public IQDatasets: ChartConfiguration<"scatter">[ "data" ]["datasets"] = [
{
data : [],
label: "C1",
pointRadius: 0.5,
showLine: false,
animation: false,
fill: false,
pointStyle: "circle",
// pointBackgroundColor: 'yellow',
backgroundColor: "yellow",
borderWidth: 0,
pointBorderColor: "yellow",
// parsing: false,
},
{
data : [],
label: "C2",
pointRadius: 0.5,
showLine: false,
animation: false,
pointStyle: "circle",
pointBackgroundColor: "cyan",
backgroundColor: "cyan",
borderWidth: 0,
pointBorderColor: "cyan",
// parsing: false,
},
{
data : [],
label: "C3",
pointRadius: 0.5,
showLine: false,
animation: false,
pointStyle: "circle",
pointBackgroundColor: "red",
backgroundColor: "red",
borderWidth: 0,
pointBorderColor: "red",
// parsing: false,
}
];
public LLRDatasets: ChartConfiguration<"scatter">[ "data" ]["datasets"] = [
{
data : [],
label: "LLR1",
pointRadius: 0.5,
showLine: false,
animation: false,
fill: false,
pointStyle: "circle",
pointBackgroundColor: "yellow",
backgroundColor: "yellow",
borderWidth: 0,
pointBorderColor: "yellow",
// parsing: false,
},
{
data : [],
label: "LL2",
pointRadius: 0.5,
showLine: false,
animation: false,
pointStyle: "circle",
pointBackgroundColor: "cyan",
backgroundColor: "cyan",
borderWidth: 0,
pointBorderColor: "cyan",
parsing: false,
},
{
data : [],
label: "LLR3",
pointRadius: 0.5,
showLine: false,
animation: false,
pointStyle: "circle",
pointBackgroundColor: "red",
backgroundColor: "red",
borderWidth: 0,
pointBorderColor: "red",
parsing: false,
}
];
public WFDatasets: ChartConfiguration<"scatter">[ "data" ]["datasets"] = [
{
data : [],
label: "WFblue",
pointRadius: 0.5,
showLine: false,
animation: false,
fill: false,
pointStyle: "circle",
pointBackgroundColor: "blue",
backgroundColor: "blue",
borderWidth: 0,
pointBorderColor: "blue",
// parsing: false,
},
{
data : [],
label: "WFgreen",
pointRadius: 0.5,
showLine: false,
animation: false,
pointStyle: "circle",
pointBackgroundColor: "green",
backgroundColor: "green",
borderWidth: 0,
pointBorderColor: "green",
// parsing: false,
},
{
data : [],
label: "WFyellow",
pointRadius: 0.5,
showLine: false,
animation: false,
pointStyle: "circle",
pointBackgroundColor: "yellow",
backgroundColor: "yellow",
borderWidth: 0,
pointBorderColor: "yellow",
// parsing: false,
},
{
data : [],
label: "WFred",
pointRadius: 0.5,
showLine: false,
animation: false,
pointStyle: "circle",
pointBackgroundColor: "red",
backgroundColor: "red",
borderWidth: 0,
pointBorderColor: "red",
// parsing: false,
}
];
// help text from backend
help_ack: string = "";
public TRespOptions: ChartConfiguration<"line">[ "options" ] = {
responsive : true,
// aspectRatio: 2,
plugins: {
legend: {display: true, labels: {boxWidth: 10, boxHeight: 10}},
tooltip: {
enabled: false,
},
},
};
TRespLabels: number[] = [ 0 ];
public IQOptions: ChartConfiguration<"scatter">[ "options" ] = {
responsive : true,
aspectRatio: 1,
// scales: {
// xAxes: {
// min: -5,
// max:5,
// }
// },
plugins: {
legend: {display: true, labels: {boxWidth: 10, boxHeight: 10}},
tooltip: {
enabled: false,
},
},
};
public LLROptions: ChartConfiguration<"scatter">[ "options" ] = {
responsive : true,
aspectRatio: 3,
scales: {
xAxes: {
min: 0,
},
// yAxes: {
// min: -100,
// max: 100
// }
},
plugins: {
legend: {display: true, labels: {boxWidth: 10, boxHeight: 10}},
tooltip: {
enabled: false,
},
},
};
public WFOptions: ChartConfiguration<"scatter">[ "options" ] = {
responsive : true,
aspectRatio: 5,
scales: {
xAxes: {
min: 0,
// max:800,
},
yAxes: {
min: 0,
max: 80,
reverse: true,
}
},
plugins: {
legend: {display: true, labels: {boxWidth: 10, boxHeight: 10}},
tooltip: {
enabled: false,
},
},
}
constructor(private scopeApi: ScopeApi, private wsService: WebSocketService, public helpApi: HelpApi)
{
console.log("Scope constructor ");
}
ngOnInit()
{
console.log("Scope ngOnInit ");
this.scopeApi.getScopeInfos$().subscribe(resp => { this.configScope(resp); });
this.helpApi.getHelpText("scope", "control", "dataack").subscribe(resp => { this.help_ack = resp; }, err => { this.help_ack = ""; });
}
ngOnDestroy()
{
console.log("Scope ngOnDestroy ");
this.wsSubscription?.unsubscribe();
}
DecodScopeBinmsgToString(message: ArrayBuffer): string
{
const enc = new TextDecoder("utf-8");
return enc.decode(message);
}
private configScope(resp: IScopeDesc)
{
if (resp.title === "none") {
this.ScopeEnabled.emit(false);
} else {
this.ScopeEnabled.emit(true);
this.scopetitle = resp.title;
this.iqgraph_list.length = 0;
this.llrgraph_list.length = 0;
for (let graphIndex = 0; graphIndex < resp.graphs.length; graphIndex++) {
if (resp.graphs[graphIndex].type == IScopeGraphType.IQs) {
this.iqgraph_list.push(resp.graphs[graphIndex]);
this.IQDatasets[this.iqgraph_list.length - 1].label = resp.graphs[graphIndex].title;
}
if (resp.graphs[graphIndex].type == IScopeGraphType.LLR) {
this.llrgraph_list.push(resp.graphs[graphIndex]);
this.LLRDatasets[this.llrgraph_list.length - 1].label = resp.graphs[graphIndex].title;
}
if (resp.graphs[graphIndex].type == IScopeGraphType.WF) {
this.WFgraph_list.push(resp.graphs[graphIndex]);
}
if (resp.graphs[graphIndex].type == IScopeGraphType.TRESP) {
this.TRespgraph = resp.graphs[graphIndex];
this.TRespDatasets[0].label = resp.graphs[graphIndex].title;
}
}
this.charts?.forEach((child) => {child.chart?.update()});
}
}
private ProcessScopeMsg(message: RxScopeMessage)
{
if (this.scopestatus === "starting") {
this.scopestatus = "started";
this.startstop = "stop";
this.startstop_color = "started";
}
let d = 0;
let x = 0;
let y = 0;
switch (message.msgtype) {
case SCOPEMSG_TYPE_TIME:
this.scopetime = this.DecodScopeBinmsgToString(message.content);
break;
case SCOPEMSG_TYPE_DATAFLOW:
let infobuff = this.DecodScopeBinmsgToString(message.content).split("|");
if (infobuff.length >= 2) {
this.skippedmsg = infobuff[0]!;
this.bufferedmsg = infobuff[1]!;
}
break;
case SCOPEMSG_TYPE_DATA:
const bufferview = new DataView(message.content);
if (message.update) {
console.log("Starting scope update chart " + message.chartid.toString() + ", dataset " + message.dataid.toString() + " data length: " + bufferview.byteLength);
}
switch (message.chartid) {
case SCOPEMSG_DATA_TRESP:
d = 0;
for (let i = 0; i < bufferview.byteLength; i = i + 4) {
this.TRespDatasets[0].data[d] = {x : d, y : bufferview.getFloat32(i, true)};
this.TRespLabels[d] = d;
d++;
}
if (message.update) {
this.trespchart!.update();
console.log(" TRESP view update completed " + d.toString() + " points ");
}
break;
case SCOPEMSG_DATA_IQ:
this.IQDatasets[message.dataid].data.length = 0;
for (let i = 0; i < bufferview.byteLength; i = i + 4) {
this.IQDatasets[message.dataid].data[i / 4] = {x : bufferview.getInt16(i, true), y : bufferview.getInt16(i + 2, true)};
}
if (message.update) {
this.iqcchart!.update();
}
break;
case SCOPEMSG_DATA_LLR:
this.LLRDatasets[message.dataid].data.length = 0;
let xoffset = 0;
d = 0;
for (let i = 4; i < (bufferview.byteLength - 2); i = i + 4) {
xoffset = xoffset + bufferview.getInt16(i + 2, true);
this.LLRDatasets[message.dataid].data[d] = {x : xoffset, y : bufferview.getInt16(i, true)};
d++;
}
this.LLRDatasets[message.dataid].data[d] = {x : bufferview.getInt32(0, true), y : 0};
if (message.update) {
this.llrchart!.update();
}
break;
case SCOPEMSG_DATA_WF:
if (message.update) {
if (message.segnum == 0) {
for (let i = 0; i < this.WFDatasets.length; i++) {
this.nwf[i] = 0;
this.WFDatasets[i].data.length = 0;
}
}
}
for (let i = 2; i < bufferview.byteLength - 2; i = i + 4) { // first 16 bits in buffer contains the number of points in the message
x = bufferview.getInt16(i, true);
y = bufferview.getInt16(i + 2, true);
this.wfchart!.scales.xAxes.max = bufferview.getInt16(bufferview.byteLength - 4, true);
this.WFDatasets[message.dataid].data[this.nwf[message.dataid]] = {x : x, y : y};
this.nwf[message.dataid]++;
}
if (message.update) {
this.wfchart!.update();
console.log(" WF view update completed " + d.toString() + "points, ");
}
break;
default:
break;
}
this.sendMsg(SCOPEMSG_TYPE_DATAACK, "Chart " + message.chartid.toString() + ", dataset " + message.dataid.toString());
break;
default:
break;
}
}
private sendMsg(type: number, strmessage: string)
{
this.wsService.send({source : webSockSrc.softscope, fullbuff : serialize({msgtype : type, content : strmessage})});
console.log("Scope sent msg type " + type.toString() + " " + strmessage);
}
private SendScopeParams(name: string, value: string, graphid: number): number
{
let status = 0;
this.scopeApi.setScopeParams$({name : name, value : value, graphid : graphid})
.subscribe(response => { console.log(response.status); },
err => {
console.log("scope SendScopeParams: error received: " + err);
this.StopScope();
},
() => console.log("scope SendScopeParams OK"));
return status;
}
private StopScope()
{
if (this.wsSubscription)
this.wsSubscription.unsubscribe();
this.scopestatus = "stopped";
this.startstop = "start";
this.startstop_color = "warn";
this.skippedmsg = "0";
this.bufferedmsg = "0";
}
startorstop()
{
if (this.scopestatus === "stopped") {
let status = this.SendScopeParams("startstop", "start", 0);
if (status == 0) {
this.llrchart = Chart.getChart("llr");
this.iqcchart = Chart.getChart("iqc");
this.wfchart = Chart.getChart("wf");
this.trespchart = Chart.getChart("tresp");
this.IQDatasets.forEach((dataset) => {dataset.data.length = 0});
this.LLRDatasets.forEach((dataset) => {dataset.data.length = 0});
this.WFDatasets.forEach((dataset) => {dataset.data.length = 0});
this.TRespDatasets.forEach((dataset) => {dataset.data.length = 0});
this.charts?.forEach((child, index) => {child.chart?.update()});
this.scopestatus = "starting";
this.SigChanged(this.selected_sig.target_id);
this.OnRefrateChange();
this.OnIQxminChange();
this.OnIQxmaxChange();
this.OnIQyminChange();
this.OnIQymaxChange();
this.OnYthreshChange();
this.OnLLRxminChange();
this.OnLLRxmaxChange();
this.channelsChanged(this.selected_channels);
this.llrchannelsChanged(this.selected_llrchannels);
this.WFChanged(this.selected_WF);
this.onEnableTResp();
for (let i = 0; i < this.WFDatasets.length; i++) {
this.nwf[i] = 0;
this.WFDatasets[i].data.length = 0;
}
this.wsService = new (WebSocketService);
this.wsSubscription = this.wsService.subject$.subscribe(msg => this.ProcessScopeMsg(deserialize(msg.fullbuff)),
err => {
console.error("WebSocket Observer got an error: " + err);
this.StopScope()
},
() => { console.error("WebSocket Observer completed: "); })
}
} else {
this.SendScopeParams("startstop", "stop", 0);
this.StopScope();
}
}
OnRefrateChange()
{
this.SendScopeParams("refrate", (this.rfrate * 10).toString(), 0);
}
OnLLRxminChange()
{
this.SendScopeParams("llrxmin", (this.llrxmin).toString(), 0);
}
OnLLRxmaxChange()
{
this.SendScopeParams("llrxmax", (this.llrxmax).toString(), 0);
}
OnIQxminChange()
{
this.SendScopeParams("xmin", (this.iqxmin).toString(), 0);
}
OnIQxmaxChange()
{
this.SendScopeParams("xmax", (this.iqxmax).toString(), 0);
}
OnIQyminChange()
{
this.SendScopeParams("ymin", (this.iqymin).toString(), 0);
}
OnIQymaxChange()
{
this.SendScopeParams("ymax", (this.iqymax).toString(), 0);
}
channelsChanged(titles: string[])
{
this.selected_channels = titles;
this.iqgraph_list.forEach(graph => {
const [enabled] = titles.filter(value => graph.title === value);
this.SendScopeParams("enabled", enabled ? "true" : "false", graph.srvidx);
})
}
llrchannelsChanged(titles: string[])
{
this.selected_llrchannels = titles;
this.llrgraph_list.forEach(graph => {
const [enabled] = titles.filter(value => graph.title === value);
this.SendScopeParams("enabled", enabled ? "true" : "false", graph.srvidx);
})
}
SigChanged(value: number)
{
this.selected_sig.target_id = value;
if (this.scopetitle === "gNB")
this.scopesubtitle = " - sig from UE" + value.toString() + " antenna" + this.selected_sig.antenna_id;
else
this.scopesubtitle = " - sig from gNB" + value.toString() + " antenna" + this.selected_sig.antenna_id;
;
this.SendScopeParams("TargetSelect", value.toString(), 0);
}
WFChanged(value: string)
{
this.selected_WF = value;
for (let i = 0; i < this.WFgraph_list.length; i++) {
if (this.WFgraph_list[i].title === value) {
this.SendScopeParams("enabled", "true", this.WFgraph_list[i].srvidx);
this.WFDatasets[0].label = value;
this.WFDatasets[1].label = "(>avg*2)";
this.WFDatasets[2].label = "(>avg*10)";
this.WFDatasets[3].label = "(>avg*100)";
} else {
this.SendScopeParams("enabled", "false", this.WFgraph_list[i].srvidx);
}
}
for (let i = 0; i < this.WFDatasets.length; i++) {
this.nwf[i] = 0;
this.WFDatasets[i].data.length = 0;
}
}
onEnableTResp()
{
this.SendScopeParams("enabled", this.enable_TResp.toString(), this.TRespgraph.srvidx);
}
onDataACKchange()
{
this.SendScopeParams("DataAck", this.data_ACK.toString(), 0);
}
OnYthreshChange()
{
this.SendScopeParams("llrythresh", this.llrythresh.toString(), 0);
}
RefreshTResp()
{
this.TRespDatasets[0].data.length = 0;
this.trespchart!.update();
}
RefreshIQV()
{
this.IQDatasets.forEach((dataset) => {dataset.data.length = 0});
this.iqxmin = this.iqmin;
this.iqymin = this.iqmin;
this.iqxmax = this.iqmax;
this.iqymax = this.iqmax;
this.iqcchart!.update();
}
RefreshLLR()
{
this.LLRDatasets.forEach((dataset) => {dataset.data.length = 0});
this.llrxmin = this.llrmin;
this.llrxmax = this.llrmax;
this.llrchart!.update();
}
RefreshWF()
{
this.WFDatasets.forEach((dataset) => {dataset.data.length = 0});
this.nwf = [ 0, 0, 0, 0 ];
this.wfchart!.update();
}
}
import {UntypedFormArray, UntypedFormControl, UntypedFormGroup} from "@angular/forms";
import {Subscription} from "rxjs";
import {Observable} from "rxjs/internal/Observable";
import {ICommand, ICommandOptions, IQuestion} from "src/app/api/commands.api";
import {HelpApi, HelpRequest, HelpResp} from "src/app/api/help.api";
const enum CmdFCN {
name = "name",
vars = "variables",
confirm = "confirm",
answer = "answer"
}
export class CmdCtrl extends UntypedFormGroup {
confirm?: string;
question?: IQuestion;
cmdname: string;
options?: ICommandOptions[];
public ResUpdTimer?: Observable<number>;
public ResUpdTimerSubscriber?: Subscription;
updbtnname: string;
hlp_cmd: string = "";
constructor(cmd: ICommand)
{
super({});
this.addControl(CmdFCN.name, new UntypedFormControl(cmd.name));
this.addControl(CmdFCN.answer, new UntypedFormControl(""));
this.addControl(CmdFCN.vars, new UntypedFormArray([]));
this.confirm = cmd.confirm;
this.question = cmd.question;
this.cmdname = cmd.name;
this.options = cmd.options;
this.updbtnname = "Start update"
}
api()
{
const doc: ICommand = {
name : this.nameFC.value,
param : this.question ? {name : this.question!.pname, value : this.answerFC.value, type : this.question!.type, modifiable : false} : undefined,
options : this.options
};
return doc;
}
isResUpdatable(): boolean
{
if (this.options) {
for (let opt = 0; opt < this.options.length; opt++) {
if (this.options[opt] == ICommandOptions.update)
return true;
}
} else {
return false;
}
return false;
}
stopUpdate()
{
if (this.ResUpdTimerSubscriber) {
this.updbtnname = "Start update"
}
}
startUpdate()
{
if (this.ResUpdTimerSubscriber && this.ResUpdTimer) {
this.updbtnname = "Stop update"
}
}
get nameFC()
{
return this.get(CmdFCN.name) as UntypedFormControl;
}
set nameFC(fc: UntypedFormControl)
{
this.setControl(CmdFCN.name, fc);
}
get answerFC()
{
return this.get(CmdFCN.answer) as UntypedFormControl;
}
get varsFA()
{
return this.get(CmdFCN.vars) as UntypedFormArray;
}
set varsFA(fa: UntypedFormArray)
{
this.setControl(CmdFCN.vars, fa);
}
public get_cmd_help(helpApi: HelpApi, module: string)
{
if (this.options) {
for (let j = 0; j < this.options!.length; j++) {
if (this.options![j] == ICommandOptions.help) {
helpApi.getHelpText("cmd", module, this.cmdname).subscribe(resp => { this.hlp_cmd = resp; }, err => { this.hlp_cmd = ""; });
}
}
}
}
}
import {UntypedFormControl, UntypedFormGroup} from "@angular/forms";
import {IInfo} from "../api/commands.api";
import {IArgType} from "../api/commands.api";
const enum InfosFCN {
name = "name",
value = "value",
type = "type",
modifiable = "modifiable"
}
export class InfoCtrl extends UntypedFormGroup {
type: IArgType;
constructor(ivar: IInfo)
{
super({});
this.type = ivar.type;
this.addControl(InfosFCN.name, new UntypedFormControl(ivar.name));
this.addControl(InfosFCN.value, new UntypedFormControl(ivar.value));
this.addControl(InfosFCN.type, new UntypedFormControl(ivar.type));
this.addControl(InfosFCN.modifiable, new UntypedFormControl(ivar.modifiable));
}
api()
{
const doc: IInfo = {
name : this.nameFC.value,
value : String(this.valueFC.value), // FIXME
type : this.typeFC.value,
modifiable : this.modifiableFC.value
};
return doc;
}
get nameFC()
{
return this.get(InfosFCN.name) as UntypedFormControl;
}
set nameFC(control: UntypedFormControl)
{
this.setControl(InfosFCN.name, control);
}
get valueFC()
{
return this.get(InfosFCN.value) as UntypedFormControl;
}
set valueFC(control: UntypedFormControl)
{
this.setControl(InfosFCN.value, control);
}
get typeFC()
{
return this.get(InfosFCN.type) as UntypedFormControl;
}
set typeFC(control: UntypedFormControl)
{
this.setControl(InfosFCN.type, control);
}
get modifiableFC()
{
return this.get(InfosFCN.modifiable) as UntypedFormControl;
}
set modifiableFC(control: UntypedFormControl)
{
this.setControl(InfosFCN.modifiable, control);
}
get btnTxtFC()
{
if (this.type != IArgType.configfile)
return "set"
else return "download"
}
}
import {UntypedFormArray, UntypedFormGroup} from "@angular/forms";
import {IModule} from "../api/commands.api";
const enum ModuleFCN {
vars = "variables",
cmds = "commands"
}
export class ModuleCtrl extends UntypedFormGroup {
name: string
constructor(imodule: IModule)
{
super({});
this.name = imodule.name;
this.addControl(ModuleFCN.vars, new UntypedFormArray([]));
this.addControl(ModuleFCN.cmds, new UntypedFormArray([]));
}
get varsFA()
{
return this.get(ModuleFCN.vars) as UntypedFormArray;
}
set varsFA(fa: UntypedFormArray)
{
this.setControl(ModuleFCN.vars, fa);
}
get cmdsFA()
{
return this.get(ModuleFCN.cmds) as UntypedFormArray;
}
set cmdsFA(fa: UntypedFormArray)
{
this.setControl(ModuleFCN.cmds, fa);
}
}
import {UntypedFormControl, UntypedFormGroup} from "@angular/forms";
import {IArgType, IColumn, IParam} from "../api/commands.api";
enum ParamFCN {
value = "value",
}
export class ParamCtrl extends UntypedFormGroup {
col: IColumn
constructor(public param: IParam)
{
super({})
this.col = param.col
let control: UntypedFormControl
switch (param.col.type)
{
case IArgType.boolean:
control = new UntypedFormControl((param.value === "true") ? true : false);
break;
case IArgType.loglvl:
control = new UntypedFormControl(param.value);
break;
default:
control = new UntypedFormControl(param.value)
}
if (!param.col.modifiable)
control
.disable()
this.addControl(ParamFCN.value, control)
}
get valueFC()
{
return this.get(ParamFCN.value) as UntypedFormControl
}
set valueFC(fc: UntypedFormControl)
{
this.setControl(ParamFCN.value, fc);
}
api()
{
let value: string
switch (this.col.type)
{
case IArgType.boolean:
value = String(this.valueFC.value);
break;
default:
value = this.valueFC.value
}
const doc: IParam = {
value : value,
col : this.col
}
return doc
}
}
import {FormControl, UntypedFormArray, UntypedFormGroup} from "@angular/forms";
import {IArgType, IParam, IRow, IVariable} from "../api/commands.api";
import {ParamCtrl} from "./param.control";
enum RowFCN {
paramsFA = "params",
}
export class RowCtrl extends UntypedFormGroup {
cmdName: string;
rawIndex: number;
cmdparam?: IVariable;
constructor(row: IRow)
{
super({})
this.cmdName = row.cmdName;
this.rawIndex = row.rawIndex;
this.addControl(RowFCN.paramsFA, new UntypedFormArray(row.params.map(param => new ParamCtrl(param))));
}
get paramsFA()
{
return this.get(RowFCN.paramsFA) as UntypedFormArray
}
set paramsFA(fa: UntypedFormArray)
{
this.setControl(RowFCN.paramsFA, fa);
}
set_cmdparam(cmdparam: IVariable)
{
this.cmdparam = cmdparam;
}
get paramsCtrls(): ParamCtrl[]{return this.paramsFA.controls as ParamCtrl[]}
api()
{
const doc: IRow = {
rawIndex : this.rawIndex,
cmdName : this.cmdName,
params : this.paramsCtrls.map(control => control.api()),
param : this.cmdparam
}
return doc
}
}
import {UntypedFormControl, UntypedFormGroup} from "@angular/forms";
import {IInfo} from "../api/commands.api";
import {IArgType} from "../api/commands.api";
const enum VariablesFCN {
name = "name",
value = "value",
type = "type",
modifiable = "modifiable"
}
export class VarCtrl extends UntypedFormGroup {
type: IArgType;
constructor(ivar: IInfo)
{
super({});
this.type = ivar.type;
this.addControl(VariablesFCN.name, new UntypedFormControl(ivar.name));
this.addControl(VariablesFCN.value, new UntypedFormControl(ivar.value));
this.addControl(VariablesFCN.type, new UntypedFormControl(ivar.type));
this.addControl(VariablesFCN.modifiable, new UntypedFormControl(ivar.modifiable));
}
api()
{
const doc: IInfo = {
name : this.nameFC.value,
value : String(this.valueFC.value), // FIXME
type : this.typeFC.value,
modifiable : this.modifiableFC.value
};
return doc;
}
get nameFC()
{
return this.get(VariablesFCN.name) as UntypedFormControl;
}
set nameFC(control: UntypedFormControl)
{
this.setControl(VariablesFCN.name, control);
}
get valueFC()
{
return this.get(VariablesFCN.value) as UntypedFormControl;
}
set valueFC(control: UntypedFormControl)
{
this.setControl(VariablesFCN.value, control);
}
get typeFC()
{
return this.get(VariablesFCN.type) as UntypedFormControl;
}
set typeFC(control: UntypedFormControl)
{
this.setControl(VariablesFCN.type, control);
}
get modifiableFC()
{
return this.get(VariablesFCN.modifiable) as UntypedFormControl;
}
set modifiableFC(control: UntypedFormControl)
{
this.setControl(VariablesFCN.modifiable, control);
}
get btnTxtFC()
{
if (this.type != IArgType.configfile)
return "set"
else return "download"
}
}
import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse} from "@angular/common/http";
import {Injectable} from "@angular/core";
import {Observable, throwError} from "rxjs";
import {catchError, tap} from "rxjs/operators";
import {DialogService as DialogService} from "../services/dialog.service";
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
constructor(public dialogService: DialogService)
{
}
intercept(request: HttpRequest<unknown>, next: HttpHandler)
{
return next.handle(request).pipe(
// tap((event: HttpEvent<any>) => {
// if (event instanceof HttpResponse) {
// switch (event.status) {
// case 200:
// case 201:
// this.log(GREEN, request.method + ' ' + event.status + ' Success');
// this.dialogService.openSnackBar(request.method + ' ' + event.status + ' Success');
// break;
// default:
// break;
// }
// // return throwError(event.body);
// }
// }),
catchError((error: HttpErrorResponse) => {
let prefix: string = "oai web interface [API]: ";
let message: string = " ";
switch (error.status) {
case 400:
case 403:
console.error(prefix + request.method + " " + error.status + " Error: " + error.error.toString());
break;
case 501:
case 500:
console.warn(prefix + request.method + " " + error.status + " Error: " + error.error.toString());
break;
default:
console.log(prefix + request.method + " " + error.status + " Error: " + error.error.toString());
break;
}
if (error.error instanceof ErrorEvent) {
message = error.error.message;
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong
message = JSON.stringify(error.error);
}
this.dialogService.openErrorDialog(prefix + " " + error.status, message);
return throwError(error);
}),
);
}
}
import {HTTP_INTERCEPTORS} from "@angular/common/http";
import {ErrorInterceptor} from "./error.interceptor";
import {SpinnerInterceptor} from "./spinner.interceptor";
export const InterceptorProviders = [
{provide : HTTP_INTERCEPTORS, useClass : SpinnerInterceptor, multi : true},
{provide : HTTP_INTERCEPTORS, useClass : ErrorInterceptor, multi : true},
];
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse} from "@angular/common/http";
import {Injectable} from "@angular/core";
import {Observable} from "rxjs";
import {finalize} from "rxjs/operators";
import {LoadingService} from "../services/loading.service";
@Injectable()
export class SpinnerInterceptor implements HttpInterceptor {
activeRequests = 0;
constructor(private loadingService: LoadingService)
{
}
intercept(request: HttpRequest<unknown>, next: HttpHandler)
{
if (this.activeRequests === 0) {
this.loadingService.startLoading();
}
this.activeRequests++;
return next.handle(request).pipe(
finalize(() => {
this.activeRequests--;
if (this.activeRequests === 0) {
this.loadingService.stopLoading();
}
}),
);
}
}
import {HttpErrorResponse} from "@angular/common/http";
import {ElementRef, Injectable, ViewChild} from "@angular/core";
import {NgModule} from "@angular/core";
import {MatDialog} from "@angular/material/dialog";
import {MatSnackBar} from "@angular/material/snack-bar";
import {BrowserModule} from "@angular/platform-browser";
import {Observable} from "rxjs";
import {of} from "rxjs";
import {tap} from "rxjs/operators";
import {CmdCtrl} from "src/app/controls/cmd.control";
import {ICommandOptions, IResp} from "../api/commands.api";
import {CommandsComponent} from "../components/commands/commands.component";
import {ConfirmDialogComponent} from "../components/confirm/confirm.component";
import {DialogComponent} from "../components/dialog/dialog.component";
import {QuestionDialogComponent} from "../components/question/question.component";
@Injectable({
providedIn : "root",
})
export class DialogService {
public isDialogOpen = false;
cmdDialogRef: any;
constructor(
private _dialog: MatDialog,
private _snackBar: MatSnackBar,
)
{
}
openErrorDialog(title: string, message: string): void
{
const ErrDiag = this._dialog.open(DialogComponent, {
width : "900px",
data : {
title : title,
body : message,
},
panelClass : "errRespDialog",
});
ErrDiag.afterClosed().subscribe(result => {});
}
openCmdDialog(control: CmdCtrl, resp: IResp, title?: string): Observable<IResp>
{
this.isDialogOpen = true;
console.log("Open Cmd dialog");
var updatable = false;
if (control.options) {
for (let opt = 0; opt < control.options.length; opt++) {
if (control.options[opt] == ICommandOptions.update)
updatable = true;
}
}
this.cmdDialogRef = this._dialog.open(DialogComponent, {
height : "80%",
hasBackdrop : false,
data : {
control : control,
title : title,
body : resp.display!.join("</p><p>"),
updatable : updatable,
},
panelClass : "cmdRespDialog",
});
this.cmdDialogRef.afterClosed().subscribe(() => {
console.log("The dialog was closed");
this.isDialogOpen = false;
control.stopUpdate();
if (control.ResUpdTimerSubscriber) {
control.ResUpdTimerSubscriber.unsubscribe();
}
});
return of(resp)
}
updateCmdDialog(control: CmdCtrl, resp: IResp, title?: string): Observable<IResp>
{
if (this.cmdDialogRef && this.isDialogOpen && resp.display.length) {
this.cmdDialogRef.componentInstance.data = {control : control, title : title, body : resp.display!.join("</p><p>"), updatable : true};
return of(resp);
} else {
return this.openCmdDialog(control, resp, title);
}
}
openVarRespDialog(resp: IResp): Observable<IResp>
{
if (this.isDialogOpen || !resp.display.length) {
return of(resp);
}
console.log("Open Var dialog");
this.isDialogOpen = true;
const dialogRef = this._dialog.open(DialogComponent, {
width : "900px",
hasBackdrop : true,
data : {
title : resp.display![0],
},
panelClass : "varRespDialog",
});
dialogRef.afterClosed().subscribe(() => {
console.log("The dialog was closed");
this.isDialogOpen = false;
});
return of(resp)
}
openSnackBar(title: string): void
{
this._snackBar.open(title, undefined, {
duration : 500,
horizontalPosition : "center",
verticalPosition : "bottom",
});
}
openConfirmDialog(question: string)
{
if (this.isDialogOpen) {
return of(undefined);
}
this.isDialogOpen = true;
return this._dialog.open(ConfirmDialogComponent, {width : "300px", data : {title : question}}).afterClosed().pipe(tap(() => this.isDialogOpen = false));
}
openQuestionDialog(title: string, control: CmdCtrl)
{
if (this.isDialogOpen) {
return of(control);
}
this.isDialogOpen = true;
const dialogRef = this._dialog.open(QuestionDialogComponent, {
width : "300px",
data : {title : title, control : control},
panelClass : "questionDialog",
})
return dialogRef.afterClosed().pipe(tap(() => this.isDialogOpen = false));
}
}
import {HttpClient} from "@angular/common/http";
import {HttpHeaders} from "@angular/common/http";
import {HttpParams} from "@angular/common/http";
import {Injectable} from "@angular/core";
import {route} from "src/app/api/commands.api";
import {environment} from "src/environments/environment";
@Injectable({
providedIn : "root",
})
export class DownloadService {
constructor(private http: HttpClient)
{
}
getFile(url: string)
{
const token = "my JWT";
const headers = new HttpHeaders().set("authorization", "Bearer " + token);
const postparams = new HttpParams().set("fname", url);
this.http.post(environment.backend + route + "/file", postparams, {headers, responseType : "blob" as "json"}).subscribe((response: any) => {
let dataType = response.type;
let binaryData = [];
binaryData.push(response);
let downloadLink = document.createElement("a");
downloadLink.href = window.URL.createObjectURL(new Blob(binaryData, {type : dataType}));
downloadLink.setAttribute("download", url);
document.body.appendChild(downloadLink);
downloadLink.click();
})
}
};
import {Injectable} from "@angular/core";
import {Subject} from "rxjs";
@Injectable({
providedIn : "root",
})
export class LoadingService {
isLoading$ = new Subject<boolean>();
constructor()
{
}
startLoading()
{
this.isLoading$.next(true);
}
stopLoading()
{
this.isLoading$.next(false);
}
}
import {Injectable} from "@angular/core";
import {webSocket, WebSocketSubject} from "rxjs/webSocket";
import {environment} from "src/environments/environment";
export enum webSockSrc {
softscope = "s".charCodeAt(0),
logview = "l".charCodeAt(0),
}
export interface Message {
source: webSockSrc;
fullbuff: ArrayBuffer;
}
export const arraybuf_data_offset = 8; // 64 bits (8 bytes) header
const deserialize = (fullbuff: ArrayBuffer):
Message => {
const header = new DataView(fullbuff, 0, arraybuf_data_offset); // header
return {source : header.getUint8(0), fullbuff : fullbuff};
}
const serialize = (msg: Message):
ArrayBuffer => {
let buffview = new DataView(msg.fullbuff);
buffview.setUint8(0, msg.source); // header
return buffview.buffer;
}
@Injectable() export class WebSocketService {
public subject$: WebSocketSubject<Message>;
constructor()
{
this.subject$ = webSocket<Message>({
url : environment.backend.replace("http", "ws") + "softscope",
openObserver : {next : () => { console.log("WS connection established") }},
closeObserver : {next : () => { console.log("WS connextion closed") }},
serializer : msg => serialize(msg),
deserializer : msg => deserialize(msg.data),
binaryType : "arraybuffer"
});
}
// public get scopeSubject$() {
// return this.subject$.multiplex(
// () => { console.log('WS scope2 connection established') },
// () => { console.log('WS scope2 connection closed') },
// msg => msg.source === webSockSrc.softscope,
// );
// }
// public get loggerSubject$() {
// return this.subject$.multiplex(
// () => { console.log('WS logger connection established') },
// () => { console.log('WS logger connection closed') },
// msg => msg.source === webSockSrc.logview,
// );
// }
public send(msg: Message)
{
console.log("Message sent to websocket: ", msg.fullbuff);
this.subject$.next(msg);
}
// public close() {
// this.subject$.complete();
// }
// public error() {
// this.subject$.error({ code: 4000, reason: 'I think our app just broke!' });
// }
}
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production : true,
backend : window.location.protocol + "//" + window.location.hostname + ":" + window.location.port + "/"
};
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production : false,
// backend: 'localhost:8090/',
backend : "http://oaifh.devhost:8090/" // define oaifh.devhost host in your linux /etc/hosts or the windows equivalent PLEASE NO HARDCODED IP HERE
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>oai softmodem</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap" rel="stylesheet" />
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
</head>
<body class="mat-typography">
<app-root></app-root>
</body>
</html>
import {enableProdMode} from "@angular/core";
import {platformBrowserDynamic} from "@angular/platform-browser-dynamic";
import {AppModule} from "./app/app.module";
import {environment} from "./environments/environment";
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule).catch((err) => console.error(err));
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import "zone.js/dist/zone"; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
/* You can add global styles to this file, and also import other style files */
html,
body {
height: 100%;
}
body {
margin: 0;
font-family: Roboto, 'Helvetica Neue', sans-serif;
}
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import "zone.js/dist/zone-testing";
import {getTestBed} from "@angular/core/testing";
import {BrowserDynamicTestingModule, platformBrowserDynamicTesting} from "@angular/platform-browser-dynamic/testing";
declare const require: {
context(
path: string,
deep?: boolean,
filter?: RegExp,
): {keys(): string[];<T>(id: string) : T;};
};
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
// Then we find all the tests.
const context = require.context("./", true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
},
"files": ["src/main.ts", "src/polyfills.ts"],
"include": ["src/**/*.d.ts"]
}
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"moduleResolution": "node",
"importHelpers": true,
"target": "es2020",
"module": "es2020",
"lib": [
"es2018",
"dom"
]
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
}
}
\ No newline at end of file
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": ["jasmine"]
},
"files": ["src/test.ts", "src/polyfills.ts"],
"include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
}
This option asks for a model id and lists the parameters of this model. Some parameters can be then modified
When a oai component, as the rf simulator, instanciates a channel model, the instanciation is added in the list of current models. The id of the instanciation is used when using the "show channelid" option.
Predefined channel model are a set of modelisation algorithm's, identified by a name, available in oai code.
Available channel modelisation algorithm's can be listed using the "channelmod"/"show predef" command. A more convenient mechanism to select this will be implemented in a next version.
The model index can be used to modify a model parameter, using the "channelmod"/"show channelid" commands
Models parameters are defined in the oai configuration file under the section "channelmod.<channel list>". <channel list> describes the parameters for each <model name>. Several <channel list> can be defined, the loaded list is defined by the "channelmod.modellist" parameter. Model names are defined by the channel modelisation "user": for example the model "rfsmu_channel_ue0" is applied by the gNB rfsimulator on the signal received from ue0, the first connecet UE.
When the rfsimulator is effectively using a channel model to modify a received signal he becomes the owner of that model.
when enabled, back-end will stop sending data when too much data have not been acknowledge by the frontend. Currently the back-end limits to 200 the number of data messages waitting for ack.
Nice value, relevant for other and batch scheduling can be changed via the "oai priority" field
the oai priority field is used to provide the linux scheduling mode, priority and nice value in a single field.
Priority value is mapped to linux scheduling mode, priority and nice as listed below:
-101 to -199: real time, Round-Robin 1 to 99
-1 to -99 : real time, fifo 1 to 99
0 to 39 : time sharing, nice value -20 to 19
40 to 79 : batch, nice value -20 to 19
>80 : idle
Linux priority field, relevant for real-time scheduling mode (Round-Robin or fifo) can be modified using the "oai priority" field
scheduling policy can be modified using the "oai priority" field.
/*
* 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.c
* \brief: implementation of web API
* \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"
#define WEBSERVERCODE
#include "common/utils/telnetsrv/telnetsrv.h"
#include "common/utils/load_module_shlib.h"
#include <arpa/inet.h>
//#define WEBSRV_AUTH
static websrv_params_t websrvparams;
static telnetsrv_params_t *dummy_telnetsrv_params = NULL;
paramdef_t websrvoptions[] = {
/*-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/* configuration parameters for telnet utility */
/* optname helpstr paramflags XXXptr defXXXval type numelt */
/*-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
{"listenaddr", "<listen ip address>\n", 0, uptr : &websrvparams.listenaddr, defstrval : "0.0.0.0", TYPE_IPV4ADDR, 0},
{"listenport", "<local port>\n", 0, uptr : &(websrvparams.listenport), defuintval : 8090, TYPE_UINT, 0},
{"priority", "<scheduling policy (0-99)\n", 0, iptr : &websrvparams.priority, defuintval : 0, TYPE_INT, 0},
{"debug", "<debug level>\n", 0, uptr : &websrvparams.dbglvl, defuintval : 0, TYPE_UINT, 0},
{"fpath", "<file directory>\n", 0, strptr : &websrvparams.fpath, defstrval : "websrv", TYPE_STRING, 0},
{"cert", "<cert file>\n", 0, strptr : &websrvparams.certfile, defstrval : NULL, TYPE_STRING, 0},
{"key", "<key file>\n", 0, strptr : &websrvparams.keyfile, defstrval : NULL, TYPE_STRING, 0},
{"rootca", "<root ca file>\n", 0, strptr : &websrvparams.rootcafile, defstrval : NULL, TYPE_STRING, 0},
};
void register_module_endpoints(cmdparser_t *module);
void websrv_gettbldata_response(struct _u_response *response, webdatadef_t *wdata, char *module, char *cmdtitle);
int websrv_callback_get_softmodemcmd(const struct _u_request *request, struct _u_response *response, void *user_data);
/*--------------------------------------------------------------------------------------------------*/
/* format a json response from a result table returned from a call to a telnet server command */
void websrv_gettbldata_response(struct _u_response *response, webdatadef_t *wdata, char *module, char *cmdtitle)
{
json_t *jcols = json_array();
json_t *jdata = json_array();
char *coltype;
for (int i = 0; i < wdata->numcols; i++) {
if (wdata->columns[i].coltype & TELNET_CHECKVAL_BOOL)
coltype = "boolean";
else if (wdata->columns[i].coltype & TELNET_CHECKVAL_LOGLVL)
coltype = "loglvl";
else if (wdata->columns[i].coltype & TELNET_CHECKVAL_SIMALGO)
coltype = "string"; //"simuTypes";
else if (wdata->columns[i].coltype & TELNET_VARTYPE_STRING)
coltype = "string";
else
coltype = "number";
json_t *acol = json_pack("{s:s,s:s,s:b,s:b}",
"name",
wdata->columns[i].coltitle,
"type",
coltype,
"modifiable",
(wdata->columns[i].coltype & TELNET_CHECKVAL_RDONLY) ? 0 : 1,
"help",
websrv_utils_testhlp(websrvparams.fpath, module, cmdtitle, wdata->columns[i].coltitle));
json_array_append_new(jcols, acol);
}
for (int i = 0; i < wdata->numlines; i++) {
json_t *jval;
json_t *jline = json_array();
for (int j = 0; j < wdata->numcols; j++) {
if (wdata->columns[j].coltype & TELNET_CHECKVAL_BOOL)
jval = json_string(wdata->lines[i].val[j]);
else if (wdata->columns[j].coltype & TELNET_VARTYPE_STRING)
jval = json_string(wdata->lines[i].val[j]);
// else if (wdata->columns[j].coltype == TELNET_VARTYPE_DOUBLE)
// jval=json_real((double)(wdata->lines[i].val[j]));
else
jval = json_integer((long)(wdata->lines[i].val[j]));
json_array_append_new(jline, jval);
}
json_array_append_new(jdata, jline);
}
json_t *jbody = json_pack("{s:[],s:{s:o,s:o}}", "display", "table", "columns", jcols, "rows", jdata);
websrv_jbody(response, jbody, 200, websrvparams.dbglvl);
telnetsrv_freetbldata(wdata);
}
int websrv_callback_auth(const struct _u_request *request, struct _u_response *response, void *user_data)
{
LOG_I(UTIL, "[websrv] authenticating %s %s\n", request->http_verb, request->http_url);
websrv_dump_request("authentication ", request, websrvparams.dbglvl);
#ifdef WEBSRV_AUTH
char *dn = NULL, *issuer_dn = NULL;
size_t lbuf = 0, libuf = 0;
if (request->client_cert != NULL) {
gnutls_x509_crt_get_dn(request->client_cert, NULL, &lbuf);
gnutls_x509_crt_get_issuer_dn(request->client_cert, NULL, &libuf);
dn = malloc(lbuf + 1);
issuer_dn = malloc(libuf + 1);
if (dn != NULL && issuer_dn != NULL) {
gnutls_x509_crt_get_dn(request->client_cert, dn, &lbuf);
gnutls_x509_crt_get_issuer_dn(request->client_cert, issuer_dn, &libuf);
dn[lbuf] = '\0';
issuer_dn[libuf] = '\0';
LOG_I(UTIL, "[websrv] dn of the client: %s, dn of the issuer: %s \n", dn, issuer_dn);
} else {
LOG_I(UTIL, "[websrv] dn of the client: %s, dn of the issuer: %s \n", (dn == NULL) ? "null" : dn, (issuer_dn == NULL) ? "null" : issuer_dn);
ulfius_set_string_body_response(response, 400, "Couldn't authenticate client");
}
free(dn);
free(issuer_dn);
} else {
LOG_I(UTIL, "No client certificate\n");
// ulfius_set_string_body_response(response, 400, "Invalid client certificate");
return U_CALLBACK_CONTINUE;
}
#endif
// return U_CALLBACK_ERROR;
return U_CALLBACK_ERROR;
}
/*----------------------------------------------------------------------------------------------*/
/* callback to answer a request to download a file from the softmodem (example: a config file */
int websrv_callback_get_softmodemfile(const struct _u_request *request, struct _u_response *response, void *user_data)
{
LOG_I(UTIL, "[websrv] %s received: %s %s\n", __FUNCTION__, request->http_verb, request->http_url);
websrv_dump_request("get softmodem file ", request, websrvparams.dbglvl);
if (request->map_post_body == NULL) {
LOG_W(UTIL, "[websrv] No post parameters in: %s %s\n", request->http_verb, request->http_url);
websrv_string_response("Invalid file request, no post parameters", response, 404, websrvparams.dbglvl);
}
char *fname = (char *)u_map_get(request->map_post_body, "fname");
if (fname == NULL) {
LOG_W(UTIL, "[websrv] fname post parameters not found: %s %s\n", request->http_verb, request->http_url);
websrv_string_response("Invalid file request, no file name in post parameters", response, 404, websrvparams.dbglvl);
}
if (websrv_getfile(fname, response) == NULL) {
LOG_W(UTIL, "[websrv] file %s not found\n", fname);
websrv_string_response("file not found", response, 501, websrvparams.dbglvl);
}
return U_CALLBACK_COMPLETE;
}
/*------------------------------------------------------------------------------------------------------------------------*/
int websrv_callback_set_moduleparams(const struct _u_request *request, struct _u_response *response, void *user_data)
{
websrv_printf_start(response, 200, false);
LOG_I(UTIL, "[websrv] %s received: %s %s\n", __FUNCTION__, request->http_verb, request->http_url);
json_error_t jserr;
json_t *jsbody = ulfius_get_json_body_request(request, &jserr);
int httpstatus = 404;
if (jsbody == NULL) {
websrv_printf("cannot find json body in %s %s\n", request->http_url, jserr.text);
httpstatus = 400;
} else {
websrv_printjson((char *)__FUNCTION__, jsbody, websrvparams.dbglvl);
if (user_data == NULL) {
httpstatus = 500;
websrv_printf("%s: NULL user data", request->http_url);
} else {
cmdparser_t *modulestruct = (cmdparser_t *)user_data;
int rawval;
char *cmdName;
json_t *parray = json_array();
json_error_t jerror;
int ures = json_unpack_ex(jsbody, &jerror, 0, "{s:i,s:s,s:o}", "rawIndex", &rawval, "cmdName", &cmdName, "params", &parray);
if (ures != 0) {
websrv_printf("cannot unpack json body from %s: %s \n", request->http_url, jerror.text);
} else {
int psize = json_array_size(parray);
webdatadef_t datatbl;
datatbl.numlines = rawval;
datatbl.numcols = psize;
LOG_I(UTIL, "[websrv] rawIndex=%i, cmdName=%s, params=array of %i table values\n", rawval, cmdName, psize);
for (int i = 0; i < psize; i++) {
json_t *jelem = json_array_get(parray, i);
char *cvalue;
char *cname;
char *ctype;
int cmod;
ures = json_unpack_ex(jelem, &jerror, 0, "{s:s,s:{s:s,s:s,s:b}}", "value", &cvalue, "col", "name", &cname, "type", &ctype, "modifiable", &cmod);
if (ures != 0) {
websrv_printf("cannot unpack json element %i %s\n", i, jerror.text);
} else {
LOG_I(UTIL, "[websrv] element%i, value=%s, name %s type %s\n", i, cvalue, cname, ctype);
snprintf(datatbl.columns[i].coltitle, TELNET_CMD_MAXSIZE - 1, "%s", cname);
datatbl.columns[i].coltype = TELNET_VARTYPE_STRING;
datatbl.lines[0].val[i] = cvalue;
} // json_unpack_ex(jelem OK
} // for i
for (telnetshell_cmddef_t *cmd = modulestruct->cmd; cmd->cmdfunc != NULL; cmd++) {
if (strcmp(cmd->cmdname, cmdName) == 0 && ((cmd->cmdflags & TELNETSRV_CMDFLAG_TELNETONLY) == 0)) {
httpstatus = 200;
char *pvalue = NULL;
if (strncmp(cmdName, "show", 4) == 0) {
sprintf(cmdName, "%s", "set");
if (cmd->cmdflags & TELNETSRV_CMDFLAG_NEEDPARAM) {
json_t *Jparam = json_object_get(jsbody, "param");
if (Jparam != NULL) {
char *pname = NULL;
ures = json_unpack_ex(Jparam, &jerror, 0, "{s:s,s:s}", "name", &pname, "value", &pvalue);
if (ures != 0) {
websrv_printf("cannot unpack json param %s\n", jerror.text);
} else {
LOG_I(UTIL, "[websrv] parameter %s=%s\n", pname, pvalue);
snprintf(datatbl.tblname, sizeof(datatbl.tblname), "%s=%s", pname, pvalue);
}
}
}
cmdName[3] = ' ';
}
cmd->webfunc_getdata(cmdName, websrvparams.dbglvl, &datatbl, websrv_printf);
if (cmd->cmdflags & TELNETSRV_CMDFLAG_WEBSRV_SETRETURNTBL) {
httpstatus = 1000;
websrv_printf_end(httpstatus, websrvparams.dbglvl);
websrv_gettbldata_response(response, &datatbl, modulestruct->module, cmd->cmdname);
return U_CALLBACK_COMPLETE;
}
break;
}
} // for *cmd
} // json_unpack_ex(jsbody OK
} // user_data
} // sbody
websrv_printf_end(httpstatus, websrvparams.dbglvl);
return U_CALLBACK_COMPLETE;
}
/*------------------------------------------------------------------------------------------------------------------------*/
/* callback to format a help response */
int websrv_callback_get_softmodemhelp(const struct _u_request *request, struct _u_response *response, void *user_data)
{
LOG_I(UTIL, "[websrv] Requested file is: %s %s\n", request->http_verb, request->http_url);
websrv_dump_request("help ", request, websrvparams.dbglvl);
char *help_string = NULL;
int httpstatus = 204; // no content
char *hlpfile = strstr(request->http_url, "helpfiles");
if (hlpfile != NULL) {
char *hlppath = malloc(strlen(hlpfile) + strlen(websrvparams.fpath) + 1);
sprintf(hlppath, "%s/%s", websrvparams.fpath, hlpfile);
help_string = websrv_read_file(hlppath);
if (help_string == NULL) {
help_string = strdup("Help file not found");
} else {
httpstatus = 201; // created
}
free(hlppath);
} else {
help_string = strdup("Help request format error");
}
json_t *jhelp = json_pack("{s:s}", "text", help_string);
websrv_jbody(response, jhelp, httpstatus, websrvparams.dbglvl);
free(help_string);
return U_CALLBACK_CONTINUE;
}
/* default callback tries to find a file in the web server repo (path exctracted from <websrvparams.url>) and if found streams it */
int websrv_callback_default(const struct _u_request *request, struct _u_response *response, void *user_data)
{
LOG_I(UTIL, "[websrv] Requested file is: %s %s\n", request->http_verb, request->http_url);
websrv_dump_request("default ", request, websrvparams.dbglvl);
char *fpath = malloc(strlen(request->http_url) + strlen(websrvparams.fpath) + 2);
if ((strcmp(request->http_url + 1, websrvparams.fpath) == 0) || (strcmp(request->http_url, "/") == 0)) {
sprintf(fpath, "%s/index.html", websrvparams.fpath);
} else {
sprintf(fpath, "%s/%s", websrvparams.fpath, request->http_url);
}
FILE *f = websrv_getfile(fpath, response);
free(fpath);
if (f == NULL)
return U_CALLBACK_ERROR;
return U_CALLBACK_CONTINUE;
}
/* callback processing url (<address>/oaisoftmodem/:module/variables or <address>/oaisoftmodem/:module/commands) */
/* is called at low priority to process requests from a module initialized after the web server started */
int websrv_callback_newmodule(const struct _u_request *request, struct _u_response *response, void *user_data)
{
LOG_I(UTIL, "[websrv] callback_newmodule received %s %s\n", request->http_verb, request->http_url);
if (user_data == NULL) {
ulfius_set_string_body_response(response, 500, "Cannot access oai softmodem data");
LOG_E(UTIL, "[websrv] callback_newmodule: user-data is NULL");
return U_CALLBACK_COMPLETE;
}
telnetsrv_params_t *telnetparams = (telnetsrv_params_t *)user_data;
for (int i = 0; i < u_map_count(request->map_url); i++) {
LOG_I(UTIL, "[websrv] url element %i %s : %s\n", i, u_map_enum_keys(request->map_url)[i], u_map_enum_values(request->map_url)[i]);
if (strcmp(u_map_enum_keys(request->map_url)[i], "module") == 0) {
for (int j = 0; telnetparams->CmdParsers[j].cmd != NULL; j++) {
/* found the module in the telnet server module array, it was likely not registered at init time */
if (strcmp(telnetparams->CmdParsers[j].module, u_map_enum_values(request->map_url)[i]) == 0) {
register_module_endpoints(&(telnetparams->CmdParsers[j]));
websrv_callback_get_softmodemcmd(request, response, &(telnetparams->CmdParsers[j]));
return U_CALLBACK_CONTINUE;
}
} /* for j */
}
} /* for i */
ulfius_set_string_body_response(response, 500, "Request for an unknown module");
return U_CALLBACK_COMPLETE;
}
/* callback processing url (<address>/oaisoftmodem/module/variables or <address>/oaisoftmodem/module/commands), options method */
int websrv_callback_okset_softmodem_cmdvar(const struct _u_request *request, struct _u_response *response, void *user_data)
{
LOG_I(UTIL, "[websrv] : callback_okset_softmodem_cmdvar received %s %s\n", request->http_verb, request->http_url);
for (int i = 0; i < u_map_count(request->map_header); i++)
LOG_I(UTIL, "[websrv] header variable %i %s : %s\n", i, u_map_enum_keys(request->map_header)[i], u_map_enum_values(request->map_header)[i]);
int us = ulfius_add_header_to_response(response, "Access-Control-Request-Method", "POST");
if (us != U_OK) {
ulfius_set_string_body_response(response, 501, "Internal server error (ulfius_add_header_to_response)");
LOG_E(UTIL, "[websrv] cannot set response header type ulfius error %d \n", us);
}
us = ulfius_add_header_to_response(response, "Access-Control-Allow-Headers", "content-type");
us = ulfius_set_empty_body_response(response, 200);
if (us != U_OK) {
ulfius_set_string_body_response(response, 501, "Internal server error (ulfius_set_empty_body_response)");
LOG_E(UTIL, "[websrv] cannot set empty body response ulfius error %d \n", us);
}
return U_CALLBACK_COMPLETE;
}
int websrv_callback_set_softmodemvar(const struct _u_request *request, struct _u_response *response, void *user_data)
{
LOG_I(UTIL, "[websrv] : callback_set_softmodemvar received %s %s\n", request->http_verb, request->http_url);
websrv_printf_start(response, 256, false);
json_error_t jserr;
json_t *jsbody = ulfius_get_json_body_request(request, &jserr);
int httpstatus = 404;
if (jsbody == NULL) {
websrv_printf("cannot find json body in %s %s\n", request->http_url, jserr.text);
httpstatus = 400;
} else {
websrv_printjson("callback_set_softmodemvar: ", jsbody, websrvparams.dbglvl);
if (user_data == NULL) {
httpstatus = 500;
websrv_printf("%s: NULL user data", request->http_url);
} else {
cmdparser_t *modulestruct = (cmdparser_t *)user_data;
json_t *J = json_object_get(jsbody, "name");
const char *vname = json_string_value(J);
for (telnetshell_vardef_t *var = modulestruct->var; var->varvalptr != NULL; var++) {
if (strncmp(var->varname, vname, TELNET_CMD_MAXSIZE) == 0) {
J = json_object_get(jsbody, "value");
if (J != NULL) {
if (json_is_string(J)) {
const char *vval = json_string_value(J);
websrv_printf("var %s set to ", var->varname);
int st = telnet_setvarvalue(var, (char *)vval, websrv_printf);
if (st >= 0) {
httpstatus = 200;
} else {
httpstatus = 500;
}
} else if (json_is_integer(J)) {
json_int_t i = json_integer_value(J);
switch (var->vartype) {
case TELNET_VARTYPE_INT64:
*(int64_t *)var->varvalptr = (int64_t)i;
break;
case TELNET_VARTYPE_INT32:
*(int32_t *)var->varvalptr = (int32_t)i;
break;
case TELNET_VARTYPE_INT16:
*(int16_t *)var->varvalptr = (int16_t)i;
break;
case TELNET_VARTYPE_INT8:
*(int8_t *)var->varvalptr = (int8_t)i;
break;
case TELNET_VARTYPE_UINT:
*(unsigned int *)var->varvalptr = (unsigned int)i;
break;
default:
httpstatus = 500;
websrv_printf(" Cannot set var %s, integer type mismatch\n", vname);
break;
}
} else if (json_is_real(J)) {
double lf = json_real_value(J);
if (var->vartype == TELNET_VARTYPE_DOUBLE) {
*(double *)var->varvalptr = lf;
httpstatus = 200;
websrv_printf(" Var %s, set to %g\n", vname, *(double *)var->varvalptr);
} else {
httpstatus = 500;
websrv_printf(" Cannot set var %s, real type mismatch\n", vname);
}
}
} else {
httpstatus = 500;
websrv_printf("Cannot set var %s, json object is NULL\n", vname);
}
break;
}
} // for
} // user_data
} // sbody
websrv_printf_end(httpstatus, websrvparams.dbglvl);
return U_CALLBACK_COMPLETE;
}
/* callback processing module url (<address>/oaisoftmodem/module/commands), post method */
int websrv_processwebfunc(struct _u_response *response, cmdparser_t *modulestruct, telnetshell_cmddef_t *cmd, json_t *jparams)
{
LOG_I(UTIL, "[websrv] : executing command %s %s\n", modulestruct->module, cmd->cmdname);
int http_status = 200;
if (cmd->cmdflags & TELNETSRV_CMDFLAG_GETWEBTBLDATA) {
webdatadef_t wdata;
memset(&wdata, 0, sizeof(wdata));
if (cmd->cmdflags & TELNETSRV_CMDFLAG_NEEDPARAM) {
if (jparams == NULL) {
LOG_W(UTIL, "No parameters sent by frontend for %s %s\n", modulestruct->module, cmd->cmdname);
} else {
int b;
char *pname, *pvalue, *ptype;
json_error_t jerror;
json_unpack_ex(jparams, &jerror, 0, "{s:s,s:s,s:s,s,b}", "name", &pname, "value", &pvalue, "type", &ptype, "modifiable", &b);
if (pvalue == NULL || pname == NULL || ptype == NULL) {
LOG_I(UTIL, "[websrv], couldn't unpack jparams, module %s, command %s: %s\n", modulestruct->module, cmd->cmdname, jerror.text);
websrv_printjson((char *)__FUNCTION__, jparams, websrvparams.dbglvl);
http_status = 500;
} else {
snprintf(wdata.columns[0].coltitle, sizeof(wdata.columns[0].coltitle) - 1, "%s", pname);
wdata.numcols = 1;
wdata.lines[0].val[0] = pvalue;
wdata.numlines = 1;
}
}
}
cmd->webfunc_getdata(cmd->cmdname, websrvparams.dbglvl, (webdatadef_t *)&wdata, NULL);
websrv_gettbldata_response(response, &wdata, modulestruct->module, cmd->cmdname);
} else {
char *sptr = index(cmd->cmdname, ' ');
if (cmd->qptr != NULL) {
websrv_printf_start(response, 16384, true);
telnet_pushcmd(cmd, (sptr == NULL) ? cmd->cmdname : sptr, websrv_async_printf);
} else {
websrv_printf_start(response, 16384, false);
cmd->cmdfunc((sptr == NULL) ? cmd->cmdname : sptr, websrvparams.dbglvl, websrv_printf);
}
websrv_printf_end(http_status, websrvparams.dbglvl);
}
return http_status;
}
int websrv_callback_exec_softmodemcmd(const struct _u_request *request, struct _u_response *response, void *user_data)
{
LOG_I(UTIL, "[websrv] : callback_exec_softmodemcmd received %s %s\n", request->http_verb, request->http_url);
if (user_data == NULL) {
ulfius_set_string_body_response(response, 500, "Cannot access oai softmodem data");
LOG_E(UTIL, "[websrv] callback_exec_softmodemcmd: user-data is NULL");
return U_CALLBACK_COMPLETE;
}
cmdparser_t *modulestruct = (cmdparser_t *)user_data;
json_t *jsbody = ulfius_get_json_body_request(request, NULL);
int httpstatus = 404;
char *msg = "";
if (jsbody == NULL) {
msg = "Unproperly formatted request body";
httpstatus = 400;
} else {
websrv_printjson("callback_exec_softmodemcmd: ", jsbody, websrvparams.dbglvl);
json_t *J = json_object_get(jsbody, "name");
const char *vname = json_string_value(J);
if (vname == NULL) {
msg = "No command name in request body";
LOG_E(UTIL, "[websrv] command name not found in body\n");
httpstatus = 400;
} else {
httpstatus = 501;
msg = "Unknown command in request body";
for (telnetshell_cmddef_t *cmd = modulestruct->cmd; cmd->cmdfunc != NULL; cmd++) {
if (strcmp(cmd->cmdname, vname) == 0 && ((cmd->cmdflags & TELNETSRV_CMDFLAG_TELNETONLY) == 0)) {
J = json_object_get(jsbody, "param");
httpstatus = websrv_processwebfunc(response, modulestruct, cmd, J);
break;
}
} // for
}
} // sbody
if (httpstatus >= 300)
ulfius_set_string_body_response(response, httpstatus, msg);
return U_CALLBACK_COMPLETE;
}
/* callback processing module url (<address>/oaisoftmodem/module/variables), get method*/
int websrv_callback_get_softmodemvar(const struct _u_request *request, struct _u_response *response, void *user_data)
{
LOG_I(UTIL, "[websrv] : callback_get_softmodemvar received %s %s module %s\n", request->http_verb, request->http_url, (user_data == NULL) ? "NULL" : ((cmdparser_t *)user_data)->module);
if (user_data == NULL) {
ulfius_set_string_body_response(response, 500, "No variables defined for this module");
return U_CALLBACK_COMPLETE;
}
cmdparser_t *modulestruct = (cmdparser_t *)user_data;
LOG_I(UTIL, "[websrv] received %s variables request\n", modulestruct->module);
json_t *modulevars = json_array();
for (int j = 0; modulestruct->var[j].varvalptr != NULL; j++) {
char *strval = telnet_getvarvalue(modulestruct->var, j);
int modifiable = 1;
if (modulestruct->var[j].checkval & TELNET_CHECKVAL_RDONLY)
modifiable = 0;
json_t *oneaction;
switch (modulestruct->var[j].vartype) {
case TELNET_VARTYPE_DOUBLE:
oneaction = json_pack("{s:s,s:s,s:g,s:b}", "type", "number", "name", modulestruct->var[j].varname, "value", *(double *)(modulestruct->var[j].varvalptr), "modifiable", modifiable);
case TELNET_VARTYPE_INT32:
case TELNET_VARTYPE_INT16:
case TELNET_VARTYPE_INT8:
case TELNET_VARTYPE_UINT:
oneaction = json_pack("{s:s,s:s,s:i,s:b}", "type", "number", "name", modulestruct->var[j].varname, "value", (int)(*(int *)(modulestruct->var[j].varvalptr)), "modifiable", modifiable);
break;
case TELNET_VARTYPE_INT64:
oneaction =
json_pack("{s:s,s:s,s:lli,s:b}", "type", "number", "name", modulestruct->var[j].varname, "value", (int64_t)(*(int64_t *)(modulestruct->var[j].varvalptr)), "modifiable", modifiable);
break;
case TELNET_VARTYPE_STRING:
oneaction = json_pack("{s:s,s:s,s:s,s:b}", "type", "string", "name", modulestruct->var[j].varname, "value", strval, "modifiable", modifiable);
break;
default:
oneaction = json_pack("{s:s,s:s,s:s,s:b}", "type", "???", "name", modulestruct->var[j].varname, "value", "???", "modifiable", modifiable);
break;
}
if (oneaction == NULL) {
LOG_E(UTIL, "[websrv] cannot encode oneaction %s/%s\n", modulestruct->module, modulestruct->var[j].varname);
} else {
websrv_printjson("oneaction", oneaction, websrvparams.dbglvl);
}
free(strval);
json_array_append_new(modulevars, oneaction);
}
if (json_array_size(modulevars) == 0) {
LOG_I(UTIL, "[websrv] no defined variables for %s\n", modulestruct->module);
} else {
websrv_printjson("modulevars", modulevars, websrvparams.dbglvl);
}
int us = ulfius_add_header_to_response(response, "content-type", "application/json");
if (us != U_OK) {
ulfius_set_string_body_response(response, 500, "Internal server error (ulfius_add_header_to_response)");
LOG_E(UTIL, "[websrv] cannot set response header type ulfius error %d \n", us);
}
us = ulfius_set_json_body_response(response, 200, modulevars);
if (us != U_OK) {
ulfius_set_string_body_response(response, 500, "Internal server error (ulfius_set_json_body_response)");
LOG_E(UTIL, "[websrv] cannot set body response ulfius error %d \n", us);
}
return U_CALLBACK_COMPLETE;
}
/* callback processing module url (<address>/oaisoftmodem/module/commands)*/
int websrv_callback_get_softmodemcmd(const struct _u_request *request, struct _u_response *response, void *user_data)
{
LOG_I(UTIL, "[websrv] : callback_get_softmodemcmd received %s %s module %s\n", request->http_verb, request->http_url, (user_data == NULL) ? "NULL" : ((cmdparser_t *)user_data)->module);
if (user_data == NULL) {
ulfius_set_string_body_response(response, 500, "No commands defined for this module");
return U_CALLBACK_COMPLETE;
}
cmdparser_t *modulestruct = (cmdparser_t *)user_data;
LOG_I(UTIL, "[websrv] received %s commands request\n", modulestruct->module);
json_t *modulesubcom = json_array();
for (int j = 0; modulestruct->cmd[j].cmdfunc != NULL; j++) {
if (strcasecmp("help", modulestruct->cmd[j].cmdname) == 0 || (modulestruct->cmd[j].cmdflags & TELNETSRV_CMDFLAG_TELNETONLY)) {
continue;
}
json_t *acmd;
if (modulestruct->cmd[j].cmdflags & TELNETSRV_CMDFLAG_CONFEXEC) {
char confstr[256];
snprintf(confstr, sizeof(confstr), "Confirm %s ?", modulestruct->cmd[j].cmdname);
acmd = json_pack("{s:s,s:s}", "name", modulestruct->cmd[j].cmdname, "confirm", confstr);
} else if (modulestruct->cmd[j].cmdflags & TELNETSRV_CMDFLAG_NEEDPARAM) {
char *pname = NULL;
char *question = NULL;
char *helpcp = NULL;
if (modulestruct->cmd[j].helpstr != NULL) {
char *tokptr;
helpcp = strdup(modulestruct->cmd[j].helpstr);
question = strtok_r(helpcp, "<[", &tokptr);
pname = (question != NULL) ? strtok_r(helpcp, ">]", &tokptr) : NULL;
}
acmd = json_pack(
"{s:s,s:{s:s,s:s,s:s}}", "name", modulestruct->cmd[j].cmdname, "question", "display", (question == NULL) ? "" : question, "pname", (pname == NULL) ? "Px" : pname, "type", "string");
free(helpcp);
} else {
acmd = json_pack("{s:s}", "name", modulestruct->cmd[j].cmdname);
}
json_t *jopts = json_array();
if (modulestruct->cmd[j].cmdflags & TELNETSRV_CMDFLAG_AUTOUPDATE) {
json_array_append_new(jopts, json_string("update"));
}
if (websrv_utils_testhlp(websrvparams.fpath, "cmd", modulestruct->module, modulestruct->cmd[j].cmdname)) {
json_array_append_new(jopts, json_string("help"));
}
if (json_array_size(jopts) > 0) {
json_t *cmdoptions = json_pack("{s:o}", "options", jopts);
json_object_update_missing(acmd, cmdoptions);
}
json_array_append_new(modulesubcom, acmd);
}
if (modulesubcom == NULL) {
LOG_E(UTIL, "[websrv] cannot encode modulesubcom response for %s\n", modulestruct->module);
} else {
websrv_printjson("modulesubcom", modulesubcom, websrvparams.dbglvl);
}
int us = ulfius_add_header_to_response(response, "content-type", "application/json");
if (us != U_OK) {
ulfius_set_string_body_response(response, 500, "Internal server error (ulfius_add_header_to_response)");
LOG_E(UTIL, "[websrv] cannot set response header type ulfius error %d \n", us);
}
us = ulfius_set_json_body_response(response, 200, modulesubcom);
if (us != U_OK) {
ulfius_set_string_body_response(response, 500, "Internal server error (ulfius_set_json_body_response)");
LOG_E(UTIL, "[websrv] cannot set body response ulfius error %d \n", us);
}
return U_CALLBACK_COMPLETE;
}
int websrv_callback_get_softmodemmodules(const struct _u_request *request, struct _u_response *response, void *user_data)
{
telnetsrv_params_t *telnetparams = (telnetsrv_params_t *)websrvparams.telnetparams;
json_t *cmdnames = json_array();
for (int i = 0; telnetparams->CmdParsers[i].var != NULL && telnetparams->CmdParsers[i].cmd != NULL; i++) {
json_t *acmd = json_pack("{s:s}", "name", telnetparams->CmdParsers[i].module);
json_array_append_new(cmdnames, acmd);
}
int us = ulfius_add_header_to_response(response, "content-type", "application/json");
if (us != U_OK) {
ulfius_set_string_body_response(response, 501, "Internal server error (ulfius_add_header_to_response)");
LOG_E(UTIL, "[websrv] cannot set modules response header type ulfius error %d \n", us);
}
us = ulfius_set_json_body_response(response, 200, cmdnames);
if (us != U_OK) {
ulfius_set_string_body_response(response, 501, "Internal server error (ulfius_set_json_body_response)");
LOG_E(UTIL, "[websrv] cannot set modules body response ulfius error %d \n", us);
} else {
websrv_printjson("cmdnames", cmdnames, websrvparams.dbglvl);
}
// ulfius_set_string_body_response(response, 200, cfgfile);
return U_CALLBACK_COMPLETE;
}
/*---------------------------------------------------------------------------*/
/* callback processing first request (<address>/oaisoftmodem) after initial connection to server */
/* utility to add a line of info in status panel */
void websrv_add_modeminfo(json_t *modemvars, char *infoname, char *infoval, char *strtype)
{
json_t *info;
int modifiable = 0;
if (strcmp(strtype, "configfile") == 0)
modifiable = 1;
info = json_pack("{s:s,s:s,s:s,s:b}", "name", infoname, "value", infoval, "type", strtype, "modifiable", modifiable);
if (info == NULL) {
LOG_E(UTIL, "[websrv] cannot encode modem info %s response\n", infoname);
} else {
websrv_printjson("status body1", info, websrvparams.dbglvl);
}
json_array_append_new(modemvars, info);
}
int websrv_callback_get_softmodemstatus(const struct _u_request *request, struct _u_response *response, void *user_data)
{
char ipstr[INET_ADDRSTRLEN];
char srvinfo[255];
if (websrvparams.instance.bind_address != NULL)
inet_ntop(AF_INET, &(websrvparams.instance.bind_address->sin_addr), ipstr, INET_ADDRSTRLEN);
else
sprintf(ipstr, "%s", "0.0.0.0");
snprintf(srvinfo, sizeof(srvinfo) - 1, "%s:%hu %s", ipstr, websrvparams.instance.port, get_softmodem_function(NULL));
json_t *modemvars = json_array();
websrv_add_modeminfo(modemvars, "connected to", srvinfo, "string");
websrv_add_modeminfo(modemvars, "config_file", CONFIG_GETCONFFILE, "configfile");
websrv_add_modeminfo(modemvars, "exec name", config_get_if()->argv[0], "string");
int len = 0;
int argc = 0;
for (int i = 1; config_get_if()->argv[i] != NULL; i++) {
len += strlen(config_get_if()->argv[i]);
argc++;
}
char *cmdline = malloc(len + argc + 10);
char *ptr = cmdline;
for (int i = 1; config_get_if()->argv[i] != NULL; i++) {
ptr += sprintf(ptr, "%s ", config_get_if()->argv[i]);
}
websrv_add_modeminfo(modemvars, "command line", cmdline, "string");
if ((config_get_if()->rtflags & CONFIG_SAVERUNCFG) && (config_get_if()->write_parsedcfg != NULL)) {
config_get_if()->write_parsedcfg();
websrv_add_modeminfo(modemvars, "Parsed config file", config_get_if()->status->debug_cfgname, "configfile");
}
int us = ulfius_add_header_to_response(response, "content-type", "application/json");
if (us != U_OK) {
ulfius_set_string_body_response(response, 501, "Internal server error (ulfius_add_header_to_response)");
LOG_E(UTIL, "[websrv] cannot set status response header type ulfius error %d \n", us);
}
us = ulfius_set_json_body_response(response, 200, modemvars);
if (us != U_OK) {
ulfius_set_string_body_response(response, 501, "Internal server error (ulfius_set_json_body_response)");
LOG_E(UTIL, "[websrv] cannot set status body response ulfius error %d \n", us);
}
// ulfius_set_string_body_response(response, 200, cfgfile);
return U_CALLBACK_COMPLETE;
}
int websrv_add_endpoint(char **http_method,
int num_method,
const char *url_prefix,
const char *url_format,
int (*callback_function[])(const struct _u_request *request, struct _u_response *response, void *user_data),
void *user_data)
{
int status;
int j = 0;
int priority = (user_data == NULL) ? 10 : 1;
for (int i = 0; i < num_method; i++) {
status = ulfius_add_endpoint_by_val(&(websrvparams.instance), http_method[i], url_prefix, url_format, priority, callback_function[i], user_data);
if (status != U_OK) {
LOG_E(UTIL, "[websrv] cannot add endpoint %s %s/%s\n", http_method[i], url_prefix, url_format);
} else {
j++;
LOG_I(UTIL, "[websrv] endpoint %s %s/%s added\n", http_method[i], url_prefix, url_format);
}
}
return j;
}
/* add endpoints for a module, as defined in cmdparser_t telnet server structure */
void register_module_endpoints(cmdparser_t *module)
{
int (*callback_functions_var[3])(const struct _u_request *request, struct _u_response *response, void *user_data) = {
websrv_callback_okset_softmodem_cmdvar, websrv_callback_set_softmodemvar, websrv_callback_get_softmodemvar};
char *http_methods[3] = {"OPTIONS", "POST", "GET"};
int (*callback_functions_cmd[3])(const struct _u_request *request, struct _u_response *response, void *user_data) = {
websrv_callback_okset_softmodem_cmdvar, websrv_callback_exec_softmodemcmd, websrv_callback_get_softmodemcmd};
char prefixurl[TELNET_CMD_MAXSIZE + 20];
snprintf(prefixurl, TELNET_CMD_MAXSIZE + 19, "oaisoftmodem/%s", module->module);
LOG_I(UTIL, "[websrv] add endpoints %s/[variables or commands] \n", prefixurl);
websrv_add_endpoint(http_methods, 3, prefixurl, "commands", callback_functions_cmd, module);
websrv_add_endpoint(http_methods, 3, prefixurl, "variables", callback_functions_var, module);
callback_functions_cmd[0] = websrv_callback_okset_softmodem_cmdvar;
callback_functions_cmd[1] = websrv_callback_set_moduleparams;
websrv_add_endpoint(http_methods, 2, prefixurl, "set", callback_functions_cmd, module);
}
void *websrv_autoinit()
{
int ret;
memset(&websrvparams, 0, sizeof(websrvparams));
config_get(websrvoptions, sizeof(websrvoptions) / sizeof(paramdef_t), "websrv");
/* check if telnet server is loaded or not */
add_telnetcmd_func_t addcmd = (add_telnetcmd_func_t)get_shlibmodule_fptr("telnetsrv", TELNET_ADDCMD_FNAME);
if (addcmd != NULL) {
websrvparams.telnetparams = get_telnetsrv_params();
} else {
LOG_I(UTIL, "[websrv], running without the telnet server\n");
dummy_telnetsrv_params = calloc(1, sizeof(telnetsrv_params_t));
websrvparams.telnetparams = dummy_telnetsrv_params;
}
telnetsrv_params_t *telnetparams = (telnetsrv_params_t *)websrvparams.telnetparams;
if (ulfius_init_instance(&(websrvparams.instance), websrvparams.listenport, NULL, NULL) != U_OK) {
LOG_W(UTIL, "[websrv] Error,cannot init websrv\n");
return (NULL);
}
u_map_put(websrvparams.instance.default_headers, "Access-Control-Allow-Origin", "*");
// Maximum body size sent by the client is 1 Kb
websrvparams.instance.max_post_body_size = 1024;
// 1: build the first page, when receiving the "oaisoftmodem" url
// ulfius_add_endpoint_by_val(&(websrvparams.instance), "GET", "oaisoftmodem", "variables", 0, &websrv_callback_get_softmodemstatus, NULL);
ulfius_add_endpoint_by_val(&(websrvparams.instance), "GET", "oaisoftmodem", "commands", 1, &websrv_callback_get_softmodemmodules, NULL);
// 2 default_endpoint declaration, it tries to open the file with the url name as specified in the request.It looks for the file
ulfius_set_default_endpoint(&(websrvparams.instance), &websrv_callback_default, NULL);
// 3 endpoints corresponding to loaded telnet modules
int (*callback_functions_var[3])(const struct _u_request *request, struct _u_response *response, void *user_data) = {
websrv_callback_get_softmodemstatus, websrv_callback_okset_softmodem_cmdvar, websrv_callback_set_softmodemvar};
char *http_methods[3] = {"GET", "OPTIONS", "POST"};
websrv_add_endpoint(http_methods, 3, "oaisoftmodem", "variables", callback_functions_var, NULL);
for (int i = 0; telnetparams->CmdParsers[i].cmd != NULL; i++) {
register_module_endpoints(&(telnetparams->CmdParsers[i]));
}
/*4 callbacks to take care of modules not yet initialized, so not visible in telnet server data when this autoinit runs: */
ulfius_add_endpoint_by_val(&(websrvparams.instance), "GET", "oaisoftmodem", "@module/commands", 10, websrv_callback_newmodule, telnetparams);
ulfius_add_endpoint_by_val(&(websrvparams.instance), "GET", "oaisoftmodem", "@module/variables", 10, websrv_callback_newmodule, telnetparams);
// 5 callback to handle file request
ulfius_add_endpoint_by_val(&(websrvparams.instance), "POST", "oaisoftmodem", "file", 1, &websrv_callback_get_softmodemfile, NULL);
// 6 callback for help request
ulfius_add_endpoint_by_val(&(websrvparams.instance), "GET", "oaisoftmodem", "helpfiles/@hlpfile", 2, &websrv_callback_get_softmodemhelp, NULL);
// init softscope interface support */
websrv_init_scope(&websrvparams);
// init websocket */
websrv_init_websocket(&websrvparams, "softscope");
// build some html files in the server repo
websrv_utils_build_hlpfiles(websrvparams.fpath);
// Start the framework
ret = U_ERROR;
if (websrvparams.keyfile != NULL && websrvparams.certfile != NULL) {
char *key_pem = websrv_read_file(websrvparams.keyfile);
char *cert_pem = websrv_read_file(websrvparams.certfile);
char *root_ca_pem = websrv_read_file(websrvparams.rootcafile);
if (key_pem != NULL && cert_pem != NULL) {
ulfius_add_endpoint_by_val(&(websrvparams.instance), "*", "", "@anyword", 0, &websrv_callback_auth, NULL);
LOG_I(UTIL, "[websrv] priority 0 authentication endpoint added/n");
#ifdef WEBSRV_AUTH
if (root_ca_pem == NULL)
ret = ulfius_start_secure_framework(&(websrvparams.instance), key_pem, cert_pem);
else
ret = ulfius_start_secure_ca_trust_framework(&(websrvparams.instance), key_pem, cert_pem, root_ca_pem);
#else
ret = ulfius_start_framework(&(websrvparams.instance));
#endif
free(key_pem);
free(cert_pem);
free(root_ca_pem);
} else {
LOG_E(UTIL, "[websrv] Unable to load key %s and cert %s\n", websrvparams.keyfile, websrvparams.certfile);
}
} else {
ret = ulfius_start_framework(&(websrvparams.instance));
}
if (ret == U_OK) {
LOG_I(UTIL, "[websrv] Web server started on port %d", websrvparams.instance.port);
} else {
LOG_W(UTIL, "[websrv] Error starting web server on port %d\n", websrvparams.instance.port);
}
return &(websrvparams.instance);
}
void websrv_end(void *webinst)
{
ulfius_stop_framework((struct _u_instance *)webinst);
ulfius_clean_instance((struct _u_instance *)webinst);
free(dummy_telnetsrv_params);
return;
}
/*
* 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.h
* \brief: implementation of web API
* \author Francois TABURET
* \date 2022
* \version 0.1
* \company NOKIA BellLabs France
* \email: francois.taburet@nokia-bell-labs.com
* \note
* \warning
*/
#ifndef WEBSRV_H
#define WEBSRV_H
#define WEBSRV_MODNAME "websrv"
#define WEBSRV_PORT 8090
/* websrv_params_t is an internal structure storing all the current parameters and */
/* global variables used by the web server */
typedef struct {
struct _u_instance instance; // ulfius (web server) instance
struct _websocket_manager *wm; // web socket instance
unsigned int dbglvl; // debug level of the server
int priority; // server running priority
unsigned int listenport; // ip port the telnet server is listening on
unsigned int listenaddr; // ip address the telnet server is listening on
unsigned int listenstdin; // enable command input from stdin
char *fpath; // directory where the server is looking for files (html...)
char *certfile; // cert file
char *keyfile; // key file
char *rootcafile; // root ca file
void *telnetparams; // pointer to telnet server parameters type is telnetsrv_params_t, don't want to enforce telnet include here
} websrv_params_t;
#define WEBSOCK_SRC_SCOPE 's'
typedef struct {
unsigned char src; // message source
unsigned char msgtype; // message type
unsigned char msgseg; // message segment number
unsigned char chartid; // identify chart (scope window)
unsigned char datasetid; // identify dataset in chart
unsigned char update; // should chart be updated
unsigned char hdr_unused[2]; // 2 unused char
} websrv_msg_hdr_t;
typedef struct {
websrv_msg_hdr_t header;
char data[1408]; // 64*22
} websrv_msg_t;
#define MAX_NIQ_WEBSOCKMSG 180000 // max number of 16 bits iq's in pre-allocated buffer
#define MAX_LLR_WEBSOCKMSG (MAX_NIQ_WEBSOCKMSG * 2) // max number of 8 bits llr's in pre-allocated buffer
typedef struct {
websrv_msg_hdr_t header;
int16_t data_xy[MAX_NIQ_WEBSOCKMSG * 2]; // data buffer
} websrv_scopedata_msg_t;
#define WEBSOCK_HEADSIZE (offsetof(websrv_msg_t, data))
#define SCOPE_STATUSMASK_UNKNOWN 0 // websocket initialized, no scope request received
#define SCOPE_STATUSMASK_AVAILABLE 1 // scope can be started (available in exec and not started with other if */
#define SCOPE_STATUSMASK_STARTED 2 // scope running
#define SCOPE_STATUSMASK_DATAACK 4 // wait for data message acknowledge before sending
#define SCOPE_STATUSMASK_DISABLED (1LL << 63) // scope disabled (running with xform interface or not implemented in exec)
/* values for websocket message type */
/* websocket softscope message: */
#define SCOPEMSG_TYPE_TIME 3 // time
#define SCOPEMSG_TYPE_DATA 10 // graph data
#define SCOPEMSG_TYPE_DATAACK 11 // graph data reception and processing acknowledge
#define SCOPEMSG_TYPE_DATAFLOW 12 // feedback to frontend of messages not sent
typedef struct {
uint64_t statusmask; //
uint32_t refrate; // in 100 ms
void *scopeform; // OAI_phy_scope_t pointer returned by create_phy_scope_xxx functions
void *scopedata; // scopeData_t pointer, filled at init time, contains pointers and functions to retrieve softmodem data
uint32_t selectedTarget; // index to UE/gNB
int xmin; // iq view data limit
int xmax;
int ymin;
int ymax;
int llr_ythresh; // llrview llr threshold
int llrxmin; // llr view x limits
int llrxmax;
uint64_t num_datamsg_max; // num_datamsg_xxxx are used to
uint64_t num_datamsg_sent; // control the data message flow to
uint64_t num_datamsg_ack; // the frontend, preventing data messages to be sent
uint64_t num_datamsg_skipped; // when date ack is requested and the the diff between sent and ack obove limit
} websrv_scope_params_t;
/*------------------------------------------------------------------------------------------------*/
/* web server utilities: */
extern void websrv_printjson(char *label, json_t *jsonobj, int dbglvl); // debug: dump a json object
extern void websrv_jbody(struct _u_response *response, json_t *jbody, int httpstatus, int dbglvl); // add a json body in a http response
extern void websrv_printf_start(struct _u_response *response, int buffsize, bool async); // start a printf, lock the buffer
extern void websrv_printf(const char *message, ...); // add characters in the printf locked buffer
extern void websrv_async_printf(const char *message, ...); // add characters in the printf locked buffer and terminate the print session
extern void websrv_printf_end(int httpstatus, int dbglvl); // add the printf buffer in the body of a http response, unlock the buffer
extern void websrv_dump_request(char *label, const struct _u_request *request, int dbglvl); // debug: dump a http request
extern int websrv_string_response(char *astring, struct _u_response *response, int httpstatus, int dbglvl); // add a string in a http response
extern void websrv_utils_build_hlpfiles(char *path); // build files to be downloade by the frontend which cannot be delivered at installation time
extern char *websrv_read_file(const char *filename); // map a file in a string variable
extern FILE *websrv_getfile(char *filename, struct _u_response *response); // answer a front-end file upload request
extern int websrv_utils_testhlp(char *dir, char *module, char *cmdtitle, char *coltitle); // test if a help file exists
/* webserver API: */
extern int websrv_init_websocket(websrv_params_t *websrvparams, char *module);
extern void websrv_websocket_send_message(char msg_src, char msg_type, char *msg_data, struct _websocket_manager *websocket_manager);
extern void websrv_websocket_process_scopemessage(char msg_type, char *msg_data, struct _websocket_manager *websocket_manager);
extern int websrv_callback_okset_softmodem_cmdvar(const struct _u_request *request, struct _u_response *response, void *user_data);
extern int websrv_add_endpoint(char **http_method,
int num_method,
const char *url_prefix,
const char *url_format,
int (*callback_function[])(const struct _u_request *request, struct _u_response *response, void *user_data),
void *user_data);
/* scope interface API: */
extern void websrv_init_scope(websrv_params_t *websrvparams);
extern void websrv_scope_ws_cb(void *user_data);
extern int websrv_scope_manager(uint64_t lcount, websrv_params_t *websrvparams);
extern void websrv_scope_senddata(int numd, int dsize, websrv_scopedata_msg_t *msg);
extern websrv_scope_params_t *websrv_scope_getparams(void);
extern void websrv_scope_stop(void);
/*-----------------------------------------------------------------------------------------------------------*/
#endif
if ( "${OPENAIR_CMAKE}" STREQUAL "")
message( FATAL_ERROR "oai Environment variables not set")
endif ( "${OPENAIR_CMAKE}" STREQUAL "")
set(WEBSRVROOT ${OPENAIR_DIR}/common/utils/websrv )
set (WBACK TRUE)
set (WFRONT TRUE)
# websrv dependencies
unset(ULFIUS)
unset(ULFIUS CACHE)
find_library(ULFIUS NAMES "libulfius.so" NO_CACHE)
if ("${ULFIUS}" STREQUAL "ULFIUS-NOTFOUND")
message( WARNING "ulfius library (https://github.com/babelouest/ulfius) not found, install libulfius-dev (ubuntu) if you need to build websrv back-end")
set (WBACK FALSE)
endif("${ULFIUS}" STREQUAL "ULFIUS-NOTFOUND")
unset(JSON)
unset(JSON CACHE)
find_library(JSON NAMES "libjansson.so" NO_CACHE )
if ("${JSON}" STREQUAL "ULFIUS-NOTFOUND")
message( WARNING "libjansson not found, install libjansson-dev for ubuntu, jansson-devel for fedora if you need to build websrv back-end")
set (WBACK FALSE)
endif("${JSON}" STREQUAL "ULFIUS-NOTFOUND")
unset(NPM)
unset(NPM CACHE)
find_program(NPM NAMES npm NO_CACHE)
if ("${NPM}" STREQUAL "ULFIUS-NOTFOUND")
message( WARNING " npm is not installed, frontend won't be built. Possibly install npm, package is available for ubuntu and fedora")
endif("${NPM}" STREQUAL "ULFIUS-NOTFOUND")
if ( ${WBACK} )
message (STATUS "websrv backend can be built")
else ( ${WBACK} )
message (STATUS "websrv backend build skipped, dependencies not found")
endif ( ${WBACK} )
if ( ${WFRONT} )
message (STATUS "websrv frontend can be built")
else( ${WFRONT} )
message (STATUS "websrv frontend build skipped, dependencies not found")
endif ( ${WFRONT} )
# build the backend ( the embedded web server)
set(WEBSRV_SOURCE
${WEBSRVROOT}/websrv.c ${WEBSRVROOT}/websrv_websockets.c
${WEBSRVROOT}/websrv_scope.c ${WEBSRVROOT}/websrv_noforms.c
${WEBSRVROOT}/websrv_scope.c ${WEBSRVROOT}/websrv_utils.c
${OPENAIR_DIR}/openair1/PHY/TOOLS/nr_phy_scope.c
)
add_library(websrv MODULE ${WEBSRV_SOURCE} )
target_link_libraries(websrv PRIVATE ulfius jansson)
target_compile_definitions(websrv PUBLIC WEBSRVSCOPE)
# build the frontend ( loaded from web server by browsers)
add_custom_target (
websrvfront_installjsdep
WORKING_DIRECTORY ${WEBSRVROOT}/frontend
COMMAND npm install
DEPENDS ${WEBSRVROOT}/frontend/package-lock.json
)
add_custom_target (
websrvfront
WORKING_DIRECTORY ${WEBSRVROOT}/frontend
COMMAND npm run build
#COMMAND npm run builddev
DEPENDS websrvfront_installjsdep
)
#install built files, required at exec time
if ( ${WBACK} )
install(TARGETS websrv DESTINATION bin)
endif ( ${WBACK} )
if ( ${WFRONT} )
if (EXISTS "${OPENAIR_CMAKE}/ran_build/build" AND IS_DIRECTORY "${OPENAIR_CMAKE}/ran_build/build")
add_custom_command(TARGET websrvfront
POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${WEBSRVROOT}/frontend/dist/softmodem-ngx ${OPENAIR_CMAKE}/ran_build/build/websrv
COMMAND ${CMAKE_COMMAND} -E copy_directory ${WEBSRVROOT}/frontend/dist/softmodem-ngx ${OPENAIR_TARGETS}/bin/websrv
COMMAND ${CMAKE_COMMAND} -E copy_directory ${WEBSRVROOT}/helpfiles ${OPENAIR_CMAKE}/ran_build/build/websrv/helpfiles
COMMAND ${CMAKE_COMMAND} -E copy_directory ${WEBSRVROOT}/helpfiles ${OPENAIR_TARGETS}/bin/websrv/helpfiles
COMMENT "Moving frontend files to:\n ${OPENAIR_CMAKE}/ran_build/build/websrv\n ${OPENAIR_TARGETS}/bin/websrv" )
endif (EXISTS "${OPENAIR_CMAKE}/ran_build/build" AND IS_DIRECTORY "${OPENAIR_CMAKE}/ran_build/build")
endif ( ${WFRONT} )
/*
* 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_noforms.c
* \brief: primitives replacing xforms when compiling nr_phy_scope.c for the webserver shared lib
* \author Francois TABURET
* \date 2022
* \version 0.1
* \company NOKIA BellLabs France
* \email: francois.taburet@nokia-bell-labs.com
* \note
* \warning
*/
#include <stdlib.h>
#include <jansson.h>
#include <ulfius.h>
#include "common/utils/LOG/log.h"
#include "common/utils/websrv/websrv.h"
#include "common/utils/websrv/websrv_noforms.h"
#include <common/utils/assertions.h>
#define MAX_LAYER 8
/* replacement for xforms function used in xforms softscope */
typedef struct {
websrv_scopedata_msg_t *buf;
} websrv_scopedata_layermsgs_t;
typedef struct {
char *title; /* overall title */
websrv_scopedata_layermsgs_t buff[MAX_LAYER]; /* buff per layer */
int n[MAX_LAYER]; /* total points */
} FLI_XYPLOT_SPEC;
FL_OBJECT *websrv_fl_add_xyplot(int t, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label)
{
FL_OBJECT *obj;
obj = (FL_OBJECT *)calloc(1, sizeof(FL_OBJECT));
AssertFatal((obj != NULL), "Cannot allocate scope object for %s\n", (label!=NULL)?label:"??");
if (obj) {
obj->spec = (FLI_XYPLOT_SPEC *)calloc(1, sizeof(FLI_XYPLOT_SPEC));
AssertFatal((obj->spec != NULL), "Cannot allocate scope object buffers for %s\n",(label!=NULL)?label:"??");
FLI_XYPLOT_SPEC *spec = (FLI_XYPLOT_SPEC *)obj->spec;
for (int j = 0; j < MAX_LAYER; j++) {
spec->buff[j].buf = (websrv_scopedata_msg_t *)calloc(1, sizeof(websrv_scopedata_msg_t));
}
obj->objclass = FL_XYPLOT;
obj->type = t;
obj->x = x;
obj->y = y;
obj->w = w;
obj->h = h;
if (label)
obj->label = strdup(label);
LOG_I(UTIL, "[websrv], scope object for \"%s\" allocated obj at %p spec at %p\n", label, obj, obj->spec);
}
return obj;
};
void websrv_free_xyplot(FL_OBJECT *obj)
{
FLI_XYPLOT_SPEC *spec = (FLI_XYPLOT_SPEC *)obj->spec;
for (int j = 0; j < MAX_LAYER; j++) {
free(spec->buff[j].buf);
}
free(obj->label);
free(spec);
free(obj);
};
FL_OBJECT *websrv_fl_add_canvas(int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label)
{
return websrv_fl_add_xyplot(type, x, y, w, h, label);
}
void websrv_fl_get_xyplot_data(FL_OBJECT *ob, float *x, float *y, int *n)
{
FLI_XYPLOT_SPEC *spec = (FLI_XYPLOT_SPEC *)(ob->spec);
*n = spec->n[0];
};
void websrv_fl_get_xyplot_data_pointer(FL_OBJECT *ob, int id, float **x, float **y, int *n)
{
FLI_XYPLOT_SPEC *spec = (FLI_XYPLOT_SPEC *)(ob->spec);
*n = spec->n[id];
};
/*----------------------------------------------------------------------*/
/* new functions for interfacing with webserver */
int websrv_nf_getdata(FL_OBJECT *graph, int layer, websrv_scopedata_msg_t **msg)
{
FLI_XYPLOT_SPEC *spec = (FLI_XYPLOT_SPEC *)(graph->spec);
*msg = spec->buff[layer].buf;
return spec->n[layer];
}
/*
* 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_noforms.h
* \brief: include file to replace forms.h when compiling nr_phy_scope.c for the webserver shared lib
* \author Francois TABURET
* \date 2022
* \version 0.1
* \company NOKIA BellLabs France
* \email: francois.taburet@nokia-bell-labs.com
* \note
* \warning
*/
#ifndef FL_FORMS_H
#define FL_FORMS_H
#define FL_VERSION 1
#define FL_REVISION 2
#define FL_FIXLEVEL "3"
#define FL_INCLUDE_VERSION (FL_VERSION * 1000 + FL_REVISION)
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <limits.h>
#if defined __cplusplus
extern "C" {
#endif
#if defined _WIN32
#define FL_WIN32
#include <windows.h>
#endif
typedef void *Window;
typedef void *Pixmap;
#define FL_EXPORT inline
/**
* \file Basic.h
*
* Basic definitions and limits.
* Window system independent prototypes
*
* Modify with care
*/
#ifndef FL_BASIC_H
#define FL_BASIC_H
#include <math.h>
#if defined __GNUC__
#define FL_UNUSED_ARG __attribute__((unused))
#else
#define FL_UNUSED_ARG
#endif
/* Some general constants */
typedef int Visual;
typedef int XEvent;
typedef int *Display;
typedef void *Cursor;
typedef void *XVisualInfo;
typedef void *XFontStruct;
typedef void *XPoint;
typedef void *XRectangle;
typedef int GC;
typedef void *Colormap;
typedef void *XrmOptionDescRec;
#define LineSolid 0
#define LineOnOffDash 1
#define LineDoubleDash 2
typedef struct {
int f1;
} XSetWindowAttributes;
typedef struct {
int f1;
} Atom;
typedef int KeySym;
enum {
FL_ON = 1,
FL_OK = 1,
FL_VALID = 1,
FL_PREEMPT = 1,
FL_AUTO = 2,
FL_WHEN_NEEDED = FL_AUTO,
FL_OFF = 0,
FL_CANCEL = 0,
FL_INVALID = 0,
/* WM_DELETE_WINDOW callback return */
FL_IGNORE = -1,
};
/* Max directory length */
#ifndef FL_PATH_MAX
#ifndef PATH_MAX
#define FL_PATH_MAX 1024
#else
#define FL_PATH_MAX PATH_MAX
#endif
#endif /* ! def FL_PATH_MAX */
/* The screen coordinate unit, FL_Coord, must be of signed type */
typedef int FL_Coord;
#define FL_COORD FL_Coord
typedef unsigned long FL_COLOR;
/* Coordinates can be in pixels, milli-meters or points (1/72inch) */
typedef enum {
FL_COORD_PIXEL, /* default, Pixel */
FL_COORD_MM, /* milli-meter */
FL_COORD_POINT, /* point */
FL_COORD_centiMM, /* one hundredth of a mm */
FL_COORD_centiPOINT /* one hundredth of a point */
} FL_COORD_UNIT;
/* All object classes. */
typedef enum {
FL_INVALID_CLASS, /* 0 */
FL_BUTTON, /* 1 */
FL_LIGHTBUTTON, /* 2 */
FL_ROUNDBUTTON, /* 3 */
FL_ROUND3DBUTTON, /* 4 */
FL_CHECKBUTTON, /* 5 */
FL_BITMAPBUTTON, /* 6 */
FL_PIXMAPBUTTON, /* 7 */
FL_BITMAP, /* 8 */
FL_PIXMAP, /* 9 */
FL_BOX, /* 10 */
FL_TEXT, /* 11 */
FL_MENU, /* 12 */
FL_CHART, /* 13 */
FL_CHOICE, /* 14 */
FL_COUNTER, /* 15 */
FL_SLIDER, /* 16 */
FL_VALSLIDER, /* 17 */
FL_INPUT, /* 18 */
FL_BROWSER, /* 19 */
FL_DIAL, /* 20 */
FL_TIMER, /* 21 */
FL_CLOCK, /* 22 */
FL_POSITIONER, /* 23 */
FL_FREE, /* 24 */
FL_XYPLOT, /* 25 */
FL_FRAME, /* 26 */
FL_LABELFRAME, /* 27 */
FL_CANVAS, /* 28 */
FL_GLCANVAS, /* 29 */
FL_TABFOLDER, /* 30 */
FL_SCROLLBAR, /* 31 */
FL_SCROLLBUTTON, /* 32 */
FL_MENUBAR, /* 33 */
FL_TEXTBOX, /* 34, for internal use only */
FL_LABELBUTTON, /* 35 */
FL_COMBOBOX, /* 36 */
FL_IMAGECANVAS, /* 37 */
FL_THUMBWHEEL, /* 38 */
FL_COLORWHEEL, /* 39 */
FL_FORMBROWSER, /* 40 */
FL_SELECT, /* 41 */
FL_NMENU, /* 42 */
FL_SPINNER, /* 43 */
FL_TBOX, /* 44 */
FL_CLASS_END /* sentinel */
} FL_CLASS;
#define FL_BEGIN_GROUP 10000
#define FL_END_GROUP 20000
#define FL_USER_CLASS_START 1001 /* min. user class value */
#define FL_USER_CLASS_END 9999 /* max. user class value */
/* Maximum border width (in pixel) */
#define FL_MAX_BW 10
/* How to display a form onto screen */
typedef enum {
FL_PLACE_FREE = 0, /* size remain resizable */
FL_PLACE_MOUSE = 1, /* mouse centered on form */
FL_PLACE_CENTER = 2, /* center of the screen */
FL_PLACE_POSITION = 4, /* specific position */
FL_PLACE_SIZE = 8, /* specific size */
FL_PLACE_GEOMETRY = 16, /* specific size and position */
FL_PLACE_ASPECT = 32, /* keep aspect ratio */
FL_PLACE_FULLSCREEN = 64, /* scale to fit to screen */
FL_PLACE_HOTSPOT = 128, /* so mouse fall on (x,y) */
FL_PLACE_ICONIC = 256, /* start in iconified form */
/* Modifiers */
FL_FREE_SIZE = (1 << 14),
FL_FIX_SIZE = (1 << 15) /* seems to be useless, but some
programs seem to rely on it... */
} FL_PLACE;
#define FL_PLACE_FREE_CENTER (FL_PLACE_CENTER | FL_FREE_SIZE)
#define FL_PLACE_CENTERFREE (FL_PLACE_CENTER | FL_FREE_SIZE)
/* Window manager decoration request and forms attributes */
enum {
FL_FULLBORDER = 1, /* normal */
FL_TRANSIENT, /* set TRANSIENT_FOR property */
FL_NOBORDER, /* use override_redirect to supress decor. */
};
/* All box types */
typedef enum {
FL_NO_BOX, /* 0 */
FL_UP_BOX, /* 1 */
FL_DOWN_BOX, /* 2 */
FL_BORDER_BOX, /* 3 */
FL_SHADOW_BOX, /* 4 */
FL_FRAME_BOX, /* 5 */
FL_ROUNDED_BOX, /* 6 */
FL_EMBOSSED_BOX, /* 7 */
FL_FLAT_BOX, /* 8 */
FL_RFLAT_BOX, /* 9 */
FL_RSHADOW_BOX, /* 10 */
FL_OVAL_BOX, /* 11 */
FL_ROUNDED3D_UPBOX, /* 12 */
FL_ROUNDED3D_DOWNBOX, /* 13 */
FL_OVAL3D_UPBOX, /* 14 */
FL_OVAL3D_DOWNBOX, /* 15 */
FL_OVAL3D_FRAMEBOX, /* 16 */
FL_OVAL3D_EMBOSSEDBOX, /* 17 */
/* for internal use only */
FL_TOPTAB_UPBOX,
FL_SELECTED_TOPTAB_UPBOX,
FL_BOTTOMTAB_UPBOX,
FL_SELECTED_BOTTOMTAB_UPBOX,
FL_MAX_BOX_STYLES /* sentinel */
} FL_BOX_TYPE;
#define FL_IS_UPBOX(t) ((t) == FL_UP_BOX || (t) == FL_OVAL3D_UPBOX || (t) == FL_ROUNDED3D_UPBOX)
#define FL_IS_DOWNBOX(t) ((t) == FL_DOWN_BOX || (t) == FL_OVAL3D_DOWNBOX || (t) == FL_ROUNDED3D_DOWNBOX)
#define FL_TO_DOWNBOX(t) ((t) == FL_UP_BOX ? FL_DOWN_BOX : ((t) == FL_ROUNDED3D_UPBOX ? FL_ROUNDED3D_DOWNBOX : ((t) == FL_OVAL3D_UPBOX ? FL_OVAL3D_DOWNBOX : (t))))
/* How to place text relative to a box */
typedef enum {
FL_ALIGN_CENTER,
FL_ALIGN_TOP = 1,
FL_ALIGN_BOTTOM = 2,
FL_ALIGN_LEFT = 4,
FL_ALIGN_RIGHT = 8,
FL_ALIGN_LEFT_TOP = (FL_ALIGN_TOP | FL_ALIGN_LEFT),
FL_ALIGN_RIGHT_TOP = (FL_ALIGN_TOP | FL_ALIGN_RIGHT),
FL_ALIGN_LEFT_BOTTOM = (FL_ALIGN_BOTTOM | FL_ALIGN_LEFT),
FL_ALIGN_RIGHT_BOTTOM = (FL_ALIGN_BOTTOM | FL_ALIGN_RIGHT),
FL_ALIGN_INSIDE = (1 << 13),
FL_ALIGN_VERT = (1 << 14), /* not functional yet */
/* the rest is for backward compatibility only, don't use! */
FL_ALIGN_TOP_LEFT = FL_ALIGN_LEFT_TOP,
FL_ALIGN_TOP_RIGHT = FL_ALIGN_RIGHT_TOP,
FL_ALIGN_BOTTOM_LEFT = FL_ALIGN_LEFT_BOTTOM,
FL_ALIGN_BOTTOM_RIGHT = FL_ALIGN_RIGHT_BOTTOM
} FL_ALIGN;
FL_EXPORT int fl_is_inside_lalign(int align)
{
return 0;
};
FL_EXPORT int fl_is_outside_lalign(int align)
{
return 0;
};
FL_EXPORT int fl_is_center_lalign(int align)
{
return 0;
};
FL_EXPORT int fl_to_inside_lalign(int align)
{
return 0;
};
FL_EXPORT int fl_to_outside_lalign(int align)
{
return 0;
};
/* Mouse buttons. Don't have to be consecutive */
enum { FL_MBUTTON1 = 1, FL_MBUTTON2, FL_MBUTTON3, FL_MBUTTON4, FL_MBUTTON5 };
#define FL_LEFT_MOUSE FL_MBUTTON1
#define FL_MIDDLE_MOUSE FL_MBUTTON2
#define FL_RIGHT_MOUSE FL_MBUTTON3
#define FL_SCROLLUP_MOUSE FL_MBUTTON4
#define FL_SCROLLDOWN_MOUSE FL_MBUTTON5
#define FL_LEFTMOUSE FL_LEFT_MOUSE
#define FL_MIDDLEMOUSE FL_MIDDLE_MOUSE
#define FL_RIGHTMOUSE FL_RIGHT_MOUSE
#define FL_SCROLLUPMOUSE FL_SCROLLUP_MOUSE
#define FL_SCROLLDOWNMOUSE FL_SCROLLDOWN_MOUSE
/* control when to return input, slider and dial etc. object. */
#define FL_RETURN_NONE 0U
#define FL_RETURN_CHANGED 1U
#define FL_RETURN_END 2U
#define FL_RETURN_END_CHANGED 4U
#define FL_RETURN_SELECTION 8U
#define FL_RETURN_DESELECTION 16U
#define FL_RETURN_TRIGGERED 1024U
#define FL_RETURN_ALWAYS (~FL_RETURN_END_CHANGED)
/* Some special color indices for FL private colormap. It does not matter
* what the value of each enum is, but it must start from 0 and be
* consecutive. */
typedef enum {
FL_BLACK,
FL_RED,
FL_GREEN,
FL_YELLOW,
FL_BLUE,
FL_MAGENTA,
FL_CYAN,
FL_WHITE,
FL_TOMATO,
FL_INDIANRED,
FL_SLATEBLUE,
FL_COL1,
FL_RIGHT_BCOL,
FL_BOTTOM_BCOL,
FL_TOP_BCOL,
FL_LEFT_BCOL,
FL_MCOL,
FL_INACTIVE,
FL_PALEGREEN,
FL_DARKGOLD,
FL_ORCHID,
FL_DARKCYAN,
FL_DARKTOMATO,
FL_WHEAT,
FL_DARKORANGE,
FL_DEEPPINK,
FL_CHARTREUSE,
FL_DARKVIOLET,
FL_SPRINGGREEN,
FL_DODGERBLUE,
FL_LIGHTER_COL1,
FL_DARKER_COL1,
FL_ALICEBLUE,
FL_ANTIQUEWHITE,
FL_AQUA,
FL_AQUAMARINE,
FL_AZURE,
FL_BEIGE,
FL_BISQUE,
FL_BLANCHEDALMOND,
FL_BLUEVIOLET,
FL_BROWN,
FL_BURLYWOOD,
FL_CADETBLUE,
FL_CHOCOLATE,
FL_CORAL,
FL_CORNFLOWERBLUE,
FL_CORNSILK,
FL_CRIMSON,
FL_DARKBLUE,
FL_DARKGOLDENROD,
FL_DARKGRAY,
FL_DARKGREEN,
FL_DARKGREY,
FL_DARKKHAKI,
FL_DARKMAGENTA,
FL_DARKOLIVEGREEN,
FL_DARKORCHID,
FL_DARKRED,
FL_DARKSALMON,
FL_DARKSEAGREEN,
FL_DARKSLATEBLUE,
FL_DARKSLATEGRAY,
FL_DARKSLATEGREY,
FL_DARKTURQUOISE,
FL_DEEPSKYBLUE,
FL_DIMGRAY,
FL_DIMGREY,
FL_FIREBRICK,
FL_FLORALWHITE,
FL_FORESTGREEN,
FL_FUCHSIA,
FL_GAINSBORO,
FL_GHOSTWHITE,
FL_GOLD,
FL_GOLDENROD,
FL_GRAY,
FL_GREENYELLOW,
FL_GREY,
FL_HONEYDEW,
FL_HOTPINK,
FL_INDIGO,
FL_IVORY,
FL_KHAKI,
FL_LAVENDER,
FL_LAVENDERBLUSH,
FL_LAWNGREEN,
FL_LEMONCHIFFON,
FL_LIGHTBLUE,
FL_LIGHTCORAL,
FL_LIGHTCYAN,
FL_LIGHTGOLDENRODYELLOW,
FL_LIGHTGRAY,
FL_LIGHTGREEN,
FL_LIGHTGREY,
FL_LIGHTPINK,
FL_LIGHTSALMON,
FL_LIGHTSEAGREEN,
FL_LIGHTSKYBLUE,
FL_LIGHTSLATEGRAY,
FL_LIGHTSLATEGREY,
FL_LIGHTSTEELBLUE,
FL_LIGHTYELLOW,
FL_LIME,
FL_LIMEGREEN,
FL_LINEN,
FL_MAROON,
FL_MEDIUMAQUAMARINE,
FL_MEDIUMBLUE,
FL_MEDIUMORCHID,
FL_MEDIUMPURPLE,
FL_MEDIUMSEAGREEN,
FL_MEDIUMSLATEBLUE,
FL_MEDIUMSPRINGGREEN,
FL_MEDIUMTURQUOISE,
FL_MEDIUMVIOLETRED,
FL_MIDNIGHTBLUE,
FL_MINTCREAM,
FL_MISTYROSE,
FL_MOCCASIN,
FL_NAVAJOWHITE,
FL_NAVY,
FL_OLDLACE,
FL_OLIVE,
FL_OLIVEDRAB,
FL_ORANGE,
FL_ORANGERED,
FL_PALEGOLDENROD,
FL_PALETURQUOISE,
FL_PALEVIOLETRED,
FL_PAPAYAWHIP,
FL_PEACHPUFF,
FL_PERU,
FL_PINK,
FL_PLUM,
FL_POWDERBLUE,
FL_PURPLE,
FL_ROSYBROWN,
FL_ROYALBLUE,
FL_SADDLEBROWN,
FL_SALMON,
FL_SANDYBROWN,
FL_SEAGREEN,
FL_SEASHELL,
FL_SIENNA,
FL_SILVER,
FL_SKYBLUE,
FL_SLATEGRAY,
FL_SLATEGREY,
FL_SNOW,
FL_STEELBLUE,
FL_TAN,
FL_TEAL,
FL_THISTLE,
FL_TURQUOISE,
FL_VIOLET,
FL_WHITESMOKE,
FL_YELLOWGREEN,
FL_FREE_COL1 = 256,
FL_FREE_COL2,
FL_FREE_COL3,
FL_FREE_COL4,
FL_FREE_COL5,
FL_FREE_COL6,
FL_FREE_COL7,
FL_FREE_COL8,
FL_FREE_COL9,
FL_FREE_COL10,
FL_FREE_COL11,
FL_FREE_COL12,
FL_FREE_COL13,
FL_FREE_COL14,
FL_FREE_COL15,
FL_FREE_COL16,
FL_NOCOLOR = INT_MAX
} FL_PD_COL;
#define FL_BUILT_IN_COLS (FL_YELLOWGREEN + 1)
#define FL_INACTIVE_COL FL_INACTIVE
/* Some aliases for a number of colors */
#define FL_GRAY16 FL_RIGHT_BCOL
#define FL_GRAY35 FL_BOTTOM_BCOL
#define FL_GRAY80 FL_TOP_BCOL
#define FL_GRAY90 FL_LEFT_BCOL
#define FL_GRAY63 FL_COL1
#define FL_GRAY75 FL_MCOL
#define FL_LCOL FL_BLACK
#define FL_NoColor FL_NOCOLOR
/* An alias probably for an earlier typo */
#define FL_DOGERBLUE FL_DODGERBLUE
/* Events that a form reacts to */
typedef enum {
FL_NOEVENT, /* 0 No event */
FL_DRAW, /* 1 object is asked to redraw itself */
FL_PUSH, /* 2 mouse button was pressed on the object */
FL_RELEASE, /* 3 mouse button was release gain */
FL_ENTER, /* 4 mouse entered the object */
FL_LEAVE, /* 5 mouse left the object */
FL_MOTION, /* 6 mouse motion over the object happend */
FL_FOCUS, /* 7 object obtained focus */
FL_UNFOCUS, /* 8 object lost focus */
FL_KEYPRESS, /* 9 key was pressed while object has focus */
FL_UPDATE, /* 10 for objects that need to update something
from time to time */
FL_STEP, /* 11 */
FL_SHORTCUT, /* 12 */
FL_FREEMEM, /* 13 object is asked to free all its memory */
FL_OTHER, /* 14 property, selection etc */
FL_DRAWLABEL, /* 15 */
FL_DBLCLICK, /* 16 double click on object */
FL_TRPLCLICK, /* 17 triple click on object */
FL_ATTRIB, /* 18 an object attribute changed */
FL_KEYRELEASE, /* 19 key was released while object has focus */
FL_PS, /* 20 dump a form into EPS */
FL_MOVEORIGIN, /* 21 dragging the form across the screen
changes its absolute x,y coords. Objects
that themselves contain forms should
ensure that they are up to date. */
FL_RESIZED, /* 22 the object has been resized by scale_form
Tell it that this has happened so that
it can resize any FL_FORMs that it
contains. */
FL_PASTE, /* 23 text was pasted into input object */
FL_TRIGGER, /* 24 result of fl_trigger_object() */
/* The following are only for backward compatibility, not used anymore */
FL_MOVE = FL_MOTION,
FL_KEYBOARD = FL_KEYPRESS,
FL_MOUSE = FL_UPDATE
} FL_EVENTS;
/* Resize policies */
typedef enum { FL_RESIZE_NONE, FL_RESIZE_X, FL_RESIZE_Y, FL_RESIZE_ALL = (FL_RESIZE_X | FL_RESIZE_Y) } FL_RESIZE_T;
/* Keyboard focus control */
typedef enum {
FL_KEY_NORMAL = 1, /* normal keys(0-255) - tab +left/right */
FL_KEY_TAB = 2, /* normal keys + 4 direction cursor */
FL_KEY_SPECIAL = 4, /* only needs special keys (>255) */
FL_KEY_ALL = 7 /* all keys */
} FL_KEY;
#define FL_ALT_MASK (1L << 25) /* alt + Key --> FL_ALT_MASK + key */
#define FL_CONTROL_MASK (1L << 26)
#define FL_SHIFT_MASK (1L << 27)
#define FL_ALT_VAL FL_ALT_MASK /* Don' use! */
#define MAX_SHORTCUTS 8
/* Pop-up menu item attributes. NOTE if more than 8, need to change
* choice and menu class where mode is kept by a single byte */
enum { FL_PUP_NONE, FL_PUP_GREY = 1, FL_PUP_BOX = 2, FL_PUP_CHECK = 4, FL_PUP_RADIO = 8 };
#define FL_PUP_GRAY FL_PUP_GREY
#define FL_PUP_TOGGLE FL_PUP_BOX /* not used anymore */
#define FL_PUP_INACTIVE FL_PUP_GREY
/* Popup and menu entries */
typedef int (*FL_PUP_CB)(int); /* callback prototype */
typedef struct {
const char *text; /* label of a popup/menu item */
FL_PUP_CB callback; /* the callback function */
const char *shortcut; /* hotkeys */
int mode; /* FL_PUP_GRAY, FL_PUP_CHECK etc */
} FL_PUP_ENTRY;
#define FL_MENU_ENTRY FL_PUP_ENTRY
/*******************************************************************
* FONTS
******************************************************************/
#define FL_MAXFONTS 48 /* max number of fonts */
typedef enum {
FL_INVALID_STYLE = -1,
FL_NORMAL_STYLE,
FL_BOLD_STYLE,
FL_ITALIC_STYLE,
FL_BOLDITALIC_STYLE,
FL_FIXED_STYLE,
FL_FIXEDBOLD_STYLE,
FL_FIXEDITALIC_STYLE,
FL_FIXEDBOLDITALIC_STYLE,
FL_TIMES_STYLE,
FL_TIMESBOLD_STYLE,
FL_TIMESITALIC_STYLE,
FL_TIMESBOLDITALIC_STYLE,
FL_MISC_STYLE,
FL_MISCBOLD_STYLE,
FL_MISCITALIC_STYLE,
FL_SYMBOL_STYLE,
/* modfier masks. Need to fit a short */
FL_SHADOW_STYLE = (1 << 9),
FL_ENGRAVED_STYLE = (1 << 10),
FL_EMBOSSED_STYLE = (1 << 11)
} FL_TEXT_STYLE;
#define FL_FONT_STYLE FL_TEXT_STYLE
#define special_style(a) ((a) >= FL_SHADOW_STYLE && (a) <= (FL_EMBOSSED_STYLE + FL_MAXFONTS))
/* Standard sizes in XForms */
#define FL_TINY_SIZE 8
#define FL_SMALL_SIZE 10
#define FL_NORMAL_SIZE 12
#define FL_MEDIUM_SIZE 14
#define FL_LARGE_SIZE 18
#define FL_HUGE_SIZE 24
#define FL_DEFAULT_SIZE FL_SMALL_SIZE
/* Defines for compatibility */
#define FL_TINY_FONT FL_TINY_SIZE
#define FL_SMALL_FONT FL_SMALL_SIZE
#define FL_NORMAL_FONT FL_NORMAL_SIZE
#define FL_MEDIUM_FONT FL_MEDIUM_SIZE
#define FL_LARGE_FONT FL_LARGE_SIZE
#define FL_HUGE_FONT FL_HUGE_SIZE
#define FL_NORMAL_FONT1 FL_SMALL_FONT
#define FL_NORMAL_FONT2 FL_NORMAL_FONT
#define FL_DEFAULT_FONT FL_SMALL_FONT
#define FL_BOUND_WIDTH (FL_Coord)1 /* Border width of boxes */
/* Definition of basic struct that holds an object */
#define FL_CLICK_TIMEOUT 400 /* double click interval */
typedef struct FL_FORM_ FL_FORM;
typedef struct FL_OBJECT_ FL_OBJECT;
typedef struct FL_pixmap_ FL_pixmap;
struct FL_OBJECT_ {
FL_FORM *form; /* the form this object belongs to */
void *u_vdata; /* anything the user likes */
char *u_cdata; /* anything the user likes */
long u_ldata; /* anything the user likes */
int objclass; /* class of object, button, slider etc */
int type; /* type within the class */
int boxtype; /* what kind of box type */
FL_Coord x, /* current obj. location and size */
y, w, h;
double fl1, /* distances of upper left hand (1) and */
fr1, /* lower right hand corner (2) to left, */
ft1, /* right, top and bottom of enclosing */
fb1, /* form */
fl2, fr2, ft2, fb2;
FL_Coord bw;
FL_COLOR col1, /* colors of obj */
col2;
char *label; /* object label */
FL_COLOR lcol; /* label color */
int align;
int lsize, /* label size and style */
lstyle;
long *shortcut;
int (*handle)(FL_OBJECT *, int, FL_Coord, FL_Coord, int, void *);
void (*object_callback)(FL_OBJECT *, long);
long argument;
void *spec; /* instantiation */
int (*prehandle)(FL_OBJECT *, int, FL_Coord, FL_Coord, int, void *);
int (*posthandle)(FL_OBJECT *, int, FL_Coord, FL_Coord, int, void *);
void (*set_return)(FL_OBJECT *, unsigned int);
/* Re-configure preference */
unsigned int resize; /* what to do if WM resizes the FORM */
unsigned int nwgravity; /* how to re-position top-left corner */
unsigned int segravity; /* how to re-position lower-right corner */
FL_OBJECT *prev; /* prev. obj in form */
FL_OBJECT *next; /* next. obj in form */
FL_OBJECT *parent;
FL_OBJECT *child;
FL_OBJECT *nc; /* next child */
FL_pixmap *flpixmap; /* pixmap double buffering stateinfo */
int use_pixmap; /* true to use pixmap double buffering*/
/* Some interaction flags */
int returned; /* what last interaction returned */
unsigned int how_return; /* under which conditions to return */
int double_buffer; /* only used by mesa/gl canvas */
int pushed;
int focus;
int belowmouse;
int active; /* if object accepts events */
int input;
int wantkey;
int radio;
int automatic;
int redraw;
int visible;
int is_under; /* if (partially) hidden by other object */
int clip;
unsigned long click_timeout;
void *c_vdata; /* for class use */
char *c_cdata; /* for class use */
long c_ldata; /* for class use */
FL_COLOR dbl_background; /* double buffer background */
char *tooltip;
int tipID;
int group_id;
int want_motion;
int want_update;
};
/* Callback function for an entire form */
typedef void (*FL_FORMCALLBACKPTR)(FL_OBJECT *, void *);
/* Object callback function */
typedef void (*FL_CALLBACKPTR)(FL_OBJECT *, long);
/* Preemptive callback function */
typedef int (*FL_RAW_CALLBACK)(FL_FORM *, void *);
/* At close (WM menu delete/close etc.) function */
typedef int (*FL_FORM_ATCLOSE)(FL_FORM *, void *);
/* Deactivate/activate callback */
typedef void (*FL_FORM_ATDEACTIVATE)(FL_FORM *, void *);
typedef void (*FL_FORM_ATACTIVATE)(FL_FORM *, void *);
typedef int (*FL_HANDLEPTR)(FL_OBJECT *, int, FL_Coord, FL_Coord, int, void *);
/* Error callback */
typedef void (*FL_ERROR_FUNC)(const char *, const char *, ...);
__attribute__((unused)) static FL_OBJECT *FL_EVENT;
/*** FORM ****/
/* Form visibility state: form->visible */
enum { FL_BEING_HIDDEN = -1, FL_HIDDEN = 0, FL_INVISIBLE = FL_HIDDEN, FL_VISIBLE = 1 };
struct FL_FORM_ {
void *fdui; /* for fdesign */
void *u_vdata; /* for application */
char *u_cdata; /* for application */
long u_ldata; /* for application */
char *label; /* window title */
Window window; /* X resource ID for window */
FL_Coord x, /* current geometry info */
y, w, h;
int handle_dec_x, handle_dec_y;
FL_Coord hotx, /* hot-spot of the form */
hoty;
double w_hr, /* high resolution width and height */
h_hr; /* (needed for precise scaling) */
FL_OBJECT *first;
FL_OBJECT *last;
FL_OBJECT *focusobj;
FL_FORMCALLBACKPTR form_callback;
FL_FORM_ATACTIVATE activate_callback;
FL_FORM_ATDEACTIVATE deactivate_callback;
void *form_cb_data;
void *activate_data;
void *deactivate_data;
FL_RAW_CALLBACK key_callback;
FL_RAW_CALLBACK push_callback;
FL_RAW_CALLBACK crossing_callback;
FL_RAW_CALLBACK motion_callback;
FL_RAW_CALLBACK all_callback;
unsigned long compress_mask;
unsigned long evmask;
/* WM_DELETE_WINDOW message handler */
FL_FORM_ATCLOSE close_callback;
void *close_data;
FL_pixmap *flpixmap; /* back buffer */
Pixmap icon_pixmap;
Pixmap icon_mask;
/* Interaction and other flags */
int deactivated; /* non-zero if deactivated */
int use_pixmap; /* true if dbl buffering */
int frozen; /* true if sync change */
int visible; /* true if mapped */
int wm_border; /* window manager info */
unsigned int prop; /* other attributes */
int num_auto_objects;
int needs_full_redraw;
int sort_of_modal; /* internal use */
FL_FORM *parent;
FL_FORM *child;
FL_OBJECT *parent_obj;
int attached; /* not independent anymore */
void (*pre_attach)(FL_FORM *);
void *attach_data;
int in_redraw;
};
/* All FD_xxx structure emitted by fdesign contains at least the
* following */
typedef struct {
FL_FORM *form;
void *vdata;
char *cdata;
long ldata;
} FD_Any;
/* Async IO stuff */
enum { FL_READ = 1, FL_WRITE = 2, FL_EXCEPT = 4 };
/* IO other than XEvent Q */
typedef void (*FL_IO_CALLBACK)(int, void *);
FL_EXPORT void fl_add_io_callback(int fd, unsigned int mask, FL_IO_CALLBACK callback, void *data){};
FL_EXPORT void fl_remove_io_callback(int fd, unsigned int mask, FL_IO_CALLBACK cb){};
/* signals */
typedef void (*FL_SIGNAL_HANDLER)(int, void *);
FL_EXPORT void fl_add_signal_callback(int s, FL_SIGNAL_HANDLER cb, void *data){};
FL_EXPORT void fl_remove_signal_callback(int s){};
FL_EXPORT void fl_signal_caught(int s){};
FL_EXPORT void fl_app_signal_direct(int y){};
enum { FL_INPUT_END_EVENT_CLASSIC = 0, FL_INPUT_END_EVENT_ALWAYS = 1 };
FL_EXPORT int fl_input_end_return_handling(int type)
{
return 0;
};
/** Generic routines that deal with FORMS **/
FL_EXPORT FL_FORM *fl_bgn_form(int type, FL_Coord w, FL_Coord h)
{
return NULL;
};
FL_EXPORT void fl_end_form(void){};
FL_EXPORT FL_OBJECT *fl_do_forms(void)
{
return NULL;
};
FL_EXPORT FL_OBJECT *fl_check_forms(void)
{
return NULL;
};
FL_EXPORT FL_OBJECT *fl_do_only_forms(void)
{
return NULL;
};
FL_EXPORT FL_OBJECT *fl_check_only_forms(void)
{
return NULL;
};
FL_EXPORT void fl_freeze_form(FL_FORM *form){};
FL_EXPORT void fl_set_focus_object(FL_FORM *form, FL_OBJECT *obj){};
FL_EXPORT FL_OBJECT *fl_get_focus_object(FL_FORM *form)
{
return NULL;
};
FL_EXPORT void fl_reset_focus_object(FL_OBJECT *ob){};
#define fl_set_object_focus fl_set_focus_object
FL_EXPORT FL_FORM_ATCLOSE fl_set_form_atclose(FL_FORM *form, FL_FORM_ATCLOSE fmclose, void *data){};
FL_EXPORT FL_FORM_ATCLOSE fl_set_atclose(FL_FORM_ATCLOSE fmclose, void *data){};
FL_EXPORT FL_FORM_ATACTIVATE fl_set_form_atactivate(FL_FORM *form, FL_FORM_ATACTIVATE cb, void *data){};
FL_EXPORT FL_FORM_ATDEACTIVATE fl_set_form_atdeactivate(FL_FORM *form, FL_FORM_ATDEACTIVATE cb, void *data){};
FL_EXPORT void fl_unfreeze_form(FL_FORM *form){};
FL_EXPORT int fl_form_is_activated(FL_FORM *form){};
FL_EXPORT void fl_deactivate_form(FL_FORM *form){};
FL_EXPORT void fl_activate_form(FL_FORM *form){};
FL_EXPORT void fl_deactivate_all_forms(void){};
FL_EXPORT void fl_activate_all_forms(void){};
FL_EXPORT void fl_freeze_all_forms(void){};
FL_EXPORT void fl_unfreeze_all_forms(void){};
FL_EXPORT void fl_scale_form(FL_FORM *form, double xsc, double ysc){};
FL_EXPORT void fl_set_form_position(FL_FORM *form, FL_Coord x, FL_Coord y){};
FL_EXPORT void fl_set_form_title(FL_FORM *form, const char *name){};
FL_EXPORT void fl_set_form_title_f(FL_FORM *form, const char *fmt, ...){};
FL_EXPORT void fl_set_app_mainform(FL_FORM *form){};
FL_EXPORT FL_FORM *fl_get_app_mainform(void)
{
return NULL;
};
FL_EXPORT void fl_set_app_nomainform(int flag){};
FL_EXPORT void fl_set_form_callback(FL_FORM *form, FL_FORMCALLBACKPTR callback, void *d){};
#define fl_set_form_call_back fl_set_form_callback
FL_EXPORT void fl_set_form_size(FL_FORM *form, FL_Coord w, FL_Coord h){};
FL_EXPORT void fl_set_form_background_color(FL_FORM *form, FL_COLOR color){};
FL_EXPORT FL_COLOR fl_get_form_background_color(FL_FORM *form){};
FL_EXPORT void fl_set_form_hotspot(FL_FORM *form, FL_Coord x, FL_Coord y){};
FL_EXPORT void fl_set_form_hotobject(FL_FORM *form, FL_OBJECT *ob){};
FL_EXPORT void fl_set_form_minsize(FL_FORM *form, FL_Coord w, FL_Coord h){};
FL_EXPORT void fl_set_form_maxsize(FL_FORM *form, FL_Coord w, FL_Coord h){};
FL_EXPORT void fl_set_form_event_cmask(FL_FORM *form, unsigned long cmask){};
FL_EXPORT unsigned long fl_get_form_event_cmask(FL_FORM *form){};
FL_EXPORT void fl_set_form_geometry(FL_FORM *form, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h){};
#define fl_set_initial_placement fl_set_form_geometry
FL_EXPORT Window fl_show_form(FL_FORM *form, int place, int border, const char *name)
{
return NULL;
};
FL_EXPORT Window fl_show_form_f(FL_FORM *form, int place, int border, const char *fmt, ...)
{
return NULL;
};
FL_EXPORT void fl_hide_form(FL_FORM *form){};
FL_EXPORT void fl_free_form(FL_FORM *form){};
FL_EXPORT void fl_redraw_form(FL_FORM *form){};
FL_EXPORT void fl_set_form_dblbuffer(FL_FORM *form, int y){};
FL_EXPORT Window fl_prepare_form_window(FL_FORM *form, int place, int border, const char *name)
{
return NULL;
};
FL_EXPORT Window fl_prepare_form_window_f(FL_FORM *form, int place, int border, const char *fmt, ...)
{
return NULL;
};
FL_EXPORT Window fl_show_form_window(FL_FORM *form)
{
return NULL;
};
FL_EXPORT double fl_adjust_form_size(FL_FORM *form)
{
return 0;
};
FL_EXPORT int fl_form_is_visible(FL_FORM *form)
{
return 0;
};
FL_EXPORT int fl_form_is_iconified(FL_FORM *form)
{
return 0;
};
FL_EXPORT FL_RAW_CALLBACK fl_register_raw_callback(FL_FORM *form, unsigned long mask, FL_RAW_CALLBACK rcb){};
#define fl_register_call_back fl_register_raw_callback
FL_EXPORT FL_OBJECT *fl_bgn_group(void)
{
return NULL;
};
FL_EXPORT void fl_end_group(void){};
FL_EXPORT FL_OBJECT *fl_addto_group(FL_OBJECT *group)
{
return NULL;
};
/****** Routines that deal with FL_OBJECTS ********/
FL_EXPORT int fl_get_object_objclass(FL_OBJECT *obj)
{
return 0;
};
FL_EXPORT int fl_get_object_type(FL_OBJECT *obj)
{
return 0;
};
FL_EXPORT void fl_set_object_boxtype(FL_OBJECT *ob, int boxtype){};
FL_EXPORT int fl_get_object_boxtype(FL_OBJECT *obj)
{
return 0;
};
FL_EXPORT void fl_set_object_bw(FL_OBJECT *ob, int bw){};
FL_EXPORT int fl_get_object_bw(FL_OBJECT *ob)
{
return 0;
};
FL_EXPORT void fl_set_object_resize(FL_OBJECT *ob, unsigned int what){};
FL_EXPORT void fl_get_object_resize(FL_OBJECT *ob, unsigned int *what){};
FL_EXPORT void fl_set_object_gravity(FL_OBJECT *ob, unsigned int nw, unsigned int se){};
FL_EXPORT void fl_get_object_gravity(FL_OBJECT *ob, unsigned int *nw, unsigned int *se){};
FL_EXPORT void fl_set_object_lsize(FL_OBJECT *obj, int lsize){};
FL_EXPORT int fl_get_object_lsize(FL_OBJECT *obj){};
FL_EXPORT void fl_set_object_lstyle(FL_OBJECT *obj, int lstyle){};
FL_EXPORT int fl_get_object_lstyle(FL_OBJECT *obj)
{
return 0;
};
FL_EXPORT void fl_set_object_lcol(FL_OBJECT *ob, FL_COLOR lcol){};
FL_EXPORT FL_COLOR fl_get_object_lcol(FL_OBJECT *obj){};
FL_EXPORT int fl_set_object_return(FL_OBJECT *ob, unsigned int when){};
FL_EXPORT void fl_set_object_lalign(FL_OBJECT *obj, int align){};
FL_EXPORT int fl_get_object_lalign(FL_OBJECT *obj)
{
return 0;
};
FL_EXPORT void fl_set_object_shortcut(FL_OBJECT *obj, const char *sstr, int showit){};
FL_EXPORT void fl_set_object_shortcutkey(FL_OBJECT *obj, unsigned int keysym){};
FL_EXPORT void fl_set_object_dblbuffer(FL_OBJECT *ob, int y){};
FL_EXPORT void fl_set_object_color(FL_OBJECT *ob, FL_COLOR col1, FL_COLOR col2){};
FL_EXPORT void fl_get_object_color(FL_OBJECT *obj, FL_COLOR *col1, FL_COLOR *col2){};
FL_EXPORT void fl_set_object_label(FL_OBJECT *ob, const char *label){};
FL_EXPORT void fl_set_object_label_f(FL_OBJECT *obj, const char *fmt, ...){};
FL_EXPORT const char *fl_get_object_label(FL_OBJECT *obj)
{
return NULL;
};
FL_EXPORT void fl_set_object_helper(FL_OBJECT *ob, const char *tip){};
FL_EXPORT void fl_set_object_helper_f(FL_OBJECT *ob, const char *fmt, ...){};
FL_EXPORT void fl_set_object_position(FL_OBJECT *obj, FL_Coord x, FL_Coord y){};
FL_EXPORT void fl_get_object_size(FL_OBJECT *obj, FL_Coord *w, FL_Coord *h){};
FL_EXPORT void fl_set_object_size(FL_OBJECT *obj, FL_Coord w, FL_Coord h){};
FL_EXPORT void fl_set_object_automatic(FL_OBJECT *obj, int flag){};
FL_EXPORT int fl_object_is_automatic(FL_OBJECT *obj){};
FL_EXPORT void fl_draw_object_label(FL_OBJECT *ob){};
FL_EXPORT void fl_draw_object_label_outside(FL_OBJECT *ob){};
FL_EXPORT FL_OBJECT *fl_get_object_component(FL_OBJECT *composite, int objclass, int type, int numb)
{
return NULL;
};
FL_EXPORT void fl_for_all_objects(FL_FORM *form, int (*cb)(FL_OBJECT *, void *), void *v){};
#define fl_draw_object_outside_label fl_draw_object_label_outside
FL_EXPORT void fl_set_object_dblclick(FL_OBJECT *obj, unsigned long timeout){};
FL_EXPORT unsigned long fl_get_object_dblclick(FL_OBJECT *obj)
{
return 0;
};
FL_EXPORT void fl_set_object_geometry(FL_OBJECT *obj, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h){};
FL_EXPORT void fl_move_object(FL_OBJECT *obj, FL_Coord dx, FL_Coord dy){};
#define fl_set_object_lcolor fl_set_object_lcol
#define fl_get_object_lcolor fl_get_object_lcol
FL_EXPORT void fl_fit_object_label(FL_OBJECT *obj, FL_Coord xmargin, FL_Coord ymargin){};
FL_EXPORT void fl_get_object_geometry(FL_OBJECT *ob, FL_Coord *x, FL_Coord *y, FL_Coord *w, FL_Coord *h){};
FL_EXPORT void fl_get_object_position(FL_OBJECT *ob, FL_Coord *x, FL_Coord *y){};
/* This one takes into account the label */
FL_EXPORT void fl_get_object_bbox(FL_OBJECT *obj, FL_Coord *x, FL_Coord *y, FL_Coord *w, FL_Coord *h)
{
*x = *y = *w = *h = 0;
};
#define fl_compute_object_geometry fl_get_object_bbox
FL_EXPORT void fl_call_object_callback(FL_OBJECT *ob){};
FL_EXPORT FL_HANDLEPTR fl_set_object_prehandler(FL_OBJECT *ob, FL_HANDLEPTR phandler){};
FL_EXPORT FL_HANDLEPTR fl_set_object_posthandler(FL_OBJECT *ob, FL_HANDLEPTR post){};
FL_EXPORT FL_CALLBACKPTR fl_set_object_callback(FL_OBJECT *obj, FL_CALLBACKPTR callback, long argument){};
#define fl_set_object_align fl_set_object_lalign
#define fl_set_call_back fl_set_object_callback
FL_EXPORT void fl_redraw_object(FL_OBJECT *obj){};
FL_EXPORT void fl_show_object(FL_OBJECT *ob){};
FL_EXPORT void fl_hide_object(FL_OBJECT *ob){};
FL_EXPORT int fl_object_is_visible(FL_OBJECT *obj){};
FL_EXPORT void fl_free_object(FL_OBJECT *obj){};
FL_EXPORT void fl_delete_object(FL_OBJECT *obj){};
FL_EXPORT int fl_get_object_return_state(FL_OBJECT *obj)
{
return 0;
};
FL_EXPORT void fl_trigger_object(FL_OBJECT *obj){};
FL_EXPORT void fl_activate_object(FL_OBJECT *ob){};
FL_EXPORT void fl_deactivate_object(FL_OBJECT *ob){};
FL_EXPORT int fl_object_is_active(FL_OBJECT *obj)
{
return 0;
};
FL_EXPORT int fl_enumerate_fonts(void (*output)(const char *s), int shortform)
{
return 0;
};
FL_EXPORT int fl_set_font_name(int n, const char *name)
{
return 0;
};
FL_EXPORT int fl_set_font_name_f(int n, const char *fmt, ...)
{
return 0;
};
FL_EXPORT void fl_set_font(int numb, int size){};
/* Routines that facilitate free object */
FL_EXPORT int fl_get_char_height(int style, int size, int *asc, int *desc)
{
return 0;
};
FL_EXPORT int fl_get_char_width(int style, int size)
{
return 0;
};
FL_EXPORT int fl_get_string_height(int style, int size, const char *s, int len, int *asc, int *desc)
{
return 0;
};
FL_EXPORT int fl_get_string_width(int style, int size, const char *s, int len)
{
return 0;
};
FL_EXPORT int fl_get_string_widthTAB(int style, int size, const char *s, int len)
{
return 0;
};
FL_EXPORT void fl_get_string_dimension(int fntstyle, int fntsize, const char *s, int len, int *width, int *height){};
#define fl_get_string_size fl_get_string_dimension
FL_EXPORT void fl_get_align_xy(int align, int x, int y, int w, int h, int xsize, int ysize, int xoff, int yoff, int *xx, int *yy){};
FL_EXPORT int fl_get_label_char_at_mouse(FL_OBJECT *obj)
{
return 0;
};
FL_EXPORT void fl_drw_text(int align, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, FL_COLOR c, int style, int size, const char *istr){};
FL_EXPORT void fl_drw_text_beside(int align, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, FL_COLOR c, int style, int size, const char *str){};
FL_EXPORT void fl_drw_text_cursor(int align, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, FL_COLOR c, int style, int size, const char *str, int cc, int pos){};
FL_EXPORT void fl_drw_box(int style, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, FL_COLOR c, int bw_in){};
typedef void (*FL_DRAWPTR)(FL_Coord, FL_Coord, FL_Coord, FL_Coord, int, FL_COLOR);
FL_EXPORT int fl_add_symbol(const char *name, FL_DRAWPTR drawit, int scalable)
{
return 0;
};
FL_EXPORT int fl_delete_symbol(const char *name)
{
return 0;
};
FL_EXPORT int fl_draw_symbol(const char *label, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, FL_COLOR col)
{
return 0;
};
FL_EXPORT unsigned long fl_mapcolor(FL_COLOR col, int r, int g, int b)
{
return 0;
};
FL_EXPORT long fl_mapcolorname(FL_COLOR col, const char *name)
{
return 0;
};
#define fl_mapcolor_name fl_mapcolorname
FL_EXPORT void fl_free_colors(FL_COLOR *c, int n){};
FL_EXPORT void fl_free_pixels(unsigned long *pix, int n){};
FL_EXPORT void fl_set_color_leak(int y){};
FL_EXPORT unsigned long fl_getmcolor(FL_COLOR i, int *r, int *g, int *b)
{
return 0;
};
FL_EXPORT unsigned long fl_get_pixel(FL_COLOR col)
{
return 0;
};
#define fl_get_flcolor fl_get_pixel
FL_EXPORT void fl_get_icm_color(FL_COLOR col, int *r, int *g, int *b){};
FL_EXPORT void fl_set_icm_color(FL_COLOR col, int r, int g, int b){};
FL_EXPORT void fl_color(FL_COLOR col){};
FL_EXPORT void fl_bk_color(FL_COLOR col){};
FL_EXPORT void fl_set_gamma(double r, double g, double b){};
FL_EXPORT void fl_show_errors(int y){};
/* Some macros */
#define FL_max(a, b) ((a) > (b) ? (a) : (b))
#define FL_min(a, b) ((a) < (b) ? (a) : (b))
#define FL_abs(a) ((a) > 0 ? (a) : (-(a)))
#define FL_nint(a) ((int)((a) > 0 ? ((a) + 0.5) : ((a)-0.5)))
#define FL_clamp(a, amin, amax) ((a) < (amin) ? (amin) : ((a) > (amax) ? (amax) : (a)))
#define FL_crnd(a) ((FL_Coord)((a) > 0 ? ((a) + 0.5) : ((a)-0.5)))
typedef int (*FL_FSCB)(const char *, void *);
/* Utilities for new objects */
__attribute__((unused)) static FL_FORM *fl_current_form;
FL_EXPORT void fl_add_object(FL_FORM *form, FL_OBJECT *obj){};
FL_EXPORT FL_FORM *fl_addto_form(FL_FORM *form)
{
return NULL;
};
FL_EXPORT FL_OBJECT *fl_make_object(int objclass, int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label, FL_HANDLEPTR handle)
{
return NULL;
};
FL_EXPORT void fl_add_child(FL_OBJECT *oa, FL_OBJECT *ob){};
FL_EXPORT void fl_set_coordunit(int u){};
FL_EXPORT void fl_set_border_width(int bw){};
FL_EXPORT void fl_set_scrollbar_type(int t){};
#define fl_set_thinscrollbar(t) fl_set_scrollbar_type(t ? FL_THIN_SCROLLBAR : FL_NORMAL_SCROLLBAR)
FL_EXPORT void fl_flip_yorigin(void){};
FL_EXPORT int fl_get_coordunit(void)
{
return 0;
};
FL_EXPORT int fl_get_border_width(void)
{
return 0;
};
/* Misc. routines */
FL_EXPORT void fl_ringbell(int percent){};
FL_EXPORT void fl_gettime(long *sec, long *usec){};
FL_EXPORT const char *fl_now(void)
{
return NULL;
};
FL_EXPORT const char *fl_whoami(void)
{
return NULL;
};
FL_EXPORT long fl_mouse_button(void)
{
return 0;
};
FL_EXPORT int fl_current_event(void)
{
return 0;
};
FL_EXPORT char *fl_strdup(const char *s)
{
return NULL;
};
FL_EXPORT void fl_set_err_logfp(FILE *fp){};
FL_EXPORT void fl_set_error_handler(FL_ERROR_FUNC user_func){};
FL_EXPORT char **fl_get_cmdline_args(int *ia)
{
return NULL;
};
/* This function was called 'fl_set_error_logfp/' in XForms 0.89. */
#define fl_set_error_logfp fl_set_err_logfp
#define fl_mousebutton fl_mouse_button
/* These give more flexibility for future changes. Also application
* can re-assign these pointers to whatever function it wants, e.g.,
* to a shared memory pool allocator. */
/*
FL_EXPORT void ( * fl_free )( void * ){};
FL_EXPORT void * ( * fl_malloc )( size_t ){};
FL_EXPORT void * ( * fl_calloc )( size_t,
size_t ){};
FL_EXPORT void * ( * fl_realloc )( void *,
size_t ){};
*/
FL_EXPORT int fl_msleep(unsigned long msec)
{
return 0;
};
#define FL_MAX_MENU_CHOICE_ITEMS 128
typedef const char *(*FL_VAL_FILTER)(FL_OBJECT *, double, int);
FL_EXPORT int fl_is_same_object(FL_OBJECT *obj1, FL_OBJECT *obj2)
{
return 0;
};
#endif /* ! defined FL_BASIC_H */
/**
* \file XBasic.h
*
* X Window dependent stuff
*
*/
#ifndef FL_XBASIC_H
#define FL_XBASIC_H
/* Draw mode */
enum { FL_XOR = 0, FL_COPY = 1, FL_AND = 2 };
#define FL_MINDEPTH 1
/* FL_xxx does not do anything anymore, but kept for compatibility */
enum {
FL_IllegalVisual = -1,
FL_StaticGray = 0,
FL_GrayScale = 1,
FL_StaticColor = 2,
FL_PseudoColor = 3,
FL_TrueColor = 4,
FL_DirectColor = 5,
FL_DefaultVisual = 10 /* special request */
};
enum { FL_North = 0, FL_NorthEast, FL_NorthWest, FL_South, FL_SouthEast, FL_SouthWest, FL_East, FL_West, FL_NoGravity, FL_ForgetGravity };
#ifndef GreyScale
#define GreyScale GrayScale
#define StaticGrey StaticGray
#endif
#define FL_is_gray(v) ((v) == GrayScale || (v) == StaticGray)
#define FL_is_rgb(v) ((v) == TrueColor || (v) == DirectColor)
/* Internal colormap size. Not really very meaningful as fl_mapcolor
* and company allow color "leakage", that is, although only FL_MAX_COLS
* are kept in the internal colormap, the server might have substantially
* more colors allocated */
#define FL_MAX_COLORS 1024
#define FL_MAX_COLS FL_MAX_COLORS
/* FL graphics state information. Some are redundant. */
typedef struct {
// XVisualInfo * xvinfo;
// XFontStruct * cur_fnt; /* current font in default GC */
// Colormap colormap; /* colormap valid for xvinfo */
Window trailblazer; /* a valid window for xvinfo */
int vclass, /* visual class and color depth */
depth;
int rgb_bits; /* primary color resolution */
int dithered; /* true if dithered color */
int pcm; /* true if colormap is not shared */
// GC gc[ 16 ]; /* working GC */
// GC textgc[ 16 ]; /* GC used exclusively for text */
// GC dimmedGC; /* A GC having a checkboard stipple */
unsigned long lut[FL_MAX_COLS]; /* secondary lookup table */
unsigned int rshift, rmask, rbits;
unsigned int gshift, gmask, gbits;
unsigned int bshift, bmask, bbits;
} FL_State;
#define FL_STATE FL_State /* for compatibility */
/***** Global variables ******/
// static Display *fl_display;
__attribute__((unused)) static int fl_screen;
__attribute__((unused)) static Window fl_root; /* root window */
__attribute__((unused)) static Window fl_vroot; /* virtual root window */
__attribute__((unused)) static int fl_scrh, /* screen dimension in pixels */
fl_scrw;
__attribute__((unused)) static int fl_vmode;
/* Current version only runs in single visual mode */
#define fl_get_vclass() fl_vmode
#define fl_get_form_vclass(a) fl_vmode
#define fl_get_gc() fl_state[fl_vmode].gc[0]
__attribute__((unused)) static FL_State fl_state[];
__attribute__((unused)) static char *fl_ul_magic_char;
FL_EXPORT int fl_mode_capable(int mode, int warn)
{
return 0;
};
#define fl_default_win() (fl_state[fl_vmode].trailblazer)
#define fl_default_window() (fl_state[fl_vmode].trailblazer)
/* All pixmaps used by FL_OBJECT to simulate double buffering have the
* following entries in the structure. FL_Coord x,y are used to shift
* the origin of the drawing routines */
struct FL_pixmap_ {
Pixmap pixmap;
Window win;
Visual *visual;
FL_Coord x, y, w, h;
int depth;
FL_COLOR dbl_background;
FL_COLOR pixel;
};
/* Fonts related */
#define FL_MAX_FONTSIZES 10
#define FL_MAX_FONTNAME_LENGTH 80
typedef struct {
XFontStruct *fs[FL_MAX_FONTSIZES]; /* cached fontstruct */
short size[FL_MAX_FONTSIZES]; /* cached sizes */
short nsize; /* cached so far */
char fname[FL_MAX_FONTNAME_LENGTH + 1]; /* without size info */
} FL_FONT;
/* Some basic drawing routines */
typedef XPoint FL_POINT;
typedef XRectangle FL_RECT;
/* Rectangles */
FL_EXPORT void fl_rectangle(int fill, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, FL_COLOR col){};
FL_EXPORT void fl_rectbound(FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, FL_COLOR col){};
#define fl_rectf(x, y, w, h, c) fl_rectangle(1, x, y, w, h, c)
#define fl_rect(x, y, w, h, c) fl_rectangle(0, x, y, w, h, c)
/* Rectangle with rounded-corners */
FL_EXPORT void fl_roundrectangle(int fill, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, FL_COLOR col){};
#define fl_roundrectf(x, y, w, h, c) fl_roundrectangle(1, x, y, w, h, c)
#define fl_roundrect(x, y, w, h, c) fl_roundrectangle(0, x, y, w, h, c)
/* General polygon and polylines */
FL_EXPORT void fl_polygon(int fill, FL_POINT *xp, int n, FL_COLOR col){};
#define fl_polyf(p, n, c) fl_polygon(1, p, n, c)
#define fl_polyl(p, n, c) fl_polygon(0, p, n, c)
#define fl_polybound(p, n, c) \
do { \
fl_polygon(1, p, n, c); \
fl_polygon(0, p, n, FL_BLACK); \
} while (0)
FL_EXPORT void fl_lines(FL_POINT *xp, int n, FL_COLOR col){};
FL_EXPORT void fl_line(FL_Coord xi, FL_Coord yi, FL_Coord xf, FL_Coord yf, FL_COLOR c){};
FL_EXPORT void fl_point(FL_Coord x, FL_Coord y, FL_COLOR c){};
FL_EXPORT void fl_points(FL_POINT *p, int np, FL_COLOR c){};
#define fl_simple_line fl_line
FL_EXPORT void fl_dashedlinestyle(const char *dash, int ndash){};
FL_EXPORT void fl_update_display(int block){};
#define fl_diagline(x, y, w, h, c) fl_line(x, y, (x) + (w)-1, (y) + (h)-1, c)
/* Line attributes */
enum { FL_SOLID = LineSolid, FL_USERDASH = LineOnOffDash, FL_USERDOUBLEDASH = LineDoubleDash, FL_DOT, FL_DOTDASH, FL_DASH, FL_LONGDASH };
FL_EXPORT void fl_linewidth(int n){};
FL_EXPORT void fl_linestyle(int n){};
FL_EXPORT void fl_drawmode(int request){};
FL_EXPORT int fl_get_linewidth(void)
{
return 0;
};
FL_EXPORT int fl_get_linestyle(void)
{
return 0;
};
FL_EXPORT int fl_get_drawmode(void)
{
return 0;
};
#define fl_set_linewidth fl_linewidth
#define fl_set_linestyle fl_linestyle
#define fl_set_drawmode fl_drawmode
/*
* Interfaces
*/
FL_EXPORT XFontStruct *fl_get_fontstruct(int style, int size){};
#define fl_get_font_struct fl_get_fontstruct
#define fl_get_fntstruct fl_get_font_struct
FL_EXPORT Window fl_get_mouse(FL_Coord *x, FL_Coord *y, unsigned int *keymask)
{
return NULL;
};
FL_EXPORT void fl_set_mouse(FL_Coord mx, FL_Coord my){};
FL_EXPORT Window fl_get_win_mouse(Window win, FL_Coord *x, FL_Coord *y, unsigned int *keymask)
{
return NULL;
};
FL_EXPORT Window fl_get_form_mouse(FL_FORM *fm, FL_Coord *x, FL_Coord *y, unsigned int *keymask)
{
return NULL;
};
FL_EXPORT FL_FORM *fl_win_to_form(Window win)
{
return NULL;
};
FL_EXPORT void fl_set_form_icon(FL_FORM *form, Pixmap p, Pixmap m){};
FL_EXPORT int fl_get_decoration_sizes(FL_FORM *form, int *top, int *right, int *bottom, int *left)
{
return 0;
};
FL_EXPORT void fl_raise_form(FL_FORM *form){};
FL_EXPORT void fl_lower_form(FL_FORM *form){};
FL_EXPORT void fl_set_foreground(GC gc, FL_COLOR color){};
FL_EXPORT void fl_set_background(GC gc, FL_COLOR color){};
/* General windowing support */
FL_EXPORT Window fl_wincreate(const char *label)
{
return NULL;
};
FL_EXPORT Window fl_winshow(Window win)
{
return NULL;
};
FL_EXPORT Window fl_winopen(const char *label)
{
return NULL;
};
FL_EXPORT void fl_winhide(Window win){};
FL_EXPORT void fl_winclose(Window win){};
FL_EXPORT void fl_winset(Window win){};
FL_EXPORT int fl_winreparent(Window win, Window new_parent)
{
return 0;
};
FL_EXPORT void fl_winfocus(Window win){};
FL_EXPORT Window fl_winget(void)
{
return NULL;
};
FL_EXPORT int fl_iconify(Window win)
{
return 0;
};
FL_EXPORT void fl_winresize(Window win, FL_Coord neww, FL_Coord newh){};
FL_EXPORT void fl_winmove(Window win, FL_Coord dx, FL_Coord dy){};
FL_EXPORT void fl_winreshape(Window win, FL_Coord dx, FL_Coord dy, FL_Coord w, FL_Coord h){};
FL_EXPORT void fl_winicon(Window win, Pixmap p, Pixmap m){};
FL_EXPORT void fl_winbackground(Window win, unsigned long bk){};
FL_EXPORT void fl_winstepsize(Window win, FL_Coord dx, FL_Coord dy){};
FL_EXPORT int fl_winisvalid(Window win)
{
return 0;
};
FL_EXPORT void fl_wintitle(Window win, const char *title){};
FL_EXPORT void fl_wintitle_f(Window win, const char *fmt, ...){};
FL_EXPORT void fl_winicontitle(Window win, const char *title){};
FL_EXPORT void fl_winicontitle_f(Window win, const char *fmt, ...){};
FL_EXPORT void fl_winposition(FL_Coord x, FL_Coord y){};
#define fl_pref_winposition fl_winposition
#define fl_win_background fl_winbackground
#define fl_winstepunit fl_winstepsize
#define fl_set_winstepunit fl_winstepsize
FL_EXPORT void fl_winminsize(Window win, FL_Coord w, FL_Coord h){};
FL_EXPORT void fl_winmaxsize(Window win, FL_Coord w, FL_Coord h){};
FL_EXPORT void fl_winaspect(Window win, FL_Coord x, FL_Coord y){};
FL_EXPORT void fl_reset_winconstraints(Window win){};
FL_EXPORT void fl_winsize(FL_Coord w, FL_Coord h){};
FL_EXPORT void fl_initial_winsize(FL_Coord w, FL_Coord h){};
#define fl_pref_winsize fl_winsize
FL_EXPORT void fl_initial_winstate(int state){};
FL_EXPORT Colormap fl_create_colormap(XVisualInfo *xv, int nfill){};
FL_EXPORT void fl_wingeometry(FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h){};
#define fl_pref_wingeometry fl_wingeometry
FL_EXPORT void fl_initial_wingeometry(FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h){};
FL_EXPORT void fl_noborder(void){};
FL_EXPORT void fl_transient(void){};
FL_EXPORT void fl_get_winsize(Window win, FL_Coord *w, FL_Coord *h){};
FL_EXPORT void fl_get_winorigin(Window win, FL_Coord *x, FL_Coord *y){};
FL_EXPORT void fl_get_wingeometry(Window win, FL_Coord *x, FL_Coord *y, FL_Coord *w, FL_Coord *h){};
/* For compatibility */
#define fl_get_win_size fl_get_winsize
#define fl_get_win_origin fl_get_winorigin
#define fl_get_win_geometry fl_get_wingeometry
#define fl_initial_winposition fl_pref_winposition
#define fl_get_display() fl_display
#define FL_FormDisplay(form) fl_display
#define FL_ObjectDisplay(object) fl_display
#define FL_IS_CANVAS(o) ((o)->objclass == FL_CANVAS || (o)->objclass == FL_GLCANVAS)
/* The window an object belongs to - for drawing */
#define FL_ObjWin(o) (FL_IS_CANVAS(o) ? fl_get_canvas_id(o) : (o)->form->window)
FL_EXPORT Window fl_get_real_object_window(FL_OBJECT *ob){};
#define FL_OBJECT_WID FL_ObjWin
/* All registerable events, including Client Message */
#define FL_ALL_EVENT (KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | ButtonMotionMask | PointerMotionMask)
/* Replacements for X functions that access the event queue */
FL_EXPORT int fl_XNextEvent(XEvent *xev)
{
return 0;
};
FL_EXPORT int fl_XPeekEvent(XEvent *xev)
{
return 0;
};
FL_EXPORT int fl_XEventsQueued(int mode)
{
return 0;
};
FL_EXPORT void fl_XPutBackEvent(XEvent *xev){};
FL_EXPORT const XEvent *fl_last_event(void)
{
return NULL;
};
typedef int (*FL_APPEVENT_CB)(XEvent *, void *);
FL_EXPORT FL_APPEVENT_CB fl_set_event_callback(FL_APPEVENT_CB callback, void *user_data){};
FL_EXPORT FL_APPEVENT_CB fl_set_idle_callback(FL_APPEVENT_CB callback, void *user_data){};
FL_EXPORT long fl_addto_selected_xevent(Window win, long mask)
{
return 0;
};
FL_EXPORT long fl_remove_selected_xevent(Window win, long mask)
{
return 0;
};
#define fl_add_selected_xevent fl_addto_selected_xevent
FL_EXPORT void fl_set_idle_delta(long delta){};
FL_EXPORT FL_APPEVENT_CB fl_add_event_callback(Window win, int ev, FL_APPEVENT_CB wincb, void *user_data){};
FL_EXPORT void fl_remove_event_callback(Window win, int ev){};
FL_EXPORT void fl_activate_event_callbacks(Window win){};
FL_EXPORT XEvent *fl_print_xevent_name(const char *where, const XEvent *xev){};
FL_EXPORT void fl_XFlush(void){};
#define metakey_down(mask) ((mask)&Mod1Mask)
#define shiftkey_down(mask) ((mask)&ShiftMask)
#define controlkey_down(mask) ((mask)&ControlMask)
#define button_down(mask) (((mask)&Button1Mask) || ((mask)&Button2Mask) || ((mask)&Button3Mask) || ((mask)&Button4Mask) || ((mask)&Button5Mask))
#define fl_keypressed fl_keysym_pressed
/****************** Resources ***************/
typedef enum { FL_NONE, FL_SHORT = 10, FL_BOOL, FL_INT, FL_LONG, FL_FLOAT, FL_STRING } FL_RTYPE;
typedef struct {
const char *res_name; /* resource name */
const char *res_class; /* resource class */
FL_RTYPE type; /* FL_INT, FL_FLOAT, FL_BOOL, FL_STRING */
void *var; /* address for the variable */
const char *defval; /* default setting in string form */
int nbytes; /* used only for strings */
} FL_RESOURCE;
#define FL_resource FL_RESOURCE
#define FL_CMD_OPT XrmOptionDescRec
FL_EXPORT Display *fl_initialize(int *na, char *arg[], const char *appclass, FL_CMD_OPT *appopt, int nappopt)
{
return NULL;
};
FL_EXPORT void fl_finish(void){};
FL_EXPORT const char *fl_get_resource(const char *rname, const char *cname, FL_RTYPE dtype, const char *defval, void *val, int size)
{
return NULL;
};
FL_EXPORT void fl_set_resource(const char *str, const char *val){};
FL_EXPORT void fl_get_app_resources(FL_RESOURCE *appresource, int n){};
FL_EXPORT void fl_set_visualID(long id){};
FL_EXPORT int fl_keysym_pressed(KeySym k)
{
return 0;
};
#define buttonLabelSize buttonFontSize
#define sliderLabelSize sliderFontSize
#define inputLabelSize inputFontSize
/* All Form control variables. Named closely as its resource name */
typedef struct {
float rgamma, ggamma, bgamma;
int debug, sync;
int depth, vclass, doubleBuffer;
int ulPropWidth, /* underline stuff */
ulThickness;
int buttonFontSize;
int sliderFontSize;
int inputFontSize;
int browserFontSize;
int menuFontSize;
int choiceFontSize;
int labelFontSize; /* all other labels fonts */
int pupFontSize, /* font for pop-up menus */
pupFontStyle;
int privateColormap;
int sharedColormap;
int standardColormap;
int scrollbarType;
int backingStore;
int coordUnit;
int borderWidth;
int safe;
char *rgbfile; /* where RGB file is, not used */
char vname[24];
} FL_IOPT;
#define FL_PDButtonLabelSize FL_PDButtonFontSize
#define FL_PDSliderLabelSize FL_PDSliderFontSize
#define FL_PDInputLabelSize FL_PDInputFontSize
/* Program default masks */
enum {
FL_PDDepth = (1 << 1),
FL_PDClass = (1 << 2),
FL_PDDouble = (1 << 3),
FL_PDSync = (1 << 4),
FL_PDPrivateMap = (1 << 5),
FL_PDScrollbarType = (1 << 6),
FL_PDPupFontSize = (1 << 7),
FL_PDButtonFontSize = (1 << 8),
FL_PDInputFontSize = (1 << 9),
FL_PDSliderFontSize = (1 << 10),
FL_PDVisual = (1 << 11),
FL_PDULThickness = (1 << 12),
FL_PDULPropWidth = (1 << 13),
FL_PDBS = (1 << 14),
FL_PDCoordUnit = (1 << 15),
FL_PDDebug = (1 << 16),
FL_PDSharedMap = (1 << 17),
FL_PDStandardMap = (1 << 18),
FL_PDBorderWidth = (1 << 19),
FL_PDSafe = (1 << 20),
FL_PDMenuFontSize = (1 << 21),
FL_PDBrowserFontSize = (1 << 22),
FL_PDChoiceFontSize = (1 << 23),
FL_PDLabelFontSize = (1 << 24)
};
#define FL_PDButtonLabel FL_PDButtonLabelSize
FL_EXPORT void fl_set_defaults(unsigned long mask, FL_IOPT *cntl){};
FL_EXPORT void fl_set_tabstop(const char *s){};
FL_EXPORT int fl_get_visual_depth(void)
{
return 0;
};
FL_EXPORT int fl_is_global_clipped(void)
{
return 0;
};
FL_EXPORT int fl_is_clipped(int include_global)
{
return 0;
};
FL_EXPORT int fl_is_text_clipped(int include_global)
{
return 0;
};
FL_EXPORT void fl_set_clipping(FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h){};
FL_EXPORT void fl_unset_clipping(){};
FL_EXPORT void fl_set_text_clipping(FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h){};
FL_EXPORT void fl_unset_text_clipping(void){};
FL_EXPORT int fl_get_global_clipping(FL_COORD *x, FL_COORD *y, FL_COORD *w, FL_COORD *h)
{
return 0;
};
FL_EXPORT int fl_get_clipping(int include_global, FL_COORD *x, FL_COORD *y, FL_COORD *w, FL_COORD *h)
{
return 0;
};
FL_EXPORT int fl_get_text_clipping(int include_global, FL_COORD *x, FL_COORD *y, FL_COORD *w, FL_COORD *h)
{
return 0;
};
FL_EXPORT void fl_set_gc_clipping(GC gc, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h){};
FL_EXPORT void fl_unset_gc_clipping(GC gc){};
/* How we pack and unpack colors */
#ifndef FL_PCBITS
typedef unsigned char FL_PCTYPE; /* primary color type */
#define FL_PCBITS 8 /* primary color bits */
#define FL_PCMAX ((1 << FL_PCBITS) - 1)
#define FL_PCCLAMP(a) ((a) > (FL_PCMAX) ? (FL_PCMAX) : ((a) < 0 ? 0 : (a)))
typedef unsigned int FL_PACKED4;
#define FL_PACKED FL_PACKED4
#define FL_RMASK 0x000000ff
#define FL_RSHIFT 0
#define FL_GMASK 0x0000ff00
#define FL_GSHIFT 8
#define FL_BMASK 0x00ff0000
#define FL_BSHIFT 16
#define FL_AMASK 0xff000000
#define FL_ASHIFT 24
/* If PCBITS is not 8, we need to apply the RGBmask */
#define FL_GETR(packed) (((packed) >> FL_RSHIFT) & FL_RMASK)
#define FL_GETG(packed) (((packed) >> FL_GSHIFT) & FL_PCMAX)
#define FL_GETB(packed) (((packed) >> FL_BSHIFT) & FL_PCMAX)
#define FL_GETA(packed) (((packed) >> FL_ASHIFT) & FL_PCMAX)
#define FL_PACK3(r, g, b) (((r) << FL_RSHIFT) | ((g) << FL_GSHIFT) | ((b) << FL_BSHIFT))
#define FL_PACK FL_PACK3
#define FL_PACK4(r, g, b, a) (FL_PACK3(r, g, b) | ((a) << FL_ASHIFT))
#define FL_UNPACK(p, r, g, b) \
do { \
r = FL_GETR(p); \
g = FL_GETG(p); \
b = FL_GETB(p); \
} while (0)
#define FL_UNPACK3 FL_UNPACK
#define FL_UNPACK4(p, r, g, b, a) \
do { \
FL_UNPACK3(p, r, g, b); \
a = FL_GETA(p); \
} while (0)
#endif
typedef struct {
unsigned int rshift, rmask, rbits;
unsigned int gshift, gmask, gbits;
unsigned int bshift, bmask, bbits;
int bits_per_rgb;
int colormap_size;
} FL_RGB2PIXEL_;
#define FL_RGB2PIXEL FL_RGB2PIXEL_
#endif /* ! defined FL_XBASIC_H */
/**
* \file box.h
*
*/
#ifndef FL_BOX_H
#define FL_BOX_H
/* Type is already defined in Basic.h */
FL_EXPORT FL_OBJECT *fl_create_box(int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label)
{
return NULL;
};
FL_EXPORT FL_OBJECT *fl_add_box(int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label)
{
return NULL;
};
#endif /* ! defined FL_BOX_H */
/**
* \file canvas.h
*
* Header for FL_CANVAS
*
*/
#ifndef FL_CANVAS_H_
#define FL_CANVAS_H_
typedef enum { FL_NORMAL_CANVAS, FL_SCROLLED_CANVAS } FL_CANVAS_TYPE;
typedef int (*FL_HANDLE_CANVAS)(FL_OBJECT *, Window, int, int, XEvent *, void *);
typedef int (*FL_MODIFY_CANVAS_PROP)(FL_OBJECT *);
/******************** Default *********************/
#define FL_CANVAS_BOXTYPE FL_DOWN_BOX /* really the decoration frame */
#define FL_CANVAS_ALIGN FL_ALIGN_TOP
/************ Interfaces ************************/
FL_EXPORT FL_OBJECT *fl_create_generic_canvas(int canvas_class, int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label)
{
return NULL;
};
extern FL_OBJECT *websrv_fl_add_canvas(int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label);
FL_EXPORT FL_OBJECT *fl_create_canvas(int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label)
{
return NULL;
};
/* backward compatibility */
#define fl_set_canvas_decoration fl_set_object_boxtype
FL_EXPORT void fl_set_canvas_colormap(FL_OBJECT *ob, Colormap colormap){};
FL_EXPORT void fl_set_canvas_visual(FL_OBJECT *obj, Visual *vi){};
FL_EXPORT void fl_set_canvas_depth(FL_OBJECT *obj, int depth){};
FL_EXPORT void fl_set_canvas_attributes(FL_OBJECT *ob, unsigned int mask, XSetWindowAttributes *xswa){};
FL_EXPORT FL_HANDLE_CANVAS fl_add_canvas_handler(FL_OBJECT *ob, int ev, FL_HANDLE_CANVAS h, void *udata)
{
return NULL;
};
FL_EXPORT Window fl_get_canvas_id(FL_OBJECT *ob)
{
return NULL;
};
FL_EXPORT Colormap fl_get_canvas_colormap(FL_OBJECT *ob)
{
return NULL;
};
FL_EXPORT int fl_get_canvas_depth(FL_OBJECT *obj)
{
return 0;
};
FL_EXPORT void fl_remove_canvas_handler(FL_OBJECT *ob, int ev, FL_HANDLE_CANVAS h){};
FL_EXPORT void fl_share_canvas_colormap(FL_OBJECT *ob, Colormap colormap){};
FL_EXPORT void fl_clear_canvas(FL_OBJECT *ob){};
FL_EXPORT void fl_modify_canvas_prop(FL_OBJECT *obj, FL_MODIFY_CANVAS_PROP init, FL_MODIFY_CANVAS_PROP activate, FL_MODIFY_CANVAS_PROP cleanup){};
FL_EXPORT void fl_canvas_yield_to_shortcut(FL_OBJECT *ob, int yes){};
/* This is an attempt to maintain some sort of backwards compatibility
* with old code whilst also getting rid of the old, system-specific
* hack. */
#ifdef AUTOINCLUDE_GLCANVAS_H
#include <glcanvas.h>
#endif
#endif /* ! defined FL_CANVAS_H */
/**
* \file text.h
*/
#ifndef FL_TEXT_H
#define FL_TEXT_H
enum { FL_NORMAL_TEXT };
#define FL_TEXT_BOXTYPE FL_FLAT_BOX
#define FL_TEXT_COL1 FL_COL1
#define FL_TEXT_COL2 FL_MCOL
#define FL_TEXT_LCOL FL_LCOL
#define FL_TEXT_ALIGN (FL_ALIGN_LEFT | FL_ALIGN_INSIDE)
FL_EXPORT FL_OBJECT *fl_create_text(int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label)
{
return NULL;
};
FL_EXPORT FL_OBJECT *fl_add_text(int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label)
{
return NULL;
};
#endif /* ! defined FL_TEXT_H */
/**
* \file xyplot.h
*/
#ifndef FL_XYPLOT_H
#define FL_XYPLOT_H
/* Class FL_XYPLOT */
typedef enum {
FL_NORMAL_XYPLOT, /* solid line */
FL_SQUARE_XYPLOT, /* with added square */
FL_CIRCLE_XYPLOT, /* with added circle */
FL_FILL_XYPLOT, /* fill completely */
FL_POINTS_XYPLOT, /* only data points */
FL_DASHED_XYPLOT, /* dashed line */
FL_IMPULSE_XYPLOT,
FL_ACTIVE_XYPLOT, /* accepts interactive manipulations */
FL_EMPTY_XYPLOT,
FL_DOTTED_XYPLOT,
FL_DOTDASHED_XYPLOT,
FL_LONGDASHED_XYPLOT,
FL_LINEPOINTS_XYPLOT /* line & points */
} FL_XYPLOT_TYPE;
enum { FL_LINEAR, FL_LOG };
enum { FL_GRID_NONE = 0, FL_GRID_MAJOR = 1, FL_GRID_MINOR = 2 };
/***** Defaults *****/
#define FL_XYPLOT_BOXTYPE FL_FLAT_BOX
#define FL_XYPLOT_COL1 FL_COL1
#define FL_XYPLOT_LCOL FL_LCOL
#define FL_XYPLOT_ALIGN FL_ALIGN_BOTTOM
#define FL_MAX_XYPLOTOVERLAY 32
/***** Others *****/
FL_EXPORT FL_OBJECT *fl_create_xyplot(int t, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label)
{
return NULL;
};
extern FL_OBJECT *websrv_fl_add_xyplot(int t, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label);
FL_EXPORT void fl_set_xyplot_data(FL_OBJECT *ob, float *x, float *y, int n, const char *title, const char *xlabel, const char *ylabel){};
FL_EXPORT void fl_set_xyplot_data_double(FL_OBJECT *ob, double *x, double *y, int n, const char *title, const char *xlabel, const char *ylabel){};
FL_EXPORT int fl_set_xyplot_file(FL_OBJECT *ob, const char *f, const char *title, const char *xl, const char *yl)
{
return 0;
};
FL_EXPORT void fl_insert_xyplot_data(FL_OBJECT *ob, int id, int n, double x, double y){};
#define fl_set_xyplot_datafile fl_set_xyplot_file
FL_EXPORT void fl_add_xyplot_text(FL_OBJECT *ob, double x, double y, const char *text, int al, FL_COLOR col){};
FL_EXPORT void fl_delete_xyplot_text(FL_OBJECT *ob, const char *text){};
FL_EXPORT int fl_set_xyplot_maxoverlays(FL_OBJECT *ob, int maxover)
{
return 0;
};
FL_EXPORT void fl_add_xyplot_overlay(FL_OBJECT *ob, int id, float *x, float *y, int n, FL_COLOR col)
{
return;
};
FL_EXPORT void fl_set_xyplot_xbounds(FL_OBJECT *ob, double xmin, double xmax){};
FL_EXPORT void fl_set_xyplot_ybounds(FL_OBJECT *ob, double ymin, double ymax){};
FL_EXPORT void fl_get_xyplot_xbounds(FL_OBJECT *ob, float *xmin, float *xmax){};
FL_EXPORT void fl_get_xyplot_ybounds(FL_OBJECT *ob, float *ymin, float *ymax){};
FL_EXPORT void fl_get_xyplot(FL_OBJECT *ob, float *x, float *y, int *i){};
FL_EXPORT int fl_get_xyplot_data_size(FL_OBJECT *obj)
{
return 0;
};
extern void websrv_fl_get_xyplot_data(FL_OBJECT *ob, float *x, float *y, int *n);
extern void websrv_fl_get_xyplot_data_pointer(FL_OBJECT *ob, int id, float **x, float **y, int *n);
FL_EXPORT void fl_set_xyplot_xgrid(FL_OBJECT *ob, int xgrid){};
typedef void (*FL_XYPLOT_SYMBOL)(FL_OBJECT *, int, FL_POINT *, int, int, int);
FL_EXPORT FL_XYPLOT_SYMBOL fl_set_xyplot_symbol(FL_OBJECT *ob, int id, FL_XYPLOT_SYMBOL symbol)
{
return NULL;
};
#endif /* FL_XYPLOT_H */
/*----------------------------------------------------------------------*/
/* new functions for interfacing with webserver */
extern int websrv_nf_getdata(FL_OBJECT *graph, int layer, websrv_scopedata_msg_t **msg);
extern void websrv_free_xyplot(FL_OBJECT *obj);
#if defined __cplusplus
}
#endif
#endif /* FL_FORMS_H */
/*
* 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_BIT && (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_BIT && (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)
{
clear_softmodem_optmask(SOFTMODEM_DOSCOPE_BIT);
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_BIT) {
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_BIT) {
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
set_softmodem_optmask(SOFTMODEM_DOSCOPE_BIT); // to trigger data copy in scope buffers
}
httpstatus = 200;
} else if (strcmp(vval, "stop") == 0) {
scope_params.statusmask &= ~SCOPE_STATUSMASK_STARTED;
clear_softmodem_optmask(SOFTMODEM_DOSCOPE_BIT);
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_BIT && 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_BIT && 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_BIT | IS_SOFTMODEM_4GUE_BIT) {
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;
}
/*
* 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_utils.c
* \brief: utility functions for all websrv back-end sources
* \author Francois TABURET
* \date 2022
* \version 0.1
* \company NOKIA BellLabs France
* \email: francois.taburet@nokia-bell-labs.com
* \note
* \warning
*/
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdbool.h>
#include "common/utils/LOG/log.h"
#include <libgen.h>
#include <jansson.h>
#include <ulfius.h>
/* websrv_printf_t is an internal structure storing messages while processing a request */
/* The meaage is used to fill a response body */
typedef struct {
pthread_mutex_t mutex; // protect the message betwween the websrv_printf_start and websrv_print_end calls
struct _u_response *response; // the ulfius response structure, used to send the message
char *buff; // a buffer to store the message, allocated in websrv_printf_start, free in websrv_print_end
int buffsize; //
char *buffptr; // pointer to free portion of buff
bool async; // the buffer will be used to print from another thread than the callback (command pushed in a tpool queue)
} websrv_printf_t;
static websrv_printf_t websrv_printf_buff;
/*--------------------- functions for help ------------------------*/
/* possibly build a help file to be downloaded by the frontend */
void websrv_utils_build_hlpfile(char *fname)
{
char *info = NULL;
if (strstr(fname, "core.html") != NULL) {
long number_of_processors = sysconf(_SC_NPROCESSORS_ONLN);
if (number_of_processors > 0) {
char *pinfo = malloc(255);
snprintf(pinfo, 254, "Available cores on this system: 0 to %d", (int)number_of_processors - 1);
info = pinfo;
}
}
if (info != NULL) {
FILE *f = fopen(fname, "w");
if (f == NULL) {
LOG_I(UTIL, "[websrv]: Help file %s couldn't be created\n", fname);
} else {
fprintf(f, "%s", info);
fclose(f);
}
free(info);
}
}
void websrv_utils_build_hlpfiles(char *path)
{
char fname[255];
snprintf(fname, sizeof(fname), "%s/helpfiles/softmodem_show_threadsched_core.html", path);
websrv_utils_build_hlpfile(fname);
}
int websrv_utils_testhlp(char *dir, char *module, char *cmdtitle, char *coltitle)
{
char fname[255];
snprintf(fname, sizeof(fname) - 1, "%s/helpfiles/%s_%s_%s.html", dir, module, cmdtitle, coltitle);
for (int i = 0; i < strlen(fname); i++) {
if (fname[i] == ' ')
fname[i] = '_';
}
int st = access(fname, R_OK);
return ((st == 0) ? 1 : 0);
}
/*-----------------------------------------------------------*/
/* functions http request/response helpers */
/* dump a json objects */
void websrv_printjson(char *label, json_t *jsonobj, int dbglvl)
{
if (dbglvl > 0) {
char *jstr = json_dumps(jsonobj, 0);
LOG_I(UTIL, "[websrv] %s:%s\n", label, (jstr == NULL) ? "??\n" : jstr);
free(jstr);
}
}
/*------------------------------------------------------------------------------*/
/* dump a request */
void websrv_dump_request(char *label, const struct _u_request *request, int dbglvl)
{
if (dbglvl > 0) {
LOG_I(UTIL, "[websrv] %s, request %s, proto %s, verb %s, path %s\n", label, request->http_url, request->http_protocol, request->http_verb, request->http_verb);
if (request->map_post_body != NULL)
for (int i = 0; i < u_map_count(request->map_post_body); i++)
LOG_I(UTIL, "[websrv] POST parameter %i %s : %s\n", i, u_map_enum_keys(request->map_post_body)[i], u_map_enum_values(request->map_post_body)[i]);
if (request->map_cookie != NULL)
for (int i = 0; i < u_map_count(request->map_cookie); i++)
LOG_I(UTIL, "[websrv] cookie variable %i %s : %s\n", i, u_map_enum_keys(request->map_cookie)[i], u_map_enum_values(request->map_cookie)[i]);
if (request->map_header != NULL)
for (int i = 0; i < u_map_count(request->map_header); i++)
LOG_I(UTIL, "[websrv] header variable %i %s : %s\n", i, u_map_enum_keys(request->map_header)[i], u_map_enum_values(request->map_header)[i]);
if (request->map_url != NULL)
for (int i = 0; i < u_map_count(request->map_url); i++)
LOG_I(UTIL, "[websrv] url variable %i %s : %s\n", i, u_map_enum_keys(request->map_url)[i], u_map_enum_values(request->map_url)[i]);
}
}
/*-----------------------------------*/
/* build a json body in a response */
void websrv_jbody(struct _u_response *response, json_t *jbody, int httpstatus, int dbglvl)
{
websrv_printjson((char *)__FUNCTION__, jbody, dbglvl);
int us = ulfius_add_header_to_response(response, "content-type", "application/json");
if (us != U_OK) {
ulfius_set_string_body_response(response, 500, "Internal server error (ulfius_add_header_to_response)");
LOG_E(UTIL, "[websrv] cannot set response header type ulfius error %d \n", us);
}
us = ulfius_set_json_body_response(response, httpstatus, jbody);
if (us != U_OK) {
ulfius_set_string_body_response(response, 500, "Internal server error (ulfius_set_json_body_response)");
LOG_E(UTIL, "[websrv] cannot set body response ulfius error %d \n", us);
}
return;
}
/*----------------------------------------------------------------*/
/* format a json string array in a response body */
int websrv_string_response(char *astring, struct _u_response *response, int httpstatus, int dbglvl)
{
json_t *jstr = json_array();
char *tokctx;
char *aline = strtok_r(astring, "\n", &tokctx);
while (aline != NULL) {
json_t *jline = json_string(aline);
json_array_append_new(jstr, jline);
aline = strtok_r(NULL, "\n", &tokctx);
}
json_t *jbody = json_pack("{s:o}", "display", jstr);
websrv_jbody(response, jbody, httpstatus, dbglvl);
return 0;
}
/*----------------------------------------------------------------------------------*/
/* set of calls to fill a buffer with a string and use this buffer in a response */
void websrv_printf_start(struct _u_response *response, int buffsize, bool async)
{
pthread_mutex_lock(&(websrv_printf_buff.mutex));
websrv_printf_buff.buff = malloc(buffsize);
websrv_printf_buff.buffptr = websrv_printf_buff.buff;
websrv_printf_buff.buffsize = buffsize;
websrv_printf_buff.response = response;
websrv_printf_buff.async = async;
}
void websrv_printf_atpos(int pos, const char *message, ...)
{
va_list va_args;
va_start(va_args, message);
websrv_printf_buff.buffptr = websrv_printf_buff.buff + pos + vsnprintf(websrv_printf_buff.buff + pos, websrv_printf_buff.buffsize - pos - 1, message, va_args);
va_end(va_args);
return;
}
void websrv_printf(const char *message, ...)
{
va_list va_args;
va_start(va_args, message);
websrv_printf_buff.buffptr += vsnprintf(websrv_printf_buff.buffptr, websrv_printf_buff.buffsize - (websrv_printf_buff.buffptr - websrv_printf_buff.buff) - 1, message, va_args);
va_end(va_args);
return;
}
void websrv_printf_end(int httpstatus, int dbglvl)
{
int count = 0;
while (__atomic_load_n(&(websrv_printf_buff.async), __ATOMIC_SEQ_CST)) {
usleep(10);
if (count == 99) {
websrv_printf("No response from server...");
break;
}
}
if (httpstatus >= 200 && httpstatus < 300) {
LOG_I(UTIL, "[websrv] %s\n", websrv_printf_buff.buff);
websrv_string_response(websrv_printf_buff.buff, websrv_printf_buff.response, httpstatus, dbglvl);
} else if (httpstatus < 1000) {
LOG_W(UTIL, "[websrv] %s\n", websrv_printf_buff.buff);
ulfius_set_binary_body_response(websrv_printf_buff.response, httpstatus, websrv_printf_buff.buff, websrv_printf_buff.buffptr - websrv_printf_buff.buff);
}
free(websrv_printf_buff.buff);
websrv_printf_buff.buff = NULL;
websrv_printf_buff.async = false;
pthread_mutex_unlock(&(websrv_printf_buff.mutex));
}
void websrv_async_printf(const char *message, ...)
{
if (__atomic_load_n(&(websrv_printf_buff.async), __ATOMIC_SEQ_CST)) {
va_list va_args;
va_start(va_args, message);
websrv_printf_buff.buffptr += vsnprintf(websrv_printf_buff.buff, websrv_printf_buff.buffsize - 1, message, va_args);
va_end(va_args);
__atomic_store_n(&(websrv_printf_buff.async), 0, __ATOMIC_SEQ_CST);
} else {
LOG_W(UTIL, "[websrv], delayed websrv_async_printf skipped\n");
}
return;
}
/* */
/*----------------------------------------------------------------------------------------------------------*/
/* callbacks and utility functions to stream a file */
char *websrv_read_file(const char *filename)
{
char *buffer = NULL;
long length;
struct stat statbuf;
FILE *f = fopen(filename, "rb");
int st = stat(filename, &statbuf);
if (f != NULL && st == 0) {
length = statbuf.st_size;
buffer = malloc(length + 1);
if (buffer != NULL) {
int rlen = fread(buffer, 1, length, f);
if (rlen != length) {
free(buffer);
LOG_E(UTIL, "[websrv] couldn't read %s_\n", filename);
fclose(f);
return NULL;
}
buffer[length] = '\0';
}
} else {
LOG_E(UTIL, "[websrv] error accessing %s: %s\n", filename, strerror(errno));
}
if (f != NULL)
fclose(f);
return buffer;
}
/* callbacks to send static streams */
static ssize_t callback_stream(void *cls, uint64_t pos, char *buf, size_t max)
{
if (cls != NULL) {
return fread(buf, sizeof(char), max, (FILE *)cls);
} else {
return U_STREAM_END;
}
}
static void callback_stream_free(void *cls)
{
if (cls != NULL) {
fclose((FILE *)cls);
}
}
FILE *websrv_getfile(char *filename, struct _u_response *response)
{
FILE *f = fopen(filename, "rb");
int length;
if (f) {
fseek(f, 0, SEEK_END);
length = ftell(f);
fseek(f, 0, SEEK_SET);
LOG_I(UTIL, "[websrv] sending %d bytes from %s\n", length, filename);
} else {
LOG_E(UTIL, "[websrv] couldn't open %s\n", filename);
return NULL;
}
char *content_type = "text/html";
size_t nl = strlen(filename);
if (nl >= 3 && !strcmp(filename + nl - 3, "css"))
content_type = "text/css";
if (nl >= 2 && !strcmp(filename + nl - 2, "js"))
content_type = "text/javascript";
int ust = ulfius_add_header_to_response(response, "content-type", content_type);
if (ust != U_OK) {
ulfius_set_string_body_response(response, 501, "Internal server error (ulfius_add_header_to_response)");
LOG_E(UTIL, "[websrv] cannot set response header type ulfius error %d \n", ust);
fclose(f);
return NULL;
}
ust = ulfius_set_stream_response(response, 200, callback_stream, callback_stream_free, length, 1024, f);
if (ust != U_OK) {
LOG_E(UTIL, "[websrv] ulfius_set_stream_response error %d\n", ust);
fclose(f);
return NULL;
}
return f;
}
/* */
/*
* 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_websockets.c
* \brief: implementation of web/websockets API
* \author Francois TABURET
* \date 2022
* \version 0.1
* \company NOKIA BellLabs France
* \email: francois.taburet@nokia-bell-labs.com
* \note
* \warning
*/
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
#include <arpa/inet.h>
#include <errno.h>
#include <jansson.h>
#include <ulfius.h>
#include "common/utils/LOG/log.h"
#include "common/utils/websrv/websrv.h"
#include "time.h"
void websrv_websocket_send_message(char msg_src, char msg_type, char *msg_data, struct _websocket_manager *websocket_manager)
{
websrv_msg_t msg;
int st;
msg.header.src = msg_src;
msg.header.msgtype = msg_type;
sprintf(msg.data, "%s", msg_data);
int len = strlen(msg.data) + WEBSOCK_HEADSIZE;
st = ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_BINARY, len, (char *)&msg);
if (st != U_OK)
LOG_I(UTIL, "Error sending scope message, length %i status %i\n", len, st);
}
/* websocket callbacks as set in callback_websocket, the initial url endpoint which triggers the websocket init */
/* function executed by ulfius when websocket is closed */
void websrv_websocket_onclose_callback(const struct _u_request *request, struct _websocket_manager *websocket_manager, void *websocket_onclose_user_data)
{
websrv_params_t *websrvparams = (websrv_params_t *)websocket_onclose_user_data;
websrv_dump_request("websocket close ", request, websrvparams->dbglvl);
websrvparams->wm = NULL;
websrv_scope_stop();
}
void websrv_close_ws(websrv_params_t *websrvparams, char *source)
{
LOG_W(UTIL, "[websrv] Websocket re-init, from %s....\n", source);
ulfius_websocket_send_close_signal(websrvparams->wm);
}
/* function executed by ulfius in a dedicated thread, should not terminate while client connection is up */
void websrv_websocket_manager_callback(const struct _u_request *request, struct _websocket_manager *websocket_manager, void *websocket_manager_user_data)
{
websrv_params_t *websrvparams = (websrv_params_t *)websocket_manager_user_data;
websrv_dump_request("websocket manager ", request, websrvparams->dbglvl);
while (websrvparams->wm != NULL) {
LOG_I(UTIL, "[websrv] Waitting a previous websocket instance to close...\n");
usleep(100000);
}
websrvparams->wm = websocket_manager;
uint64_t lcount = 0;
int st = U_WEBSOCKET_STATUS_OPEN;
while (st == U_WEBSOCKET_STATUS_OPEN) {
st = ulfius_websocket_status(websocket_manager /* ms */);
if (st == U_WEBSOCKET_STATUS_OPEN) {
if (websrv_scope_manager(lcount, websrvparams) < 0) {
websrv_close_ws(websrvparams, "ws scope manager");
};
lcount++;
} else if (st == U_WEBSOCKET_STATUS_ERROR) {
LOG_I(UTIL, "[websrv] manager: Websocket error, errno %s\n", strerror(errno));
break;
} else {
LOG_I(UTIL, "[websrv] manager: Websocket has been closed\n");
break;
}
usleep(100000);
} /* while */
LOG_I(UTIL, "Closing websocket_manager_callback...\n");
}
void websrv_websocket_incoming_message_callback(const struct _u_request *request,
struct _websocket_manager *websocket_manager,
const struct _websocket_message *last_message,
void *websocket_incoming_message_user_data)
{
LOG_I(UTIL, "Incoming message, opcode: 0x%02x, mask: %d, len: %zu\n", last_message->opcode, last_message->has_mask, last_message->data_len);
websrv_params_t *websrvparams = (websrv_params_t *)websocket_incoming_message_user_data;
if (websrvparams->wm != NULL && websrvparams->wm != websocket_manager) {
websrv_close_ws(websrvparams, "ws incoming message callback");
}
if (last_message->opcode == U_WEBSOCKET_OPCODE_TEXT) {
LOG_I(UTIL, "[websrv] text payload '%.*s'", (int)last_message->data_len, last_message->data);
} else if (last_message->opcode == U_WEBSOCKET_OPCODE_BINARY) {
websrv_msg_t *msg = (websrv_msg_t *)last_message->data;
LOG_I(UTIL, "[websrv] binary payload from %c type %i\n", msg->header.src, (int)msg->header.msgtype);
switch (msg->header.src) {
case 's':
websrv_websocket_process_scopemessage(msg->header.msgtype, msg->data, websocket_manager);
break;
default:
LOG_W(UTIL, "[websrv] Unknown message source: %c\n", msg->header.src);
break;
}
} else if (last_message->opcode == U_WEBSOCKET_OPCODE_CLOSE) {
LOG_I(UTIL, "[websrv] websocket closed\n");
} else {
LOG_I(UTIL, "[websrv] message ignored\n");
}
}
/**
* callback function, called when the url corresponding to the endpoint set in
* websrv_init_websocket is requested. that simply set the websocket callbacks
*/
int websrv_callback_websocket(const struct _u_request *request, struct _u_response *response, void *user_data)
{
int ret;
websrv_params_t *websrvparams = (websrv_params_t *)user_data;
websrv_dump_request("websocket ", request, websrvparams->dbglvl);
if (websrvparams->wm != NULL) {
websrv_close_ws(websrvparams, "ws url callback");
}
if ((ret = ulfius_set_websocket_response(
response, NULL, NULL, websrv_websocket_manager_callback, user_data, websrv_websocket_incoming_message_callback, user_data, websrv_websocket_onclose_callback, user_data))
== U_OK) {
return U_CALLBACK_COMPLETE;
} else {
return U_CALLBACK_ERROR;
}
}
int websrv_init_websocket(websrv_params_t *websrvparams, char *module)
{
int status = ulfius_add_endpoint_by_val(&(websrvparams->instance), "GET", NULL, module, 1, &websrv_callback_websocket, websrvparams);
return status;
}
......@@ -61,6 +61,12 @@ uint64_t set_softmodem_optmask(uint64_t bitmask) {
return softmodem_params.optmask;
}
uint64_t clear_softmodem_optmask(uint64_t bitmask)
{
softmodem_params.optmask = softmodem_params.optmask & (~bitmask);
return softmodem_params.optmask;
}
softmodem_params_t *get_softmodem_params(void) {
return &softmodem_params;
}
......@@ -89,6 +95,7 @@ void get_common_options(uint32_t execmask) {
uint32_t online_log_messages=0;
uint32_t glog_level=0 ;
uint32_t start_telnetsrv = 0, start_telnetclt = 0;
uint32_t start_websrv = 0;
uint32_t noS1 = 0, nokrnmod = 1, nonbiot = 0;
uint32_t rfsim = 0, do_forms = 0;
char *logmem_filename = NULL;
......@@ -146,6 +153,10 @@ void get_common_options(uint32_t execmask) {
set_softmodem_optmask(SOFTMODEM_DOSCOPE_BIT);
}
if (start_websrv) {
load_module_shlib("websrv", NULL, 0, NULL);
}
if(parallel_config != NULL) set_parallel_conf(parallel_config);
if(worker_config != NULL) set_worker_conf(worker_config);
......
......@@ -178,21 +178,21 @@ extern int usrp_tx_thread;
#define CONFIG_HLP_FLOG "Enable online log \n"
#define CONFIG_HLP_LOGL "Set the global log level, valid options: (4:trace, 3:debug, 2:info, 1:warn, (0:error))\n"
#define CONFIG_HLP_TELN "Start embedded telnet server \n"
#define CONFIG_HLP_WEB "Start embedded web server \n"
#define CONFIG_FLOG_OPT "R"
#define CONFIG_LOGL_OPT "g"
/*-------------------------------------------------------------------------------------------------------------------------------------------------*/
/* command line parameters for LOG utility */
/* optname helpstr paramflags XXXptr defXXXval type numelt */
/*-------------------------------------------------------------------------------------------------------------------------------------------------*/
#define CMDLINE_LOGPARAMS_DESC { \
{CONFIG_FLOG_OPT , CONFIG_HLP_FLOG, 0, uptr:&online_log_messages, defintval:1, TYPE_INT, 0}, \
{CONFIG_LOGL_OPT , CONFIG_HLP_LOGL, 0, uptr:&glog_level, defintval:0, TYPE_UINT, 0}, \
{"telnetsrv", CONFIG_HLP_TELN, PARAMFLAG_BOOL, uptr:&start_telnetsrv, defintval:0, TYPE_UINT, 0}, \
{"log-mem", NULL, 0, strptr:&logmem_filename, defstrval:NULL, TYPE_STRING, 0}, \
{"telnetclt", NULL, 0, uptr:&start_telnetclt, defstrval:NULL, TYPE_UINT, 0}, \
#define CMDLINE_LOGPARAMS_DESC \
{ \
{CONFIG_FLOG_OPT, CONFIG_HLP_FLOG, 0, uptr : &online_log_messages, defintval : 1, TYPE_INT, 0}, {CONFIG_LOGL_OPT, CONFIG_HLP_LOGL, 0, uptr : &glog_level, defintval : 0, TYPE_UINT, 0}, \
{"telnetsrv", CONFIG_HLP_TELN, PARAMFLAG_BOOL | PARAMFLAG_CMDLINEONLY, uptr : &start_telnetsrv, defintval : 0, TYPE_UINT, 0}, \
{"websrv", CONFIG_HLP_WEB, PARAMFLAG_BOOL | PARAMFLAG_CMDLINEONLY, uptr : &start_websrv, defintval : 0, TYPE_UINT, 0}, \
{"log-mem", NULL, 0, strptr : &logmem_filename, defstrval : NULL, TYPE_STRING, 0}, {"telnetclt", NULL, 0, uptr : &start_telnetclt, defstrval : NULL, TYPE_UINT, 0}, \
}
/* check function for global log level */
#define CMDLINE_LOGPARAMS_CHECK_DESC { \
{ .s5= {NULL} } , \
......@@ -272,6 +272,7 @@ typedef struct {
extern uint64_t get_softmodem_optmask(void);
extern uint64_t set_softmodem_optmask(uint64_t bitmask);
extern uint64_t clear_softmodem_optmask(uint64_t bitmask);
extern softmodem_params_t *get_softmodem_params(void);
extern void get_common_options(uint32_t execmask);
extern char *get_softmodem_function(uint64_t *sofmodemfunc_mask_ptr);
......
......@@ -41,13 +41,10 @@
static int coding_setmod_cmd(char *buff, int debug, telnet_printfunc_t prnt);
static telnetshell_cmddef_t coding_cmdarray[] = {
{"mode","[sse,avx2,stdc,none]",coding_setmod_cmd},
{"","",NULL},
};
telnetshell_vardef_t coding_vardef[] = {
{"maxiter",TELNET_VARTYPE_INT32,&max_turbo_iterations},
{"",0,NULL}
{"mode", "[sse,avx2,stdc,none]", coding_setmod_cmd, {NULL}, 0, NULL},
{"", "", NULL, {NULL}, 0, NULL},
};
telnetshell_vardef_t coding_vardef[] = {{"maxiter", TELNET_VARTYPE_INT32, 0, &max_turbo_iterations}, {"", 0, 0, NULL}};
/* PHY/defs.h contains MODE_DECODE_XXX macros, following table must match */
static char *modedesc[] = {"none","sse","C","avx2"};
static int curmode;
......
......@@ -24,64 +24,27 @@
/* Form definition file generated by fdesign */
#include <stdlib.h>
#include "nr_phy_scope.h"
#include "executables/softmodem-common.h"
#include "executables/nr-softmodem-common.h"
#ifndef WEBSRVSCOPE
#include <forms.h>
#define TPUT_WINDOW_LENGTH 100
#define ScaleZone 4
#define localBuff(NaMe,SiZe) float NaMe[SiZe]; memset(NaMe,0,sizeof(NaMe));
#define STATICFORXSCOPE static
static const int scope_enb_num_ue = 1;
#else
#include <ulfius.h>
#include "common/utils/websrv/websrv.h"
#include "common/utils/websrv/websrv_noforms.h"
#define STATICFORXSCOPE
#define fl_add_canvas websrv_fl_add_canvas
#define fl_add_xyplot websrv_fl_add_xyplot
#define fl_get_xyplot_data_pointer websrv_fl_get_xyplot_data_pointer
#endif
#include "nr_phy_scope.h"
#include "phy_scope.h"
const FL_COLOR rx_antenna_colors[4] = {FL_RED,FL_BLUE,FL_GREEN,FL_YELLOW};
const FL_COLOR water_colors[4] = {FL_BLUE,FL_GREEN,FL_YELLOW,FL_RED};
typedef c16_t scopeSample_t;
#define SquaredNorm(VaR) ((VaR).r*(VaR).r+(VaR).i*(VaR).i)
typedef struct {
int dataSize;
int elementSz;
int colSz;
int lineSz;
} scopeGraphData_t;
typedef struct OAIgraph {
FL_OBJECT *graph;
FL_OBJECT *text;
float maxX;
float maxY;
float minX;
float minY;
int x;
int y;
int w;
int h;
int waterFallh;
double *waterFallAvg;
bool initDone;
int iteration;
void (*gNBfunct) (struct OAIgraph *graph, scopeData_t *p, int UE_id);
void (*nrUEfunct)(scopeGraphData_t **data, struct OAIgraph *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id);
} OAIgraph_t;
/* Forms and Objects */
typedef struct {
FL_FORM *phy_scope;
OAIgraph_t graph[20];
FL_OBJECT *button_0;
} OAI_phy_scope_t;
typedef struct {
FL_FORM *stats_form;
void *vdata;
char *cdata;
long ldata;
FL_OBJECT *stats_text;
FL_OBJECT *stats_button;
} FD_stats_form;
static void drawsymbol(FL_OBJECT *obj, int id,
FL_POINT *p, int n, int w, int h) {
fl_points( p, n, FL_YELLOW);
......@@ -103,7 +66,130 @@ static void dl_traffic_on_off( FL_OBJECT *button, long arg) {
#endif
#define WATERFALL 10000
#ifdef WEBSRVSCOPE
/* copy data from softmodem buffer to message body */
int websrv_cpiqbuff_tomsg(OAIgraph_t *graph, scopeSample_t *c, int n, int id, int base)
{
int I = 0;
websrv_scope_params_t *WP = websrv_scope_getparams();
int newn = n;
websrv_scopedata_msg_t *msg;
websrv_nf_getdata(graph->graph, id, &msg);
if (n > MAX_NIQ_WEBSOCKMSG) {
LOG_E(UTIL, "Buffer id %i too small for %i iqs...\n", id, n);
return 0;
}
/* copy and sort the chart data on x to improve frontend perf */
int16_t *data_xy = msg->data_xy + base;
int16_t max_x = INT16_MIN;
for (int i = 0; i < n; i++) {
if (c[i].r < WP->xmin || c[i].i < WP->ymin || c[i].r > WP->xmax || c[i].i > WP->ymax) {
newn--;
continue;
}
if (max_x <= c[i].r) {
data_xy[I] = max_x = c[i].r;
data_xy[I + 1] = c[i].i;
I = I + 2;
} else {
for (int j = I + (base * 2); j >= 0; j = j - 2) {
if (msg->data_xy[j] <= c[i].r || j == 0) {
for (int k = I + (base * 2); k > j; k = k - 2) {
msg->data_xy[k + 2] = msg->data_xy[k];
msg->data_xy[k + 3] = msg->data_xy[k + 1];
}
msg->data_xy[j + 2] = c[i].r;
msg->data_xy[j + 3] = c[i].i;
I = I + 2;
break;
}
}
}
}
return newn;
}
int websrv_cpllrbuff_tomsg(OAIgraph_t *graph, int16_t *llrs, int n, int id, int iteration, int max_iteration)
{
websrv_scopedata_msg_t *msg;
websrv_nf_getdata(graph->graph, id, &msg);
websrv_scope_params_t *WP = websrv_scope_getparams();
if (n > MAX_LLR_WEBSOCKMSG) {
LOG_E(UTIL, "Buffer id %i too small for %i iqs...\n", id, n);
return 0;
}
/* save number of points (xmax) at beginning of buffer, as we try to minimize data sent */
/* all iterations (UE pdschh case) have same n (number of data points) */
if (iteration == 0) {
int32_t *iptr = (int32_t *)(msg->data_xy);
*iptr = n;
}
/* for each point we save llr and the corresponding offset if llr's below the configured threshold have been skipped */
/* offset is saved in 8bits so llr always transmitted when offset reach the 8 bits max. offset relative to x of previously transmitted point */
int16_t *dptr = msg->data_xy;
int newn = 2; // 2 first int16 used for saving maxx
int xoffset = 1;
int xres = (n / 1000 > CHAR_MAX) ? CHAR_MAX : (n / 1000);
int16_t latestllr = 0;
int imin = WP->llrxmin;
int imax = WP->llrxmax;
if (imax > n)
imax = n;
if (imin > imax)
imin = imax;
/* to keep data sorted, we insert points of each iteration at their x position, */
for (int i = imin; i < imax; i++) {
if (((llrs[i] - latestllr) >= WP->llr_ythresh) || ((latestllr - llrs[i]) >= WP->llr_ythresh) || (xoffset >= xres)) {
dptr[newn + iteration] = llrs[i];
latestllr = llrs[i];
dptr[newn + iteration + 1] = (int16_t)xoffset;
xoffset = 1;
newn = newn + max_iteration + 2;
} else {
xoffset++;
}
}
/* discard points which cannot be distinguished on graph: x and llr have almost identical values
for ( int i=4; i<(newn-1); i=i+2) {
for (int j=i+2 ; j<(i+(xres*2)) && j<(newn-1); j=j+2) {
if ( ((dptr[j] - dptr[i]) < yres) || ((dptr[i] - dptr[j]) < yres) ) {
newn=newn-2;
for (int k = j; k< newn; k++) {
((int8_t *)(msg->data_xy))[k] = (int8_t)msg->data_xy[k+2];
((int8_t *)(msg->data_xy))[k+1] = (int8_t)msg->data_xy[k+3];
}
}
}
}
*/
return newn;
}
void websrv_get_WF_buffers(OAIgraph_t *graph, websrv_scopedata_msg_t *msgp[])
{
for (int i = 0; i < sizeof(water_colors) / sizeof(FL_COLOR); i++) {
websrv_scopedata_msg_t *msg;
websrv_nf_getdata(graph->graph, i, &msg);
msg->header.msgtype = SCOPEMSG_TYPE_DATA;
msg->header.chartid = graph->chartid;
msg->header.datasetid = i;
msg->header.update = (i == (sizeof(water_colors) / sizeof(FL_COLOR) - 1)) ? 1 : 0;
msg->data_xy[0] = 0;
msgp[i] = msg;
}
}
void websrv_setpoint(int x, int y, websrv_scopedata_msg_t *msg)
{
msg->data_xy[0]++;
msg->data_xy[msg->data_xy[0]] = (int16_t)x;
msg->data_xy[0]++;
msg->data_xy[msg->data_xy[0]] = (int16_t)y;
}
#endif
static void commonGraph(OAIgraph_t *graph, int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label, FL_COLOR pointColor) {
if (type==WATERFALL) {
graph->waterFallh=h-15;
......@@ -136,6 +222,11 @@ static void commonGraph(OAIgraph_t *graph, int type, FL_Coord x, FL_Coord y, FL_
graph->minY=0;
graph->initDone=false;
graph->iteration=0;
#ifdef WEBSRVSCOPE
graph->enabled = false;
#else
graph->enabled = true;
#endif
}
static OAIgraph_t gNBcommonGraph( void (*funct) (OAIgraph_t *graph, scopeData_t *p, int UE_id),
......@@ -155,7 +246,7 @@ static OAIgraph_t nrUEcommonGraph( void (*funct) (scopeGraphData_t **data, OAIgr
graph.nrUEfunct=funct;
return graph;
}
#ifndef WEBSRVSCOPE
static void setRange(OAIgraph_t *graph, float minX, float maxX, float minY, float maxY) {
if ( maxX > graph->maxX || minX < graph->minX ||
abs(maxX-graph->maxX)>abs(graph->maxX)/2 ||
......@@ -187,7 +278,7 @@ static void oai_xygraph_getbuff(OAIgraph_t *graph, float **x, float **y, int len
fl_get_xyplot_data_pointer(graph->graph, layer, &old_x, &old_y, &old_len);
if (old_len != len) {
LOG_W(HW,"allocating graph of %d scope\n", len);
LOG_W(HW, "allocating graph of %d points\n", len);
float values[len];
float time[len];
......@@ -201,14 +292,26 @@ static void oai_xygraph_getbuff(OAIgraph_t *graph, float **x, float **y, int len
fl_add_xyplot_overlay(graph->graph,layer,time,values,len,rx_antenna_colors[layer]);
fl_get_xyplot_data_pointer(graph->graph, layer, &old_x, &old_y, &old_len);
AssertFatal(old_len==len,"");
AssertFatal(old_len == len, "graph len %i old_len %i\n", len, old_len);
}
*x=old_x;
*y=old_y;
}
#endif
static void oai_xygraph(OAIgraph_t *graph, float *x, float *y, int len, int layer, bool NoAutoScale) {
#ifdef WEBSRVSCOPE
websrv_scopedata_msg_t *msg = NULL;
websrv_nf_getdata(graph->graph, layer, &msg);
msg->header.msgtype = SCOPEMSG_TYPE_DATA;
msg->header.chartid = graph->chartid;
msg->header.datasetid = graph->datasetid;
msg->header.msgseg = 0;
msg->header.update = 1;
websrv_scope_senddata(len, (msg->header.chartid == SCOPEMSG_DATAID_LLR) ? 2 : 4, msg);
#else
fl_redraw_object(graph->graph);
if ( NoAutoScale && graph->iteration%NoAutoScale == 0) {
......@@ -223,14 +326,17 @@ static void oai_xygraph(OAIgraph_t *graph, float *x, float *y, int len, int laye
setRange(graph, minX-5, maxX+5, minY-5, maxY+5);
}
#endif
graph->iteration++;
}
static void genericWaterFall (OAIgraph_t *graph, scopeSample_t *values, const int datasize, const int divisions, const char *label) {
if ( values == NULL )
return;
#ifdef WEBSRVSCOPE
websrv_scopedata_msg_t *msgp[sizeof(water_colors) / sizeof(FL_COLOR)];
websrv_get_WF_buffers(graph, msgp);
#endif
fl_winset(FL_ObjWin(graph->graph));
const int samplesPerPixel=datasize/graph->w;
int displayPart=graph->waterFallh-ScaleZone;
......@@ -266,10 +372,14 @@ static void genericWaterFall (OAIgraph_t *graph, scopeSample_t *values, const in
if (val > avg*100 )
col=3;
#ifdef WEBSRVSCOPE
websrv_setpoint(pix, graph->iteration % displayPart, msgp[col]);
#else
fl_point(pix, graph->iteration%displayPart, water_colors[col]);
#endif
}
#ifndef WEBSRVSCOPE
if (graph->initDone==false) {
for ( int i=0; i < graph->waterFallh; i++ )
for ( int j = 0 ; j < graph->w ; j++ )
......@@ -283,20 +393,40 @@ static void genericWaterFall (OAIgraph_t *graph, scopeSample_t *values, const in
}
fl_set_object_label_f(graph->text, "%s, avg I/Q pow: %4.1f", label, 0/*sqrt(avg)*/);
#else
for (int i = 0; i < sizeof(water_colors) / sizeof(FL_COLOR); i++) {
msgp[i]->header.msgseg = graph->iteration % displayPart;
websrv_scope_senddata(msgp[i]->data_xy[0], 2, msgp[i]);
}
#endif
graph->iteration++;
}
static void genericPowerPerAntena(OAIgraph_t *graph, const int nb_ant, const scopeSample_t **data, const int len) {
#ifndef WEBSRVSCOPE
float *values, *time;
oai_xygraph_getbuff(graph, &time, &values, len, 0);
#else
websrv_scopedata_msg_t *msg = NULL;
websrv_nf_getdata(graph->graph, 0, &msg);
float *values = (float *)msg->data_xy;
#endif
for (int ant=0; ant<nb_ant; ant++) {
if (data[ant] != NULL) {
for (int i=0; i<len; i++) {
values[i] = SquaredNorm(data[ant][i]);
}
#ifndef WEBSRVSCOPE
oai_xygraph(graph,time,values, len, ant, 10);
#else
msg->header.msgtype = SCOPEMSG_TYPE_DATA;
msg->header.chartid = graph->chartid;
msg->header.datasetid = graph->datasetid;
msg->header.msgseg = 0;
msg->header.update = (ant == (nb_ant - 1)) ? 1 : 0;
websrv_scope_senddata(len, 4, msg);
#endif
}
}
}
......@@ -323,12 +453,23 @@ static void timeSignal (OAIgraph_t *graph, PHY_VARS_gNB *phy_vars_gnb, RU_t *phy
*/
static void timeResponse (OAIgraph_t *graph, scopeData_t *p, int nb_UEs) {
const int len=p->gNB->frame_parms.ofdm_symbol_size;
const int len = p->gNB->frame_parms.ofdm_symbol_size;
#ifndef WEBSRVSCOPE
float *values, *time;
oai_xygraph_getbuff(graph, &time, &values, len, 0);
const int ant=0; // display antenna 0 for each UE
#else
websrv_scopedata_msg_t *msg = NULL;
websrv_nf_getdata(graph->graph, 0, &msg);
float *values = (float *)msg->data_xy;
#endif
for (int ue=0; ue<nb_UEs; ue++) {
const int ant = 0; // display antenna 0 for each UE
#ifdef WEBSRVSCOPE
int uestart = nb_UEs - 1; // web scope shows one UE signal, that can be selected from GUI
#else
int uestart = 0; // xforms scope designed to display nb_UEs signals
#endif
for (int ue = uestart; ue < nb_UEs; ue++) {
if ( p->gNB->pusch_vars && p->gNB->pusch_vars[ue] &&
p->gNB->pusch_vars[ue]->ul_ch_estimates_time &&
p->gNB->pusch_vars[ue]->ul_ch_estimates_time[ant] ) {
......@@ -338,8 +479,16 @@ static void timeResponse (OAIgraph_t *graph, scopeData_t *p, int nb_UEs) {
for (int i=0; i<len; i++) {
values[i] = SquaredNorm(data[i]);
}
#ifndef WEBSRVSCOPE
oai_xygraph(graph,time,values, len, ue, 10);
#else
msg->header.msgtype = SCOPEMSG_TYPE_DATA;
msg->header.chartid = graph->chartid;
msg->header.datasetid = graph->datasetid;
msg->header.msgseg = 0;
msg->header.update = 1;
websrv_scope_senddata(len, 4, msg);
#endif
}
}
}
......@@ -363,24 +512,34 @@ static void frequencyResponse (OAIgraph_t *graph, PHY_VARS_gNB *phy_vars_gnb, RU
*/
static void puschLLR (OAIgraph_t *graph, scopeData_t *p, int nb_UEs) {
#ifdef WEBSRVSCOPE
int uestart = nb_UEs - 1; // web scope shows one UE signal, that can be selected from GUI
#else
int uestart = 0; // xforms scope designed to display nb_UEs signals
#endif
NR_DL_FRAME_PARMS *frame_parms=&p->gNB->frame_parms;
int num_re = frame_parms->N_RB_UL*12*frame_parms->symbols_per_slot;
int Qm = 2;
int coded_bits_per_codeword = num_re*Qm;
for (int ue=0; ue<nb_UEs; ue++) {
for (int ue = uestart; ue < nb_UEs; ue++) {
if ( p->gNB->pusch_vars &&
p->gNB->pusch_vars[ue] &&
p->gNB->pusch_vars[ue]->llr ) {
int16_t *pusch_llr = (int16_t *)p->gNB->pusch_vars[ue]->llr;
float *llr, *bit;
int nx = coded_bits_per_codeword;
#ifdef WEBSRVSCOPE
nx = websrv_cpllrbuff_tomsg(graph, pusch_llr, coded_bits_per_codeword, ue, 0, 0);
#else
oai_xygraph_getbuff(graph, &bit, &llr, coded_bits_per_codeword, ue);
for (int i=0; i<coded_bits_per_codeword; i++) {
llr[i] = (float) pusch_llr[i];
}
oai_xygraph(graph,bit,llr,coded_bits_per_codeword,ue,10);
#endif
oai_xygraph(graph, bit, llr, nx, ue, 10);
}
}
}
......@@ -388,22 +547,31 @@ static void puschLLR (OAIgraph_t *graph, scopeData_t *p, int nb_UEs) {
static void puschIQ (OAIgraph_t *graph, scopeData_t *p, int nb_UEs) {
NR_DL_FRAME_PARMS *frame_parms=&p->gNB->frame_parms;
int sz=frame_parms->N_RB_UL*12*frame_parms->symbols_per_slot;
for (int ue=0; ue<nb_UEs; ue++) {
int newsz = sz;
#ifdef WEBSRVSCOPE
int uestart = nb_UEs - 1; // web scope shows one UE signal, that can be selected from GUI
#else
int uestart = 0; // xforms scope designed to display nb_UEs signals
#endif
for (int ue = uestart; ue < nb_UEs; ue++) {
if ( p->gNB->pusch_vars &&
p->gNB->pusch_vars[ue] &&
p->gNB->pusch_vars[ue]->rxdataF_comp &&
p->gNB->pusch_vars[ue]->rxdataF_comp[0] ) {
scopeSample_t *pusch_comp = (scopeSample_t *) p->gNB->pusch_vars[ue]->rxdataF_comp[0];
scopeSample_t *pusch_comp = (scopeSample_t *)p->gNB->pusch_vars[ue]->rxdataF_comp[0];
float *I, *Q;
#ifdef WEBSRVSCOPE
newsz = websrv_cpiqbuff_tomsg(graph, pusch_comp, sz, 0, 0);
#else
oai_xygraph_getbuff(graph, &I, &Q, sz, ue);
for (int k=0; k<sz; k++ ) {
I[k] = pusch_comp[k].r;
Q[k] = pusch_comp[k].i;
}
#endif
oai_xygraph(graph,I,Q,sz,ue,10);
oai_xygraph(graph, I, Q, newsz, ue, 10);
}
}
}
......@@ -452,7 +620,8 @@ static void puschThroughtput (OAIgraph_t *graph, scopeData_t *p, int nb_UEs) {
// fl_set_xyplot_ybounds(form->pusch_tput,0,ymax);
*/
}
static OAI_phy_scope_t *create_phy_scope_gnb(void) {
STATICFORXSCOPE OAI_phy_scope_t *create_phy_scope_gnb(void)
{
FL_OBJECT *obj;
OAI_phy_scope_t *fdui = calloc(( sizeof *fdui ),1);
// Define form
......@@ -465,24 +634,34 @@ static OAI_phy_scope_t *create_phy_scope_gnb(void) {
// Received signal
fdui->graph[0] = gNBcommonGraph( gNBWaterFall, WATERFALL, 0, curY, 400, 100,
"Received Signal (Time-Domain, one frame)", FL_RED );
fdui->graph[0].chartid = SCOPEMSG_DATAID_WF; // tells websrv frontend to use WF chart for displaying
fdui->graph[0].datasetid = 0; // not used for WF
// Time-domain channel response
fdui->graph[1] = gNBcommonGraph( timeResponse, FL_NORMAL_XYPLOT, 410, curY, 400, 100,
"SRS Frequency Response (samples, abs)", FL_RED );
fl_get_object_bbox(fdui->graph[1].graph,&x, &y,&w, &h);
curY+=h;
fdui->graph[1].chartid = SCOPEMSG_DATAID_TRESP;
// Frequency-domain channel response
fdui->graph[2] = gNBcommonGraph( gNBfreqWaterFall, WATERFALL, 0, curY, 800, 100,
"Channel Frequency domain (RE, one frame)", FL_RED );
fl_get_object_bbox(fdui->graph[2].graph,&x, &y,&w, &h);
curY+=h+20;
fdui->graph[2].chartid = SCOPEMSG_DATAID_WF; // tells websrv frontend to use WF chart for displaying
fdui->graph[2].datasetid = 0; // not used for WF
// LLR of PUSCH
fdui->graph[3] = gNBcommonGraph( puschLLR, FL_POINTS_XYPLOT, 0, curY, 500, 200,
"PUSCH Log-Likelihood Ratios (LLR, mag)", FL_YELLOW );
fdui->graph[3].chartid = SCOPEMSG_DATAID_LLR; // tells websrv frontend to use LLR chart for displaying
fdui->graph[3].datasetid = 0; // tells websrv frontend to use dataset index 0 in LLR chart
// I/Q PUSCH comp
fdui->graph[4] = gNBcommonGraph( puschIQ, FL_POINTS_XYPLOT, 500, curY, 300, 200,
"PUSCH I/Q of MF Output", FL_YELLOW );
fl_get_object_bbox(fdui->graph[3].graph,&x, &y,&w, &h);
curY+=h;
fdui->graph[4].chartid = SCOPEMSG_DATAID_IQ; // tells websrv frontend to use constellation chart for displaying
fdui->graph[4].datasetid = 0; // tells websrv frontend to use dataset 0 of constellation chart
// I/Q PUCCH comp (format 1)
fdui->graph[5] = gNBcommonGraph( pucchEnergy, FL_POINTS_XYPLOT, 0, curY, 300, 100,
"PUCCH1 Energy (SR)", FL_YELLOW );
......@@ -492,19 +671,21 @@ static OAI_phy_scope_t *create_phy_scope_gnb(void) {
"PUCCH I/Q of MF Output", FL_YELLOW );
fl_get_object_bbox(fdui->graph[6].graph,&x, &y,&w, &h);
curY+=h;
fdui->graph[6].chartid = SCOPEMSG_DATAID_IQ; // tells websrv frontend to use constellation chart for displaying
fdui->graph[6].datasetid = 1; // tells websrv frontend to use dataset 1 of constellation chart
// Throughput on PUSCH
fdui->graph[7] = gNBcommonGraph( puschThroughtput, FL_NORMAL_XYPLOT, 0, curY, 500, 100,
"PUSCH Throughput [frame]/[kbit/s]", FL_WHITE );
fdui->graph[8].graph=NULL;
fl_end_form( );
if (fdui->phy_scope)
fdui->phy_scope->fdui = fdui;
fl_show_form (fdui->phy_scope, FL_PLACE_HOTSPOT, FL_FULLBORDER, "LTE UL SCOPE gNB");
return fdui;
}
static const int scope_enb_num_ue = 1;
void phy_scope_gNB(OAI_phy_scope_t *form,
scopeData_t *p,
int UE_id) {
STATICFORXSCOPE void phy_scope_gNB(OAI_phy_scope_t *form, scopeData_t *p, int UE_id)
{
static OAI_phy_scope_t *rememberForm=NULL;
if (form==NULL)
......@@ -518,13 +699,15 @@ void phy_scope_gNB(OAI_phy_scope_t *form,
int i=0;
while (form->graph[i].graph) {
form->graph[i].gNBfunct(form->graph+i, p, UE_id);
if (form->graph[i].enabled)
form->graph[i].gNBfunct(form->graph + i, p, UE_id);
i++;
}
//fl_check_forms();
}
#ifndef WEBSRVSCOPE
static void *scope_thread_gNB(void *arg) {
scopeData_t *p=(scopeData_t *) arg;
size_t stksize=0;
......@@ -549,6 +732,7 @@ static void *scope_thread_gNB(void *arg) {
return NULL;
}
#endif
static void copyRxdataF(int32_t *data, int slot, void *scopeData) {
scopeData_t *scope=(scopeData_t *)scopeData;
......@@ -557,7 +741,8 @@ static void copyRxdataF(int32_t *data, int slot, void *scopeData) {
scope->gNB->frame_parms.samples_per_slot_wCP*sizeof(int32_t));
}
void gNBinitScope(scopeParms_t *p) {
STATICFORXSCOPE void gNBinitScope(scopeParms_t *p)
{
AssertFatal(p->gNB->scopeData=malloc(sizeof(scopeData_t)),"");
scopeData_t *scope=(scopeData_t *) p->gNB->scopeData;
scope->argc=p->argc;
......@@ -566,8 +751,10 @@ void gNBinitScope(scopeParms_t *p) {
scope->gNB=p->gNB;
scope->slotFunc=copyRxdataF;
AssertFatal(scope->liveData= calloc(p->gNB->frame_parms.samples_per_frame_wCP*sizeof(int32_t),1),"");
#ifndef WEBSRVSCOPE
pthread_t forms_thread;
threadCreate(&forms_thread, scope_thread_gNB, p->gNB->scopeData, "scope", -1, OAI_PRIORITY_RT_LOW);
#endif
}
static void ueWaterFall (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
// Received signal in time domain of receive antenna 0
......@@ -651,13 +838,17 @@ static void uePbchLLR (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_
// We take the first antenna only for now
int16_t *llrs = (int16_t *) (data[pbchLlr]+1);
float *llr_pbch, *bit_pbch;
int nx = sz;
#ifdef WEBSRVSCOPE
nx = websrv_cpllrbuff_tomsg(graph, llrs, sz, UE_id, 0, 0);
#else
oai_xygraph_getbuff(graph, &bit_pbch, &llr_pbch, sz, 0);
for (int i=0; i<sz; i++) {
llr_pbch[i] = llrs[i];
}
oai_xygraph(graph,bit_pbch,llr_pbch,sz,0,10);
#endif
oai_xygraph(graph, bit_pbch, llr_pbch, nx, 0, 10);
}
static void uePbchIQ (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
......@@ -667,15 +858,20 @@ static void uePbchIQ (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_U
scopeSample_t *pbch_comp = (scopeSample_t *) (data[pbchRxdataF_comp]+1);
const int sz=data[pbchRxdataF_comp]->lineSz;
int newsz = sz;
float *I, *Q;
#ifdef WEBSRVSCOPE
newsz = websrv_cpiqbuff_tomsg(graph, pbch_comp, sz, 0, 0);
#else
oai_xygraph_getbuff(graph, &I, &Q, sz, 0);
for (int i=0; i<sz; i++) {
I[i]=pbch_comp[i].r;
Q[i]=pbch_comp[i].i;
}
oai_xygraph(graph, I, Q, sz, 0, true);
#endif
oai_xygraph(graph, I, Q, newsz, 0, true);
}
static void uePcchLLR (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
......@@ -687,15 +883,19 @@ static void uePcchLLR (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_
//int Qm = 2;
const int sz=data[pdcchLlr]->lineSz;
float *llr, *bit;
oai_xygraph_getbuff(graph, &bit, &llr, sz, 0);
int nx = sz;
int16_t *pdcch_llr = (int16_t *)(data[pdcchLlr]+1);
#ifdef WEBSRVSCOPE
nx = websrv_cpllrbuff_tomsg(graph, pdcch_llr, sz, UE_id, 0, 0);
#else
oai_xygraph_getbuff(graph, &bit, &llr, sz, 0);
for (int i=0; i<sz; i++) {
llr[i] = (float) pdcch_llr[i];
}
oai_xygraph(graph,bit,llr,sz,0,10);
#endif
oai_xygraph(graph, bit, llr, nx, 0, 10);
}
static void uePcchIQ (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
// PDCCH I/Q of MF Output
......@@ -703,19 +903,22 @@ static void uePcchIQ (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_U
return;
const int sz=data[pdcchRxdataF_comp]->lineSz;
int newsz = sz;
//const int antennas=data[pdcchRxdataF_comp]->colSz;
// We take the first antenna only for now
float *I, *Q;
oai_xygraph_getbuff(graph, &I, &Q, sz, 0);
scopeSample_t *pdcch_comp = (scopeSample_t *) (data[pdcchRxdataF_comp]+1);
#ifdef WEBSRVSCOPE
newsz = websrv_cpiqbuff_tomsg(graph, pdcch_comp, sz, 0, 0);
#else
oai_xygraph_getbuff(graph, &I, &Q, sz, 0);
for (int i=0; i<sz; i++) {
I[i] = pdcch_comp[i].r;
Q[i] = pdcch_comp[i].i;
}
oai_xygraph(graph,I,Q,sz,0,10);
#endif
oai_xygraph(graph, I, Q, newsz, 0, 10);
}
static void uePdschLLR (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
// PDSCH LLRs
......@@ -725,24 +928,31 @@ static void uePdschLLR (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR
int num_re = 4500;
int Qm = 2;
int coded_bits_per_codeword = num_re*Qm;
int newsz = 0;
float *llr, *bit;
#ifndef WEBSRVSCOPE
oai_xygraph_getbuff(graph, &bit, &llr, coded_bits_per_codeword*RX_NB_TH_MAX, 0);
#endif
int base=0;
for (int thr=0 ; thr < RX_NB_TH_MAX ; thr ++ ) {
int16_t *pdsch_llr = (int16_t *) phy_vars_ue->pdsch_vars[eNB_id]->llr[0]; // stream 0
#ifdef WEBSRVSCOPE
newsz += websrv_cpllrbuff_tomsg(graph, pdsch_llr, coded_bits_per_codeword, 0, thr, RX_NB_TH_MAX);
#else
for (int i=0; i<coded_bits_per_codeword; i++) {
llr[base+i] = (float) pdsch_llr[i];
bit[base+i] = (float) base+i;
}
newsz += coded_bits_per_codeword;
#endif
base+=coded_bits_per_codeword;
}
AssertFatal(base <= coded_bits_per_codeword*RX_NB_TH_MAX, "");
//fl_set_xyplot_xbounds(form->pdsch_llr,0,coded_bits_per_codeword);
oai_xygraph(graph,bit,llr,base,0,10);
oai_xygraph(graph, bit, llr, newsz, 0, 10);
}
static void uePdschIQ (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
// PDSCH I/Q of MF Output
......@@ -751,25 +961,31 @@ static void uePdschIQ (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_
NR_DL_FRAME_PARMS *frame_parms = &phy_vars_ue->frame_parms;
int sz=7*2*frame_parms->N_RB_DL*12; // size of the malloced buffer
int newsz = 0;
float *I, *Q;
oai_xygraph_getbuff(graph, &I, &Q, sz*RX_NB_TH_MAX, 0);
int base=0;
#ifndef WEBSRVSCOPE
oai_xygraph_getbuff(graph, &I, &Q, sz * RX_NB_TH_MAX, 0);
memset(I+base, 0, sz*RX_NB_TH_MAX * sizeof(*I));
memset(Q+base, 0, sz*RX_NB_TH_MAX * sizeof(*Q));
#endif
for (int thr=0 ; thr < RX_NB_TH_MAX ; thr ++ ) {
scopeSample_t *pdsch_comp = (scopeSample_t *) phy_vars_ue->pdsch_vars[eNB_id]->rxdataF_comp0[0];
#ifdef WEBSRVSCOPE
newsz += websrv_cpiqbuff_tomsg(graph, pdsch_comp, sz, 0, base);
#else
for (int s=0; s<sz; s++) {
I[s+base] += pdsch_comp[s].r;
Q[s+base] += pdsch_comp[s].i;
}
newsz += sz;
#endif
base+=sz;
}
AssertFatal(base <= sz*RX_NB_TH_MAX, "");
oai_xygraph(graph,I,Q,sz*RX_NB_TH_MAX,0,10);
oai_xygraph(graph, I, Q, newsz, 0, 10);
}
static void uePdschThroughput (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
/*
......@@ -794,7 +1010,8 @@ static void uePdschThroughput (scopeGraphData_t **data, OAIgraph_t *graph, PHY_
fl_set_xyplot_ybounds(form->pdsch_tput,0,tput_ue_max[UE_id]);
*/
}
static OAI_phy_scope_t *create_phy_scope_nrue( int ID ) {
STATICFORXSCOPE OAI_phy_scope_t *create_phy_scope_nrue(int ID)
{
FL_OBJECT *obj;
OAI_phy_scope_t *fdui = calloc(( sizeof *fdui ),1);
// Define form
......@@ -807,9 +1024,12 @@ static OAI_phy_scope_t *create_phy_scope_nrue( int ID ) {
// Received signal
fdui->graph[0] = nrUEcommonGraph(ueWaterFall,
WATERFALL, 0, curY, 400, 100, "Received Signal (Time-Domain, one frame)", FL_RED );
fdui->graph[0].chartid = SCOPEMSG_DATAID_WF; // tells websrv frontend to use WF chart for displaying
fdui->graph[0].datasetid = 0; // not used for WF
// Time-domain channel response
fdui->graph[1] = nrUEcommonGraph(ueChannelResponse,
FL_NORMAL_XYPLOT, 400, curY, 400, 100, "Channel Impulse Response (samples, abs)", FL_RED );
fdui->graph[1].chartid = SCOPEMSG_DATAID_TRESP;
fl_get_object_bbox(fdui->graph[1].graph,&x, &y,&w, &h);
curY+=h;
// Frequency-domain channel response
......@@ -817,31 +1037,45 @@ static OAI_phy_scope_t *create_phy_scope_nrue( int ID ) {
WATERFALL, 0, curY, 800, 100, "Channel Frequency (RE, one slot)", FL_RED );
fl_get_object_bbox(fdui->graph[2].graph,&x, &y,&w, &h);
curY+=h+20;
fdui->graph[2].chartid = SCOPEMSG_DATAID_WF; // tells websrv frontend to use WF chart for displaying
fdui->graph[2].datasetid = 0; // not used for WF
// LLR of PBCH
fdui->graph[3] = nrUEcommonGraph(uePbchLLR,
FL_POINTS_XYPLOT, 0, curY, 500, 100, "PBCH Log-Likelihood Ratios (LLR, mag)", FL_GREEN );
fl_set_xyplot_xgrid(fdui->graph[3].graph,FL_GRID_MAJOR);
fdui->graph[3].chartid = SCOPEMSG_DATAID_LLR; // tells websrv frontend to use LLR chart for displaying
fdui->graph[3].datasetid = 0; // tells websrv frontend to use dataset index 0 in LLR chart
// I/Q PBCH comp
fdui->graph[4] = nrUEcommonGraph(uePbchIQ,
FL_POINTS_XYPLOT, 500, curY, 300, 100, "PBCH I/Q of MF Output", FL_GREEN );
fl_get_object_bbox(fdui->graph[3].graph,&x, &y,&w, &h);
curY+=h;
curY += h;
fdui->graph[4].chartid = SCOPEMSG_DATAID_IQ; // tells websrv frontend to use constellation chart for displaying
fdui->graph[4].datasetid = 0; // tells websrv frontend which dataset to use in the window
// LLR of PDCCH
fdui->graph[5] = nrUEcommonGraph(uePcchLLR,
FL_POINTS_XYPLOT, 0, curY, 500, 100, "PDCCH Log-Likelihood Ratios (LLR, mag)", FL_CYAN );
fdui->graph[5].chartid = SCOPEMSG_DATAID_LLR; // tells websrv frontend to use LLR chart for displaying
fdui->graph[5].datasetid = 1; // tells websrv frontend to use dataset index 1 in LLR chart
// I/Q PDCCH comp
fdui->graph[6] = nrUEcommonGraph(uePcchIQ,
FL_POINTS_XYPLOT, 500, curY, 300, 100, "PDCCH I/Q of MF Output", FL_CYAN );
fl_get_object_bbox(fdui->graph[5].graph,&x, &y,&w, &h);
curY+=h;
fdui->graph[6].chartid = SCOPEMSG_DATAID_IQ; // tells websrv frontend to use constellation chart for displaying
fdui->graph[6].datasetid = 1; // tells websrv frontend which dataset to use in the window
// LLR of PDSCH
fdui->graph[7] = nrUEcommonGraph(uePdschLLR,
FL_POINTS_XYPLOT, 0, curY, 500, 200, "PDSCH Log-Likelihood Ratios (LLR, mag)", FL_YELLOW );
fdui->graph[7].chartid = SCOPEMSG_DATAID_LLR; // tells websrv frontend to use LLR chart for displaying
fdui->graph[7].datasetid = 2; // tells websrv frontend to use dataset index 2 in LLR chart
// I/Q PDSCH comp
fdui->graph[8] = nrUEcommonGraph(uePdschIQ,
FL_POINTS_XYPLOT, 500, curY, 300, 200, "PDSCH I/Q of MF Output", FL_YELLOW );
fl_get_object_bbox(fdui->graph[8].graph,&x, &y,&w, &h);
curY+=h;
fdui->graph[8].chartid = SCOPEMSG_DATAID_IQ; // tells websrv frontend to use constellation chart for displaying
fdui->graph[8].datasetid = 2; // tells websrv frontend which dataset to use in the window
// Throughput on PDSCH
fdui->graph[9] = nrUEcommonGraph(uePdschThroughput,
FL_NORMAL_XYPLOT, 0, curY, 500, 100, "PDSCH Throughput [frame]/[kbit/s]", FL_WHITE );
......@@ -858,6 +1092,7 @@ static OAI_phy_scope_t *create_phy_scope_nrue( int ID ) {
fl_hide_object(fdui->button_0);
#endif
fl_end_form( );
if (fdui->phy_scope)
fdui->phy_scope->fdui = fdui;
char buf[100];
sprintf(buf,"NR DL SCOPE UE %d", ID);
......@@ -865,11 +1100,8 @@ static OAI_phy_scope_t *create_phy_scope_nrue( int ID ) {
return fdui;
}
void phy_scope_nrUE(scopeGraphData_t **UEliveData,
OAI_phy_scope_t *form,
PHY_VARS_NR_UE *phy_vars_ue,
int eNB_id,
int UE_id) {
STATICFORXSCOPE void phy_scope_nrUE(scopeGraphData_t **UEliveData, OAI_phy_scope_t *form, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id)
{
static OAI_phy_scope_t *remeberForm=NULL;
if (form==NULL)
......@@ -883,13 +1115,15 @@ void phy_scope_nrUE(scopeGraphData_t **UEliveData,
int i=0;
while (form->graph[i].graph) {
form->graph[i].nrUEfunct(UEliveData, form->graph+i, phy_vars_ue, eNB_id, UE_id);
if (form->graph[i].enabled)
form->graph[i].nrUEfunct(UEliveData, form->graph + i, phy_vars_ue, eNB_id, UE_id);
i++;
}
//fl_check_forms();
}
#ifndef WEBSRVSCOPE
static void *nrUEscopeThread(void *arg) {
PHY_VARS_NR_UE *ue=(PHY_VARS_NR_UE *)arg;
size_t stksize;
......@@ -914,6 +1148,7 @@ static void *nrUEscopeThread(void *arg) {
pthread_exit((void *)arg);
}
#endif
void UEcopyData(PHY_VARS_NR_UE *ue, enum UEdataType type, void *dataIn, int elementSz, int colSz, int lineSz) {
// Local static copy of the scope data bufs
......@@ -956,13 +1191,16 @@ void UEcopyData(PHY_VARS_NR_UE *ue, enum UEdataType type, void *dataIn, int elem
}
}
void nrUEinitScope(PHY_VARS_NR_UE *ue) {
STATICFORXSCOPE void nrUEinitScope(PHY_VARS_NR_UE *ue)
{
AssertFatal(ue->scopeData=malloc(sizeof(scopeData_t)),"");
scopeData_t *scope=(scopeData_t *) ue->scopeData;
scope->copyData=UEcopyData;
AssertFatal(scope->liveData=calloc(sizeof(scopeGraphData_t *), UEdataTypeNumberOfItems),"");
#ifndef WEBSRVSCOPE
pthread_t forms_thread;
threadCreate(&forms_thread, nrUEscopeThread, ue, "scope", -1, OAI_PRIORITY_RT_LOW);
#endif
}
void nrscope_autoinit(void *dataptr) {
......
/*
* 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
*
* Author and copyright: Laurent Thomas, open-cells.com
*
* 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
*/
#include "phy_scope_interface.h"
#ifndef phy_scope_h_
#define phy_scope_h_
#define TPUT_WINDOW_LENGTH 100
#define ScaleZone 4
#define localBuff(NaMe, SiZe) \
float NaMe[SiZe]; \
memset(NaMe, 0, sizeof(NaMe));
/* scope chart window id's, used to select which frontend chart to use for displaying data */
#define SCOPEMSG_DATAID_IQ 1
#define SCOPEMSG_DATAID_LLR 2
#define SCOPEMSG_DATAID_WF 3
#define SCOPEMSG_DATAID_TRESP 4
typedef c16_t scopeSample_t;
#define SquaredNorm(VaR) ((VaR).r * (VaR).r + (VaR).i * (VaR).i)
typedef struct {
int dataSize;
int elementSz;
int colSz;
int lineSz;
} scopeGraphData_t;
typedef struct OAIgraph {
FL_OBJECT *graph;
FL_OBJECT *text;
float maxX;
float maxY;
float minX;
float minY;
int x;
int y;
int w;
int h;
int waterFallh;
double *waterFallAvg;
bool initDone;
int iteration;
void (*gNBfunct)(struct OAIgraph *graph, scopeData_t *p, int UE_id);
void (*nrUEfunct)(scopeGraphData_t **data, struct OAIgraph *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id);
bool enabled;
char chartid;
char datasetid;
} OAIgraph_t;
/* Forms and Objects */
typedef struct {
FL_FORM *phy_scope;
OAIgraph_t graph[20];
FL_OBJECT *button_0;
} OAI_phy_scope_t;
typedef struct {
FL_FORM *stats_form;
void *vdata;
char *cdata;
long ldata;
FL_OBJECT *stats_text;
FL_OBJECT *stats_button;
} FD_stats_form;
#ifdef WEBSRVSCOPE
extern OAI_phy_scope_t *create_phy_scope_gnb(void);
extern OAI_phy_scope_t *create_phy_scope_nrue(int ueid);
extern void phy_scope_gNB(OAI_phy_scope_t *form, scopeData_t *p, int nb_UE);
extern void phy_scope_nrUE(scopeGraphData_t **UEliveData, OAI_phy_scope_t *form, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id);
extern void nrUEinitScope(PHY_VARS_NR_UE *ue);
extern void gNBinitScope(scopeParms_t *p);
typedef int16_t chart_data_t;
#endif
#endif
......@@ -44,20 +44,32 @@ extern void print_shorts(char *s,__m128i *x);
static mapping channelmod_names[] = {
CHANNELMOD_MAP_INIT
};
static char *module_id_str[] = MODULEID_STR_INIT;
static int channelmod_show_cmd(char *buff, int debug, telnet_printfunc_t prnt);
static int channelmod_modify_cmd(char *buff, int debug, telnet_printfunc_t prnt);
static int channelmod_print_help(char *buff, int debug, telnet_printfunc_t prnt);
int get_modchannel_index(char *buf, int debug, void *vdata, telnet_printfunc_t prnt);
int get_channel_params(char *buf, int debug, void *tdata, telnet_printfunc_t prnt);
int get_currentchannels_type(char *buf, int debug, void *vdata, telnet_printfunc_t prnt);
#define HELP_WEBIF_MODIFCHAN_STRING "Current channel index? <chanidx>"
static telnetshell_cmddef_t channelmod_cmdarray[] = {
{"help","",channelmod_print_help},
{"show","<predef,current>",channelmod_show_cmd},
{"modify","<channelid> <param> <value>",channelmod_modify_cmd},
{"","",NULL},
{"help", "", channelmod_print_help, {NULL}, 0, NULL},
{"show", "<predef,current>", channelmod_show_cmd, {NULL}, TELNETSRV_CMDFLAG_TELNETONLY, NULL},
{"show predef", "", channelmod_show_cmd, {NULL}, TELNETSRV_CMDFLAG_WEBSRVONLY, NULL},
{"show current", "", channelmod_show_cmd, {NULL}, TELNETSRV_CMDFLAG_WEBSRVONLY, NULL},
{"modify", "<channelid> <param> <value>", channelmod_modify_cmd, {NULL}, TELNETSRV_CMDFLAG_TELNETONLY, NULL},
{"show params", "<channelid> <param> <value>", channelmod_modify_cmd, {webfunc_getdata : get_currentchannels_type}, TELNETSRV_CMDFLAG_GETWEBTBLDATA | TELNETSRV_CMDFLAG_WEBSRV_SETRETURNTBL, NULL},
{"show channelid",
HELP_WEBIF_MODIFCHAN_STRING,
channelmod_modify_cmd,
{webfunc_getdata : get_channel_params},
TELNETSRV_CMDFLAG_NEEDPARAM | TELNETSRV_CMDFLAG_WEBSRVONLY | TELNETSRV_CMDFLAG_GETWEBTBLDATA,
NULL},
{"", "", NULL, {NULL}, 0, NULL},
};
static telnetshell_vardef_t channelmod_vardef[] = {
{"",0,NULL}
};
static telnetshell_vardef_t channelmod_vardef[] = {{"", 0, 0, NULL}};
static double snr_dB=25;
static double sinr_dB=0;
......@@ -1924,7 +1936,8 @@ double N_RB2channel_bandwidth(uint16_t N_RB) {
return(channel_bandwidth);
}
/*-----------------------------------------------------------------------------------------------------------*/
/* functions for telnet server and webserver */
static int channelmod_print_help(char *buff, int debug, telnet_printfunc_t prnt ) {
prnt("channelmod commands can be used to display or modify channel models parameters\n");
prnt("channelmod show predef: display predefined model algorithms available in oai\n");
......@@ -1935,9 +1948,70 @@ static int channelmod_print_help(char *buff, int debug, telnet_printfunc_t prnt
return CMDSTATUS_FOUND;
}
static char *pnames[] = {"riceanf", "aoa", "randaoa", "ploss", "noise_power_dB", "offset", "forgetf", NULL};
static char *pformat[] = {"%lf", "%lf", "%i", "%lf", "%lf", "%i", "%lf", NULL};
int get_channel_params(char *buf, int debug, void *vdata, telnet_printfunc_t prnt)
{
if (buf == NULL) {
LOG_I(UTIL, "%s received NULL buffer\n", __FUNCTION__);
return -1;
}
if (debug)
LOG_I(UTIL, "%s received %s\n", __FUNCTION__, buf);
int chanidx = 0;
webdatadef_t *tdata = (webdatadef_t *)vdata;
if (strstr(buf, "show") == buf) {
if (tdata->lines[0].val[0] != NULL) {
chanidx = strtol(tdata->lines[0].val[0], NULL, 0);
} else {
LOG_I(UTIL, "Channel index set to 0, not available in received data\n");
}
if (tdata != NULL && defined_channels[chanidx] != NULL) {
tdata->numcols = 2;
snprintf(tdata->columns[0].coltitle, sizeof(tdata->columns[0].coltitle), "parameter");
tdata->columns[0].coltype = TELNET_VARTYPE_STRING | TELNET_CHECKVAL_RDONLY;
snprintf(tdata->columns[1].coltitle, sizeof(tdata->columns[1].coltitle), "value");
tdata->columns[1].coltype = TELNET_VARTYPE_STRING | TELNET_VAR_NEEDFREE;
tdata->numlines = 0;
channel_desc_t *cd = defined_channels[chanidx];
void *valptr[] = {&(cd->ricean_factor), &(cd->aoa), &(cd->random_aoa), &(cd->path_loss_dB), &(cd->noise_power_dB), &(cd->channel_offset), &(cd->forgetting_factor)};
for (int i = 0; pnames[i] != NULL; i++) {
tdata->lines[tdata->numlines].val[0] = malloc(strlen(pnames[i] + 1));
tdata->lines[tdata->numlines].val[1] = malloc(64);
strcpy(tdata->lines[tdata->numlines].val[0], pnames[i]);
if (pformat[i][1] == 'i') {
snprintf(tdata->lines[tdata->numlines].val[1], 64, pformat[i], *(int *)valptr[i]);
} else {
snprintf(tdata->lines[tdata->numlines].val[1], 64, pformat[i], *(double *)valptr[i]);
}
tdata->numlines++;
}
}
return tdata->numlines;
} /* show */ else if (strstr(buf, "set") == buf) {
char cmdbuf[TELNET_MAX_MSGLENGTH];
int sst = sscanf(tdata->tblname, "%*[^=]=%i", &chanidx);
if (sst == 1) {
int pidx = tdata->numlines;
if (pformat[pidx][1] == 'i') {
sprintf(cmdbuf, "channelmod modify %i %s %s", chanidx, pnames[pidx], tdata->lines[0].val[0]);
} else {
sprintf(cmdbuf, "channelmod modify %i %s %s", chanidx, pnames[pidx], tdata->lines[0].val[1]);
}
channelmod_modify_cmd(cmdbuf, debug, prnt);
return 200;
} else {
prnt(" channel index not found in cannelmod command\n");
}
} else {
prnt("%s not implemented\n", buf);
}
return 500;
} /* get_currentchannel_type */
static void display_channelmodel(channel_desc_t *cd,int debug, telnet_printfunc_t prnt) {
char *module_id_str[]=MODULEID_STR_INIT;
prnt("model owner: %s\n",(cd->module_id != 0)?module_id_str[cd->module_id]:"not set");
prnt("nb_tx: %i nb_rx: %i taps: %i bandwidth: %lf sampling: %lf\n",cd->nb_tx, cd->nb_rx, cd->nb_taps, cd->channel_bandwidth, cd->sampling_rate);
prnt("channel length: %i Max path delay: %lf ricean fact.: %lf angle of arrival: %lf (randomized:%s)\n",
......@@ -1952,10 +2026,60 @@ static void display_channelmodel(channel_desc_t *cd,int debug, telnet_printfunc_
}
}
int get_currentchannels_type(char *buf, int debug, void *vdata, telnet_printfunc_t prnt)
{
webdatadef_t *tdata;
if (buf == NULL) {
LOG_I(UTIL, "%s received NULL buffer\n", __FUNCTION__);
return -1;
}
if (debug)
LOG_I(UTIL, "%s received %s\n", __FUNCTION__, buf);
if (vdata != NULL) {
tdata = (webdatadef_t *)vdata;
} else {
LOG_I(UTIL, "%s vdata is NULL\n", __FUNCTION__);
return -1;
}
if (strncmp(buf, "set", 3) == 0) {
tdata->numcols = 1;
return get_channel_params(buf, debug, vdata, prnt);
}
tdata->numcols = 4;
snprintf(tdata->columns[0].coltitle, sizeof(tdata->columns[0].coltitle), "model index");
tdata->columns[0].coltype = TELNET_VARTYPE_STRING | TELNET_CHECKVAL_RDONLY | TELNET_VAR_NEEDFREE;
snprintf(tdata->columns[1].coltitle, sizeof(tdata->columns[1].coltitle), "model name");
tdata->columns[1].coltype = TELNET_VARTYPE_STRING | TELNET_CHECKVAL_RDONLY;
snprintf(tdata->columns[2].coltitle, sizeof(tdata->columns[2].coltitle), "module owner");
tdata->columns[2].coltype = TELNET_VARTYPE_STRING | TELNET_CHECKVAL_RDONLY;
snprintf(tdata->columns[3].coltitle, sizeof(tdata->columns[3].coltitle), "algorithm");
tdata->columns[3].coltype = TELNET_VARTYPE_STRING | TELNET_CHECKVAL_SIMALGO;
tdata->numlines = 0;
for (int i = 0; ((i < max_chan) && (i < TELNET_MAXLINE_NUM)); i++) {
if (defined_channels[i] != NULL) {
tdata->lines[tdata->numlines].val[0] = malloc(64);
snprintf(tdata->lines[tdata->numlines].val[0], 64, "%02u", (unsigned int)i);
tdata->lines[tdata->numlines].val[1] = (defined_channels[i]->model_name != NULL) ? defined_channels[i]->model_name : "(not set)";
tdata->lines[tdata->numlines].val[2] = (defined_channels[i]->module_id != 0) ? module_id_str[defined_channels[i]->module_id] : "not set";
tdata->lines[tdata->numlines].val[3] = map_int_to_str(channelmod_names, defined_channels[i]->modelid);
tdata->numlines++;
}
}
return tdata->numlines;
} /* get_currentchannel_type */
static int channelmod_show_cmd(char *buff, int debug, telnet_printfunc_t prnt) {
char *subcmd=NULL;
int s = sscanf(buff,"%ms\n",&subcmd);
int s;
if (buff == NULL) {
subcmd = strdup(""); // enforce help display
s = 2;
} else {
s = sscanf(buff, "%ms\n", &subcmd);
}
if (s>0) {
if ( strcmp(subcmd,"predef") == 0) {
......@@ -1986,7 +2110,7 @@ static int channelmod_show_cmd(char *buff, int debug, telnet_printfunc_t prnt) {
static int channelmod_modify_cmd(char *buff, int debug, telnet_printfunc_t prnt) {
char *param=NULL, *value=NULL;
int cd_id= -1;
int s = sscanf(buff,"%i %ms %ms \n",&cd_id,&param, &value);
int s = sscanf(buff, "%*s %*s %i %ms %ms \n", &cd_id, &param, &value);
if (cd_id<0 || cd_id >= max_chan) {
prnt("ERROR, %i: Channel model id outof range (0-%i)\n",cd_id,max_chan-1);
......@@ -2049,6 +2173,36 @@ static int channelmod_modify_cmd(char *buff, int debug, telnet_printfunc_t prnt)
return CMDSTATUS_FOUND;
}
int get_modchannel_index(char *buf, int debug, void *vdata, telnet_printfunc_t prnt)
{
if (buf == NULL) {
LOG_I(UTIL, "%s received NULL buffer\n", __FUNCTION__);
return -1;
}
if (debug)
LOG_I(UTIL, "%s received %s\n", __FUNCTION__, buf);
webdatadef_t *tdata = (webdatadef_t *)vdata;
tdata->numlines = 0;
if (strncmp(buf, "set", 3) == 0) {
return get_channel_params(buf, debug, vdata, prnt);
}
if (tdata != NULL) {
for (int i = 0; i < max_chan; i++) {
if (defined_channels[i] != NULL) {
tdata->numlines++;
}
}
tdata->numcols = 0;
if (tdata->numlines > 0)
snprintf(tdata->tblname, sizeof(tdata->tblname) - 1, "Running channel index (0-%i)", (tdata->numlines - 1));
else {
snprintf(tdata->tblname, sizeof(tdata->tblname) - 1, "No running model in the system");
}
}
return tdata->numlines;
} /* get_currentchannel_type */
/*------------------------------------------------------------------------------------------------------------------*/
int modelid_fromstrtype(char *modeltype) {
int modelid=map_str_to_int(channelmod_names,modeltype);
......
......@@ -87,23 +87,23 @@
{"offset", "<channel offset in samps>\n", simOpt, iptr:&(rfsimulator->chan_offset), defintval:0, TYPE_INT, 0 }\
};
static void getset_currentchannels_type(char *buf, int debug, webdatadef_t *tdata, telnet_printfunc_t prnt);
extern int get_currentchannels_type(char *buf, int debug, webdatadef_t *tdata, telnet_printfunc_t prnt); // in random_channel.c
static int rfsimu_setchanmod_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg);
static int rfsimu_setdistance_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg);
static int rfsimu_getdistance_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg);
static int rfsimu_vtime_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg);
static telnetshell_cmddef_t rfsimu_cmdarray[] = {
{"setmodel","<model name> <model type>",(cmdfunc_t)rfsimu_setchanmod_cmd,TELNETSRV_CMDFLAG_PUSHINTPOOLQ},
{"setdistance","<model name> <distance>", (cmdfunc_t)rfsimu_setdistance_cmd, TELNETSRV_CMDFLAG_PUSHINTPOOLQ},
{"getdistance","<model name>", (cmdfunc_t) rfsimu_getdistance_cmd, TELNETSRV_CMDFLAG_PUSHINTPOOLQ},
{"vtime","", (cmdfunc_t) rfsimu_vtime_cmd, TELNETSRV_CMDFLAG_PUSHINTPOOLQ},
{"","",NULL},
{"show models", "", (cmdfunc_t)rfsimu_setchanmod_cmd, {(webfunc_t)getset_currentchannels_type}, TELNETSRV_CMDFLAG_WEBSRVONLY | TELNETSRV_CMDFLAG_GETWEBTBLDATA, NULL},
{"setmodel", "<model name> <model type>", (cmdfunc_t)rfsimu_setchanmod_cmd, {NULL}, TELNETSRV_CMDFLAG_PUSHINTPOOLQ | TELNETSRV_CMDFLAG_TELNETONLY, NULL},
{"setdistance", "<model name> <distance>", (cmdfunc_t)rfsimu_setdistance_cmd, {NULL}, TELNETSRV_CMDFLAG_PUSHINTPOOLQ},
{"getdistance", "<model name>", (cmdfunc_t)rfsimu_getdistance_cmd, {NULL}, TELNETSRV_CMDFLAG_PUSHINTPOOLQ},
{"vtime", "", (cmdfunc_t)rfsimu_vtime_cmd, {NULL}, TELNETSRV_CMDFLAG_PUSHINTPOOLQ | TELNETSRV_CMDFLAG_AUTOUPDATE},
{"", "", NULL},
};
static telnetshell_cmddef_t *setmodel_cmddef = &(rfsimu_cmdarray[1]);
static telnetshell_vardef_t rfsimu_vardef[] = {
{"",0,NULL}
};
static telnetshell_vardef_t rfsimu_vardef[] = {{"", 0, 0, NULL}};
pthread_mutex_t Sockmutex;
typedef c16_t sample_t; // 2*16 bits complex number
......@@ -322,6 +322,15 @@ static void rfsimulator_readconfig(rfsimulator_state_t *rfsimulator) {
static int rfsimu_setchanmod_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg) {
char *modelname=NULL;
char *modeltype=NULL;
rfsimulator_state_t *t = (rfsimulator_state_t *)arg;
if (t->channelmod == false) {
prnt("ERROR channel modelisation disabled...\n");
return 0;
}
if (buff == NULL) {
prnt("ERROR wrong rfsimu setchannelmod command...\n");
return 0;
}
if (debug)
prnt("rfsimu_setchanmod_cmd buffer \"%s\"\n",buff);
int s = sscanf(buff,"%m[^ ] %ms\n",&modelname, &modeltype);
......@@ -375,6 +384,19 @@ static int rfsimu_setchanmod_cmd(char *buff, int debug, telnet_printfunc_t prnt,
return CMDSTATUS_FOUND;
}
static void getset_currentchannels_type(char *buf, int debug, webdatadef_t *tdata, telnet_printfunc_t prnt)
{
if (strncmp(buf, "set", 3) == 0) {
char cmd[256];
snprintf(cmd, sizeof(cmd), "setmodel %s %s", tdata->lines[0].val[1], tdata->lines[0].val[3]);
push_telnetcmd_func_t push_telnetcmd = (push_telnetcmd_func_t)get_shlibmodule_fptr("telnetsrv", TELNET_PUSHCMD_FNAME);
push_telnetcmd(setmodel_cmddef, cmd, prnt);
} else {
get_currentchannels_type("modify type", debug, tdata, prnt);
}
}
/*getset_currentchannels_type */
//static void print_cirBuf(struct complex16 *circularBuf,
// uint64_t firstSample,
// uint32_t cirSize,
......@@ -472,17 +494,18 @@ static int rfsimu_setdistance_cmd(char *buff, int debug, telnet_printfunc_t prnt
/* Set distance in rfsim and channel model, update channel and ringbuffer */
for (int i=0; i<FD_SETSIZE; i++) {
buffer_t *b=&t->buf[i];
if (b->conn_sock <= 0
|| b->channel_model == NULL
|| b->channel_model->model_name == NULL
|| strcmp(b->channel_model->model_name, modelname) != 0)
if (b->conn_sock <= 0 || b->channel_model == NULL || b->channel_model->model_name == NULL || strcmp(b->channel_model->model_name, modelname) != 0) {
if (b->channel_model != NULL && b->channel_model->model_name != NULL)
prnt(" model %s unmodified\n", b->channel_model->model_name);
continue;
}
channel_desc_t *cd = b->channel_model;
const int old_offset = cd->channel_offset;
cd->channel_offset = new_offset;
const int nbTx = cd->nb_tx;
prnt(" Modifying model %s...\n", modelname);
rfsimu_offset_change_cirBuf(b->circularBuf, t->nextRxTstamp, CirSize, old_offset, new_offset, nbTx);
}
......@@ -494,14 +517,7 @@ static int rfsimu_setdistance_cmd(char *buff, int debug, telnet_printfunc_t prnt
static int rfsimu_getdistance_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg)
{
if (debug)
prnt("%s() buffer \"%s\"\n", __func__, buff);
char *modelname;
int s = sscanf(buff,"%ms\n", &modelname);
if (s != 1) {
prnt("require exact two parameters\n");
return CMDSTATUS_VARNOTFOUND;
}
prnt("%s() buffer \"%s\"\n", __func__, (buff != NULL) ? buff : "NULL");
rfsimulator_state_t *t = (rfsimulator_state_t *)arg;
const double sample_rate = t->sample_rate;
......@@ -509,16 +525,13 @@ static int rfsimu_getdistance_cmd(char *buff, int debug, telnet_printfunc_t prnt
for (int i=0; i<FD_SETSIZE; i++) {
buffer_t *b=&t->buf[i];
if (b->conn_sock <= 0
|| b->channel_model == NULL
|| b->channel_model->model_name == NULL
|| strcmp(b->channel_model->model_name, modelname) != 0)
if (b->conn_sock <= 0 || b->channel_model == NULL || b->channel_model->model_name == NULL)
continue;
channel_desc_t *cd = b->channel_model;
const int offset = cd->channel_offset;
const double distance = (double) offset * c / sample_rate;
prnt("\noffset %d distance %.3f m\n", offset, distance);
prnt("\%s offset %d distance %.3f m\n", cd->model_name, offset, distance);
}
return CMDSTATUS_FOUND;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment