Commit 23c92ee0 authored by Robert Schmidt's avatar Robert Schmidt

Merge remote-tracking branch 'origin/websrv2' into integration_2022_wk50

parents 69a1eb2f e65f13e1
......@@ -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
...
......@@ -2855,6 +2855,7 @@ make_driver(ue_ip ${OPENAIR2_DIR}/NETWORK_DRIVER/UE_IP ${ue_ip_src})
include (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(doc)
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=()
......@@ -644,9 +651,27 @@ function main() {
if [ ! -z "$BUILD_OPTLIB" ] ; then
for oklib in $BUILD_OPTLIB ; do
compilations $BUILD_DIR $oklib
if [ "$oklib" == "websrv" ] ; then
BUILD_WEBSRVFRONT=1
echo_info "Build of websrv frontend enabled"
fi
done
fi
############################################
# Optional targets (others than libraries) #
############################################
if [ "$BUILD_WEBSRVFRONT" = "1" ] ; then
npmv=`command npm -v`
npms=$?
nodev=`command node -v`
nodes=$?
if [ "$npms" == "0" -a "$nodes" == "0" ] ; then
compilations $BUILD_DIR websrvfront
else
echo_warning "build of websrv front-end skipped, dependencies not met : " $npmv " " $nodev
fi
fi
####################################################
# Build RF device and transport protocol libraries #
####################################################
......
......@@ -18,6 +18,7 @@ The debug level is a mask:
* bit 2: print memory allocation/free performed by the config module
* bit 3: print command line processing messages
* bit 4: disable execution abort when parameters checking fails
* bit 5: write a config file reflecting the parameters after reading the configuration file and processing the command line
As a oai user, you may have to use bit 1 (dbgl1) , to check your configuration and get the full name of a parameter you would like to modify on the command line. Other bits are for developers usage, (dbgl7 will print all debug messages).
......@@ -27,7 +28,10 @@ $ ./lte-softmodem -O libconfig:<config>:dbgl1
```bash
$ ./lte-uesoftmodem -O cmdlineonly:dbgl1
```
bit 5 (dbgl32) can be useful to detect unused parameters from a config file or to detect parameters set to their default values, and not present in the config file
To get help on supported parameters you can use specific options:
* ---help: print help for command line only parameters and for parameters not defined in a specific section
* ---help_< prefix > : print help for parameters defined under the section < prefix >
......
......@@ -88,10 +88,26 @@ int load_config_sharedlib(configmodule_interface_t *cfgptr) {
st = -1;
}
if (cfgptr->rtflags & CONFIG_SAVERUNCFG) {
sprintf(fname, "config_%s_set", cfgptr->cfgmode);
cfgptr->set = dlsym(lib_handle, fname);
if (cfgptr->set == NULL) {
printf("[CONFIG] %s %d no function %s for config mode %s\n", __FILE__, __LINE__, fname, cfgptr->cfgmode);
st = -1;
}
sprintf(fname, "config_%s_write_parsedcfg", cfgptr->cfgmode);
cfgptr->write_parsedcfg = dlsym(lib_handle, fname);
if (cfgptr->write_parsedcfg == NULL) {
printf("[CONFIG] %s %d no function %s for config mode %s\n", __FILE__, __LINE__, fname, cfgptr->cfgmode);
}
}
sprintf (fname,"config_%s_end",cfgptr->cfgmode);
cfgptr->end = dlsym(lib_handle,fname);
if (cfgptr->getlist == NULL ) {
if (cfgptr->end == NULL) {
printf("[CONFIG] %s %d no function %s for config mode %s\n",
__FILE__, __LINE__,fname, cfgptr->cfgmode);
}
......@@ -193,6 +209,7 @@ configmodule_interface_t *load_configmodule(int argc,
uint32_t tmpflags=0;
int i;
int OoptIdx=-1;
int OWoptIdx = -1;
printf("CMDLINE: ");
for (int i=0; i<argc; i++)
......@@ -209,6 +226,10 @@ configmodule_interface_t *load_configmodule(int argc,
OoptIdx=i;
}
char *OWopt = strstr(argv[i], "OW");
if (OWopt == argv[i] + 2) {
OWoptIdx = i;
}
if ( strstr(argv[i], "help_config") != NULL ) {
config_printhelp(Config_Params,CONFIG_PARAMLENGTH(Config_Params),CONFIG_SECTIONNAME);
exit(0);
......@@ -258,6 +279,11 @@ configmodule_interface_t *load_configmodule(int argc,
/* argv[0] is the exec name, always Ok */
cfgptr->argv_info[0] |= CONFIG_CMDLINEOPT_PROCESSED;
/* When reuested _(_--OW or rtflag is 5), a file with config parameters, as defined after all processing, will be created */
if (OWoptIdx >= 0) {
cfgptr->argv_info[OWoptIdx] |= CONFIG_CMDLINEOPT_PROCESSED;
cfgptr->rtflags |= CONFIG_SAVERUNCFG;
}
/* when OoptIdx is >0, -O option has been detected at position OoptIdx
* we must memorize arv[OoptIdx is Ok */
if (OoptIdx >= 0) {
......@@ -294,14 +320,18 @@ configmodule_interface_t *load_configmodule(int argc,
printf("%s ",cfgptr->cfgP[i]);
}
printf(", debug flags: 0x%08x\n",cfgptr->rtflags);
if (cfgptr->rtflags & CONFIG_PRINTPARAMS) {
cfgptr->status = malloc(sizeof(configmodule_status_t));
}
if (strstr(cfgparam,CONFIG_CMDLINEONLY) == NULL) {
i=load_config_sharedlib(cfgptr);
if (i == 0) {
printf("[CONFIG] config module %s loaded\n",cfgmode);
Config_Params[CONFIGPARAM_DEBUGFLAGS_IDX].uptr=&(cfgptr->rtflags);
int idx = config_paramidx_fromname(Config_Params, CONFIG_PARAMLENGTH(Config_Params), CONFIGP_DEBUGFLAGS);
Config_Params[idx].uptr = &(cfgptr->rtflags);
idx = config_paramidx_fromname(Config_Params, CONFIG_PARAMLENGTH(Config_Params), CONFIGP_TMPDIR);
Config_Params[idx].strptr = &(cfgptr->tmpdir);
config_get(Config_Params,CONFIG_PARAMLENGTH(Config_Params), CONFIG_SECTIONNAME );
} else {
fprintf(stderr,"[CONFIG] %s %d config module \"%s\" couldn't be loaded\n", __FILE__, __LINE__,cfgmode);
......@@ -314,6 +344,8 @@ configmodule_interface_t *load_configmodule(int argc,
cfgptr->end = (configmodule_endfunc_t)nooptfunc;
}
printf("[CONFIG] debug flags: 0x%08x\n", cfgptr->rtflags);
if (modeparams != NULL) free(modeparams);
if (cfgmode != NULL) free(cfgmode);
......@@ -326,10 +358,28 @@ configmodule_interface_t *load_configmodule(int argc,
return cfgptr;
}
/* Possibly write config file, with parameters which have been read and after command line parsing */
void write_parsedcfg(void)
{
if (cfgptr->status && (cfgptr->rtflags & CONFIG_SAVERUNCFG)) {
printf_params("[CONFIG] Runtime params creation status: %i null values, %i errors, %i empty list or array, %i successfull \n",
cfgptr->status->num_err_nullvalue,
cfgptr->status->num_err_write,
cfgptr->status->emptyla,
cfgptr->status->num_write);
}
if (cfgptr != NULL) {
if (cfgptr->write_parsedcfg != NULL) {
printf("[CONFIG] calling config module write_parsedcfg function...\n");
cfgptr->write_parsedcfg();
}
}
}
/* free memory allocated when reading parameters */
/* config module could be initialized again after this call */
void end_configmodule(void) {
write_parsedcfg();
if (cfgptr != NULL) {
if (cfgptr->end != NULL) {
printf ("[CONFIG] calling config module end function...\n");
......
......@@ -54,6 +54,7 @@
#define CONFIG_DEBUGPTR (1<<1) // print memory allocation/free debug messages
#define CONFIG_DEBUGCMDLINE (1<<2) // print command line processing messages
#define CONFIG_NOABORTONCHKF (1<<4) // disable abort execution when parameter checking function fails
#define CONFIG_SAVERUNCFG (1 << 5) // create config file with running values, as after cfg file + command line processing
#define CONFIG_NOEXITONHELP (1<<19) // do not exit after printing help
#define CONFIG_HELP (1<<20) // print help message
#define CONFIG_ABORT (1<<21) // config failed,abort execution
......@@ -61,8 +62,21 @@
typedef int(*configmodule_initfunc_t)(char *cfgP[],int numP);
typedef int(*configmodule_getfunc_t)(paramdef_t *,int numparams, char *prefix);
typedef int(*configmodule_getlistfunc_t)(paramlist_def_t *, paramdef_t *,int numparams, char *prefix);
typedef int (*configmodule_setfunc_t)(paramdef_t *cfgoptions, int numoptions, char *prefix);
typedef void(*configmodule_endfunc_t)(void);
typedef struct configmodule_status {
int num_paramgroups;
char **paramgroups_names;
int num_err_nullvalue;
int emptyla;
int num_err_read;
int num_err_write;
int num_read;
int num_write;
char *debug_cfgname;
} configmodule_status_t;
typedef struct oneBlock_s{
void *ptrs;
int sz;
......@@ -80,11 +94,15 @@ typedef struct configmodule_interface {
configmodule_initfunc_t init;
configmodule_getfunc_t get;
configmodule_getlistfunc_t getlist;
configmodule_setfunc_t set;
configmodule_endfunc_t end;
pthread_mutex_t memBlocks_mutex;
configmodule_endfunc_t write_parsedcfg;
uint32_t numptrs;
uint32_t rtflags;
oneBlock_t oneBlock[CONFIG_MAX_ALLOCATEDPTRS];
char *tmpdir;
configmodule_status_t *status; // allocated in debug mode only
} configmodule_interface_t;
#ifdef CONFIG_LOADCONFIG_MAIN
......@@ -97,16 +115,19 @@ static char config_helpstr [] = "\n lte-softmodem -O [config mode]<:dbgl[debugfl
incp parameter can be used to define the include path used for config files (@include directive)\n \
defaults is set to the path of the main config file.\n";
#define CONFIG_SECTIONNAME "config"
#define CONFIGPARAM_DEBUGFLAGS_IDX 0
#define CONFIG_HELP_TMPDIR "<Repository to create temporary file>"
#define CONFIG_SECTIONNAME "config"
#define CONFIGP_DEBUGFLAGS "debugflags"
#define CONFIGP_TMPDIR "tmpdir"
static paramdef_t Config_Params[] = {
/*-----------------------------------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------------------------------------------*/
/* config parameters for config module */
/* optname helpstr paramflags XXXptr defXXXval type numelt */
/*-----------------------------------------------------------------------------------------------------------------------*/
{"debugflags", config_helpstr, 0, uptr:NULL, defintval:0, TYPE_MASK, 0},
/*--------------------------------------------------------------------------------------------------------------------------*/
{CONFIGP_DEBUGFLAGS, config_helpstr, 0, uptr : NULL, defintval : 0, TYPE_MASK, 0},
{CONFIGP_TMPDIR, CONFIG_HELP_TMPDIR, PARAMFLAG_NOFREE, strptr : NULL, defstrval : "/tmp", TYPE_STRING, 0},
};
#else
......@@ -126,6 +147,8 @@ extern configmodule_interface_t *load_configmodule(int argc, char **argv, uint32
* a new config module init
*/
extern void end_configmodule(void);
extern void write_parsedcfg(void);
/* free all config module memory, to be used at end of program as
* it will free parameters values even those specified with the PARAMFLAG_NOFREE flag */
extern void free_configmodule(void);
......
......@@ -46,6 +46,7 @@
#define PARAMFLAG_NOFREE (1 << 3) // don't free parameter in end function
#define PARAMFLAG_BOOL (1 << 4) // integer param can be 0 or 1
#define PARAMFLAG_CMDLINE_NOPREFIXENABLED (1 << 5) // on the command line, allow a parameter to be specified without the prefix
#define PARAMFLAG_CMDLINEONLY (1 << 6) // this parameter cannot be specified in config file
/* Flags used by config modules to return info to calling modules and/or to for internal usage*/
#define PARAMFLAG_MALLOCINCONFIG (1 << 15) // parameter allocated in config module
......
......@@ -275,6 +275,9 @@ int config_get(paramdef_t *params, int numparams, char *prefix) {
if (ret >= 0) {
config_process_cmdline(params, numparams, prefix);
if (cfgif->rtflags & CONFIG_SAVERUNCFG) {
config_get_if()->set(params, numparams, prefix);
}
config_execcheck(params, numparams, prefix);
}
......@@ -312,6 +315,9 @@ int config_getlist(paramlist_def_t *ParamList, paramdef_t *params, int numparams
// TODO config_process_cmdline?
sprintf(cfgpath, "%s.[%i]", newprefix, i);
config_process_cmdline(ParamList->paramarray[i],numparams,cfgpath);
if (config_get_if()->rtflags & CONFIG_SAVERUNCFG) {
config_get_if()->set(ParamList->paramarray[i], numparams, cfgpath);
}
config_execcheck(ParamList->paramarray[i], numparams, cfgpath);
}
......
This diff is collapsed.
......@@ -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)
This diff is collapsed.
......@@ -48,19 +48,54 @@
#define CMDSTATUS_VARNOTFOUND 3
#define CMDSTATUS_NOTFOUND 4
/* definitions to store 2 dim table, used to store command results before */
/* displaying them either on console or web page */
#define TELNET_MAXLINE_NUM 100
#define TELNET_MAXCOL_NUM 10
typedef struct col {
char coltitle[TELNET_CMD_MAXSIZE];
unsigned int coltype;
} acol_t;
typedef struct line {
char *val[TELNET_MAXCOL_NUM];
} aline_t;
typedef struct webdatadef {
char tblname[TELNET_HELPSTR_SIZE];
int numlines;
int numcols;
acol_t columns[TELNET_MAXCOL_NUM];
aline_t lines[TELNET_MAXLINE_NUM];
} webdatadef_t;
/*----------------------------------------------------------------------------*/
/* structure to be used when adding a module to the telnet server */
/* This is the second parameter of the add_telnetcmd function, which can be used */
/* to add a set of new command to the telnet server shell */
typedef void(*telnet_printfunc_t)(const char* format, ...);
typedef int(*cmdfunc_t)(char*, int, telnet_printfunc_t prnt);
typedef int (*webfunc_t)(char *cmdbuff, int debug, telnet_printfunc_t prnt, ...);
typedef int (*webfunc_getdata_t)(char *cmdbuff, int debug, void *data, telnet_printfunc_t prnt);
typedef int(*qcmdfunc_t)(char*, int, telnet_printfunc_t prnt,void *arg);
#define TELNETSRV_CMDFLAG_PUSHINTPOOLQ (1<<0) // ask the telnet server to push the command in a thread pool queue
#define TELNETSRV_CMDFLAG_PUSHINTPOOLQ (1 << 0) // ask the telnet server to push the command in a thread pool queue
#define TELNETSRV_CMDFLAG_GETWEBDATA (1 << 1) // When called from web server, use the getdata variant of the function
#define TELNETSRV_CMDFLAG_TELNETONLY (1 << 2) // Command only for telnet client connections
#define TELNETSRV_CMDFLAG_WEBSRVONLY (1 << 3) // Command only for web server connections
#define TELNETSRV_CMDFLAG_CONFEXEC (1 << 4) // Ask for confirm before exec
#define TELNETSRV_CMDFLAG_GETWEBTBLDATA (1 << 8) // When called from web server, use the get table data variant of the function
#define TELNETSRV_CMDFLAG_NEEDPARAM (1 << 10) // websrv: The command needs a parameter
#define TELNETSRV_CMDFLAG_WEBSRV_SETRETURNTBL (1 << 11) // websrv: set callback returns a new table
#define TELNETSRV_CMDFLAG_AUTOUPDATE (1 << 12) // command can be re-submitted automatically for result update
typedef struct cmddef {
char cmdname[TELNET_CMD_MAXSIZE];
char helpstr[TELNET_HELPSTR_SIZE];
cmdfunc_t cmdfunc;
union {
webfunc_t webfunc;
webfunc_getdata_t webfunc_getdata;
};
unsigned int cmdflags;
void *qptr;
} telnetshell_cmddef_t;
......@@ -78,6 +113,8 @@ typedef struct telnetsrv_qmsg {
/*structure to be used when adding a module to the telnet server */
/* This is the first parameter of the add_telnetcmd function, which can be used */
/* to add a set of new variables which can be got/set from the telnet server shell */
/* var type: bits 0-3, type value (1 to 15)
* type modifier, bits 4-31 */
#define TELNET_VARTYPE_INT32 1
#define TELNET_VARTYPE_INT16 2
#define TELNET_VARTYPE_INT64 3
......@@ -85,9 +122,16 @@ typedef struct telnetsrv_qmsg {
#define TELNET_VARTYPE_DOUBLE 5
#define TELNET_VARTYPE_INT8 6
#define TELNET_VARTYPE_UINT 7
#define TELNET_CHECKVAL_RDONLY 16
#define TELNET_CHECKVAL_BOOL 32
#define TELNET_VAR_NEEDFREE 64
#define TELNET_CHECKVAL_LOGLVL 128
#define TELNET_CHECKVAL_SIMALGO 256
typedef struct variabledef {
char varname[TELNET_CMD_MAXSIZE];
char vartype;
char checkval;
void *varvalptr;
} telnetshell_vardef_t;
......@@ -113,6 +157,7 @@ typedef struct {
int priority; // server running priority
char *histfile; // command history
int histsize; // command history length
char *logfile; // filename to use when redirecting output to file
int new_socket; // socket of the client connection
int logfilefd; // file id of the log file when log output is redirected to a file
int saved_stdout; // file id of the previous stdout, used to be able to restore original stdout
......@@ -145,14 +190,25 @@ VT escape sequence definition, for smarter display....
#define STDFMT "\x1b[0m"
/*---------------------------------------------------------------------------------------------*/
#define NICE_MAX 19
#define NICE_MIN -20
#define TELNET_ADDCMD_FNAME "add_telnetcmd"
#define TELNET_POLLCMDQ_FNAME "poll_telnetcmdq"
#define TELNET_PUSHCMD_FNAME "telnet_pushcmd"
typedef int(*add_telnetcmd_func_t)(char *, telnetshell_vardef_t *, telnetshell_cmddef_t *);
typedef void(*poll_telnetcmdq_func_t)(void *qid,void *arg);
typedef void (*push_telnetcmd_func_t)(telnetshell_cmddef_t *cmd, char *cmdbuff, telnet_printfunc_t prnt);
#ifdef TELNETSERVERCODE
int add_telnetcmd(char *modulename, telnetshell_vardef_t *var, telnetshell_cmddef_t *cmd);
void set_sched(pthread_t tid, int pid,int priority);
void set_affinity(pthread_t tid, int pid, int coreid);
extern int get_phybsize(void);
#endif
#ifdef WEBSERVERCODE
extern void telnet_pushcmd(telnetshell_cmddef_t *cmd, char *cmdbuff, telnet_printfunc_t prnt);
extern telnetsrv_params_t *get_telnetsrv_params(void);
extern char *telnet_getvarvalue(telnetshell_vardef_t *var, int varindex);
extern int telnet_setvarvalue(telnetshell_vardef_t *var, char *strval, telnet_printfunc_t prnt);
extern void telnetsrv_freetbldata(webdatadef_t *wdata);
#endif
#endif
......@@ -32,6 +32,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
......
This diff is collapsed.
......@@ -44,11 +44,12 @@
extern int proccmd_show(char *buf, int debug, telnet_printfunc_t prnt);
extern int proccmd_thread(char *buf, int debug, telnet_printfunc_t prnt);
extern int proccmd_exit(char *buf, int debug, telnet_printfunc_t prnt);
extern int proccmd_restart(char *buf, int debug, telnet_printfunc_t prnt);
extern int proccmd_log(char *buf, int debug, telnet_printfunc_t prnt);
telnetshell_vardef_t proc_vardef[] = {
{"",0,NULL}
};
#define PROCCMD_LOG_HELP_STRING " log sub commands: \n\
extern int proccmd_websrv_getdata(char *cmdbuff, int debug, void *data, telnet_printfunc_t prnt);
telnetshell_vardef_t proc_vardef[] = {{"", 0, 0, NULL}};
#define PROCCMD_LOG_HELP_STRING \
" log sub commands: \n\
show: display current log configuration \n\
online, noonline: enable or disable console logs \n\
enable, disable id1-id2: enable or disable logs for components index id1 to id2 \n\
......@@ -62,22 +63,30 @@ telnetshell_vardef_t proc_vardef[] = {
dump_<mod> debug_<mod > disable or enable the debug file generation or debug code\
for \"mod\" module. use the show command to get the available modules\n"
#define PROCCMD_THREAD_HELP_STRING " thread sub commands: \n\
#define PROCCMD_THREAD_HELP_STRING \
" thread sub commands: \n\
<thread id> aff <core> : set affinity of thread <thread id> to core <core> \n\
<thread id> prio <prio> : set scheduling parameters for thread <thread id> \n\
if prio < -20: linux scheduling policy set to FIFO, \n\
with priority = -20 - prio \n\
if prio > 19: linux scheduling policy set to OTHER, \n\
with priority (nice value) = prio \n\
-100 < prio < 0: linux scheduling policy set to FIFO, priority -prio, \n\
-200 < prio < -100: linux scheduling policy set to RR, priority -(prio+100), \n\
0 <= prio < 40: linux scheduling policy set to NORMAL, nice value prio-20 \n\
40 <= prio < 80: linux scheduling policy set to BATCH, nice value prio-60 \n\
prio >=80: linux scheduling policy set to IDLE \n\
use \"softmodem show thread\" to get <thread id> \n"
telnetshell_cmddef_t proc_cmdarray[] = {
{"show","loglvl|thread|config", proccmd_show},
{"log","(enter help for details)", proccmd_log},
{"thread","(enter help for details)", proccmd_thread},
{"exit","", proccmd_exit},
{"","",NULL},
{"show", "loglvl|thread|config", proccmd_show, {(webfunc_t)proccmd_websrv_getdata}, TELNETSRV_CMDFLAG_TELNETONLY, NULL},
{"log", "(enter help for details)", proccmd_log, {NULL}, TELNETSRV_CMDFLAG_TELNETONLY, NULL},
{"show loglvl", "", proccmd_log, {(webfunc_t)proccmd_websrv_getdata}, TELNETSRV_CMDFLAG_WEBSRVONLY | TELNETSRV_CMDFLAG_GETWEBTBLDATA, NULL},
{"show logopt", "", proccmd_log, {(webfunc_t)proccmd_websrv_getdata}, TELNETSRV_CMDFLAG_WEBSRVONLY | TELNETSRV_CMDFLAG_GETWEBTBLDATA, NULL},
{"show dbgopt", "", proccmd_log, {(webfunc_t)proccmd_websrv_getdata}, TELNETSRV_CMDFLAG_WEBSRVONLY | TELNETSRV_CMDFLAG_GETWEBTBLDATA, NULL},
{"show config", "", proccmd_show, {(webfunc_t)proccmd_show}, TELNETSRV_CMDFLAG_WEBSRVONLY, NULL},
{"show thread", "", proccmd_thread, {(webfunc_t)proccmd_show}, TELNETSRV_CMDFLAG_WEBSRVONLY | TELNETSRV_CMDFLAG_AUTOUPDATE, NULL},
{"thread", "(enter help for details)", proccmd_thread, {NULL}, TELNETSRV_CMDFLAG_TELNETONLY, NULL},
{"show threadsched", "", proccmd_show, {(webfunc_t)proccmd_websrv_getdata}, TELNETSRV_CMDFLAG_WEBSRVONLY | TELNETSRV_CMDFLAG_GETWEBTBLDATA, NULL},
{"exit", "", proccmd_exit, {NULL}, TELNETSRV_CMDFLAG_CONFEXEC, NULL},
{"restart", "", proccmd_restart, {NULL}, TELNETSRV_CMDFLAG_CONFEXEC, NULL},
{"", "", NULL},
};
#else
extern void add_proccmd_cmds(void);
......
The oai web server is an optional monitoring and debugging tool. Purpose is to give access to oai softmodems functionalities via a web interface. In this first release interface to the telnet server commands and to the softscope are delivered.
* [Using the web server](webserverusage.md)
* [enhancing the webserver](webserverdevcmd.md)
* [web server architecture ](webserverarch.md)
[oai Wikis home](https://gitlab.eurecom.fr/oai/openairinterface5g/wikis/home)
# web server interface principles
The web server interface is implemented in two parts: a back-end, included in the softmodem process and a front-end which is a browser application.
The oai web server back-end is implemented in a shared library to be loaded by the [oai shared library loader](loader) when `--websrv` option is specified on the command line. `libwebsrv.so ` code is common to all oai softmodem executables, the current release has been tested with the nr UE and the gNB.
The front-end is an [angular](https://angular.io/docs) application. After being built and installed it is delivered to browsers by the back-end at connection time.
Front-end and back-end communicate via http request - http response transactions, including `json` body. these `json` interfaces are defined in the [frontend/src/app/api/XXXX.api.ts](https://gitlab.eurecom.fr/oai/openairinterface5g/tree/develop/common/utils/websrv/src/frontend/src/app/api/) files. Back-end decapsulates the http requests it receives, looking for the `json`body to determine the requested information or command. Then the back-end builds a http response, with a `json`body encapsulating the requested information or the requested command result.
When unsolicited communication, from back-end to front-end is necessary, a [websocket](https://www.rfc-editor.org/rfc/rfc6455) link is opened. This is the case for the softscope interface.
# web server interface source files
web server source files are located in [common/utils/websrv](https://gitlab.eurecom.fr/oai/openairinterface5g/tree/develop/common/utils/websrv)
1. back-end files are directly located in the `websrv` repository
1. The [frontend/src](https://gitlab.eurecom.fr/oai/openairinterface5g/tree/develop/common/utils/websrv/src/frontend) sub-directory contains the angular front-end source tree.
1. [common/utils/websrv/helpfiles](https://gitlab.eurecom.fr/oai/openairinterface5g/tree/develop/common/utils/websrv/helpfiles) contains files delivered to the front-end to respond to help requests.
[oai web server interface home](websrv.md)
\ No newline at end of file
This diff is collapsed.
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# For the full list of supported browsers by the Angular framework, please see:
# https://angular.io/guide/browser-support
# You can see what browsers were selected by your queries by running:
# npx browserslist
last 1 Chrome version
last 1 Firefox version
last 2 Edge major versions
last 2 Safari major versions
last 2 iOS major versions
Firefox ESR
not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false
{
"root": true,
"ignorePatterns": [
"projects/**/*"
],
"overrides": [
{
"files": [
"*.ts"
],
"parserOptions": {
"project": [
"common/utils/websrv/frontend/tsconfig.json"
],
"createDefaultProgram": true
},
"extends": [
"plugin:@angular-eslint/recommended",
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {
"@angular-eslint/directive-selector": [
"error",
{
"type": "attribute",
"prefix": "app",
"style": "camelCase"
}
],
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "app",
"style": "kebab-case"
}
]
}
},
{
"files": [
"*.html"
],
"extends": [
"plugin:@angular-eslint/template/recommended"
],
"rules": {}
}
]
}
\ No newline at end of file
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
/artifacts
/builds
# Only exists if Bazel was run
/bazel-out
# dependencies
/node_modules
# profiling files
chrome-profiler-events*.json
speed-measure-plugin*.json
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# misc
/.angular/cache
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db
config.toml
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"cli": {
"analytics": false,
"schematicCollections": [
"@angular-eslint/schematics"
]
},
"version": 1,
"newProjectRoot": "projects",
"projects": {
"softmodemngx": {
"projectType": "application",
"schematics": {
"@schematics/angular:application": {
"strict": true
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/softmodem-ngx",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/purple-green.css",
"src/styles.css"
],
"scripts": [],
"allowedCommonJsDependencies": [
"@firebase/auth",
"firebase"
]
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "1.5mb",
"maximumError": "2mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"outputHashing": "all"
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"browserTarget": "softmodemngx:build:production"
},
"development": {
"browserTarget": "softmodemngx:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "softmodemngx:build"
}
},
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
"lintFilePatterns": [
"src/**/*.ts",
"src/**/*.html"
]
}
}
}
}
}
}
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const {SpecReporter, StacktraceOption} = require("jasmine-spec-reporter");
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout : 11000,
specs : [ "./src/**/*.e2e-spec.ts" ],
capabilities : {browserName : "chrome", chromeOptions : {args : [ "--headless", "--no-sandbox" ]}},
directConnect : true,
SELENIUM_PROMISE_MANAGER : true,
baseUrl : "http://localhost:4200/",
framework : "jasmine",
jasmineNodeOpts : {showColors : true, defaultTimeoutInterval : 30000, print : function() {}},
onPrepare() {
require("ts-node").register({project : require("path").join(__dirname, "./tsconfig.json")});
jasmine.getEnv().addReporter(new SpecReporter({spec : {displayStacktrace : StacktraceOption.PRETTY}}));
}
};
\ No newline at end of file
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const {SpecReporter, StacktraceOption} = require("jasmine-spec-reporter");
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout : 11000,
specs : [ "./src/**/*.e2e-spec.ts" ],
capabilities : {browserName : "chrome"},
directConnect : true,
SELENIUM_PROMISE_MANAGER : false,
baseUrl : "http://localhost:4200/",
framework : "jasmine",
jasmineNodeOpts : {showColors : true, defaultTimeoutInterval : 30000, print : function() {}},
onPrepare() {
require("ts-node").register({project : require("path").join(__dirname, "./tsconfig.json")});
jasmine.getEnv().addReporter(new SpecReporter({spec : {displayStacktrace : StacktraceOption.PRETTY}}));
}
};
import {browser, logging} from "protractor";
import {AppPage} from "./app.po";
describe("workspace-project App", () => {
let page: AppPage;
beforeEach(() => { page = new AppPage(); });
// it('should display welcome message', async () => {
// await page.navigateTo();
// expect(await page.getTitleText()).toEqual('example app is running!');
// });
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level : logging.Level.SEVERE,
} as logging.Entry));
});
});
import {browser, by, element} from "protractor";
export class AppPage {
async navigateTo(): Promise<unknown>
{
return browser.get(browser.baseUrl);
}
async getTitleText(): Promise<string>
{
return element(by.css("app-root .content span")).getText();
}
}
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es2018",
"types": ["jasmine", "node"]
}
}
This diff is collapsed.
{
"name": "softmodemngx",
"version": "2.0.0",
"scripts": {
"lint": "ng lint",
"build": "ng build",
"dev": "ng serve --configuration=development",
"prod": "ng serve --configuration=production"
},
"private": true,
"dependencies": {
"@angular-slider/ngx-slider": "^2.0.4",
"@angular/animations": "^14.1.0",
"@angular/cdk": "^14.1.0",
"@angular/common": "^14.1.0",
"@angular/compiler": "^14.1.0",
"@angular/core": "^14.1.0",
"@angular/flex-layout": "^14.0.0-beta.40",
"@angular/forms": "^14.1.0",
"@angular/material": "^14.1.0",
"@angular/platform-browser": "^14.1.0",
"@angular/platform-browser-dynamic": "^14.1.0",
"@angular/router": "^14.1.0",
"chart.js": "^3.8.2",
"moment": "^2.29.3",
"ng2-charts": "^4.0.0",
"rxjs": "^6.6.6",
"tslib": "^2.0.0",
"zone.js": "^0.11.4"
},
"devDependencies": {
"@angular-devkit/architect": "^0.1401.0",
"@angular-devkit/build-angular": "^14.1.0",
"@angular-eslint/builder": "14.0.2",
"@angular-eslint/eslint-plugin": "14.0.2",
"@angular-eslint/eslint-plugin-template": "14.0.2",
"@angular-eslint/schematics": "14.0.2",
"@angular-eslint/template-parser": "14.0.2",
"@angular/cli": "^14.1.0",
"@angular/compiler-cli": "^14.1.0",
"@angular/language-service": "^14.1.0",
"@types/node": "^12.20.23",
"@typescript-eslint/eslint-plugin": "^5.29.0",
"@typescript-eslint/parser": "^5.29.0",
"eslint": "^8.18.0",
"fs-extra": "^10.0.0",
"fuzzy": "^0.1.3",
"husky": "^5.1.3",
"inquirer": "^6.2.2",
"inquirer-autocomplete-prompt": "^1.0.1",
"jasmine-core": "~3.7.1",
"jasmine-spec-reporter": "~5.0.0",
"jsonc-parser": "^3.0.0",
"open": "^7.0.3",
"ts-node": "^8.10.2",
"tslint-angular": "^3.0.3",
"typescript": "~4.6.4"
},
"husky": {
"hooks": {
"pre-commit": "pretty-quick --staged && ng lint && ng test",
"pre-push": "ng build --aot true"
}
}
}
import {HttpClient} from "@angular/common/http";
import {Injectable} from "@angular/core";
import {environment} from "src/environments/environment";
export interface IInfo {
name: string;
value: string;
type: IArgType;
modifiable: boolean; // set command ?
}
export interface IModule {
name: string;
}
export enum ILogLvl {
error = "error",
warn = "warn",
analysis = "analysis",
info = "info",
debug = "debug",
trace = "trace"
}
export enum ILogOutput {
stdout = "stdout",
telnet = "telnet",
web = "web",
file = "/tmp/<component>.log",
}
export enum IArgType {
boolean = "boolean",
list = "list",
loglvl = "loglvl",
range = "range",
number = "number",
string = "string",
configfile = "configfile",
simuTypes = "simuTypes",
}
export enum ICommandOptions {
update = "update", // result can be updated, triggers update button on result page when set
help = "help" // help tooltip available on command buttons
}
export interface IVariable {
name: string;
value: string;
type: IArgType;
modifiable: boolean; // set command ?
}
export interface ICommand {
name: string;
confirm?: string;
question?: IQuestion;
param?: IVariable;
options?: ICommandOptions[];
}
export interface ITable {
columns: IColumn[];
rows: string[];
}
export interface IQuestion {
display: string;
pname: string;
type: IArgType;
}
export interface IResp {
display: string[], table?: ITable
}
export interface IParam {
value: string, col: IColumn
}
export interface IColumn { // should use IVariable ?
name: string;
type: IArgType;
modifiable: boolean; // set command ?
help: boolean; // is help available
}
export interface IRow {
params: IParam[], rawIndex: number, cmdName: string,
param?: IVariable // to transmit the initial command parameter, ex: the channel model index when modify a channel model
}
export const route = "oaisoftmodem/";
@Injectable({
providedIn : "root",
})
export class CommandsApi {
constructor(private httpClient: HttpClient)
{
}
public readInfos$ = () => this.httpClient.get<IInfo[]>(environment.backend + route + "variables/");
public setInfo$ = (info: IInfo) => this.httpClient.post<IResp>(environment.backend + route + "variables/", info);
public readModules$ = () => this.httpClient.get<IModule[]>(environment.backend + route + "commands/");
public readVariables$ = (moduleName: string) => this.httpClient.get<IInfo[]>(environment.backend + route + moduleName + "/variables/");
public readCommands$ = (moduleName: string) => this.httpClient.get<ICommand[]>(environment.backend + route + moduleName + "/commands/");
public runCommand$ = (command: ICommand, moduleName: string) => this.httpClient.post<IResp>(environment.backend + route + moduleName + "/commands/", command);
public setCmdVariable$ = (variable: IInfo, moduleName: string) => this.httpClient.post<IResp>(environment.backend + route + moduleName + "/variables/", variable);
public setCmdParams$ = (row: IRow, moduleName: string) => this.httpClient.post<IResp>(environment.backend + route + moduleName + "/set/", row);
}
import {HttpClient} from "@angular/common/http";
import {Injectable} from "@angular/core";
import {Observable} from "rxjs";
import {map} from "rxjs/operators";
import {environment} from "src/environments/environment";
export interface HelpRequest {
module: string;
command: string;
object: string;
}
export interface HelpResp {
text: string;
}
const hlproute = "oaisoftmodem/helpfiles/";
@Injectable({
providedIn : "root",
})
export class HelpApi {
constructor(private httpClient: HttpClient)
{
}
public getHelp$ = (req: HelpRequest) => this.httpClient.get<HelpResp>(environment.backend + hlproute + req.module + "_" + req.command + "_" + req.object + ".html", {observe : "response"});
public getHelpText(module: string, command: string, object: string): Observable<string>
{
return this.getHelp$({module : module, command : command.replace(" ", "_"), object : object.replace(" ", "_")})
.pipe(map(
(response => { return (response.status == 201) ? response.body!.text : ""; }),
)); // pipe
}
}
import {HttpClient} from "@angular/common/http";
import {Injectable} from "@angular/core";
import {environment} from "src/environments/environment";
export enum IScopeGraphType {
IQs = "IQs",
LLR = "LLR",
WF = "WF",
TRESP = "TRESP",
UNSUP = "UNSUP"
}
export interface IGraphDesc {
title: string;
type: IScopeGraphType;
id: number;
srvidx: number;
}
export interface IScopeDesc {
title: string;
graphs: IGraphDesc[];
}
export interface IScopeCmd {
name: string;
graphid?: number; // the graph srvidx
value: string;
}
export interface ISigDesc {
target_id: number;
antenna_id: number;
}
const route = "oaisoftmodem/scopectrl/";
@Injectable({
providedIn : "root",
})
export class ScopeApi {
constructor(private httpClient: HttpClient)
{
}
public getScopeInfos$ = () => this.httpClient.get<IScopeDesc>(environment.backend + route);
public setScopeParams$ = (cmd: IScopeCmd) => this.httpClient.post(environment.backend + route, cmd, {observe : "response"});
}
import {NgModule} from "@angular/core";
import {RouterModule, Routes} from "@angular/router";
import {AppComponent} from "./app.component";
import {CommandsComponent} from "./components/commands/commands.component";
const routes: Routes = [
{path : "", redirectTo : "/websrv", pathMatch : "full"},
{path : "websrv", component : AppComponent},
{path : "**", redirectTo : ""},
];
@NgModule({
imports : [ RouterModule.forRoot(routes, {relativeLinkResolution : "legacy"}) ],
exports : [ RouterModule ],
})
export class AppRoutingModule {
}
.mat-tab-group {
margin-bottom: 10px;
}
.mat-ink-bar{
height: 8px !important;
}
.mat-tab-label-active
{
color: #000000 !important;
}
<mat-tab-group backgroundColor="primary" color="accent">
<mat-tab label="Commands">
<app-commands></app-commands>
</mat-tab>
<mat-tab label={{scopelabel}} disabled={{!isscopeavailable}}>
<app-scope (ScopeEnabled)="onScopeEnabled($event)"></app-scope>
</mat-tab>
</mat-tab-group>
\ No newline at end of file
import {Component} from "@angular/core";
import {MatTabsModule} from "@angular/material/tabs";
@Component({
selector : "app-root",
templateUrl : "./app.component.html",
styleUrls : [ "./app.component.css" ],
})
export class AppComponent {
title = "oai softmodem";
isscopeavailable = false;
scopelabel = "";
constructor()
{
this.scopelabel = "";
this.isscopeavailable = false;
}
onScopeEnabled(enabled: boolean)
{
this.isscopeavailable = enabled;
this.scopelabel = enabled ? "Scope" : "";
}
}
import {DragDropModule} from "@angular/cdk/drag-drop";
import {HttpClientModule} from "@angular/common/http";
import {NgModule} from "@angular/core";
import {FlexLayoutModule} from "@angular/flex-layout";
import {FormsModule, ReactiveFormsModule} from "@angular/forms";
import {MatButtonModule} from "@angular/material/button";
import {MatCardModule} from "@angular/material/card";
import {MatChipsModule} from "@angular/material/chips";
import {MatDialogModule} from "@angular/material/dialog";
import {MatFormFieldModule} from "@angular/material/form-field";
import {MatGridListModule} from "@angular/material/grid-list";
import {MatInputModule} from "@angular/material/input";
import {MatListModule} from "@angular/material/list";
import {MatProgressSpinnerModule} from "@angular/material/progress-spinner";
import {MatSelectModule} from "@angular/material/select";
import {MatSlideToggleModule} from "@angular/material/slide-toggle";
import {MatSliderModule} from "@angular/material/slider";
import {MatSnackBarModule} from "@angular/material/snack-bar";
import {MatTableModule} from "@angular/material/table";
import {MatTabsModule} from "@angular/material/tabs";
import {MatToolbarModule} from "@angular/material/toolbar";
import {MatTooltipModule} from "@angular/material/tooltip";
import {BrowserModule} from "@angular/platform-browser";
import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
import {NgChartsModule} from "ng2-charts";
import {CommandsApi} from "./api/commands.api";
import {ScopeApi} from "./api/scope.api";
import {AppRoutingModule} from "./app-routing.module";
import {AppComponent} from "./app.component";
import {CommandsComponent} from "./components/commands/commands.component";
import {ConfirmDialogComponent} from "./components/confirm/confirm.component";
import {DialogComponent} from "./components/dialog/dialog.component";
import {QuestionDialogComponent} from "./components/question/question.component";
import {ScopeComponent} from "./components/scope/scope.component";
import {InterceptorProviders} from "./interceptors/interceptors";
import {LoadingService} from "./services/loading.service";
import {WebSocketService} from "./services/websocket.service";
@NgModule({
declarations : [ AppComponent, CommandsComponent, ConfirmDialogComponent, QuestionDialogComponent, DialogComponent, ScopeComponent ],
imports : [
BrowserModule, AppRoutingModule, FormsModule, ReactiveFormsModule, BrowserAnimationsModule, HttpClientModule, MatButtonModule, FlexLayoutModule, MatDialogModule, DragDropModule,
MatSliderModule, MatFormFieldModule, MatInputModule, MatChipsModule, MatProgressSpinnerModule, MatToolbarModule, MatTableModule, MatListModule, MatSelectModule, MatSnackBarModule,
MatSlideToggleModule, MatGridListModule, MatCardModule, MatTabsModule, MatTooltipModule, NgChartsModule,
],
providers : [
// services
LoadingService,
WebSocketService,
// api
CommandsApi,
ScopeApi,
// interceptors
InterceptorProviders,
],
bootstrap : [ AppComponent ]
})
export class AppModule {
}
<div class="grid-container" >
<mat-grid-list cols="2" rowHeight="15vh" >
<mat-grid-tile [colspan]="1" [rowspan]="2" >
<mat-card class="dashboard-card">
<mat-card-header >
<mat-card-title>Connection info</mat-card-title>
</mat-card-header>
<mat-card-content class="dashboard-card-content">
<div *ngIf="infos$ | async as infos">
<div class="spaceddiv" *ngFor="let info of infos">
<mat-form-field class="scrollablefield" >
<mat-label>{{ info.nameFC.value }}</mat-label>
<input matInput [formControl]="info.valueFC" [readonly]="!info.modifiableFC.value" />
</mat-form-field>
<button mat-raised-button color="primary" [disabled]="!info.modifiableFC.value"
(click)="onInfoSubmit(info)">
{{ info.btnTxtFC }}
</button>
</div>
</div>
</mat-card-content>
</mat-card>
</mat-grid-tile>
<mat-grid-tile [colspan]="1" [rowspan]="2">
<mat-card class="dashboard-card">
<mat-card-header>
<mat-card-title>Softmodem commands</mat-card-title>
</mat-card-header>
<mat-card-content class="dashboard-card-content">
<div class="spaceddiv" *ngIf="modules$ | async as modules" fxLaypout="row">
<mat-form-field>
<mat-label>Module</mat-label>
<mat-select (selectionChange)="onModuleSelect($event.value)" [(value)]="selectedModule">
<mat-option *ngFor="let module of modules" [value]="module"> {{ module.name }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div *ngIf="cmds$ | async as cmds">
<div *ngIf="cmds.length">
<mat-chip-list>
<mat-chip *ngFor="let cmd of cmds" (click)="onCmdSubmit(cmd)" [matTooltip]="cmd.hlp_cmd" > {{cmd.nameFC.value}}
</mat-chip>
</mat-chip-list>
</div>
</div>
</mat-card-content>
</mat-card>
</mat-grid-tile>
<mat-grid-tile *ngIf="vars$ | async as vars" [colspan]="1" [rowspan]="4">
<mat-card *ngIf="vars.length" class="dashboard-card">
<mat-card-header>
<mat-card-title>{{ selectedModule!.name }} variables</mat-card-title>
</mat-card-header>
<mat-card-content class="dashboard-card-content">
<div class="spaceddiv" *ngFor="let variable of vars">
<mat-form-field class="scrollablefield">
<mat-label>{{ variable.nameFC.value }}</mat-label>
<input matInput [formControl]="variable.valueFC" [readonly]="!variable.modifiableFC.value" />
</mat-form-field>
<button mat-raised-button color="primary" [disabled]="!variable.modifiableFC.value"
(click)="onVarsubmit(variable)">
set
</button>
</div>
</mat-card-content>
</mat-card>
</mat-grid-tile>
<mat-grid-tile *ngIf="(rows$ | async)?.length" [colspan]="1" [rowspan]="4">
<mat-card class="dashboard-card">
<mat-card-header>
<mat-card-title>{{ selectedModule!.name }} {{ selectedCmd!.name }} {{ selectedCmd!.param?.value}}</mat-card-title>
</mat-card-header>
<mat-card-content class="dashboard-card-content">
<div >
<mat-table mat-table [dataSource]="rows$" multiTemplateDataRows class="mat-elevation-z8">
<div class="TableRowdiv" *ngFor="let col of columns; index as colIndex" matColumnDef="{{col.name}}">
<mat-header-cell *matHeaderCellDef fxLayoutAlign="start center">
<div [matTooltip]="hlp_cc[colIndex]">
<h4>{{ col.name }}</h4>
</div>
</mat-header-cell>
<mat-cell *matCellDef="let row">
<div [ngSwitch]="col.type" [formGroup]="row" fxLayoutAlign="start center">
<mat-slide-toggle *ngSwitchCase="IArgType.boolean" [formControl]="row.paramsCtrls[colIndex].valueFC"
[checked]="row.paramsCtrls[colIndex].valueFC.value">
</mat-slide-toggle>
<mat-list-item role="listitem">
<mat-form-field *ngSwitchCase="IArgType.loglvl">
<mat-select [formControl]="row.paramsCtrls[colIndex].valueFC"
[(value)]="row.paramsCtrls[colIndex].valueFC.value">
<mat-option *ngFor="let level of logLvlValues" [value]="level"> {{ level }}
</mat-option>
</mat-select>
</mat-form-field>
</mat-list-item>
<div *ngSwitchDefault>
<mat-form-field>
<input matInput [formControl]="row.paramsCtrls[colIndex].valueFC" [readonly]="!col.modifiable" />
</mat-form-field>
</div>
</div>
</mat-cell>
</div>
<div matColumnDef="button">
<mat-header-cell *matHeaderCellDef> </mat-header-cell>
<mat-cell *matCellDef="let row" fxLayoutAlign="center">
<button class="TableBtn" mat-raised-button color="primary" (click)="onParamSubmit(row)" [disabled]="row.pristine"> set
</button>
</mat-cell>
</div>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
</mat-table>
</div>
</mat-card-content>
</mat-card>
</mat-grid-tile>
</mat-grid-list>
</div>
.spaceddiv {
height: 45px;
width: 40vw;
display: flex;
justify-content: space-between;
margin: 0 auto;
padding: 10px 0;
}
.TableRowdiv {
height: 38px;
display: flex;
/* justify-content: space-between;*/
margin: 2px;
width: 95%;
.item:nth-child(1n){
width: 10%;
}
.item {
width: 4%;
margin: 10px;
}
}
.TableBtn {
height: 25px;
width: 35p;
display:flex;
padding-bottom:8px;
}
.TableText {
display: flex;
width: 8em;
overflow-x: auto;
}
.scrollablefield {
width: 80%;
height: 20px;
.mat-input-element {
overflow-x: auto;
}
}
.mat-card {
height: 95%;
overflow-y: auto;
overflow-x: scroll;
}
.status {
color: green;
font-weight: 800;
}
.ng-invalid .status {
color: red;
}
.grid-container {
margin: 20px;
}
.dashboard-card {
position: absolute;
top: 15px;
left: 15px;
right: 15px;
bottom: 15px;
}
.more-button {
position: absolute;
top: 5px;
right: 10px;
}
.dashboard-card-content {
text-align: center;
}
import {Component} from "@angular/core";
import {ViewEncapsulation} from "@angular/core";
import {UntypedFormArray} from "@angular/forms";
import {BehaviorSubject, forkJoin, Observable, of, timer} from "rxjs";
import {filter, map, switchMap, tap} from "rxjs/operators";
import {CommandsApi, IArgType, IColumn, ICommand, ICommandOptions, IInfo, ILogLvl, IParam, IRow} from "src/app/api/commands.api";
import {HelpApi, HelpRequest, HelpResp} from "src/app/api/help.api";
import {CmdCtrl} from "src/app/controls/cmd.control";
import {InfoCtrl} from "src/app/controls/info.control";
import {ModuleCtrl} from "src/app/controls/module.control";
import {RowCtrl} from "src/app/controls/row.control";
import {VarCtrl} from "src/app/controls/var.control";
import {DialogService} from "src/app/services/dialog.service";
import {DownloadService} from "src/app/services/download.service";
import {LoadingService} from "src/app/services/loading.service";
const CHANNEL_MOD_MODULE = "channelmod"
const PREDEF_CMD = "show predef"
@Component({
selector : "app-commands",
templateUrl : "./commands.component.html",
styleUrls : [ "./commands.component.scss" ],
encapsulation : ViewEncapsulation.None,
}) export class CommandsComponent {
hlp_cc: string[] = [];
hlp_cmd: string[] = [];
IArgType = IArgType;
logLvlValues = Object.values(ILogLvl);
// softmodem
infos$: Observable<VarCtrl[]>;
modules$: Observable<ModuleCtrl[]>;
// module
selectedModule?: ModuleCtrl;
vars$?: Observable<VarCtrl[]>;
cmds$?: Observable<CmdCtrl[]>;
// command
selectedCmd?: ICommand
displayedColumns: string[] = [];
rows$: BehaviorSubject<RowCtrl[]> = new BehaviorSubject<RowCtrl[]>([]);
columns: IColumn[] = [];
constructor(
public commandsApi: CommandsApi,
public helpApi: HelpApi,
public loadingService: LoadingService,
public dialogService: DialogService,
public downloadService: DownloadService,
)
{
this.infos$ = this.commandsApi.readInfos$().pipe(map((infos) => infos.map(info => new InfoCtrl(info))));
this.modules$ = this.commandsApi.readModules$().pipe(
map(imodules => imodules.map(imodule => new ModuleCtrl(imodule))), filter(controls => controls.length > 0), tap(controls => this.onModuleSelect(controls[0])));
}
// get types$() {
// return this.modules$.pipe(
// filter(modules => modules.map(module => module.name).includes(CHANNEL_MOD_MODULE)),
// mergeMap(modules => this.commandsApi.readCommands$(modules[0]!.name)),
// map(icmds => icmds.filter(cmd => cmd.name === PREDEF_CMD)),
// filter(icmds => icmds.length > 0),
// map(icmds => new CmdCtrl(icmds[0])),
// mergeMap(control => this.commandsApi.runCommand$(control.api(), CHANNEL_MOD_MODULE)),
// map(resp => resp.display.map(line => line.match('/\s*[0-9]*\s*(\S*)\n/gm')![0])),
// tap(types => console.log(types.join(', ')))
// );
// }
onInfoSubmit(control: InfoCtrl)
{
let info: IInfo = control.api();
if (info.type === IArgType.configfile) {
this.downloadService.getFile(info.value)
} else {
this.commandsApi.setInfo$(info).subscribe();
}
}
onModuleSelect(module: ModuleCtrl)
{
this.selectedModule = module
this.selectedCmd = undefined
this.cmds$ = this.commandsApi.readCommands$(module.name).pipe(
map(icmds => icmds.map(icmd => new CmdCtrl(icmd))),
map(cmds => {
module.cmdsFA = new UntypedFormArray(cmds)
for (let i = 0; i < cmds.length; i++)
{
cmds[i].get_cmd_help(this.helpApi, module.name)
}
return module.cmdsFA.controls as CmdCtrl[]
})
)
this.vars$ = this.commandsApi.readVariables$(module.name).pipe(
map(ivars => ivars.map(ivar => new VarCtrl(ivar))),
map(vars => {
module.varsFA = new UntypedFormArray(vars)
return module.varsFA.controls as VarCtrl[]
})
)
}
onVarsubmit(control: VarCtrl)
{
this.commandsApi.setCmdVariable$(control.api(), this.selectedModule!.name).pipe(map(resp => this.dialogService.openVarRespDialog(resp))).subscribe();
}
onCmdSubmit(control: CmdCtrl)
{
this.selectedCmd = control.api()
const obsparam$ = forkJoin([ control.confirm ? this.dialogService.openConfirmDialog(control.confirm)
: control.question ? this.dialogService.openQuestionDialog(this.selectedModule! + " " + this.selectedModule!.name, control)
: of(true) ]);
obsparam$
.pipe(switchMap(results => {
if (!results[0])
return of(null);
return this.execCmd$(control);
}))
.subscribe();
}
private execCmd$(control: CmdCtrl)
{
let cmd = control!.api();
if (this.selectedCmd!.param)
this.selectedCmd!.param!.value = cmd.param!.value;
this.commandsApi.runCommand$(cmd, this.selectedModule!.name)
.subscribe(
resp => {
if (resp.display[0])
this.dialogService.updateCmdDialog(control, resp, "cmd " + control.nameFC.value + " response:")
// else return of(resp)
const controls: RowCtrl[] = [];
this.displayedColumns = [];
if (resp.table) {
this.columns = resp.table.columns;
this.displayedColumns = this.columns.map(col => col.name);
this.displayedColumns.push("button");
// possibly load help..
for (let i = 0; i < this.columns.length; i = i + 1) {
if (this.columns[i].help) {
this.helpApi.getHelp$({module : this.selectedModule!.name, command : control!.api().name.replace(" ", "_"), object : this.columns[i].name.replace(" ", "_")})
.subscribe(
response => {
if (response.status == 201)
this.hlp_cc[i] = response.body!.text;
},
err => { this.hlp_cc[i] = ""; },
);
} else {
this.hlp_cc[i] = "";
}
}
for (let rawIndex = 0; rawIndex < resp.table.rows.length; rawIndex++) {
let params: IParam[] = [];
for (let i = 0; i < this.columns.length; i = i + 1) {
params.push({value : resp.table.rows[rawIndex][i], col : this.columns[i]})
}
const irow: IRow = {params : params, rawIndex : rawIndex, cmdName : this.selectedCmd!.name}
controls[rawIndex] = new RowCtrl(irow)
}
}
this.rows$.next(controls)
},
err => console.error("execCmd error: " + err),
() => {
console.log("execCmd completed: ");
if (control.isResUpdatable()) {
if (!(control.ResUpdTimerSubscriber) || control.ResUpdTimerSubscriber.closed) {
if (!control.ResUpdTimer)
control.ResUpdTimer = timer(1000, 1000);
control.ResUpdTimerSubscriber = control.ResUpdTimer.subscribe(iteration => {
console.log("Update timer fired" + iteration);
if (control.updbtnname === "Stop update")
this.execCmd$(control);
});
}
}
},
) // map resp
return of(null);
}
onParamSubmit(control: RowCtrl)
{
if (this.selectedCmd!.param)
control.set_cmdparam(this.selectedCmd!.param);
this.commandsApi.setCmdParams$(control.api(), this.selectedModule!.name).subscribe(() => this.execCmd$(new CmdCtrl(this.selectedCmd!)));
}
}
<h3>{{ data.title }}</h3>
<div fxLayoutGap="10px" mat-dialog-actions fxLayout="row" fxLayoutAlign="center start">
<button mat-button [mat-dialog-close]="true" cdkFocusInitial>Ok</button>
<button mat-button (click)="onNoClick()">Cancel</button>
</div>
\ No newline at end of file
/* eslint-disable @typescript-eslint/naming-convention */
import {Component, Inject} from "@angular/core";
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
@Component({selector : "app-confirm", templateUrl : "./confirm.component.html", styleUrls : [ "./confirm.component.css" ]})
export class ConfirmDialogComponent {
constructor(public dialogRef: MatDialogRef<void>, @Inject(MAT_DIALOG_DATA) public data: any)
{
}
onNoClick()
{
this.dialogRef.close();
}
}
/* icon that can be used to drag a dialog */
::ng-deep.dialogdrag-handle {
top: 1px;
left: 1px;
color: #ccc;
cursor: move;
width: 24px;
height: 24px;
}
/* make dialog for command response using the display field scrollable */
::ng-deep.cmdRespDialog {
resize: both;
overflow: scroll;
}
/* make only the dialog for command response using the display field movable */
/* (hide the close button and the drag icon) */
::ng-deep.varRespDialog #dialogclose {
display:none ;
}
::ng-deep.varRespDialog #dialogdrag {
display:none;
}
::ng-deep.errRespDialog #dialogclose {
display:none ;
}
::ng-deep.errRespDialog #dialogdrag {
display:none;
}
<div id="dialogdrag" cdkDrag cdkDragRootElement=".cdk-overlay-pane">
<div class="dialogdrag-handle" cdkDragHandle>
<svg width="24px" fill="currentColor" viewBox="0 0 24 24">
<path
d="M10 9h4V6h3l-5-5-5 5h3v3zm-1 1H6V7l-5 5 5 5v-3h3v-4zm14 2l-5-5v3h-3v4h3v3l5-5zm-9 3h-4v3H7l5 5 5-5h-3v-3z">
</path>
<path d="M0 0h24v24H0z" fill="none"></path>
</svg>
</div>
</div>
<h3> {{ data.title }}</h3>
<pre [innerHTML]="data.body"></pre>
<mat-dialog-actions fxLayoutAlign="end">
<button id="dialogclose" mat-button mat-dialog-close>Close</button>
<button id="dialogupdate" mat-button (click)="onUpdate(data.control)"
*ngIf="data.updatable">{{data.control.updbtnname}}</button>
</mat-dialog-actions>
\ No newline at end of file
import {Component, ElementRef, Inject, OnInit, ViewChild} from "@angular/core";
import {MAT_DIALOG_DATA} from "@angular/material/dialog";
import {CmdCtrl} from "src/app/controls/cmd.control";
import {DialogService} from "src/app/services/dialog.service";
@Component({
selector : "app-dialog",
templateUrl : "./dialog.component.html",
styleUrls : [ "./dialog.component.css" ],
})
export class DialogComponent {
constructor(@Inject(MAT_DIALOG_DATA) public data: any)
{
}
onUpdate(control: CmdCtrl)
{
if (control.updbtnname === "Stop update") {
control.stopUpdate();
} else {
control.startUpdate();
}
}
}
<h3>{{ data.title }}</h3>
<div mat-dialog-content >
<mat-form-field appearance="fill">
<label for="answer">{{data.control.question?.display}}</label>
<input id="answer" type="text" style="width:10vw" [formControl]="data.control.answerFC">
</mat-form-field>
</div>
<div fxLayoutGap=" 10px" mat-dialog-actions fxLayout="row" fxLayoutAlign="center start">
<button mat-button [mat-dialog-close]="true" cdkFocusInitial>Ok</button>
<button mat-button [mat-dialog-close]="false">Cancel</button>
</div>
/* eslint-disable @typescript-eslint/naming-convention */
import {Component, Inject} from "@angular/core";
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
import {CmdCtrl} from "src/app/controls/cmd.control";
export interface QuestionDialogData {
title: string;
control: CmdCtrl;
}
@Component({selector : "app-question", templateUrl : "./question.component.html", styleUrls : [ "./question.component.css" ]})
export class QuestionDialogComponent {
constructor(
public dialogRef: MatDialogRef<QuestionDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: QuestionDialogData,
)
{
}
onNoClick()
{
this.dialogRef.close();
}
}
mat-form-field {
width: 80%;
}
.status {
color: green;
font-weight: 800;
}
.ng-invalid .status {
color: red;
}
.grid-container {
margin: 20px;
}
.dashboard-card {
position: absolute;
top: 15px;
left: 15px;
right: 15px;
bottom: 15px;
}
.more-button {
position: absolute;
top: 5px;
right: 10px;
}
.dashboard-card-content {
text-align: center;
}
.scope-slider {
width: 90%;
}
.mat-mini-fab.mat-started {
background-color: #00a825;
}
import {UntypedFormArray, UntypedFormControl, UntypedFormGroup} from "@angular/forms";
import {Subscription} from "rxjs";
import {Observable} from "rxjs/internal/Observable";
import {ICommand, ICommandOptions, IQuestion} from "src/app/api/commands.api";
import {HelpApi, HelpRequest, HelpResp} from "src/app/api/help.api";
const enum CmdFCN {
name = "name",
vars = "variables",
confirm = "confirm",
answer = "answer"
}
export class CmdCtrl extends UntypedFormGroup {
confirm?: string;
question?: IQuestion;
cmdname: string;
options?: ICommandOptions[];
public ResUpdTimer?: Observable<number>;
public ResUpdTimerSubscriber?: Subscription;
updbtnname: string;
hlp_cmd: string = "";
constructor(cmd: ICommand)
{
super({});
this.addControl(CmdFCN.name, new UntypedFormControl(cmd.name));
this.addControl(CmdFCN.answer, new UntypedFormControl(""));
this.addControl(CmdFCN.vars, new UntypedFormArray([]));
this.confirm = cmd.confirm;
this.question = cmd.question;
this.cmdname = cmd.name;
this.options = cmd.options;
this.updbtnname = "Start update"
}
api()
{
const doc: ICommand = {
name : this.nameFC.value,
param : this.question ? {name : this.question!.pname, value : this.answerFC.value, type : this.question!.type, modifiable : false} : undefined,
options : this.options
};
return doc;
}
isResUpdatable(): boolean
{
if (this.options) {
for (let opt = 0; opt < this.options.length; opt++) {
if (this.options[opt] == ICommandOptions.update)
return true;
}
} else {
return false;
}
return false;
}
stopUpdate()
{
if (this.ResUpdTimerSubscriber) {
this.updbtnname = "Start update"
}
}
startUpdate()
{
if (this.ResUpdTimerSubscriber && this.ResUpdTimer) {
this.updbtnname = "Stop update"
}
}
get nameFC()
{
return this.get(CmdFCN.name) as UntypedFormControl;
}
set nameFC(fc: UntypedFormControl)
{
this.setControl(CmdFCN.name, fc);
}
get answerFC()
{
return this.get(CmdFCN.answer) as UntypedFormControl;
}
get varsFA()
{
return this.get(CmdFCN.vars) as UntypedFormArray;
}
set varsFA(fa: UntypedFormArray)
{
this.setControl(CmdFCN.vars, fa);
}
public get_cmd_help(helpApi: HelpApi, module: string)
{
if (this.options) {
for (let j = 0; j < this.options!.length; j++) {
if (this.options![j] == ICommandOptions.help) {
helpApi.getHelpText("cmd", module, this.cmdname).subscribe(resp => { this.hlp_cmd = resp; }, err => { this.hlp_cmd = ""; });
}
}
}
}
}
import {UntypedFormControl, UntypedFormGroup} from "@angular/forms";
import {IInfo} from "../api/commands.api";
import {IArgType} from "../api/commands.api";
const enum InfosFCN {
name = "name",
value = "value",
type = "type",
modifiable = "modifiable"
}
export class InfoCtrl extends UntypedFormGroup {
type: IArgType;
constructor(ivar: IInfo)
{
super({});
this.type = ivar.type;
this.addControl(InfosFCN.name, new UntypedFormControl(ivar.name));
this.addControl(InfosFCN.value, new UntypedFormControl(ivar.value));
this.addControl(InfosFCN.type, new UntypedFormControl(ivar.type));
this.addControl(InfosFCN.modifiable, new UntypedFormControl(ivar.modifiable));
}
api()
{
const doc: IInfo = {
name : this.nameFC.value,
value : String(this.valueFC.value), // FIXME
type : this.typeFC.value,
modifiable : this.modifiableFC.value
};
return doc;
}
get nameFC()
{
return this.get(InfosFCN.name) as UntypedFormControl;
}
set nameFC(control: UntypedFormControl)
{
this.setControl(InfosFCN.name, control);
}
get valueFC()
{
return this.get(InfosFCN.value) as UntypedFormControl;
}
set valueFC(control: UntypedFormControl)
{
this.setControl(InfosFCN.value, control);
}
get typeFC()
{
return this.get(InfosFCN.type) as UntypedFormControl;
}
set typeFC(control: UntypedFormControl)
{
this.setControl(InfosFCN.type, control);
}
get modifiableFC()
{
return this.get(InfosFCN.modifiable) as UntypedFormControl;
}
set modifiableFC(control: UntypedFormControl)
{
this.setControl(InfosFCN.modifiable, control);
}
get btnTxtFC()
{
if (this.type != IArgType.configfile)
return "set"
else return "download"
}
}
import {UntypedFormArray, UntypedFormGroup} from "@angular/forms";
import {IModule} from "../api/commands.api";
const enum ModuleFCN {
vars = "variables",
cmds = "commands"
}
export class ModuleCtrl extends UntypedFormGroup {
name: string
constructor(imodule: IModule)
{
super({});
this.name = imodule.name;
this.addControl(ModuleFCN.vars, new UntypedFormArray([]));
this.addControl(ModuleFCN.cmds, new UntypedFormArray([]));
}
get varsFA()
{
return this.get(ModuleFCN.vars) as UntypedFormArray;
}
set varsFA(fa: UntypedFormArray)
{
this.setControl(ModuleFCN.vars, fa);
}
get cmdsFA()
{
return this.get(ModuleFCN.cmds) as UntypedFormArray;
}
set cmdsFA(fa: UntypedFormArray)
{
this.setControl(ModuleFCN.cmds, fa);
}
}
import {UntypedFormControl, UntypedFormGroup} from "@angular/forms";
import {IArgType, IColumn, IParam} from "../api/commands.api";
enum ParamFCN {
value = "value",
}
export class ParamCtrl extends UntypedFormGroup {
col: IColumn
constructor(public param: IParam)
{
super({})
this.col = param.col
let control: UntypedFormControl
switch (param.col.type)
{
case IArgType.boolean:
control = new UntypedFormControl((param.value === "true") ? true : false);
break;
case IArgType.loglvl:
control = new UntypedFormControl(param.value);
break;
default:
control = new UntypedFormControl(param.value)
}
if (!param.col.modifiable)
control
.disable()
this.addControl(ParamFCN.value, control)
}
get valueFC()
{
return this.get(ParamFCN.value) as UntypedFormControl
}
set valueFC(fc: UntypedFormControl)
{
this.setControl(ParamFCN.value, fc);
}
api()
{
let value: string
switch (this.col.type)
{
case IArgType.boolean:
value = String(this.valueFC.value);
break;
default:
value = this.valueFC.value
}
const doc: IParam = {
value : value,
col : this.col
}
return doc
}
}
import {FormControl, UntypedFormArray, UntypedFormGroup} from "@angular/forms";
import {IArgType, IParam, IRow, IVariable} from "../api/commands.api";
import {ParamCtrl} from "./param.control";
enum RowFCN {
paramsFA = "params",
}
export class RowCtrl extends UntypedFormGroup {
cmdName: string;
rawIndex: number;
cmdparam?: IVariable;
constructor(row: IRow)
{
super({})
this.cmdName = row.cmdName;
this.rawIndex = row.rawIndex;
this.addControl(RowFCN.paramsFA, new UntypedFormArray(row.params.map(param => new ParamCtrl(param))));
}
get paramsFA()
{
return this.get(RowFCN.paramsFA) as UntypedFormArray
}
set paramsFA(fa: UntypedFormArray)
{
this.setControl(RowFCN.paramsFA, fa);
}
set_cmdparam(cmdparam: IVariable)
{
this.cmdparam = cmdparam;
}
get paramsCtrls(): ParamCtrl[]{return this.paramsFA.controls as ParamCtrl[]}
api()
{
const doc: IRow = {
rawIndex : this.rawIndex,
cmdName : this.cmdName,
params : this.paramsCtrls.map(control => control.api()),
param : this.cmdparam
}
return doc
}
}
import {UntypedFormControl, UntypedFormGroup} from "@angular/forms";
import {IInfo} from "../api/commands.api";
import {IArgType} from "../api/commands.api";
const enum VariablesFCN {
name = "name",
value = "value",
type = "type",
modifiable = "modifiable"
}
export class VarCtrl extends UntypedFormGroup {
type: IArgType;
constructor(ivar: IInfo)
{
super({});
this.type = ivar.type;
this.addControl(VariablesFCN.name, new UntypedFormControl(ivar.name));
this.addControl(VariablesFCN.value, new UntypedFormControl(ivar.value));
this.addControl(VariablesFCN.type, new UntypedFormControl(ivar.type));
this.addControl(VariablesFCN.modifiable, new UntypedFormControl(ivar.modifiable));
}
api()
{
const doc: IInfo = {
name : this.nameFC.value,
value : String(this.valueFC.value), // FIXME
type : this.typeFC.value,
modifiable : this.modifiableFC.value
};
return doc;
}
get nameFC()
{
return this.get(VariablesFCN.name) as UntypedFormControl;
}
set nameFC(control: UntypedFormControl)
{
this.setControl(VariablesFCN.name, control);
}
get valueFC()
{
return this.get(VariablesFCN.value) as UntypedFormControl;
}
set valueFC(control: UntypedFormControl)
{
this.setControl(VariablesFCN.value, control);
}
get typeFC()
{
return this.get(VariablesFCN.type) as UntypedFormControl;
}
set typeFC(control: UntypedFormControl)
{
this.setControl(VariablesFCN.type, control);
}
get modifiableFC()
{
return this.get(VariablesFCN.modifiable) as UntypedFormControl;
}
set modifiableFC(control: UntypedFormControl)
{
this.setControl(VariablesFCN.modifiable, control);
}
get btnTxtFC()
{
if (this.type != IArgType.configfile)
return "set"
else return "download"
}
}
import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse} from "@angular/common/http";
import {Injectable} from "@angular/core";
import {Observable, throwError} from "rxjs";
import {catchError, tap} from "rxjs/operators";
import {DialogService as DialogService} from "../services/dialog.service";
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
constructor(public dialogService: DialogService)
{
}
intercept(request: HttpRequest<unknown>, next: HttpHandler)
{
return next.handle(request).pipe(
// tap((event: HttpEvent<any>) => {
// if (event instanceof HttpResponse) {
// switch (event.status) {
// case 200:
// case 201:
// this.log(GREEN, request.method + ' ' + event.status + ' Success');
// this.dialogService.openSnackBar(request.method + ' ' + event.status + ' Success');
// break;
// default:
// break;
// }
// // return throwError(event.body);
// }
// }),
catchError((error: HttpErrorResponse) => {
let prefix: string = "oai web interface [API]: ";
let message: string = " ";
switch (error.status) {
case 400:
case 403:
console.error(prefix + request.method + " " + error.status + " Error: " + error.error.toString());
break;
case 501:
case 500:
console.warn(prefix + request.method + " " + error.status + " Error: " + error.error.toString());
break;
default:
console.log(prefix + request.method + " " + error.status + " Error: " + error.error.toString());
break;
}
if (error.error instanceof ErrorEvent) {
message = error.error.message;
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong
message = JSON.stringify(error.error);
}
this.dialogService.openErrorDialog(prefix + " " + error.status, message);
return throwError(error);
}),
);
}
}
import {HTTP_INTERCEPTORS} from "@angular/common/http";
import {ErrorInterceptor} from "./error.interceptor";
import {SpinnerInterceptor} from "./spinner.interceptor";
export const InterceptorProviders = [
{provide : HTTP_INTERCEPTORS, useClass : SpinnerInterceptor, multi : true},
{provide : HTTP_INTERCEPTORS, useClass : ErrorInterceptor, multi : true},
];
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse} from "@angular/common/http";
import {Injectable} from "@angular/core";
import {Observable} from "rxjs";
import {finalize} from "rxjs/operators";
import {LoadingService} from "../services/loading.service";
@Injectable()
export class SpinnerInterceptor implements HttpInterceptor {
activeRequests = 0;
constructor(private loadingService: LoadingService)
{
}
intercept(request: HttpRequest<unknown>, next: HttpHandler)
{
if (this.activeRequests === 0) {
this.loadingService.startLoading();
}
this.activeRequests++;
return next.handle(request).pipe(
finalize(() => {
this.activeRequests--;
if (this.activeRequests === 0) {
this.loadingService.stopLoading();
}
}),
);
}
}
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production : true,
backend : window.location.protocol + "//" + window.location.hostname + ":" + window.location.port + "/"
};
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/* You can add global styles to this file, and also import other style files */
html,
body {
height: 100%;
}
body {
margin: 0;
font-family: Roboto, 'Helvetica Neue', sans-serif;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment