Initial web server implementation
This commit is contained in:
@@ -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},
|
||||
/*--------------------------------------------------------------------------------------------------------------------------*/
|
||||
/* config parameters for config module */
|
||||
/* optname helpstr paramflags XXXptr defXXXval type numelt */
|
||||
/*--------------------------------------------------------------------------------------------------------------------------*/
|
||||
{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)
|
||||
|
||||
+248
-191
@@ -67,51 +67,53 @@ static telnetsrv_params_t telnetparams;
|
||||
#define TELNETSRV_OPTNAME_SHRMOD "shrmod"
|
||||
|
||||
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 }
|
||||
};
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
/* 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},
|
||||
{"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},
|
||||
telnetshell_cmddef_t telnet_cmdarray[] = {
|
||||
{"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
|
||||
*/
|
||||
char *telnet_getvarvalue(telnetshell_vardef_t *var, int varindex)
|
||||
{
|
||||
char *val;
|
||||
switch (var[varindex].vartype) {
|
||||
case TELNET_VARTYPE_INT32:
|
||||
val = malloc(64);
|
||||
snprintf(val, 64, "%i", *(int32_t *)(var[varindex].varvalptr));
|
||||
break;
|
||||
|
||||
int setgetvar(int moduleindex,char getorset,char *params) {
|
||||
int n,i;
|
||||
case TELNET_VARTYPE_INT64:
|
||||
val = malloc(128);
|
||||
snprintf(val, 128, "%lli", (long long)*(int64_t *)(var[varindex].varvalptr));
|
||||
break;
|
||||
|
||||
case TELNET_VARTYPE_INT16:
|
||||
val = malloc(16);
|
||||
snprintf(val, 16, "%hi", *(short *)(var[varindex].varvalptr));
|
||||
break;
|
||||
|
||||
case TELNET_VARTYPE_INT8:
|
||||
val = malloc(16);
|
||||
snprintf(val, 16, "%i", (int)(*(int8_t *)(var[varindex].varvalptr)));
|
||||
break;
|
||||
|
||||
case TELNET_VARTYPE_UINT:
|
||||
val = malloc(64);
|
||||
snprintf(val, 64, "%u", *(unsigned int *)(var[varindex].varvalptr));
|
||||
break;
|
||||
|
||||
case TELNET_VARTYPE_DOUBLE:
|
||||
val = malloc(32);
|
||||
snprintf(val, 32, "%g\n", *(double *)(var[varindex].varvalptr));
|
||||
break;
|
||||
|
||||
case TELNET_VARTYPE_STRING:
|
||||
val = strdup(*(char **)(var[varindex].varvalptr));
|
||||
break;
|
||||
|
||||
default:
|
||||
val = malloc(64);
|
||||
snprintf(val, 64, "ERR:var %i unknown type", varindex);
|
||||
break;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
int telnet_setvarvalue(telnetshell_vardef_t *var, char *strval, telnet_printfunc_t prnt)
|
||||
{
|
||||
int st = 0;
|
||||
switch (var->vartype) {
|
||||
case TELNET_VARTYPE_INT32:
|
||||
*(int *)(var->varvalptr) = (int)strtol(strval, NULL, 0);
|
||||
if (prnt != NULL)
|
||||
prnt("%i\n", *(int *)(var->varvalptr));
|
||||
break;
|
||||
|
||||
case TELNET_VARTYPE_INT16:
|
||||
*(short *)(var->varvalptr) = (short)strtol(strval, NULL, 0);
|
||||
if (prnt != NULL)
|
||||
prnt("%hi\n", *(short *)(var->varvalptr));
|
||||
break;
|
||||
|
||||
case TELNET_VARTYPE_INT8:
|
||||
*(char *)(var->varvalptr) = (char)strtol(strval, NULL, 0);
|
||||
if (prnt != NULL)
|
||||
prnt("%i\n", *(int *)(var->varvalptr));
|
||||
break;
|
||||
|
||||
case TELNET_VARTYPE_UINT:
|
||||
*(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 *)(var->varvalptr) = strtod(strval, NULL);
|
||||
if (prnt != NULL)
|
||||
prnt("%g\n", *(double *)(var->varvalptr));
|
||||
break;
|
||||
|
||||
case TELNET_VARTYPE_STRING:
|
||||
sprintf(*(char **)(var->varvalptr), "%s", strval);
|
||||
if (prnt != NULL)
|
||||
prnt("\"%s\"\n", *(char **)(var->varvalptr));
|
||||
break;
|
||||
|
||||
default:
|
||||
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);
|
||||
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) {
|
||||
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) {
|
||||
case TELNET_VARTYPE_INT32:
|
||||
client_printf("%i\n",*(int32_t *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
|
||||
break;
|
||||
|
||||
case TELNET_VARTYPE_INT64:
|
||||
client_printf("%lli\n",*(int64_t *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
|
||||
break;
|
||||
|
||||
case TELNET_VARTYPE_INT16:
|
||||
client_printf("%hi\n",*(short *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
|
||||
break;
|
||||
|
||||
case TELNET_VARTYPE_INT8:
|
||||
client_printf("%i\n",(int)(*(int8_t *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr)));
|
||||
break;
|
||||
|
||||
case TELNET_VARTYPE_UINT:
|
||||
client_printf("%u\n",*(unsigned int *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
|
||||
break;
|
||||
|
||||
case TELNET_VARTYPE_DOUBLE:
|
||||
client_printf("%g\n",*(double *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
|
||||
break;
|
||||
|
||||
case TELNET_VARTYPE_STRING:
|
||||
client_printf("\"%s\"\n",*(char **)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
|
||||
break;
|
||||
|
||||
default:
|
||||
client_printf("unknown type\n");
|
||||
break;
|
||||
}
|
||||
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);
|
||||
|
||||
switch(telnetparams.CmdParsers[moduleindex].var[i].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));
|
||||
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));
|
||||
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));
|
||||
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));
|
||||
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));
|
||||
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));
|
||||
break;
|
||||
|
||||
default:
|
||||
client_printf("unknown type\n");
|
||||
break;
|
||||
}
|
||||
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,15 +568,11 @@ 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].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);
|
||||
} else {
|
||||
if (telnetparams.CmdParsers[i].cmd[k].cmdflags & TELNETSRV_CMDFLAG_WEBSRVONLY)
|
||||
continue;
|
||||
if (telnetparams.CmdParsers[i].cmd[k].qptr != NULL) {
|
||||
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);
|
||||
}
|
||||
rt= CMDSTATUS_FOUND;
|
||||
@@ -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) {
|
||||
if (afifo == NULL) {
|
||||
afifo = malloc(sizeof(notifiedFIFO_t));
|
||||
initNotifiedFIFO(afifo);
|
||||
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[j].qptr = afifo;
|
||||
}
|
||||
}
|
||||
cmd[k].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;
|
||||
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) {
|
||||
long int policy;
|
||||
if (strlen(procfile_fiels) == 0)
|
||||
continue;
|
||||
fieldcnt++;
|
||||
sprintf(toksep," ");
|
||||
switch(fieldcnt) {
|
||||
case 1: /* id */
|
||||
lptr+=sprintf(lptr,"%9.9s ",procfile_fiels);
|
||||
sprintf(toksep,")");
|
||||
break;
|
||||
case 2: /* name */
|
||||
lptr+=sprintf(lptr,"%20.20s ",procfile_fiels+1);
|
||||
break;
|
||||
case 3: //thread state
|
||||
lptr+=sprintf(lptr," %c ",procfile_fiels[0]);
|
||||
break;
|
||||
case 14: //time in user mode
|
||||
case 15: //time in kernel mode
|
||||
lptr+=sprintf(lptr,"%9.9s ",procfile_fiels);
|
||||
break;
|
||||
case 18: //priority
|
||||
case 19: //nice
|
||||
lptr+=sprintf(lptr,"%3.3s ",procfile_fiels);
|
||||
break;
|
||||
case 23: //vsize
|
||||
lptr+=sprintf(lptr,"%9.9s ",procfile_fiels);
|
||||
break;
|
||||
case 39: //processor
|
||||
lptr+=sprintf(lptr," %2.2s ",procfile_fiels);
|
||||
break;
|
||||
case 41: //policy
|
||||
lptr+=sprintf(lptr,"%3.3s ",procfile_fiels);
|
||||
policy=strtol(procfile_fiels,NULL,0);
|
||||
switch(policy) {
|
||||
case SCHED_FIFO:
|
||||
lptr+=sprintf(lptr,"%s ","rt: fifo");
|
||||
break;
|
||||
case SCHED_OTHER:
|
||||
lptr+=sprintf(lptr,"%s ","other");
|
||||
break;
|
||||
case SCHED_IDLE:
|
||||
lptr+=sprintf(lptr,"%s ","idle");
|
||||
break;
|
||||
case SCHED_BATCH:
|
||||
lptr+=sprintf(lptr,"%s ","batch");
|
||||
break;
|
||||
case SCHED_RR:
|
||||
lptr+=sprintf(lptr,"%s ","rt: rr");
|
||||
break;
|
||||
default:
|
||||
lptr+=sprintf(lptr,"%s ","????");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}/* switch on fieldcnr */
|
||||
procfile_fiels =strtok_r(NULL,toksep,&strtokptr);
|
||||
} /* while on proc_fields != NULL */
|
||||
prnt("%s\n",prntline);
|
||||
int priority = 0;
|
||||
int nice = 0;
|
||||
while (procfile_fields != NULL && fieldcnt < 42) {
|
||||
long int policy;
|
||||
if (strlen(procfile_fields) == 0)
|
||||
continue;
|
||||
fieldcnt++;
|
||||
sprintf(toksep, " ");
|
||||
switch (fieldcnt) {
|
||||
case 1: /* id */
|
||||
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 */
|
||||
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_fields[0]);
|
||||
break;
|
||||
case 14: // time in user mode
|
||||
case 15: // time in kernel mode
|
||||
lptr += sprintf(lptr, "%9.9s ", procfile_fields);
|
||||
break;
|
||||
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_fields);
|
||||
break;
|
||||
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_fields);
|
||||
policy = strtol(procfile_fields, NULL, 0);
|
||||
char strschedp[64];
|
||||
switch (policy) {
|
||||
case SCHED_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:
|
||||
snprintf(strschedp, sizeof(strschedp), "%s ", "other");
|
||||
priority = nice - NICE_MIN; // linux nice is -20 to 19
|
||||
break;
|
||||
case SCHED_IDLE:
|
||||
snprintf(strschedp, sizeof(strschedp), "%s ", "idle");
|
||||
priority = 2 * (NICE_MAX - NICE_MIN + 1);
|
||||
break;
|
||||
case SCHED_BATCH:
|
||||
snprintf(strschedp, sizeof(strschedp), "%s ", "batch");
|
||||
priority = (NICE_MAX - NICE_MIN + 1) + nice - NICE_MIN;
|
||||
break;
|
||||
case SCHED_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:
|
||||
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_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));
|
||||
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);
|
||||
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] != '.') {
|
||||
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) {
|
||||
@@ -263,8 +459,12 @@ int proccmd_thread(char *buf, int debug, telnet_printfunc_t prnt)
|
||||
{
|
||||
int bv1,bv2;
|
||||
int res;
|
||||
char sv1[64];
|
||||
|
||||
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\
|
||||
@@ -60,24 +61,32 @@ telnetshell_vardef_t proc_vardef[] = {
|
||||
print_<opt> <0|1> disable or enable the \"opt\" log option, use the show command to get \
|
||||
the available options\n\
|
||||
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"
|
||||
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);
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
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)
|
||||
@@ -0,0 +1,21 @@
|
||||
# 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)
|
||||
@@ -0,0 +1,212 @@
|
||||
|
||||
|
||||
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)
|
||||
@@ -0,0 +1,17 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,16 @@
|
||||
# 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
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"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": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
# 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
|
||||
@@ -0,0 +1 @@
|
||||
# SoftModemNgx
|
||||
@@ -0,0 +1,108 @@
|
||||
{
|
||||
"$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"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// @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}}));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
// @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}}));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
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));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/* 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"]
|
||||
}
|
||||
}
|
||||
+24319
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
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"});
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
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 {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
.mat-tab-group {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.mat-ink-bar{
|
||||
height: 8px !important;
|
||||
}
|
||||
.mat-tab-label-active
|
||||
{
|
||||
color: #000000 !important;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<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>
|
||||
@@ -0,0 +1,24 @@
|
||||
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" : "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
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 {
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
<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>
|
||||
@@ -0,0 +1,80 @@
|
||||
.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;
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
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!)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<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>
|
||||
@@ -0,0 +1,15 @@
|
||||
/* 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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<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>
|
||||
@@ -0,0 +1,23 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<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>
|
||||
@@ -0,0 +1,22 @@
|
||||
/* 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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
<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 }} msg skipped: {{ skippedmsg }} msg buffered: {{ bufferedmsg }} </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>
|
||||
@@ -0,0 +1,705 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
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 = ""; });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
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"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
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"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
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);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
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},
|
||||
];
|
||||
@@ -0,0 +1,33 @@
|
||||
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();
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
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();
|
||||
})
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
|
||||
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!' });
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// 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 + "/"
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
// 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.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,18 @@
|
||||
<!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>
|
||||
@@ -0,0 +1,11 @@
|
||||
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));
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@@ -0,0 +1,10 @@
|
||||
/* 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;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// 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);
|
||||
@@ -0,0 +1,10 @@
|
||||
/* 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"]
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/* 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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/* 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"]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
This option asks for a model id and lists the parameters of this model. Some parameters can be then modified
|
||||
@@ -0,0 +1 @@
|
||||
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.
|
||||
@@ -0,0 +1 @@
|
||||
Predefined channel model are a set of modelisation algorithm's, identified by a name, available in oai code.
|
||||
@@ -0,0 +1 @@
|
||||
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.
|
||||
@@ -0,0 +1 @@
|
||||
The model index can be used to modify a model parameter, using the "channelmod"/"show channelid" commands
|
||||
@@ -0,0 +1 @@
|
||||
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.
|
||||
@@ -0,0 +1 @@
|
||||
When the rfsimulator is effectively using a channel model to modify a received signal he becomes the owner of that model.
|
||||
@@ -0,0 +1 @@
|
||||
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.
|
||||
@@ -0,0 +1 @@
|
||||
Nice value, relevant for other and batch scheduling can be changed via the "oai priority" field
|
||||
@@ -0,0 +1,7 @@
|
||||
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
|
||||
@@ -0,0 +1 @@
|
||||
Linux priority field, relevant for real-time scheduling mode (Round-Robin or fifo) can be modified using the "oai priority" field
|
||||
@@ -0,0 +1 @@
|
||||
scheduling policy can be modified using the "oai priority" field.
|
||||
@@ -0,0 +1,883 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -0,0 +1,90 @@
|
||||
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} )
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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];
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user