mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-13 20:50:28 +00:00
Compare commits
347 Commits
microamp_F
...
logger
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ef5fafe89 | ||
|
|
9f6e46bd8a | ||
|
|
7372378732 | ||
|
|
71d20e0543 | ||
|
|
33fd2343ce | ||
|
|
48ef96959d | ||
|
|
c73ce11828 | ||
|
|
66546b7ad0 | ||
|
|
11087fff0f | ||
|
|
ffa13d99e5 | ||
|
|
ccae22f648 | ||
|
|
4e932c9248 | ||
|
|
0c9dd4d04a | ||
|
|
67a5bfaa43 | ||
|
|
501192a1f8 | ||
|
|
83d00d4c21 | ||
|
|
12504106eb | ||
|
|
3bb957722f | ||
|
|
ae2abbc6e2 | ||
|
|
544c723372 | ||
|
|
1e34ab443c | ||
|
|
519b38817a | ||
|
|
3da668576c | ||
|
|
ce46cab866 | ||
|
|
553c6377aa | ||
|
|
b42694254f | ||
|
|
50821cace3 | ||
|
|
2fb9752bff | ||
|
|
7b57a3d4ed | ||
|
|
d81b670ac2 | ||
|
|
461ba045b1 | ||
|
|
03a8ff2913 | ||
|
|
6e85528582 | ||
|
|
81ba755984 | ||
|
|
e8ca483c56 | ||
|
|
f8eba8400d | ||
|
|
6d2bd1025f | ||
|
|
36c0cf0133 | ||
|
|
7b31678bed | ||
|
|
b2ba88667a | ||
|
|
0aca093122 | ||
|
|
73f5496ffa | ||
|
|
f809e4689c | ||
|
|
5ae59cc394 | ||
|
|
def1ebc661 | ||
|
|
b57963dd2a | ||
|
|
d33ae02d39 | ||
|
|
86ba2b0822 | ||
|
|
4bfc929bad | ||
|
|
db81a3f0b9 | ||
|
|
abf14c0bb4 | ||
|
|
a1d4514ae0 | ||
|
|
d813fb8470 | ||
|
|
e2615dd886 | ||
|
|
cc24755a34 | ||
|
|
ca45cbb2b7 | ||
|
|
deb2a453c6 | ||
|
|
5a08d8109c | ||
|
|
65c858757d | ||
|
|
55003cafd2 | ||
|
|
57a7488e85 | ||
|
|
da85cf5183 | ||
|
|
32a6dd85c2 | ||
|
|
d9216265dc | ||
|
|
99ecd83133 | ||
|
|
c4958e50e8 | ||
|
|
7b16f7e863 | ||
|
|
e0c7980df5 | ||
|
|
4b002029fb | ||
|
|
27d626dc48 | ||
|
|
4fa9e2b39b | ||
|
|
fb5c768159 | ||
|
|
0966ceb70b | ||
|
|
b75d4f90b7 | ||
|
|
8cbe53b82b | ||
|
|
13e17339d3 | ||
|
|
6d457fcb56 | ||
|
|
748d4cb49a | ||
|
|
1e77b753ff | ||
|
|
2964ef7e3e | ||
|
|
7f69632bbb | ||
|
|
17af1562a8 | ||
|
|
c3378c7f84 | ||
|
|
38dca6387f | ||
|
|
4a42ff54df | ||
|
|
22d4495c20 | ||
|
|
3b93a9c5cb | ||
|
|
6407df90e2 | ||
|
|
d75da26833 | ||
|
|
b0f0106cc7 | ||
|
|
2690cbd08e | ||
|
|
868f9d9042 | ||
|
|
ad0dcfe287 | ||
|
|
7bd44da548 | ||
|
|
185e5934b7 | ||
|
|
24802ee18b | ||
|
|
8916cfdfd2 | ||
|
|
b135c1dd8f | ||
|
|
6e61c8a05a | ||
|
|
c957882da8 | ||
|
|
7404ad7a5c | ||
|
|
4e8fa01a25 | ||
|
|
ec0d3fde1c | ||
|
|
58618be544 | ||
|
|
7e7c097942 | ||
|
|
94796957e8 | ||
|
|
55d9200a48 | ||
|
|
d815edebe7 | ||
|
|
f1d18b4edb | ||
|
|
06355e9cf1 | ||
|
|
9bcf269856 | ||
|
|
9bfcbe7faa | ||
|
|
fa92bf1a79 | ||
|
|
a348ada211 | ||
|
|
e0f76e6ca6 | ||
|
|
351f069adc | ||
|
|
64f84cdd6a | ||
|
|
8bd1b3b53a | ||
|
|
2aef88960f | ||
|
|
51d6fd7ec2 | ||
|
|
f2d43636bf | ||
|
|
dbeda2d33f | ||
|
|
5d9f91997e | ||
|
|
210429f388 | ||
|
|
dc40e0dca1 | ||
|
|
fe8f418e7a | ||
|
|
98de3b9049 | ||
|
|
55d83f6045 | ||
|
|
6882b63991 | ||
|
|
45896f2583 | ||
|
|
ed6b13c759 | ||
|
|
eee4d3fc07 | ||
|
|
b8f64823bf | ||
|
|
d37952f94d | ||
|
|
12e2eded38 | ||
|
|
d910e92669 | ||
|
|
9e2f2db88c | ||
|
|
85929b7933 | ||
|
|
5673a31814 | ||
|
|
7da80421ff | ||
|
|
35e4f29cf6 | ||
|
|
c1abfcfc50 | ||
|
|
b2e8b78a01 | ||
|
|
53e160728a | ||
|
|
522f2ad140 | ||
|
|
4a26a44ee0 | ||
|
|
59722d1a91 | ||
|
|
218a524cd2 | ||
|
|
f44f105cb3 | ||
|
|
32ff4b33f1 | ||
|
|
bf0492ba87 | ||
|
|
7a9c13422a | ||
|
|
3e3b33e94c | ||
|
|
58ae227080 | ||
|
|
81a671a4f3 | ||
|
|
039c6d24bc | ||
|
|
ec80ef106f | ||
|
|
60f17d4d45 | ||
|
|
5e86379be4 | ||
|
|
62789c5161 | ||
|
|
bede89b233 | ||
|
|
4a29496988 | ||
|
|
a809a8c722 | ||
|
|
ad3ce4ddb6 | ||
|
|
72df29f225 | ||
|
|
42ab0247bf | ||
|
|
a439a0cce0 | ||
|
|
f7c9075337 | ||
|
|
15fc3ccce0 | ||
|
|
f2500d9297 | ||
|
|
72bc9baf2e | ||
|
|
6cef7a8f67 | ||
|
|
b32524dd74 | ||
|
|
a45dd3b8bc | ||
|
|
a981bb382b | ||
|
|
f17aba0f5e | ||
|
|
ca0398c5bc | ||
|
|
ab6bb2534e | ||
|
|
c875ce639c | ||
|
|
23a1d8d9bc | ||
|
|
c0f6b39da5 | ||
|
|
095be9fe6a | ||
|
|
82ebc9fa7b | ||
|
|
526720cb24 | ||
|
|
433dbcd41a | ||
|
|
20b3210062 | ||
|
|
b959af152d | ||
|
|
e5335a0188 | ||
|
|
1387bdc16e | ||
|
|
d2dfe830e7 | ||
|
|
3da8b0e5eb | ||
|
|
de0e075381 | ||
|
|
826b405d47 | ||
|
|
d761f67bcf | ||
|
|
975835804d | ||
|
|
b467fd2c68 | ||
|
|
daec2ba3ea | ||
|
|
e705c88dd5 | ||
|
|
7121bf6d05 | ||
|
|
1228608cc2 | ||
|
|
47ac986d8c | ||
|
|
8ea378eea8 | ||
|
|
2c688f230f | ||
|
|
cbec64c28f | ||
|
|
1c352f3138 | ||
|
|
5e964d660f | ||
|
|
2e66c87f4d | ||
|
|
82a771dd61 | ||
|
|
c99f52207d | ||
|
|
3555edff76 | ||
|
|
6723fc531b | ||
|
|
26cc637d1b | ||
|
|
e63f1454b3 | ||
|
|
c69d980613 | ||
|
|
4c4917442f | ||
|
|
bfb06225a5 | ||
|
|
d62892ff8a | ||
|
|
78ed7d4570 | ||
|
|
d969a124ee | ||
|
|
b68e614850 | ||
|
|
17a4dea3e1 | ||
|
|
43130b687e | ||
|
|
fdf2728d02 | ||
|
|
2783acb933 | ||
|
|
e78e720a46 | ||
|
|
2d9bac6359 | ||
|
|
2ebb462a8e | ||
|
|
8ea5fd7894 | ||
|
|
c939a9cdf5 | ||
|
|
31c9a03e83 | ||
|
|
b7b41fefc0 | ||
|
|
29c5129b0a | ||
|
|
3b0f050a33 | ||
|
|
dc6dd67827 | ||
|
|
9f10bfebf2 | ||
|
|
b34a44a4f9 | ||
|
|
0b7201244a | ||
|
|
28bd9ea4bc | ||
|
|
044ed48fc7 | ||
|
|
1b071df0bc | ||
|
|
45967c8b77 | ||
|
|
fc758069fb | ||
|
|
9be7b12654 | ||
|
|
160abd88cc | ||
|
|
e2eebc7a28 | ||
|
|
a52adaa95e | ||
|
|
ced3078e30 | ||
|
|
b7d2840e04 | ||
|
|
0a88f1a2d8 | ||
|
|
4855b4102d | ||
|
|
2dac45f1a3 | ||
|
|
e73acbc776 | ||
|
|
21d0fdb92a | ||
|
|
68e2cb7800 | ||
|
|
de24e2da1e | ||
|
|
bc4eb38eec | ||
|
|
8600b7815d | ||
|
|
8bee5da854 | ||
|
|
40bfb7b07a | ||
|
|
46c1dd511a | ||
|
|
d9cd942f3b | ||
|
|
147fb74085 | ||
|
|
7546e614c7 | ||
|
|
9a58bae112 | ||
|
|
9e99113aa1 | ||
|
|
ea4d82bc31 | ||
|
|
94efccba21 | ||
|
|
28c688b16f | ||
|
|
6d991456a9 | ||
|
|
bb840b7f7e | ||
|
|
9fecc570f8 | ||
|
|
1a5da9a1aa | ||
|
|
adb13cd4d4 | ||
|
|
9f8ebe86f4 | ||
|
|
46329a4f2d | ||
|
|
1f4dcc9fda | ||
|
|
7dda3dde08 | ||
|
|
e108aaa7db | ||
|
|
7da12ca861 | ||
|
|
2b987e8e00 | ||
|
|
c2106e49d8 | ||
|
|
0b473838fa | ||
|
|
3e15c59e00 | ||
|
|
9aa6e61197 | ||
|
|
4c58063268 | ||
|
|
25f1b46647 | ||
|
|
68ae77a79e | ||
|
|
a4ad969553 | ||
|
|
415e9e8495 | ||
|
|
5a5d8670ea | ||
|
|
9e20b2464f | ||
|
|
ea5c125dbd | ||
|
|
66646cd17a | ||
|
|
81b0df6f1d | ||
|
|
e9dadf1706 | ||
|
|
f9e45856fc | ||
|
|
0f9fcb3f0a | ||
|
|
17af664374 | ||
|
|
da3bda8bbb | ||
|
|
a5d66d1f4a | ||
|
|
4b289a53df | ||
|
|
d5eb164a20 | ||
|
|
fec25da5e7 | ||
|
|
4ec7d0a824 | ||
|
|
a873c0080a | ||
|
|
06f905853a | ||
|
|
179b8f57b3 | ||
|
|
9b1e5db390 | ||
|
|
32e5f13002 | ||
|
|
4497800fe1 | ||
|
|
6846459ea5 | ||
|
|
f7dafc4771 | ||
|
|
ee11159d5d | ||
|
|
c0a3b39688 | ||
|
|
679dcae557 | ||
|
|
b14c4b2aa6 | ||
|
|
92c534dcaa | ||
|
|
07fb404e33 | ||
|
|
a010b4d89f | ||
|
|
b340427363 | ||
|
|
654a2b7f63 | ||
|
|
b0cb9b8615 | ||
|
|
9bb75d82cf | ||
|
|
535239a5ae | ||
|
|
89b2a966ba | ||
|
|
29e2a542dd | ||
|
|
98a736c9d7 | ||
|
|
8404af8d18 | ||
|
|
7572d8cb46 | ||
|
|
db43c3e12e | ||
|
|
ea066dc0dc | ||
|
|
c0037b1f98 | ||
|
|
29aa6407be | ||
|
|
f6d4c77e75 | ||
|
|
c0e019efbd | ||
|
|
ded656208d | ||
|
|
9c36a8ebc2 | ||
|
|
1dda299dc3 | ||
|
|
c8d1b99e42 | ||
|
|
b274f3c8d3 | ||
|
|
1d7775875e | ||
|
|
3a560604ec | ||
|
|
f677cc34b3 | ||
|
|
cdab201890 | ||
|
|
20186aec26 | ||
|
|
79fc8623e3 | ||
|
|
c104b12302 |
31
.vscode/launch.json
vendored
31
.vscode/launch.json
vendored
@@ -1,31 +0,0 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "(gdb) Launch",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/cmake_targets/ran_build/build/lte-softmodem",
|
||||
"args": [
|
||||
"-O", "../ci-scripts/conf_files/rcc.band7.tm1.nfapi.conf",
|
||||
"--noS1"
|
||||
],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}/cmake_targets",
|
||||
"environment": [],
|
||||
"externalConsole": false,
|
||||
"MIMode": "gdb",
|
||||
"miDebuggerPath": "${workspaceFolder}/cmake_targets/sudo-gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -3182,3 +3182,4 @@ ADD_CUSTOM_TARGET(oarf
|
||||
|
||||
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)
|
||||
|
||||
@@ -58,7 +58,7 @@ CMAKE_CMD="$CMAKE"
|
||||
NOAVX512="False"
|
||||
BUILD_ECLIPSE=0
|
||||
NR="False"
|
||||
OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope"
|
||||
OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope websrv"
|
||||
RU=0
|
||||
CMAKE_C_FLAGS=()
|
||||
CMAKE_CXX_FLAGS=()
|
||||
|
||||
@@ -88,6 +88,8 @@ int processoption(paramdef_t *cfgoptions, char *value) {
|
||||
config_check_valptr(cfgoptions, cfgoptions->strptr, strlen(tmpval)+1);
|
||||
sprintf(*(cfgoptions->strptr), "%s",tmpval);
|
||||
} else {
|
||||
AssertFatal( ( strlen(*(cfgoptions->strptr)) < cfgoptions->numelt), "[CONFIG] Error: %s too long, %s value cannot exceed %i bytes\n",
|
||||
cfgoptions->optname,tmpval, cfgoptions->numelt) ;
|
||||
sprintf( (char *)(cfgoptions->strptr), "%s",tmpval);
|
||||
}
|
||||
|
||||
|
||||
@@ -88,13 +88,32 @@ 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return st;
|
||||
@@ -186,6 +205,7 @@ configmodule_interface_t *load_configmodule(int argc,
|
||||
uint32_t initflags)
|
||||
{
|
||||
char *cfgparam=NULL;
|
||||
|
||||
char *modeparams=NULL;
|
||||
char *cfgmode=NULL;
|
||||
char *strtokctx=NULL;
|
||||
@@ -193,7 +213,8 @@ 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++)
|
||||
printf("\"%s\" ", argv[i]);
|
||||
@@ -208,7 +229,11 @@ configmodule_interface_t *load_configmodule(int argc,
|
||||
cfgparam = argv[i+1];
|
||||
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 +283,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 +324,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);
|
||||
@@ -313,7 +347,10 @@ configmodule_interface_t *load_configmodule(int argc,
|
||||
cfgptr->getlist = config_cmdlineonly_getlist;
|
||||
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 +363,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");
|
||||
@@ -355,7 +410,10 @@ void end_configmodule(void) {
|
||||
void free_configmodule(void) {
|
||||
if (cfgptr != NULL) {
|
||||
end_configmodule();
|
||||
|
||||
if( cfgptr->status != NULL) {
|
||||
free(cfgptr->status->debug_cfgname);
|
||||
free(cfgptr->status);
|
||||
}
|
||||
if( cfgptr->cfgmode != NULL) free(cfgptr->cfgmode);
|
||||
|
||||
printf ("[CONFIG] free %i config parameter pointers\n",cfgptr->num_cfgP);
|
||||
|
||||
@@ -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,7 +62,20 @@
|
||||
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 configmodule_interface {
|
||||
int argc;
|
||||
char **argv;
|
||||
@@ -72,11 +86,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;
|
||||
configmodule_endfunc_t write_parsedcfg;
|
||||
uint32_t numptrs;
|
||||
uint32_t rtflags;
|
||||
char *ptrs[CONFIG_MAX_ALLOCATEDPTRS];
|
||||
bool ptrsAllocated[CONFIG_MAX_ALLOCATEDPTRS];
|
||||
char *tmpdir;
|
||||
configmodule_status_t *status; // allocated in debug mode only
|
||||
} configmodule_interface_t;
|
||||
|
||||
#ifdef CONFIG_LOADCONFIG_MAIN
|
||||
@@ -89,16 +107,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
|
||||
@@ -113,6 +134,7 @@ extern configmodule_interface_t *cfgptr;
|
||||
#define CONFIG_ENABLECMDLINEONLY (1<<1)
|
||||
extern configmodule_interface_t *load_configmodule(int argc, char **argv, uint32_t initflags);
|
||||
extern void end_configmodule(void);
|
||||
extern void write_parsedcfg(void);
|
||||
#define CONFIG_PRINTF_ERROR(f, x... ) if (isLogInitDone ()) { LOG_E(ENB_APP,f,x);} else {printf(f,x);}; if ( !CONFIG_ISFLAGSET(CONFIG_NOABORTONCHKF) ) exit_fun("exit because configuration failed\n");
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -237,6 +237,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);
|
||||
}
|
||||
|
||||
@@ -271,9 +274,11 @@ int config_getlist(paramlist_def_t *ParamList, paramdef_t *params, int numparams
|
||||
char cfgpath[MAX_OPTNAME_SIZE*2 + 6]; /* prefix.listname.[listindex] */
|
||||
|
||||
for (int i = 0; i < ParamList->numelt; ++i) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,8 +34,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"
|
||||
@@ -87,8 +90,222 @@ 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 {
|
||||
if (cfgoptions[i].numelt == 0 ) {
|
||||
value = *(cfgoptions[i].strptr);
|
||||
} else {
|
||||
value = (char *)(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 ) {
|
||||
@@ -376,14 +593,43 @@ 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;
|
||||
|
||||
@@ -40,12 +40,11 @@ extern "C"
|
||||
|
||||
#include "common/config/config_paramdesc.h"
|
||||
|
||||
|
||||
|
||||
|
||||
typedef struct libconfig_privatedata {
|
||||
char *configfile;
|
||||
config_t cfg;
|
||||
config_t runtcfg;
|
||||
} libconfig_privatedata_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -302,8 +302,8 @@ void log_getconfig(log_t *g_log)
|
||||
|
||||
for (int i=MIN_LOG_COMPONENTS; i < MAX_LOG_PREDEF_COMPONENTS; i++) {
|
||||
if(g_log->log_component[i].name == NULL) {
|
||||
g_log->log_component[i].name = malloc(16);
|
||||
sprintf((char *)g_log->log_component[i].name,"comp%i?",i);
|
||||
g_log->log_component[i].name = calloc(1,16);
|
||||
snprintf((char *)g_log->log_component[i].name,15,"undefcomp%i",i);
|
||||
logparams_logfile[i].paramflags = PARAMFLAG_DONOTREAD;
|
||||
logparams_level[i].paramflags = PARAMFLAG_DONOTREAD;
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ paramdef_t telnetoptions[] = {
|
||||
{"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 hen 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 }
|
||||
@@ -90,25 +91,31 @@ int get_phybsize(void) {
|
||||
};
|
||||
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}
|
||||
{"debug", TELNET_VARTYPE_INT32,0,&telnetparams.telnetdbg},
|
||||
{"prio", TELNET_VARTYPE_INT32,0,&telnetparams.priority},
|
||||
{"loopc", TELNET_VARTYPE_INT32,0,&telnetparams.loopcount},
|
||||
{"loopd", TELNET_VARTYPE_INT32,0,&telnetparams.loopdelay},
|
||||
{"phypb", TELNET_VARTYPE_INT32,0,&telnetparams.phyprntbuff_size},
|
||||
{"hsize", TELNET_VARTYPE_INT32,0,&telnetparams.histsize},
|
||||
{"hfile", TELNET_VARTYPE_STRING,TELNET_CHECKVAL_RDONLY,&telnetparams.histfile},
|
||||
{"logfile",TELNET_VARTYPE_STRING,0,&telnetparams.logfile},
|
||||
{"",0,0,NULL}
|
||||
};
|
||||
|
||||
telnetshell_cmddef_t telnet_cmdarray[] = {
|
||||
{"redirlog","[here,file,off]",setoutput},
|
||||
{"param","[prio]",setparam},
|
||||
{"history","[list,reset]",history_cmd},
|
||||
{"","",NULL},
|
||||
{"redirlog","[here,file,off]",setoutput,{NULL},TELNETSRV_CMDFLAG_TELNETONLY,NULL},
|
||||
{"redirlog file","",setoutput,{NULL},TELNETSRV_CMDFLAG_WEBSRVONLY,NULL},
|
||||
{"redirlog off","",setoutput,{NULL},TELNETSRV_CMDFLAG_WEBSRVONLY,NULL},
|
||||
{"param","[prio]",setparam,{wsetparam},0,NULL},
|
||||
{"history","[list,reset]",history_cmd,{NULL},TELNETSRV_CMDFLAG_TELNETONLY,NULL},
|
||||
{"","",NULL,{NULL},0,NULL},
|
||||
};
|
||||
|
||||
|
||||
@@ -127,36 +134,40 @@ 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)){// fifo mapped to oai priority 80
|
||||
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 +183,15 @@ 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);
|
||||
errno,strerror(errno),tid,niceval);
|
||||
}
|
||||
} else {
|
||||
client_printf("Error %i: %s trying to get nice value of thread %u \n",
|
||||
@@ -189,17 +200,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 +247,29 @@ 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 +285,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 +301,10 @@ 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,6 +368,98 @@ 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;
|
||||
|
||||
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;
|
||||
@@ -357,81 +473,15 @@ int setgetvar(int moduleindex,char getorset,char *params) {
|
||||
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;
|
||||
}
|
||||
char *strval=telnet_getvarvalue(telnetparams.CmdParsers[moduleindex].var, i);
|
||||
client_printf("%s\n",strval);
|
||||
free(strval);
|
||||
}
|
||||
|
||||
if (n > 1 && (getorset == 's' || getorset == 'S')) {
|
||||
client_printf("%s, %s set to \n", telnetparams.CmdParsers[moduleindex].module,
|
||||
telnetparams.CmdParsers[moduleindex].var[i].varname);
|
||||
telnet_setvarvalue(&(telnetparams.CmdParsers[moduleindex].var[i]),varval, client_printf);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -442,6 +492,13 @@ 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,6 +506,16 @@ 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 i,j,k;
|
||||
@@ -459,7 +526,7 @@ int process_command(char *buf) {
|
||||
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 +541,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);
|
||||
@@ -489,7 +558,7 @@ int process_command(char *buf) {
|
||||
j = sscanf(buf,"%19s %19s %m[^\t\n]",modulename,cmd,&cmdb);
|
||||
|
||||
if (telnetparams.telnetdbg > 0)
|
||||
printf("process_command: %i words, module=%s cmd=%s, parameters= %s\n",j,modulename,cmd,cmdb);
|
||||
printf("process_command: %i words, module=%s cmd=%s, parameters= %s\n",j,modulename,cmd,(cmdb==NULL)?"":cmdb);
|
||||
|
||||
for (i=0; j>=2 && telnetparams.CmdParsers[i].var != NULL && telnetparams.CmdParsers[i].cmd != NULL; i++) {
|
||||
if ( (strncasecmp(telnetparams.CmdParsers[i].module,modulename,strlen(telnetparams.CmdParsers[i].module)) == 0)) {
|
||||
@@ -504,14 +573,10 @@ int process_command(char *buf) {
|
||||
} else {
|
||||
for (k=0 ; telnetparams.CmdParsers[i].cmd[k].cmdfunc != NULL ; k++) {
|
||||
if (strncasecmp(cmd, telnetparams.CmdParsers[i].cmd[k].cmdname,sizeof(telnetparams.CmdParsers[i].cmd[k].cmdname)) == 0) {
|
||||
if (telnetparams.CmdParsers[i].cmd[k].cmdflags & TELNETSRV_CMDFLAG_WEBSRVONLY)
|
||||
continue;
|
||||
if (telnetparams.CmdParsers[i].cmd[k].qptr != NULL) {
|
||||
notifiedFIFO_elt_t *msg =newNotifiedFIFO_elt(sizeof(telnetsrv_qmsg_t),0,NULL,NULL);
|
||||
telnetsrv_qmsg_t *cmddata=NotifiedFifoData(msg);
|
||||
cmddata->cmdfunc=(qcmdfunc_t)telnetparams.CmdParsers[i].cmd[k].cmdfunc;
|
||||
cmddata->prnt=client_printf;
|
||||
cmddata->debug=telnetparams.telnetdbg;
|
||||
cmddata->cmdbuff=strdup(cmdb);
|
||||
pushNotifiedFIFO(telnetparams.CmdParsers[i].cmd[k].qptr, msg);
|
||||
telnet_pushcmd(&(telnetparams.CmdParsers[i].cmd[k]), cmdb,client_printf );
|
||||
} else {
|
||||
telnetparams.CmdParsers[i].cmd[k].cmdfunc(cmdb, telnetparams.telnetdbg, client_printf);
|
||||
}
|
||||
@@ -859,12 +924,14 @@ int add_telnetcmd(char *modulename, telnetshell_vardef_t *var, telnetshell_cmdde
|
||||
strncpy(telnetparams.CmdParsers[i].module,modulename,sizeof(telnetparams.CmdParsers[i].module)-1);
|
||||
telnetparams.CmdParsers[i].cmd = cmd;
|
||||
telnetparams.CmdParsers[i].var = var;
|
||||
if (cmd->cmdflags & TELNETSRV_CMDFLAG_PUSHINTPOOLQ) {
|
||||
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->qptr = afifo;
|
||||
cmd[j].qptr = afifo;
|
||||
}
|
||||
}
|
||||
printf("[TELNETSRV] Telnet server: module %i = %s added to shell\n",
|
||||
i,telnetparams.CmdParsers[i].module);
|
||||
@@ -894,10 +961,16 @@ 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)[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,55 @@
|
||||
#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_PRINTWEBTBLDATA (1<<9) // websrv Print as a single column table
|
||||
#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 +114,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,10 +123,18 @@ 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 +159,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 +192,25 @@ VT escape sequence definition, for smarter display....
|
||||
#define STDFMT "\x1b[0m"
|
||||
|
||||
/*---------------------------------------------------------------------------------------------*/
|
||||
#define TELNET_ADDCMD_FNAME "add_telnetcmd"
|
||||
#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")
|
||||
|
||||
@@ -110,9 +110,8 @@ int dump_phyvars(char *buf, int debug, telnet_printfunc_t prnt) {
|
||||
|
||||
|
||||
telnetshell_cmddef_t phy_cmdarray[] = {
|
||||
{"disp","[phycnt,uedump,uestat UE<x>]", dump_phyvars},
|
||||
|
||||
{"","",NULL},
|
||||
{"disp","[phycnt,uedump,uestat UE<x>]", dump_phyvars,{NULL},0,NULL},
|
||||
{"","",NULL,{NULL},0,NULL},
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -42,24 +42,29 @@
|
||||
|
||||
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);
|
||||
|
||||
|
||||
if (strcasestr(buff,"params") != NULL) {
|
||||
prnt( "loader parameters:\n");
|
||||
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) {
|
||||
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);
|
||||
|
||||
@@ -39,11 +39,11 @@
|
||||
|
||||
|
||||
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}
|
||||
{"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;
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ telnetshell_cmddef_t measur_cmdarray[] = {
|
||||
};
|
||||
|
||||
telnetshell_vardef_t measur_vardef[] = {
|
||||
{"",0,NULL}
|
||||
{"",0,0,NULL}
|
||||
};
|
||||
/* function to be implemented in any telnetsrv_xxx_measurements.c sources
|
||||
to allow common measurment code to access measurments data */
|
||||
|
||||
@@ -53,7 +53,7 @@ telnetshell_vardef_t phy_vardef[] = {
|
||||
//{"txshift",TELNET_VARTYPE_INT32,NULL},
|
||||
//{"rachemin",TELNET_VARTYPE_INT32,NULL},
|
||||
//{"rachdmax",TELNET_VARTYPE_INT32,NULL},
|
||||
{"",0,NULL}
|
||||
{"",0,0,NULL}
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
@@ -57,89 +57,121 @@
|
||||
#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);
|
||||
procfile_fields =strtok_r(record," ",&strtokptr);
|
||||
lptr= prntline;
|
||||
/*http://man7.org/linux/man-pages/man5/proc.5.html gives the structure of the stat file */
|
||||
|
||||
while( procfile_fiels != NULL && fieldcnt < 42) {
|
||||
int priority=0;
|
||||
int nice=0;
|
||||
while( procfile_fields != NULL && fieldcnt < 42) {
|
||||
long int policy;
|
||||
if (strlen(procfile_fiels) == 0)
|
||||
if (strlen(procfile_fields) == 0)
|
||||
continue;
|
||||
fieldcnt++;
|
||||
sprintf(toksep," ");
|
||||
switch(fieldcnt) {
|
||||
case 1: /* id */
|
||||
lptr+=sprintf(lptr,"%9.9s ",procfile_fiels);
|
||||
sprintf(toksep,")");
|
||||
if (tdata != NULL) {
|
||||
tdata->lines[tdata->numlines].val[0]=strdup(procfile_fields);
|
||||
}
|
||||
lptr+=sprintf(lptr,"%9.9s ",procfile_fields);
|
||||
sprintf(toksep,")");
|
||||
break;
|
||||
case 2: /* name */
|
||||
lptr+=sprintf(lptr,"%20.20s ",procfile_fiels+1);
|
||||
if (tdata != NULL) {
|
||||
tdata->lines[tdata->numlines].val[1]=strdup(procfile_fields);
|
||||
}
|
||||
lptr+=sprintf(lptr,"%20.20s ",procfile_fields+1);
|
||||
break;
|
||||
case 3: //thread state
|
||||
lptr+=sprintf(lptr," %c ",procfile_fiels[0]);
|
||||
lptr+=sprintf(lptr," %c ",procfile_fields[0]);
|
||||
break;
|
||||
case 14: //time in user mode
|
||||
case 15: //time in kernel mode
|
||||
lptr+=sprintf(lptr,"%9.9s ",procfile_fiels);
|
||||
lptr+=sprintf(lptr,"%9.9s ",procfile_fields);
|
||||
break;
|
||||
case 18: //priority
|
||||
case 19: //nice
|
||||
lptr+=sprintf(lptr,"%3.3s ",procfile_fiels);
|
||||
case 18: //priority column index 2 in tdata, -2 to -100 (1, min to 99, highest prio)
|
||||
priority=strtol(procfile_fields,NULL,0);
|
||||
case 19: //nice column index 3 in tdata 0 to 39 (-20, highest prio, to 19)
|
||||
if (tdata != NULL) {
|
||||
tdata->lines[tdata->numlines].val[fieldcnt-16]=strdup(procfile_fields);
|
||||
}
|
||||
lptr+=sprintf(lptr,"%3.3s ",procfile_fields);
|
||||
nice=strtol(procfile_fields,NULL,0);
|
||||
break;
|
||||
case 23: //vsize
|
||||
lptr+=sprintf(lptr,"%9.9s ",procfile_fiels);
|
||||
lptr+=sprintf(lptr,"%9.9s ",procfile_fields);
|
||||
break;
|
||||
case 39: //processor
|
||||
lptr+=sprintf(lptr," %2.2s ",procfile_fiels);
|
||||
case 39: //processor
|
||||
if (tdata != NULL) {
|
||||
tdata->lines[tdata->numlines].val[4]=strdup(procfile_fields);
|
||||
}
|
||||
lptr+=sprintf(lptr," %2.2s ",procfile_fields);
|
||||
break;
|
||||
case 41: //policy
|
||||
lptr+=sprintf(lptr,"%3.3s ",procfile_fiels);
|
||||
policy=strtol(procfile_fiels,NULL,0);
|
||||
lptr+=sprintf(lptr,"%3.3s ",procfile_fields);
|
||||
policy=strtol(procfile_fields,NULL,0);
|
||||
char strschedp[64];
|
||||
switch(policy) {
|
||||
case SCHED_FIFO:
|
||||
lptr+=sprintf(lptr,"%s ","rt: fifo");
|
||||
snprintf(strschedp,sizeof(strschedp),"%s ","rt:fifo");
|
||||
priority= priority+1; //in /proc file system priority 1 to 99 mapped to -2 to -100
|
||||
break;
|
||||
case SCHED_OTHER:
|
||||
lptr+=sprintf(lptr,"%s ","other");
|
||||
snprintf(strschedp,sizeof(strschedp),"%s ","other");
|
||||
priority=nice-NICE_MIN; // linux nice is -20 to 19
|
||||
break;
|
||||
case SCHED_IDLE:
|
||||
lptr+=sprintf(lptr,"%s ","idle");
|
||||
snprintf(strschedp,sizeof(strschedp),"%s ","idle");
|
||||
priority=2*(NICE_MAX-NICE_MIN+1);
|
||||
break;
|
||||
case SCHED_BATCH:
|
||||
lptr+=sprintf(lptr,"%s ","batch");
|
||||
snprintf(strschedp,sizeof(strschedp),"%s ","batch");
|
||||
priority=(NICE_MAX-NICE_MIN+1)+nice-NICE_MIN;
|
||||
break;
|
||||
case SCHED_RR:
|
||||
lptr+=sprintf(lptr,"%s ","rt: rr");
|
||||
snprintf(strschedp,sizeof(strschedp),"%s ","rt:rr");
|
||||
priority= priority-99 ;
|
||||
break;
|
||||
#ifdef SCHED_DEADLINE
|
||||
case SCHED_DEADLINE:
|
||||
lptr+=sprintf(lptr,"%s ","rt: deadline");
|
||||
snprintf(strschedp,sizeof(strschedp),"%s ","rt:deadline");
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
lptr+=sprintf(lptr,"%s ","????");
|
||||
snprintf(strschedp,sizeof(strschedp),"%s ","????");
|
||||
break;
|
||||
}
|
||||
lptr+=sprintf(lptr,"%s ",strschedp);
|
||||
if (tdata != NULL) {
|
||||
tdata->lines[tdata->numlines].val[5]=strdup(strschedp);
|
||||
tdata->lines[tdata->numlines].val[6]=malloc(10);
|
||||
snprintf(tdata->lines[tdata->numlines].val[6],9,"%i",priority);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}/* switch on fieldcnr */
|
||||
procfile_fiels =strtok_r(NULL,toksep,&strtokptr);
|
||||
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];
|
||||
@@ -157,21 +189,55 @@ 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("System has %d cores %d threads %d Actual threads",eax,ebx,edx);
|
||||
|
||||
|
||||
|
||||
prnt(" id name state USRmod KRNmod prio nice vsize proc pol \n\n");
|
||||
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);
|
||||
read_statfile(aname,debug,prnt,NULL);
|
||||
prnt("\n");
|
||||
snprintf(aname, sizeof(aname), "/proc/%d/task", getpid());
|
||||
proc_dir = opendir(aname);
|
||||
@@ -181,20 +247,150 @@ struct dirent *entry;
|
||||
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 */
|
||||
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) {
|
||||
@@ -270,6 +466,10 @@ int bv1,bv2;
|
||||
int res;
|
||||
char sv1[64];
|
||||
|
||||
if (buf == NULL) {
|
||||
prnt("ERROR wrong thread command...\n");
|
||||
return 0;
|
||||
}
|
||||
bv1=0;
|
||||
bv2=0;
|
||||
sv1[0]=0;
|
||||
@@ -284,7 +484,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;
|
||||
}
|
||||
|
||||
@@ -307,11 +507,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)
|
||||
{
|
||||
@@ -396,7 +605,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);
|
||||
@@ -435,7 +644,7 @@ int s = sscanf(buf,"%ms %i-%i\n",&logsubcmd, &idx1,&idx2);
|
||||
} else {
|
||||
prnt("%s%s unknown log sub command \n",logparam, tmpstr);
|
||||
}
|
||||
} else {
|
||||
} else {level=map_str_to_int(log_level_names,tmpstr);
|
||||
prnt("%s unknown log sub command \n",logsubcmd);
|
||||
}
|
||||
if (logparam != NULL) free(logparam);
|
||||
@@ -459,6 +668,7 @@ int s = sscanf(buf,"%ms %i-%i\n",&logsubcmd, &idx1,&idx2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------------------*/
|
||||
|
||||
void add_softmodem_cmds(void)
|
||||
|
||||
@@ -44,9 +44,11 @@
|
||||
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);
|
||||
extern int proccmd_websrv_getdata(char *cmdbuff, int debug, void *data,telnet_printfunc_t prnt);
|
||||
telnetshell_vardef_t proc_vardef[] = {
|
||||
{"",0,NULL}
|
||||
{"",0,0,NULL}
|
||||
};
|
||||
#define PROCCMD_LOG_HELP_STRING " log sub commands: \n\
|
||||
show: display current log configuration \n\
|
||||
@@ -65,18 +67,26 @@ telnetshell_vardef_t proc_vardef[] = {
|
||||
#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},
|
||||
{"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
|
||||
|
||||
7
common/utils/websrv/DOC/websrv.md
Normal file
7
common/utils/websrv/DOC/websrv.md
Normal file
@@ -0,0 +1,7 @@
|
||||
The oai web server is an optional monitoring and debugging tool. It gives access to the same functionalities as the telnet server via a web interface
|
||||
|
||||
* [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)
|
||||
31
common/utils/websrv/DOC/websrvarch.md
Normal file
31
common/utils/websrv/DOC/websrvarch.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# telnet server principles
|
||||
|
||||
The oai telnet server is implemented in shared libraries to be loaded by the [oai shared library loader](loader). `libtelnetsrv.so ` contains the code common to all oai softmodem executables, where `libtelnetsrv_<app>.so` contains the code specific to the executable identified by `app`.
|
||||
|
||||
| `<app>` | executable |
|
||||
| :-----: | :---------------: |
|
||||
| enb | `lte-softmodem` |
|
||||
| gnb | `nr-softmodem` |
|
||||
| 4Gue | `lte-uesoftmodem` |
|
||||
| 5Gue | `nr-uesoftmodem` |
|
||||
|
||||
The implementation includes a `telnetsrv_autoinit` function which is automatically called at load time, starts the telnet server and registers a first set of commands, which are delivered with the server (telnet, softmodem, loader). it also uses the `get_softmodem_function` call to retrieve the running executable id and possibly loads the corresponding `libtelnetsrv_<app>.so`library.
|
||||
|
||||
Currently the telnet server only supports one user connection. The same dedicated thread is used to wait for a user connection and process the input received from this connection.
|
||||
|
||||
The telnet server provides an API which can be used by any oai component to add new CLI commands to the server. A pre-defined command can be used to get or set a list of variables.
|
||||
|
||||
|
||||
|
||||
# telnet server source files
|
||||
|
||||
telnet server source files are located in [common/utils/telnetsrv](https://gitlab.eurecom.fr/oai/openairinterface5g/tree/develop/common/utils/telnetsrv)
|
||||
|
||||
1. [telnetsrv.c](https://gitlab.eurecom.fr/oai/openairinterface5g/tree/develop/common/utils/telnetsrv/telnetsrv.c) contains the telnet server implementation, including the implementation of the `telnet` CLI command. This implementation is compatible with all softmodem executables and is in charge of loading any additional `libtelnetsrv_<app> .so` containing code specific to the running executables.
|
||||
1. [telnetsrv.h](https://gitlab.eurecom.fr/oai/openairinterface5g/tree/develop/common/utils/telnetsrv/telnetsrv.h) is the telnet server include file containing both private and public data type definitions. It also contains API prototypes for functions that are used to register a new command in the server.
|
||||
1. `telnetsrv_<XXX\>.c`: implementation of \<XXX\> CLI command which are delivered with the telnet server and are common to all softmodem executables.
|
||||
1. `telnetsrv_<XXX\>.h`: include file for the implementation of XXX CLI command. Usually included only in the corresponding `.c`file
|
||||
1. `telnetsrv_<app>_<XXX>.c`: implementation of \<XXX\> CLI command specific to the executable identified by \<app\>.These sources are used to create `libtelnetsrv_<app>.so` at build time.
|
||||
1. [telnetsrv_CMakeLists.txt](https://gitlab.eurecom.fr/oai/openairinterface5g/blob/develop/common/utils/telnetsrv/telnetsrv_CMakeLists.txt): CMakelists file containing the cmake instructions to build the telnet server. this file is included in the [global oai CMakelists](https://gitlab.eurecom.fr/oai/openairinterface5g/blob/develop/cmake_targets/CMakeLists.txt).
|
||||
|
||||
[oai telnet server home](telnetsrv.md)
|
||||
40
common/utils/websrv/DOC/websrvuse.md
Normal file
40
common/utils/websrv/DOC/websrvuse.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# starting the softmodem with the telnet server
|
||||
By default the embedded telnet server, 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 telnetsrv
|
||||
```
|
||||
|
||||
This will create the `libtelnetsrv.so` and `libtelnetsrv_<app> file in the `targets/bin` and `cmake_targets/ran_build/build` sub directories of the oai repository. <app> can be "enb", "gnb", "4GUE" or "5GUE", each library containing functions specific to a given executable.
|
||||
|
||||
When starting the softmodem, you must specify the **_\-\-telnetsrv_** option to load and start the telnet server. The telnet server is loaded via the [oai shared library loader](loader).
|
||||
|
||||
# using the Command Line Interface
|
||||
By default the telnet server listen on all the ip addresses configured on the system and on port 9090. This behavior can be changed using the `listenaddr` and `listenport` parameters.
|
||||
The telnet server includes a basic help, listing available commands and some commands also provide a specific detailed help sub-command.
|
||||
Below are examples of telnet sessions:
|
||||
|
||||
* [getting help](telnethelp.md)
|
||||
* [using the history](telnethist.md)
|
||||
* [using the get and set commands](telnetgetset.md)
|
||||
* [using the loop command](telnetloop.md)
|
||||
* [loader command](telnetloader.md)
|
||||
* [log command](telnetlog.md)
|
||||
* [measur command](telnetmeasur.md)
|
||||
|
||||
# telnet server parameters
|
||||
The telnet server is using the [oai configuration module](Config/Rtusage). Telnet parameters must be specified in the `telnetsrv` section. Some parameters can be modified via the telnet telnet server command, as specified in the last column of the following table.
|
||||
|
||||
| name | type | default | description | dynamic |
|
||||
|:---:|:---:|:---:|:----|:----:|
|
||||
| `listenaddr` | `ipV4 address, ascii format` | "0.0.0.0" | local address the server is listening on| N |
|
||||
| `listenport` | `integer` | 9090 | port number the server is listening on | N |
|
||||
| `loopcount` | `integer` | 10 | number of iterations for the loop command | Y |
|
||||
| `loopdelay` | `integer` | 5000 | delay (in ms) between 2 loop command iterations | Y |
|
||||
| `histfile` | `character string` | "oaitelnet.history" | file used for command history persistency | Y |
|
||||
| `histfsize` | `integer` | 50 | maximum number of commands saved in the history | Y |
|
||||
|
||||
[oai telnet server home](telnetsrv.md)
|
||||
17
common/utils/websrv/frontend/.browserslistrc
Normal file
17
common/utils/websrv/frontend/.browserslistrc
Normal file
@@ -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.
|
||||
16
common/utils/websrv/frontend/.editorconfig
Normal file
16
common/utils/websrv/frontend/.editorconfig
Normal file
@@ -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
|
||||
50
common/utils/websrv/frontend/.eslintrc.json
Normal file
50
common/utils/websrv/frontend/.eslintrc.json
Normal file
@@ -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": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
50
common/utils/websrv/frontend/.gitignore
vendored
Normal file
50
common/utils/websrv/frontend/.gitignore
vendored
Normal file
@@ -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
|
||||
1
common/utils/websrv/frontend/README.md
Normal file
1
common/utils/websrv/frontend/README.md
Normal file
@@ -0,0 +1 @@
|
||||
# SoftModemNgx
|
||||
108
common/utils/websrv/frontend/angular.json
Normal file
108
common/utils/websrv/frontend/angular.json
Normal file
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
common/utils/websrv/frontend/e2e/protractor-ci.conf.js
Normal file
40
common/utils/websrv/frontend/e2e/protractor-ci.conf.js
Normal file
@@ -0,0 +1,40 @@
|
||||
// @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
|
||||
}
|
||||
}));
|
||||
}
|
||||
};
|
||||
37
common/utils/websrv/frontend/e2e/protractor.conf.js
Normal file
37
common/utils/websrv/frontend/e2e/protractor.conf.js
Normal file
@@ -0,0 +1,37 @@
|
||||
// @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
|
||||
}
|
||||
}));
|
||||
}
|
||||
};
|
||||
23
common/utils/websrv/frontend/e2e/src/app.e2e-spec.ts
Normal file
23
common/utils/websrv/frontend/e2e/src/app.e2e-spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
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));
|
||||
});
|
||||
});
|
||||
11
common/utils/websrv/frontend/e2e/src/app.po.ts
Normal file
11
common/utils/websrv/frontend/e2e/src/app.po.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
10
common/utils/websrv/frontend/e2e/tsconfig.json
Normal file
10
common/utils/websrv/frontend/e2e/tsconfig.json
Normal file
@@ -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
common/utils/websrv/frontend/package-lock.json
generated
Normal file
24319
common/utils/websrv/frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
65
common/utils/websrv/frontend/package.json
Normal file
65
common/utils/websrv/frontend/package.json
Normal file
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
116
common/utils/websrv/frontend/src/app/api/commands.api.ts
Normal file
116
common/utils/websrv/frontend/src/app/api/commands.api.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
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
|
||||
}
|
||||
|
||||
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 ?
|
||||
}
|
||||
export interface IRow {
|
||||
params: IParam[],
|
||||
rawIndex: number,
|
||||
cmdName: string
|
||||
}
|
||||
|
||||
|
||||
export const route = 'oaisoftmodem/';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class CommandsApi {
|
||||
constructor(private httpClient: HttpClient) { }
|
||||
|
||||
public readInfos$ = () => this.httpClient.get<IInfo[]>('http://' + environment.backend + route + 'variables/');
|
||||
|
||||
public setInfo$ = (info: IInfo) => this.httpClient.post<IResp>('http://' + environment.backend + route + 'variables/', info);
|
||||
|
||||
public readModules$ = () => this.httpClient.get<IModule[]>('http://' + environment.backend + route + 'commands/');
|
||||
|
||||
public readVariables$ = (moduleName: string) => this.httpClient.get<IInfo[]>('http://' + environment.backend + route + moduleName + '/variables/');
|
||||
|
||||
public readCommands$ = (moduleName: string) => this.httpClient.get<ICommand[]>('http://' + environment.backend + route + moduleName + '/commands/');
|
||||
|
||||
public runCommand$ = (command: ICommand, moduleName: string) => this.httpClient.post<IResp>('http://' + environment.backend + route + moduleName + '/commands/', command);
|
||||
|
||||
public setCmdVariable$ = (variable: IInfo, moduleName: string) => this.httpClient.post<IResp>('http://' + environment.backend + route + moduleName + '/variables/', variable);
|
||||
|
||||
public setCmdParams$ = (row: IRow, moduleName: string) => this.httpClient.post<IResp>('http://' + environment.backend + route + moduleName + '/set/', row);
|
||||
|
||||
}
|
||||
47
common/utils/websrv/frontend/src/app/api/scope.api.ts
Normal file
47
common/utils/websrv/frontend/src/app/api/scope.api.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
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",
|
||||
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>('http://' + environment.backend + route);
|
||||
|
||||
public setScopeParams$ = (cmd: IScopeCmd) => this.httpClient.post('http://' + environment.backend + route, cmd);
|
||||
|
||||
}
|
||||
16
common/utils/websrv/frontend/src/app/app-routing.module.ts
Normal file
16
common/utils/websrv/frontend/src/app/app-routing.module.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } 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 { }
|
||||
11
common/utils/websrv/frontend/src/app/app.component.css
Normal file
11
common/utils/websrv/frontend/src/app/app.component.css
Normal file
@@ -0,0 +1,11 @@
|
||||
.mat-tab-group {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.mat-ink-bar{
|
||||
height: 8px !important;
|
||||
}
|
||||
.mat-tab-label-active
|
||||
{
|
||||
color: #000000 !important;
|
||||
}
|
||||
|
||||
11
common/utils/websrv/frontend/src/app/app.component.html
Normal file
11
common/utils/websrv/frontend/src/app/app.component.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<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 label="Logger">
|
||||
<app-logger></app-logger>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
22
common/utils/websrv/frontend/src/app/app.component.ts
Normal file
22
common/utils/websrv/frontend/src/app/app.component.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
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' : '';
|
||||
}
|
||||
}
|
||||
88
common/utils/websrv/frontend/src/app/app.module.ts
Normal file
88
common/utils/websrv/frontend/src/app/app.module.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
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 { 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';
|
||||
import { LoggerComponent } from './components/logger/logger.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
CommandsComponent,
|
||||
ConfirmDialogComponent,
|
||||
QuestionDialogComponent,
|
||||
DialogComponent,
|
||||
ScopeComponent,
|
||||
LoggerComponent
|
||||
],
|
||||
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,
|
||||
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)"> {{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 }} </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>
|
||||
<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,197 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { UntypedFormArray } from '@angular/forms';
|
||||
import { BehaviorSubject, forkJoin, timer } from 'rxjs';
|
||||
import { Observable } from 'rxjs';
|
||||
import { of } from 'rxjs';
|
||||
import { filter } from 'rxjs/operators';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { switchMap } from 'rxjs/operators';
|
||||
import { CommandsApi, IArgType, IColumn, ICommand, IInfo, ILogLvl, IParam, IRow } from 'src/app/api/commands.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';
|
||||
import { ViewEncapsulation } from '@angular/core';
|
||||
|
||||
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 {
|
||||
|
||||
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 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)
|
||||
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);
|
||||
if (control.isResUpdatable())
|
||||
control.ResUpdTimer = timer(2000, 1000);
|
||||
return this.execCmd$(control);
|
||||
}))
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
private execCmd$(control: CmdCtrl) {
|
||||
if (control.isResUpdatable() && control.ResUpdTimer) {
|
||||
if (!(control.ResUpdTimerSubscriber)) {
|
||||
control.ResUpdTimerSubscriber = control.ResUpdTimer.subscribe(iteration => {
|
||||
console.log("Update timer fired" + iteration);
|
||||
if (control.updbtnname === "Stop update")
|
||||
this.execCmd$(control);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.commandsApi.runCommand$(control!.api(), 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')
|
||||
|
||||
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: '),
|
||||
) // map resp
|
||||
|
||||
|
||||
return of(null);
|
||||
}
|
||||
|
||||
onParamSubmit(control: RowCtrl) {
|
||||
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,21 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } 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, ViewChild, ElementRef, Inject, OnInit } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { DialogService } from 'src/app/services/dialog.service';
|
||||
import { CmdCtrl } from 'src/app/controls/cmd.control';
|
||||
|
||||
@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,5 @@
|
||||
<div id="logDisplay">
|
||||
<ul>
|
||||
<li *ngFor="let msg of messages">{{JSON.stringify(msg)}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LoggerComponent } from './logger.component';
|
||||
|
||||
describe('LoggerComponent', () => {
|
||||
let component: LoggerComponent;
|
||||
let fixture: ComponentFixture<LoggerComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ LoggerComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(LoggerComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { Message, WebSocketService } from 'src/app/services/websocket.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-logger',
|
||||
templateUrl: './logger.component.html',
|
||||
styleUrls: ['./logger.component.css']
|
||||
})
|
||||
export class LoggerComponent implements OnInit {
|
||||
|
||||
JSON = JSON
|
||||
|
||||
// websocket service object and related subscription for message reception
|
||||
wsSubscription?: Subscription;
|
||||
|
||||
messages: Message[] = [];
|
||||
|
||||
constructor(
|
||||
private wsService: WebSocketService
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.wsSubscription = this.wsService.subject$.subscribe(
|
||||
(msg: Message) => this.messages.push(msg)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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,24 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } 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,204 @@
|
||||
<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 }} </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 [checked]="data_ACK" (change)="onDataACKchange()" [(ngModel)]="data_ACK">data 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:60%">
|
||||
<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>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</mat-grid-tile>
|
||||
<!-- right area for -->
|
||||
<mat-grid-tile [colspan]="3" [rowspan]="2">
|
||||
<mat-card class="dashboard-card">
|
||||
<mat-card-content class="dashboard-card-content">
|
||||
|
||||
</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 90% ">
|
||||
<canvas baseChart id="llr" width="90%"
|
||||
[datasets]=" LLRDatasets"
|
||||
[options]="LLROptions"
|
||||
[type]="'scatter'">
|
||||
</canvas>
|
||||
</div>
|
||||
<div fxFlex="0 1 5%" 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 5%">
|
||||
<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>
|
||||
</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%">
|
||||
<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>
|
||||
<!-- 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,584 @@
|
||||
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 { 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_STATUSUPD = 1;
|
||||
const SCOPEMSG_TYPE_REFRATE = 2;
|
||||
const SCOPEMSG_TYPE_TIME = 3;
|
||||
const SCOPEMSG_TYPE_DATA = 10;
|
||||
const SCOPEMSG_TYPE_DATAACK = 11;
|
||||
|
||||
|
||||
const SCOPEMSG_DATA_IQ = 1;
|
||||
const SCOPEMSG_DATA_LLR = 2;
|
||||
const SCOPEMSG_DATA_WF = 3;
|
||||
/*---------------------------------------*/
|
||||
@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';
|
||||
//data for scope control area
|
||||
startstop = 'start';
|
||||
startstop_color = 'warn';
|
||||
rfrate = 2;
|
||||
data_ACK = 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;
|
||||
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 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,
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
|
||||
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
|
||||
) {
|
||||
console.log("Scope constructor ");
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
console.log("Scope ngOnInit ");
|
||||
this.scopeApi.getScopeInfos$().subscribe(resp => {
|
||||
this.configScope(resp);
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
console.log("Scope ngOnDestroy ");
|
||||
this.wsSubscription?.unsubscribe();
|
||||
}
|
||||
|
||||
DecodScopeBinmsgToString(message: ArrayBuffer) {
|
||||
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]);
|
||||
}
|
||||
}
|
||||
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_DATA:
|
||||
const bufferview = new DataView(message.content);
|
||||
|
||||
|
||||
switch (message.chartid) {
|
||||
case SCOPEMSG_DATA_IQ:
|
||||
this.IQDatasets[message.dataid].data.length = 0;
|
||||
if (message.update) {
|
||||
console.log("Starting scope update chart " + message.chartid.toString() + ", dataset " + message.dataid.toString());
|
||||
}
|
||||
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:
|
||||
if (message.update) {
|
||||
console.log("Starting scope update chart " + message.chartid.toString() + ", dataset " + message.dataid.toString());
|
||||
}
|
||||
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) {
|
||||
console.log("Starting scope update chart " + message.chartid.toString() + ", dataset " + message.dataid.toString());
|
||||
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(" scope 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) {
|
||||
this.scopeApi.setScopeParams$({ name: name, value: value, graphid: graphid }).subscribe();
|
||||
}
|
||||
|
||||
startorstop() {
|
||||
if (this.scopestatus === 'stopped') {
|
||||
|
||||
this.scopeApi.setScopeParams$({ name: "startstop", value: "start" }).subscribe(
|
||||
() => {
|
||||
this.llrchart = Chart.getChart("llr");
|
||||
this.iqcchart = Chart.getChart("iqc");
|
||||
this.wfchart = Chart.getChart("wf");
|
||||
this.IQDatasets.forEach((dataset) => { dataset.data.length = 0 });
|
||||
this.LLRDatasets.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);
|
||||
for (let i = 0; i < this.WFDatasets.length; i++) {
|
||||
this.nwf[i] = 0;
|
||||
this.WFDatasets[i].data.length = 0;
|
||||
}
|
||||
|
||||
this.wsSubscription = this.wsService.subject$.subscribe(
|
||||
(msg: Message) => this.ProcessScopeMsg(deserialize(msg.fullbuff))
|
||||
)
|
||||
},
|
||||
err => {
|
||||
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.scopeApi.setScopeParams$({ name: "startstop", value: "stop" }).subscribe(
|
||||
() => {
|
||||
if (this.wsSubscription)
|
||||
this.wsSubscription.unsubscribe();
|
||||
|
||||
this.scopestatus = 'stopped';
|
||||
this.startstop = 'start';
|
||||
this.startstop_color = 'warn';
|
||||
this.charts?.forEach((child, index) => { child.chart?.update() });
|
||||
|
||||
},
|
||||
err => {
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
onDataACKchange() {
|
||||
this.SendScopeParams("DataAck", this.data_ACK.toString(), 0);
|
||||
}
|
||||
|
||||
OnYthreshChange() {
|
||||
this.SendScopeParams("llrythresh", this.llrythresh.toString(), 0);
|
||||
}
|
||||
}
|
||||
95
common/utils/websrv/frontend/src/app/controls/cmd.control.ts
Normal file
95
common/utils/websrv/frontend/src/app/controls/cmd.control.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
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';
|
||||
|
||||
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
|
||||
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.ResUpdTimerSubscriber.unsubscribe();
|
||||
this.updbtnname = "Start update"
|
||||
}
|
||||
}
|
||||
|
||||
startUpdate() {
|
||||
if (this.ResUpdTimerSubscriber && this.ResUpdTimer) {
|
||||
this.ResUpdTimerSubscriber = this.ResUpdTimer.subscribe();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
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,38 @@
|
||||
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,67 @@
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
51
common/utils/websrv/frontend/src/app/controls/row.control.ts
Normal file
51
common/utils/websrv/frontend/src/app/controls/row.control.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
|
||||
import { UntypedFormArray, FormControl, UntypedFormGroup } from '@angular/forms';
|
||||
import { IArgType, IParam, IRow } from '../api/commands.api';
|
||||
import { ParamCtrl } from './param.control';
|
||||
|
||||
|
||||
|
||||
enum RowFCN {
|
||||
paramsFA = 'params',
|
||||
}
|
||||
|
||||
|
||||
export class RowCtrl extends UntypedFormGroup {
|
||||
|
||||
cmdName: string
|
||||
rawIndex: number
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
return doc
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
74
common/utils/websrv/frontend/src/app/controls/var.control.ts
Normal file
74
common/utils/websrv/frontend/src/app/controls/var.control.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
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,55 @@
|
||||
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) => {
|
||||
switch (error.status) {
|
||||
case 400:
|
||||
case 403:
|
||||
case 501:
|
||||
case 500:
|
||||
this.log(YELLOW, request.method + ' ' + error.status + ' Error: ' + error.error);
|
||||
this.dialogService.openErrorDialog(error);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return throwError(error.error);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
private log = (color: string, txt: string) => console.log(color, '[API] ' + txt);
|
||||
}
|
||||
|
||||
const RED = '\x1b[31m%s\x1b[0m';
|
||||
const GREEN = '\x1b[32m%s\x1b[0m';
|
||||
const YELLOW = '\x1b[33m%s\x1b[0m';
|
||||
const MAGENTA = '\x1b[35m%s\x1b[0m';
|
||||
const CYAN = '\x1b[36m%s%s\x1b[0m';
|
||||
@@ -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,29 @@
|
||||
import { finalize } from 'rxjs/operators';
|
||||
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
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();
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
160
common/utils/websrv/frontend/src/app/services/dialog.service.ts
Normal file
160
common/utils/websrv/frontend/src/app/services/dialog.service.ts
Normal file
@@ -0,0 +1,160 @@
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { Injectable, ViewChild, ElementRef } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { Observable } from 'rxjs';
|
||||
import { of } from 'rxjs';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { IResp, ICommandOptions} from '../api/commands.api';
|
||||
import { ConfirmDialogComponent } from '../components/confirm/confirm.component';
|
||||
import { QuestionDialogComponent } from '../components/question/question.component';
|
||||
import { DialogComponent } from '../components/dialog/dialog.component';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { CmdCtrl } from 'src/app/controls/cmd.control';
|
||||
import { CommandsComponent } from '../components/commands/commands.component';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class DialogService {
|
||||
public isDialogOpen = false;
|
||||
cmdDialogRef : any;
|
||||
constructor(
|
||||
private _dialog: MatDialog,
|
||||
private _snackBar: MatSnackBar,
|
||||
) { }
|
||||
|
||||
openErrorDialog(error: HttpErrorResponse): Observable<any> {
|
||||
if (this.isDialogOpen) {
|
||||
return of(undefined);
|
||||
}
|
||||
this.isDialogOpen = true;
|
||||
return this._dialog.open(DialogComponent, {
|
||||
width: '900px',
|
||||
data: {
|
||||
title: error.status + ' Error',
|
||||
body: error.error,
|
||||
},
|
||||
panelClass: 'errRespDialog',
|
||||
})
|
||||
.afterClosed()
|
||||
.pipe(tap(() => this.isDialogOpen = false));
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
});
|
||||
|
||||
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 { Injectable } from '@angular/core';
|
||||
import { environment } from 'src/environments/environment';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { HttpHeaders } from '@angular/common/http';
|
||||
import { HttpParams } from '@angular/common/http';
|
||||
import {route } from 'src/app/api/commands.api';
|
||||
@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,19 @@
|
||||
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,76 @@
|
||||
|
||||
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: 'ws://' + environment.backend + '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
common/utils/websrv/frontend/src/assets/.gitkeep
Normal file
0
common/utils/websrv/frontend/src/assets/.gitkeep
Normal file
@@ -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.hostname + ':' + window.location.port + '/'
|
||||
};
|
||||
19
common/utils/websrv/frontend/src/environments/environment.ts
Normal file
19
common/utils/websrv/frontend/src/environments/environment.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
// 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: '10.130.163.206:8090/'
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* 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.
|
||||
BIN
common/utils/websrv/frontend/src/favicon.ico
Normal file
BIN
common/utils/websrv/frontend/src/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
18
common/utils/websrv/frontend/src/index.html
Normal file
18
common/utils/websrv/frontend/src/index.html
Normal file
@@ -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>
|
||||
13
common/utils/websrv/frontend/src/main.ts
Normal file
13
common/utils/websrv/frontend/src/main.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
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));
|
||||
53
common/utils/websrv/frontend/src/polyfills.ts
Normal file
53
common/utils/websrv/frontend/src/polyfills.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
10
common/utils/websrv/frontend/src/styles.css
Normal file
10
common/utils/websrv/frontend/src/styles.css
Normal file
@@ -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;
|
||||
}
|
||||
23
common/utils/websrv/frontend/src/test.ts
Normal file
23
common/utils/websrv/frontend/src/test.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
// 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);
|
||||
10
common/utils/websrv/frontend/tsconfig.app.json
Normal file
10
common/utils/websrv/frontend/tsconfig.app.json
Normal file
@@ -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"]
|
||||
}
|
||||
30
common/utils/websrv/frontend/tsconfig.json
Normal file
30
common/utils/websrv/frontend/tsconfig.json
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
10
common/utils/websrv/frontend/tsconfig.spec.json
Normal file
10
common/utils/websrv/frontend/tsconfig.spec.json
Normal file
@@ -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"]
|
||||
}
|
||||
1041
common/utils/websrv/websrv.c
Normal file
1041
common/utils/websrv/websrv.c
Normal file
File diff suppressed because it is too large
Load Diff
151
common/utils/websrv/websrv.h
Normal file
151
common/utils/websrv/websrv.h
Normal file
@@ -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_printf_t is an internal structure storing messages while processing a request */
|
||||
/* The meaage is used to fill a response body */
|
||||
typedef struct {
|
||||
pthread_mutex_t mutex; // protect the message betwween the websrv_printf_start and websrv_print_end calls
|
||||
struct _u_response * response; // the ulfius response structure, used to send the message
|
||||
char *buff; // a buffer to store the message, allocated in websrv_printf_start, free in websrv_print_end
|
||||
int buffsize; //
|
||||
char *buffptr; // pointer to free portion of buff
|
||||
} websrv_printf_t;
|
||||
|
||||
/* 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 *url; // url for the main (initial) web page
|
||||
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_STATUSUPD 1 // scope status: available, started....
|
||||
#define SCOPEMSG_TYPE_REFRATE 2 // scope refresh delay
|
||||
#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_DEFINEWINDOW 20 // scope window definition
|
||||
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;
|
||||
} websrv_scope_params_t;
|
||||
|
||||
/*------------------------------------------------------------------------------------------------*/
|
||||
/* web server utilities: */
|
||||
|
||||
extern void websrv_printjson(char * label, json_t *jsonobj); //debug: dump a json object
|
||||
extern void websrv_jbody( struct _u_response * response, json_t *jbody); // add a json body in a http response
|
||||
|
||||
extern void websrv_printf_start(struct _u_response * response, int buffsize ); // start a printf, lock the buffer
|
||||
extern void websrv_printf( const char *message, ...); // add characters in the printf locked buffer
|
||||
extern void websrv_printf_end(int httpstatus ); // 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); // debug: dump a http request
|
||||
extern int websrv_string_response(char *astring, struct _u_response * response, int httpstatus) ; // add a string in a http response
|
||||
|
||||
/* 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
|
||||
67
common/utils/websrv/websrv_CMakeLists.txt
Normal file
67
common/utils/websrv/websrv_CMakeLists.txt
Normal file
@@ -0,0 +1,67 @@
|
||||
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
|
||||
#set( ULFIUS "")
|
||||
find_library(ULFIUS NAMES "libulfius.so" NO_CACHE)
|
||||
if ("${ULFIUS}" STREQUAL "")
|
||||
message( FATAL_ERROR "ulfius library (https://github.com/babelouest/ulfius) not found, install libulfius-dev (ubuntu) ")
|
||||
set (WBACK FALSE)
|
||||
endif("${ULFIUS}" STREQUAL "")
|
||||
|
||||
find_library(JSON NAMES "libjansson.so" NO_CACHE )
|
||||
if ("${JSON}" STREQUAL "")
|
||||
message( ERROR "libjansson not found, install libjansson-dev for ubuntu, jansson-devel for fedora")
|
||||
set (WBACK FALSE)
|
||||
endif("${JSON}" STREQUAL "")
|
||||
|
||||
#set (NPM "")
|
||||
find_program(NPM NAMES npm NO_CACHE)
|
||||
if ("${NPM}" STREQUAL "")
|
||||
message( ERROR " npm is not installed, frontend won't be built. Possibly install npm, package is available for ubuntu and fedora")
|
||||
endif("${NPM}" STREQUAL "")
|
||||
|
||||
|
||||
# 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
|
||||
${OPENAIR_DIR}/openair1/PHY/TOOLS/nr_phy_scope.c
|
||||
)
|
||||
|
||||
if ( ${WBACK} )
|
||||
add_library(websrv MODULE ${WEBSRV_SOURCE} )
|
||||
target_link_libraries(websrv PRIVATE ulfius jansson)
|
||||
target_compile_definitions(websrv PUBLIC WEBSRVSCOPE)
|
||||
endif ( ${WBACK} )
|
||||
|
||||
# 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 (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
|
||||
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")
|
||||
135
common/utils/websrv/websrv_noforms.c
Normal file
135
common/utils/websrv/websrv_noforms.c
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
|
||||
#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 = calloc( 1, sizeof (FL_OBJECT) );
|
||||
AssertFatal(obj!=NULL,"Cannot allocate scope object for %s\n",label);
|
||||
obj->spec = calloc( 1, sizeof (FLI_XYPLOT_SPEC) );
|
||||
AssertFatal(obj->spec!=NULL,"Cannot allocate scope object buffers for %s\n",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];
|
||||
}
|
||||
|
||||
|
||||
|
||||
6402
common/utils/websrv/websrv_noforms.h
Normal file
6402
common/utils/websrv/websrv_noforms.h
Normal file
File diff suppressed because it is too large
Load Diff
291
common/utils/websrv/websrv_scope.c
Normal file
291
common/utils/websrv/websrv_scope.c
Normal file
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
/*! \file common/utils/websrv/websrv_scope.c
|
||||
* \brief: implementation of web API specific for oai softscope
|
||||
* \author Francois TABURET
|
||||
* \date 2022
|
||||
* \version 0.1
|
||||
* \company NOKIA BellLabs France
|
||||
* \email: francois.taburet@nokia-bell-labs.com
|
||||
* \note
|
||||
* \warning
|
||||
*/
|
||||
#include <libgen.h>
|
||||
#include <jansson.h>
|
||||
#include <ulfius.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <gnutls/x509.h>
|
||||
#include "common/config/config_userapi.h"
|
||||
#include "common/utils/LOG/log.h"
|
||||
#include "common/utils/websrv/websrv.h"
|
||||
#include "executables/softmodem-common.h"
|
||||
#include "common/ran_context.h"
|
||||
#include "common/utils/websrv/websrv_noforms.h"
|
||||
#include "openair1/PHY/TOOLS/phy_scope.h"
|
||||
#include "openair1/PHY/TOOLS/phy_scope_interface.h"
|
||||
#include "common/utils/load_module_shlib.h"
|
||||
|
||||
extern PHY_VARS_NR_UE ***PHY_vars_UE_g;
|
||||
|
||||
|
||||
static websrv_scope_params_t scope_params = {0,1000,NULL,NULL,65535};
|
||||
static websrv_params_t *websrvparams_ptr;
|
||||
static int cansend=1;
|
||||
void websrv_scope_senddata(int numd, int dsize, websrv_scopedata_msg_t *msg) {
|
||||
/*
|
||||
|
||||
for ( int i=0; i<n; i++) {
|
||||
msg->data_xy[2*i]=(i>(n/2))? 10 : -10;
|
||||
msg->data_xy[(2*i)+1]= (i>(n/4))? 10 : -10;
|
||||
}*/
|
||||
msg->header.src=WEBSOCK_SRC_SCOPE ;
|
||||
if (cansend || !(scope_params.statusmask & SCOPE_STATUSMASK_DATAACK)) {
|
||||
int st = ulfius_websocket_send_message( websrvparams_ptr->wm, U_WEBSOCKET_OPCODE_BINARY,(numd*dsize)+WEBSOCK_HEADSIZE, (char *)msg);
|
||||
if (st != U_OK)
|
||||
LOG_I(UTIL, "Error sending scope IQs, status %i\n",st);
|
||||
cansend=0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void websrv_websocket_send_scopemessage(char msg_type, char *msg_data, struct _websocket_manager * websocket_manager) {
|
||||
websrv_websocket_send_message(WEBSOCK_SRC_SCOPE,msg_type, msg_data,websrvparams_ptr->wm) ;
|
||||
}
|
||||
|
||||
void websrv_websocket_process_scopemessage(char msg_type, char *msg_data, struct _websocket_manager * websocket_manager) {
|
||||
LOG_I(UTIL,"[websrv] processing scope message type %i\n", msg_type);
|
||||
switch ( msg_type ) {
|
||||
|
||||
case SCOPEMSG_TYPE_STATUSUPD:
|
||||
if (strncmp(msg_data,"disabled",8) == 0){
|
||||
LOG_I(UTIL,"[websrv] SoftScope disabled state client ack \n");
|
||||
scope_params.statusmask = SCOPE_STATUSMASK_DISABLED;
|
||||
}
|
||||
break;
|
||||
case SCOPEMSG_TYPE_DATAACK:
|
||||
cansend=1;
|
||||
break;
|
||||
default:
|
||||
LOG_W(UTIL,"[websrv] Unprocessed scope message type: %c /n",msg_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int websrv_scope_manager(uint64_t lcount,websrv_params_t *websrvparams) {
|
||||
time_t linuxtime;
|
||||
struct tm loctime;
|
||||
char strtime[64];
|
||||
if (scope_params.statusmask & SCOPE_STATUSMASK_STARTED) {
|
||||
if( lcount%10 == 0 ) {
|
||||
linuxtime=time(NULL);
|
||||
localtime_r(&linuxtime,&loctime);
|
||||
snprintf(strtime,sizeof(strtime),"%d/%d/%d %d:%d:%d",loctime.tm_mday,loctime.tm_mon,loctime.tm_year+1900,loctime.tm_hour,loctime.tm_min,loctime.tm_sec);
|
||||
websrv_websocket_send_scopemessage(SCOPEMSG_TYPE_TIME, strtime, websrvparams_ptr->wm);
|
||||
}
|
||||
if ( (lcount % scope_params.refrate) == 0) {
|
||||
if (IS_SOFTMODEM_GNB_BIT && (scope_params.statusmask & SCOPE_STATUSMASK_STARTED)) {
|
||||
phy_scope_gNB(scope_params.scopeform, scope_params.scopedata, 1);
|
||||
}
|
||||
if (IS_SOFTMODEM_5GUE_BIT && (scope_params.statusmask & SCOPE_STATUSMASK_STARTED)) {
|
||||
phy_scope_nrUE(scope_params.scopeform, (PHY_VARS_NR_UE *)scope_params.scopedata, 0, scope_params.selectedTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* free scope resources, as websrv scope interface can be stopped and started */
|
||||
void websrv_scope_stop(void) {
|
||||
clear_softmodem_optmask(SOFTMODEM_DOSCOPE_BIT);
|
||||
scope_params.statusmask &= ~SCOPE_STATUSMASK_STARTED;
|
||||
OAI_phy_scope_t *sp = (OAI_phy_scope_t *)scope_params.scopeform;
|
||||
if(sp != NULL )
|
||||
for (int i=0; sp->graph[i].graph != NULL ; i++) {
|
||||
websrv_free_xyplot(sp->graph[i].graph);
|
||||
}
|
||||
free(sp);
|
||||
scope_params.scopeform=NULL;
|
||||
}
|
||||
|
||||
char *websrv_scope_initdata(void) {
|
||||
if (IS_SOFTMODEM_GNB_BIT) {
|
||||
scopeParms_t p;
|
||||
p.ru=RC.ru[0];
|
||||
p.gNB=RC.gNB[0];
|
||||
gNBinitScope(&p);
|
||||
scope_params.scopedata=p.gNB->scopeData;
|
||||
scope_params.scopeform = create_phy_scope_gnb();
|
||||
scope_params.statusmask |= SCOPE_STATUSMASK_AVAILABLE;
|
||||
return "gNB";
|
||||
} else if (IS_SOFTMODEM_5GUE_BIT) {
|
||||
scope_params.scopedata = PHY_vars_UE_g[0][0] ;
|
||||
nrUEinitScope(PHY_vars_UE_g[0][0]);
|
||||
scope_params.scopeform = create_phy_scope_nrue(scope_params.selectedTarget);
|
||||
scope_params.statusmask |= SCOPE_STATUSMASK_AVAILABLE;
|
||||
return "5GUE";
|
||||
} else {
|
||||
LOG_I(UTIL,"[websrv] SoftScope web interface not implemented for this softmodem\n");
|
||||
}
|
||||
return "none";
|
||||
} /* websrv_scope_init */
|
||||
|
||||
/* callback to process control commands received from frontend */
|
||||
int websrv_scope_callback_set_params (const struct _u_request * request, struct _u_response * response, void * user_data) {
|
||||
websrv_dump_request("scope set params ", request);
|
||||
json_error_t jserr;
|
||||
json_t* jsbody = ulfius_get_json_body_request (request, &jserr);
|
||||
int httpstatus=404;
|
||||
|
||||
if (jsbody == NULL) {
|
||||
LOG_W(UTIL,"cannot find json body in %s %s\n",request->http_url, jserr.text );
|
||||
httpstatus=400;
|
||||
} else {
|
||||
websrv_printjson("websrv_scope_callback_set_params: ",jsbody);
|
||||
json_t *J=json_object_get(jsbody, "name");
|
||||
const char *vname=json_string_value(J);
|
||||
J=json_object_get(jsbody, "value");
|
||||
const char *vval=json_string_value(J);
|
||||
if(strcmp(vname,"startstop") == 0) {
|
||||
if( strcmp(vval,"start") == 0) {
|
||||
if ( scope_params.statusmask & SCOPE_STATUSMASK_AVAILABLE) {
|
||||
websrv_scope_initdata();
|
||||
scope_params.statusmask |= SCOPE_STATUSMASK_STARTED;
|
||||
scope_params.selectedTarget=0; // 1 UE to be received from GUI (for xNB scope's
|
||||
set_softmodem_optmask(SOFTMODEM_DOSCOPE_BIT); // to trigger data copy in scope buffers
|
||||
}
|
||||
httpstatus=200;
|
||||
} else if( strcmp(vval,"stop") == 0) {
|
||||
scope_params.statusmask &= ~SCOPE_STATUSMASK_STARTED;
|
||||
clear_softmodem_optmask(SOFTMODEM_DOSCOPE_BIT);
|
||||
httpstatus=200;
|
||||
} else {
|
||||
LOG_W(UTIL,"invalid startstop command value: %s\n",vval);
|
||||
httpstatus=400;
|
||||
}
|
||||
} else if (strcmp(vname,"refrate") == 0) {
|
||||
scope_params.refrate = (uint32_t)strtol(vval,NULL,10);
|
||||
httpstatus=200;
|
||||
} else if (strcmp(vname,"enabled") == 0) {
|
||||
J=json_object_get(jsbody, "graphid");
|
||||
const int gid = json_integer_value(J);
|
||||
OAI_phy_scope_t *sp = (OAI_phy_scope_t *)scope_params.scopeform;
|
||||
if ( sp == NULL ) {
|
||||
httpstatus=500;
|
||||
} else {
|
||||
sp->graph[gid].enabled = (strcmp(vval,"true")==0)?true:false;
|
||||
httpstatus=200;
|
||||
}
|
||||
} else if (strcmp(vname,"xmin") == 0) {
|
||||
scope_params.xmin = strtol(vval,NULL,10);
|
||||
httpstatus=200;
|
||||
} else if (strcmp(vname,"xmax") == 0) {
|
||||
scope_params.xmax = strtol(vval,NULL,10);
|
||||
httpstatus=200;
|
||||
} else if (strcmp(vname,"ymin") == 0) {
|
||||
scope_params.ymin = strtol(vval,NULL,10);
|
||||
httpstatus=200;
|
||||
} else if (strcmp(vname,"ymax") == 0) {
|
||||
scope_params.ymax = strtol(vval,NULL,10);
|
||||
httpstatus=200;
|
||||
} else if (strcmp(vname,"llrxmax") == 0) {
|
||||
scope_params.llrxmax = strtol(vval,NULL,10);
|
||||
httpstatus=200;
|
||||
} else if (strcmp(vname,"llrxmin") == 0) {
|
||||
scope_params.llrxmin = strtol(vval,NULL,10);
|
||||
httpstatus=200;
|
||||
} else if (strcmp(vname,"TargetSelect") == 0) {
|
||||
scope_params.selectedTarget=strtol(vval,NULL,10);
|
||||
httpstatus=200;
|
||||
} else if (strcmp(vname,"DataAck") == 0) {
|
||||
if (strcasecmp(vval,"true")==0)
|
||||
scope_params.statusmask |= SCOPE_STATUSMASK_DATAACK;
|
||||
else
|
||||
scope_params.statusmask &= (~SCOPE_STATUSMASK_DATAACK);
|
||||
httpstatus=200;
|
||||
} else if (strcmp(vname,"llrythresh") == 0) {
|
||||
scope_params.llr_ythresh = strtol(vval,NULL,10);
|
||||
httpstatus=200;
|
||||
} else {
|
||||
httpstatus=500;
|
||||
LOG_W(UTIL,"Unknown scope command: %s\n",vname );
|
||||
}
|
||||
} //sbody
|
||||
ulfius_set_empty_body_response(response, httpstatus);
|
||||
return U_CALLBACK_COMPLETE;
|
||||
}
|
||||
|
||||
int websrv_scope_callback_get_desc (const struct _u_request * request, struct _u_response * response, void * user_data) {
|
||||
websrv_dump_request("scope get desc ", request);
|
||||
json_t *jgraph = json_array();
|
||||
char gtype[20];
|
||||
char stitle[64];
|
||||
|
||||
|
||||
websrv_scope_stop(); // in case it's not the first connection
|
||||
if (IS_SOFTMODEM_DOSCOPE | IS_SOFTMODEM_ENB_BIT | IS_SOFTMODEM_4GUE_BIT) {
|
||||
strcpy(stitle,"none");
|
||||
} else {
|
||||
strcpy(stitle,websrv_scope_initdata());
|
||||
}
|
||||
OAI_phy_scope_t *sp = (OAI_phy_scope_t *)scope_params.scopeform;
|
||||
if(sp != NULL && (scope_params.statusmask & SCOPE_STATUSMASK_AVAILABLE))
|
||||
for (int i=0; sp->graph[i].graph != NULL ; i++) {
|
||||
json_t *agraph =NULL;
|
||||
switch (sp->graph[i].chartid ) {
|
||||
case SCOPEMSG_DATAID_IQ:
|
||||
strcpy(gtype,"IQs");
|
||||
agraph=json_pack("{s:s,s:s,s:i,s:i,s:i,s:i}","title",sp->graph[i].graph->label,"type",gtype,
|
||||
"id", sp->graph[i].datasetid,"srvidx",i,"w", sp->graph[i].w,"h",sp->graph[i].h);
|
||||
break;
|
||||
case SCOPEMSG_DATAID_LLR:
|
||||
strcpy(gtype,"LLR");
|
||||
agraph=json_pack("{s:s,s:s,s:i,s:i,s:i,s:i}","title",sp->graph[i].graph->label,"type",gtype,
|
||||
"id", sp->graph[i].datasetid,"srvidx",i,"w", sp->graph[i].w,"h",sp->graph[i].h);
|
||||
break;
|
||||
case SCOPEMSG_DATAID_WF:
|
||||
strcpy(gtype,"WF");
|
||||
agraph=json_pack("{s:s,s:s,s:i,s:i,s:i,s:i}","title",sp->graph[i].graph->label,"type",gtype,
|
||||
"id", sp->graph[i].datasetid,"srvidx",i,"w", sp->graph[i].w,"h",sp->graph[i].h);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (agraph != NULL)
|
||||
json_array_append_new(jgraph,agraph);
|
||||
}
|
||||
json_t *jbody = json_pack("{s:s,s:o}","title",stitle,"graphs",jgraph);
|
||||
websrv_jbody(response,jbody);
|
||||
return U_CALLBACK_COMPLETE;
|
||||
}
|
||||
|
||||
void websrv_init_scope(websrv_params_t *websrvparams) {
|
||||
int (* callback_functions_scope[3])(const struct _u_request * request,
|
||||
struct _u_response * response,
|
||||
void * user_data) ={websrv_callback_okset_softmodem_cmdvar,websrv_scope_callback_set_params,websrv_scope_callback_get_desc};
|
||||
char *http_methods[3]={"OPTIONS","POST","GET"};
|
||||
websrvparams_ptr=websrvparams;
|
||||
websrv_add_endpoint(http_methods,3,"oaisoftmodem","scopectrl" ,callback_functions_scope, websrvparams);
|
||||
|
||||
}
|
||||
|
||||
websrv_scope_params_t *websrv_scope_getparams(void) {
|
||||
return &scope_params;
|
||||
}
|
||||
160
common/utils/websrv/websrv_websockets.c
Normal file
160
common/utils/websrv/websrv_websockets.c
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
/*! \file common/utils/websrv/websrv_websockets.c
|
||||
* \brief: implementation of web/websockets API
|
||||
* \author Francois TABURET
|
||||
* \date 2022
|
||||
* \version 0.1
|
||||
* \company NOKIA BellLabs France
|
||||
* \email: francois.taburet@nokia-bell-labs.com
|
||||
* \note
|
||||
* \warning
|
||||
*/
|
||||
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <gnutls/x509.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <jansson.h>
|
||||
#include <ulfius.h>
|
||||
#include "common/utils/LOG/log.h"
|
||||
#include "common/utils/websrv/websrv.h"
|
||||
|
||||
#include "time.h"
|
||||
|
||||
|
||||
|
||||
void websrv_websocket_send_message(char msg_src, char msg_type, char *msg_data, struct _websocket_manager * websocket_manager) {
|
||||
websrv_msg_t msg;
|
||||
int st;
|
||||
msg.header.src=msg_src ;
|
||||
msg.header.msgtype=msg_type;
|
||||
sprintf(msg.data,"%s",msg_data);
|
||||
int len=strlen(msg.data)+WEBSOCK_HEADSIZE;
|
||||
st = ulfius_websocket_send_message( websocket_manager, U_WEBSOCKET_OPCODE_BINARY,len, (char *)&msg);
|
||||
if (st != U_OK)
|
||||
LOG_I(UTIL, "Error sending scope message, length %i status %i\n",len,st);
|
||||
}
|
||||
/* websocket callbacks as set in callback_websocket, the initial url endpoint which triggers the websocket init */
|
||||
/* function executed by ulfius when websocket is closed */
|
||||
void websrv_websocket_onclose_callback (const struct _u_request * request,
|
||||
struct _websocket_manager * websocket_manager,
|
||||
void * websocket_onclose_user_data) {
|
||||
websrv_params_t *websrvparams = (websrv_params_t *)websocket_onclose_user_data;
|
||||
websrv_dump_request("websocket close ",request);
|
||||
websrvparams->wm=NULL;
|
||||
websrv_scope_stop();
|
||||
}
|
||||
|
||||
void websrv_close_ws( websrv_params_t *websrvparams, char *source) {
|
||||
LOG_W(UTIL,"[websrv] Websocket re-init, from %s....\n",source);
|
||||
ulfius_websocket_send_close_signal(websrvparams->wm);
|
||||
}
|
||||
|
||||
/* function executed by ulfius in a dedicated thread, should not terminate while client connection is up */
|
||||
void websrv_websocket_manager_callback(const struct _u_request * request,
|
||||
struct _websocket_manager * websocket_manager,
|
||||
void * websocket_manager_user_data) {
|
||||
|
||||
websrv_dump_request("websocket manager ",request);
|
||||
websrv_params_t *websrvparams = (websrv_params_t *)websocket_manager_user_data;
|
||||
while(websrvparams->wm != NULL) {
|
||||
LOG_I(UTIL,"[websrv] Waitting a previous websocket instance to close...\n");
|
||||
usleep(100000);
|
||||
}
|
||||
websrvparams->wm = websocket_manager;
|
||||
uint64_t lcount=0;
|
||||
int st=U_WEBSOCKET_STATUS_OPEN;
|
||||
while(st== U_WEBSOCKET_STATUS_OPEN) {
|
||||
st=ulfius_websocket_status(websocket_manager /* ms */) ;
|
||||
if (st == U_WEBSOCKET_STATUS_OPEN) {
|
||||
if (websrv_scope_manager(lcount, websrvparams) < 0) {
|
||||
websrv_close_ws( websrvparams, "ws scope manager");
|
||||
};
|
||||
lcount++;
|
||||
} else if (st == U_WEBSOCKET_STATUS_ERROR) {
|
||||
LOG_I(UTIL,"[websrv] manager: Websocket error, errno %s\n",strerror(errno));
|
||||
break;
|
||||
} else {
|
||||
LOG_I(UTIL,"[websrv] manager: Websocket has been closed\n");
|
||||
break;
|
||||
}
|
||||
usleep(100000);
|
||||
} /* while */
|
||||
LOG_I(UTIL, "Closing websocket_manager_callback...\n");
|
||||
}
|
||||
|
||||
void websrv_websocket_incoming_message_callback (const struct _u_request * request,
|
||||
struct _websocket_manager * websocket_manager,
|
||||
const struct _websocket_message * last_message,
|
||||
void * websocket_incoming_message_user_data) {
|
||||
|
||||
|
||||
LOG_I(UTIL, "Incoming message, opcode: 0x%02x, mask: %d, len: %zu\n", last_message->opcode, last_message->has_mask, last_message->data_len);
|
||||
websrv_params_t *websrvparams = (websrv_params_t *)websocket_incoming_message_user_data;
|
||||
if ( websrvparams->wm != NULL && websrvparams->wm != websocket_manager) {
|
||||
websrv_close_ws( websrvparams, "ws incoming message callback");
|
||||
}
|
||||
if (last_message->opcode == U_WEBSOCKET_OPCODE_TEXT) {
|
||||
LOG_I(UTIL, "[websrv] text payload '%.*s'", (int)last_message->data_len, last_message->data);
|
||||
} else if (last_message->opcode == U_WEBSOCKET_OPCODE_BINARY) {
|
||||
websrv_msg_t *msg = (websrv_msg_t *)last_message->data;
|
||||
LOG_I(UTIL, "[websrv] binary payload from %c type %i\n",msg->header.src, (int)msg->header.msgtype);
|
||||
switch(msg->header.src) {
|
||||
case 's':
|
||||
websrv_websocket_process_scopemessage(msg->header.msgtype, msg->data,websocket_manager);
|
||||
break;
|
||||
default:
|
||||
LOG_W(UTIL, "[websrv] Unknown message source: %c\n",msg->header.src);
|
||||
break;
|
||||
}
|
||||
} else if ( last_message->opcode == U_WEBSOCKET_OPCODE_CLOSE) {
|
||||
LOG_I(UTIL, "[websrv] websocket closed\n");
|
||||
|
||||
} else {
|
||||
LOG_I(UTIL, "[websrv] message ignored\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* callback function, called when the url corresponding to the endpoint set in
|
||||
* websrv_init_websocket is requested. that simply set the websocket callbacks
|
||||
*/
|
||||
int websrv_callback_websocket (const struct _u_request * request, struct _u_response * response, void * user_data) {
|
||||
int ret;
|
||||
|
||||
websrv_dump_request("websocket ",request);
|
||||
websrv_params_t *websrvparams = (websrv_params_t *)user_data;
|
||||
if (websrvparams->wm != NULL) {
|
||||
websrv_close_ws(websrvparams,"ws url callback");
|
||||
}
|
||||
if ((ret = ulfius_set_websocket_response(response, NULL, NULL, websrv_websocket_manager_callback, user_data, websrv_websocket_incoming_message_callback,user_data, websrv_websocket_onclose_callback, user_data)) == U_OK) {
|
||||
return U_CALLBACK_COMPLETE;
|
||||
} else {
|
||||
return U_CALLBACK_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
int websrv_init_websocket(websrv_params_t *websrvparams,char *module) {
|
||||
int status=ulfius_add_endpoint_by_val(&(websrvparams->instance), "GET", NULL, module, 1, &websrv_callback_websocket, websrvparams);
|
||||
return status;
|
||||
}
|
||||
@@ -1508,7 +1508,8 @@ int start_rf(RU_t *ru) {
|
||||
}
|
||||
|
||||
int stop_rf(RU_t *ru) {
|
||||
ru->rfdevice.trx_end_func(&ru->rfdevice);
|
||||
if (ru->rfdevice.trx_end_func )
|
||||
ru->rfdevice.trx_end_func(&ru->rfdevice);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,11 @@ uint64_t set_softmodem_optmask(uint64_t bitmask) {
|
||||
return softmodem_params.optmask;
|
||||
}
|
||||
|
||||
uint64_t clear_softmodem_optmask(uint64_t bitmask) {
|
||||
softmodem_params.optmask = softmodem_params.optmask & (~bitmask);
|
||||
return softmodem_params.optmask;
|
||||
}
|
||||
|
||||
softmodem_params_t *get_softmodem_params(void) {
|
||||
return &softmodem_params;
|
||||
}
|
||||
@@ -89,6 +94,7 @@ void get_common_options(uint32_t execmask) {
|
||||
uint32_t online_log_messages=0;
|
||||
uint32_t glog_level=0 ;
|
||||
uint32_t start_telnetsrv = 0, start_telnetclt = 0;
|
||||
uint32_t start_websrv = 0;
|
||||
uint32_t noS1 = 0, nokrnmod = 1, nonbiot = 0;
|
||||
uint32_t rfsim = 0, do_forms = 0;
|
||||
char *logmem_filename = NULL;
|
||||
@@ -118,6 +124,8 @@ void get_common_options(uint32_t execmask) {
|
||||
set_softmodem_optmask(SOFTMODEM_TELNETCLT_BIT);
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (logmem_filename != NULL && strlen(logmem_filename) > 0) {
|
||||
log_mem_filename = &logmem_filename[0];
|
||||
log_mem_flag = 1;
|
||||
@@ -145,7 +153,11 @@ void get_common_options(uint32_t execmask) {
|
||||
if (do_forms) {
|
||||
set_softmodem_optmask(SOFTMODEM_DOSCOPE_BIT);
|
||||
}
|
||||
|
||||
|
||||
if (start_websrv) {
|
||||
load_module_shlib("websrv",NULL,0,NULL);
|
||||
}
|
||||
|
||||
if(parallel_config != NULL) set_parallel_conf(parallel_config);
|
||||
|
||||
if(worker_config != NULL) set_worker_conf(worker_config);
|
||||
@@ -196,6 +208,7 @@ void signal_handler(int sig) {
|
||||
softmodem_printresources(sig,(telnet_printfunc_t)printf);
|
||||
if (sig != SOFTMODEM_RTSIGNAL) {
|
||||
printf("Linux signal %s...\n",strsignal(sig));
|
||||
end_configmodule();
|
||||
exit_function(__FILE__, __FUNCTION__, __LINE__,"softmodem starting exit procedure\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,18 +179,20 @@ extern int usrp_tx_thread;
|
||||
#define CONFIG_HLP_FLOG "Enable online log \n"
|
||||
#define CONFIG_HLP_LOGL "Set the global log level, valid options: (4:trace, 3:debug, 2:info, 1:warn, (0:error))\n"
|
||||
#define CONFIG_HLP_TELN "Start embedded telnet server \n"
|
||||
#define CONFIG_HLP_WEB "Start embedded web server \n"
|
||||
#define CONFIG_FLOG_OPT "R"
|
||||
#define CONFIG_LOGL_OPT "g"
|
||||
/*-------------------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
/* command line parameters for LOG utility */
|
||||
/* optname helpstr paramflags XXXptr defXXXval type numelt */
|
||||
/*-------------------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
/*------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
/* command line parameters for LOG utility */
|
||||
/* optname helpstr paramflags XXXptr defXXXval type numelt */
|
||||
/*------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
#define CMDLINE_LOGPARAMS_DESC { \
|
||||
{CONFIG_FLOG_OPT , CONFIG_HLP_FLOG, 0, uptr:&online_log_messages, defintval:1, TYPE_INT, 0}, \
|
||||
{CONFIG_LOGL_OPT , CONFIG_HLP_LOGL, 0, uptr:&glog_level, defintval:0, TYPE_UINT, 0}, \
|
||||
{"telnetsrv", CONFIG_HLP_TELN, PARAMFLAG_BOOL, uptr:&start_telnetsrv, defintval:0, TYPE_UINT, 0}, \
|
||||
{"log-mem", NULL, 0, strptr:(char **)&logmem_filename, defstrval:NULL, TYPE_STRING, 0}, \
|
||||
{"telnetclt", NULL, 0, uptr:&start_telnetclt, defstrval:NULL, TYPE_UINT, 0}, \
|
||||
{CONFIG_FLOG_OPT , CONFIG_HLP_FLOG, 0, uptr:&online_log_messages, defintval:1, TYPE_INT, 0}, \
|
||||
{CONFIG_LOGL_OPT , CONFIG_HLP_LOGL, 0, uptr:&glog_level, defintval:0, TYPE_UINT, 0}, \
|
||||
{"telnetsrv", CONFIG_HLP_TELN, PARAMFLAG_BOOL|PARAMFLAG_CMDLINEONLY, uptr:&start_telnetsrv, defintval:0, TYPE_UINT, 0}, \
|
||||
{"websrv", CONFIG_HLP_WEB, PARAMFLAG_BOOL|PARAMFLAG_CMDLINEONLY, uptr:&start_websrv, defintval:0, TYPE_UINT, 0}, \
|
||||
{"log-mem", NULL, 0, strptr:(char **)&logmem_filename, defstrval:NULL, TYPE_STRING, 0}, \
|
||||
{"telnetclt", NULL, 0, uptr:&start_telnetclt, defstrval:NULL, TYPE_UINT, 0}, \
|
||||
}
|
||||
|
||||
|
||||
@@ -273,6 +275,7 @@ typedef struct {
|
||||
|
||||
extern uint64_t get_softmodem_optmask(void);
|
||||
extern uint64_t set_softmodem_optmask(uint64_t bitmask);
|
||||
extern uint64_t clear_softmodem_optmask(uint64_t bitmask);
|
||||
extern softmodem_params_t *get_softmodem_params(void);
|
||||
extern void get_common_options(uint32_t execmask);
|
||||
extern char *get_softmodem_function(uint64_t *sofmodemfunc_mask_ptr);
|
||||
|
||||
@@ -41,12 +41,12 @@
|
||||
|
||||
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},
|
||||
};
|
||||
telnetshell_vardef_t coding_vardef[] = {
|
||||
{"maxiter",TELNET_VARTYPE_INT32,&max_turbo_iterations},
|
||||
{"",0,NULL}
|
||||
{"maxiter",TELNET_VARTYPE_INT32,0,&max_turbo_iterations},
|
||||
{"",0,0,NULL}
|
||||
};
|
||||
/* PHY/defs.h contains MODE_DECODE_XXX macros, following table must match */
|
||||
static char *modedesc[] = {"none","sse","C","avx2"};
|
||||
|
||||
@@ -24,64 +24,26 @@
|
||||
/* Form definition file generated by fdesign */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "nr_phy_scope.h"
|
||||
#include "executables/softmodem-common.h"
|
||||
#include "executables/nr-softmodem-common.h"
|
||||
#include <forms.h>
|
||||
|
||||
#define TPUT_WINDOW_LENGTH 100
|
||||
#define ScaleZone 4
|
||||
#define localBuff(NaMe,SiZe) float NaMe[SiZe]; memset(NaMe,0,sizeof(NaMe));
|
||||
|
||||
#ifndef WEBSRVSCOPE
|
||||
# include <forms.h>
|
||||
# define STATICFORXSCOPE static
|
||||
#else
|
||||
# include <ulfius.h>
|
||||
# include "common/utils/websrv/websrv.h"
|
||||
# include "common/utils/websrv/websrv_noforms.h"
|
||||
# define STATICFORXSCOPE
|
||||
# define fl_add_canvas websrv_fl_add_canvas
|
||||
# define fl_add_xyplot websrv_fl_add_xyplot
|
||||
# define fl_get_xyplot_data_pointer websrv_fl_get_xyplot_data_pointer
|
||||
#endif
|
||||
#include "nr_phy_scope.h"
|
||||
#include "phy_scope.h"
|
||||
|
||||
const FL_COLOR rx_antenna_colors[4] = {FL_RED,FL_BLUE,FL_GREEN,FL_YELLOW};
|
||||
const FL_COLOR water_colors[4] = {FL_BLUE,FL_GREEN,FL_YELLOW,FL_RED};
|
||||
|
||||
typedef c16_t scopeSample_t;
|
||||
#define SquaredNorm(VaR) ((VaR).r*(VaR).r+(VaR).i*(VaR).i)
|
||||
|
||||
typedef struct {
|
||||
int dataSize;
|
||||
int elementSz;
|
||||
int colSz;
|
||||
int lineSz;
|
||||
} scopeGraphData_t;
|
||||
|
||||
typedef struct OAIgraph {
|
||||
FL_OBJECT *graph;
|
||||
FL_OBJECT *text;
|
||||
float maxX;
|
||||
float maxY;
|
||||
float minX;
|
||||
float minY;
|
||||
int x;
|
||||
int y;
|
||||
int w;
|
||||
int h;
|
||||
int waterFallh;
|
||||
double *waterFallAvg;
|
||||
bool initDone;
|
||||
int iteration;
|
||||
void (*gNBfunct) (struct OAIgraph *graph, scopeData_t *p, int UE_id);
|
||||
void (*nrUEfunct)(scopeGraphData_t **data, struct OAIgraph *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id);
|
||||
} OAIgraph_t;
|
||||
|
||||
/* Forms and Objects */
|
||||
typedef struct {
|
||||
FL_FORM *phy_scope;
|
||||
OAIgraph_t graph[20];
|
||||
FL_OBJECT *button_0;
|
||||
} OAI_phy_scope_t;
|
||||
|
||||
typedef struct {
|
||||
FL_FORM *stats_form;
|
||||
void *vdata;
|
||||
char *cdata;
|
||||
long ldata;
|
||||
FL_OBJECT *stats_text;
|
||||
FL_OBJECT *stats_button;
|
||||
} FD_stats_form;
|
||||
|
||||
static void drawsymbol(FL_OBJECT *obj, int id,
|
||||
FL_POINT *p, int n, int w, int h) {
|
||||
fl_points( p, n, FL_YELLOW);
|
||||
@@ -103,7 +65,128 @@ static void dl_traffic_on_off( FL_OBJECT *button, long arg) {
|
||||
#endif
|
||||
|
||||
#define WATERFALL 10000
|
||||
#ifdef WEBSRVSCOPE
|
||||
/* copy data from softmodem buffer to message body */
|
||||
int websrv_cpiqbuff_tomsg(OAIgraph_t *graph, scopeSample_t *c,int n, int id, int base) {
|
||||
int I=0;
|
||||
websrv_scope_params_t *WP=websrv_scope_getparams();
|
||||
int newn=n;
|
||||
websrv_scopedata_msg_t *msg;
|
||||
websrv_nf_getdata(graph->graph, id,&msg);
|
||||
|
||||
if (n>MAX_NIQ_WEBSOCKMSG) {
|
||||
LOG_E(UTIL,"Buffer id %i too small for %i iqs...\n",id,n);
|
||||
return 0;
|
||||
}
|
||||
/* copy and sort the chart data on x to improve frontend perf */
|
||||
int16_t *data_xy=msg->data_xy+base;
|
||||
int16_t max_x= INT16_MIN;
|
||||
for ( int i=0; i<n; i++) {
|
||||
if( c[i].r < WP->xmin || c[i].i < WP->ymin || c[i].r > WP->xmax || c[i].i > WP->ymax) {
|
||||
newn--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( max_x <= c[i].r ) {
|
||||
data_xy[I]=max_x=c[i].r;
|
||||
data_xy[I+1]=c[i].i;
|
||||
I=I+2;
|
||||
} else {
|
||||
for (int j=I+(base*2); j>=0; j=j-2) {
|
||||
if (msg->data_xy[j] <= c[i].r || j==0) {
|
||||
for (int k=I+(base*2) ; k>j ; k=k-2) {
|
||||
msg->data_xy[k+2]=msg->data_xy[k];
|
||||
msg->data_xy[k+3]=msg->data_xy[k+1];
|
||||
}
|
||||
msg->data_xy[j+2]=c[i].r;
|
||||
msg->data_xy[j+3]=c[i].i;
|
||||
I=I+2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return newn;
|
||||
}
|
||||
|
||||
int websrv_cpllrbuff_tomsg(OAIgraph_t *graph, int16_t *llrs, int n, int id, int iteration, int max_iteration) {
|
||||
|
||||
websrv_scopedata_msg_t *msg;
|
||||
websrv_nf_getdata(graph->graph, id,&msg);
|
||||
websrv_scope_params_t *WP=websrv_scope_getparams();
|
||||
if (n>MAX_LLR_WEBSOCKMSG) {
|
||||
LOG_E(UTIL,"Buffer id %i too small for %i iqs...\n",id,n);
|
||||
return 0;
|
||||
}
|
||||
/* save number of points (xmax) at beginning of buffer, as we try to minimize data sent */
|
||||
/* all iterations (UE pdschh case) have same n (number of data points) */
|
||||
if (iteration == 0){
|
||||
int32_t *iptr= (int32_t *)(msg->data_xy);
|
||||
*iptr=n;
|
||||
}
|
||||
/* for each point we save llr and the corresponding offset if llr's below the configured threshold have been skipped */
|
||||
/* offset is saved in 8bits so llr always transmitted when offset reach the 8 bits max. offset relative to x of previously transmitted point */
|
||||
int16_t *dptr=msg->data_xy;
|
||||
int newn=2; //2 first int16 used for saving maxx
|
||||
int xoffset=1;
|
||||
int xres=(n/1000 > CHAR_MAX)?CHAR_MAX:(n/1000);
|
||||
int16_t latestllr=0;
|
||||
int imin=WP->llrxmin;
|
||||
int imax=WP->llrxmax;
|
||||
|
||||
if(imax > n)
|
||||
imax=n;
|
||||
if(imin > imax)
|
||||
imin=imax;
|
||||
/* to keep data sorted, we insert points of each iteration at their x position, */
|
||||
for ( int i=imin; i<imax; i++) {
|
||||
if ( ( (llrs[i]-latestllr) >= WP->llr_ythresh ) || ( (latestllr-llrs[i]) >= WP->llr_ythresh ) || (xoffset>=xres) ) {
|
||||
dptr[newn+iteration]=llrs[i];
|
||||
latestllr=llrs[i];
|
||||
dptr[newn+iteration+1]=(int16_t)xoffset;
|
||||
xoffset=1;
|
||||
newn=newn+max_iteration+2;
|
||||
} else {
|
||||
xoffset++;
|
||||
}
|
||||
}
|
||||
|
||||
/* discard points which cannot be distinguished on graph: x and llr have almost identical values
|
||||
for ( int i=4; i<(newn-1); i=i+2) {
|
||||
for (int j=i+2 ; j<(i+(xres*2)) && j<(newn-1); j=j+2) {
|
||||
if ( ((dptr[j] - dptr[i]) < yres) || ((dptr[i] - dptr[j]) < yres) ) {
|
||||
newn=newn-2;
|
||||
for (int k = j; k< newn; k++) {
|
||||
((int8_t *)(msg->data_xy))[k] = (int8_t)msg->data_xy[k+2];
|
||||
((int8_t *)(msg->data_xy))[k+1] = (int8_t)msg->data_xy[k+3];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
return newn;
|
||||
}
|
||||
void websrv_get_WF_buffers(OAIgraph_t *graph,websrv_scopedata_msg_t *msgp[]) {
|
||||
|
||||
for(int i=0; i< sizeof(water_colors)/sizeof(FL_COLOR); i++) {
|
||||
websrv_scopedata_msg_t *msg;
|
||||
websrv_nf_getdata(graph->graph, i,&msg);
|
||||
msg->header.msgtype=SCOPEMSG_TYPE_DATA ;
|
||||
msg->header.chartid=graph->chartid;
|
||||
msg->header.datasetid=i;
|
||||
msg->header.update=(i == (sizeof(water_colors)/sizeof(FL_COLOR)-1))? 1:0;
|
||||
msg->data_xy[0]=0;
|
||||
msgp[i]=msg;
|
||||
}
|
||||
}
|
||||
|
||||
void websrv_setpoint(int x,int y, websrv_scopedata_msg_t *msg){
|
||||
msg->data_xy[0]++;
|
||||
msg->data_xy[msg->data_xy[0]]=(int16_t)x;
|
||||
msg->data_xy[0]++;
|
||||
msg->data_xy[msg->data_xy[0]]=(int16_t)y;
|
||||
}
|
||||
#endif
|
||||
static void commonGraph(OAIgraph_t *graph, int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label, FL_COLOR pointColor) {
|
||||
if (type==WATERFALL) {
|
||||
graph->waterFallh=h-15;
|
||||
@@ -136,6 +219,11 @@ static void commonGraph(OAIgraph_t *graph, int type, FL_Coord x, FL_Coord y, FL_
|
||||
graph->minY=0;
|
||||
graph->initDone=false;
|
||||
graph->iteration=0;
|
||||
#ifdef WEBSRVSCOPE
|
||||
graph->enabled=false;
|
||||
#else
|
||||
graph->enabled=true;
|
||||
#endif
|
||||
}
|
||||
|
||||
static OAIgraph_t gNBcommonGraph( void (*funct) (OAIgraph_t *graph, scopeData_t *p, int UE_id),
|
||||
@@ -177,8 +265,8 @@ static void setRange(OAIgraph_t *graph, float minX, float maxX, float minY, floa
|
||||
fl_set_xyplot_ybounds(graph->graph, graph->minY*1.2, graph->maxY*1.2);
|
||||
}
|
||||
}
|
||||
|
||||
static void oai_xygraph_getbuff(OAIgraph_t *graph, float **x, float **y, int len, int layer) {
|
||||
#ifndef WEBSRVSCOPE
|
||||
static void oai_xygraph_getbuff(OAIgraph_t *graph, float **x, float **y, int len, int layer) {
|
||||
float *old_x;
|
||||
float *old_y;
|
||||
int old_len=-1;
|
||||
@@ -187,28 +275,43 @@ static void oai_xygraph_getbuff(OAIgraph_t *graph, float **x, float **y, int len
|
||||
fl_get_xyplot_data_pointer(graph->graph, layer, &old_x, &old_y, &old_len);
|
||||
|
||||
if (old_len != len) {
|
||||
LOG_W(HW,"allocating graph of %d scope\n", len);
|
||||
LOG_W(HW,"allocating graph of %d points\n", len);
|
||||
float values[len];
|
||||
float time[len];
|
||||
|
||||
// make time in case we will use it
|
||||
|
||||
for (int i=0; i<len; i++)
|
||||
time[i] = values[i] = i;
|
||||
|
||||
if (layer==0)
|
||||
fl_set_xyplot_data(graph->graph,time,values,len,"","","");
|
||||
else
|
||||
fl_add_xyplot_overlay(graph->graph,layer,time,values,len,rx_antenna_colors[layer]);
|
||||
|
||||
fl_get_xyplot_data_pointer(graph->graph, layer, &old_x, &old_y, &old_len);
|
||||
AssertFatal(old_len==len,"");
|
||||
AssertFatal(old_len==len,"graph len %i old_len %i\n",len, old_len);
|
||||
}
|
||||
|
||||
*x=old_x;
|
||||
*y=old_y;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void oai_xygraph(OAIgraph_t *graph, float *x, float *y, int len, int layer, bool NoAutoScale) {
|
||||
|
||||
#ifdef WEBSRVSCOPE
|
||||
// wsc.sigid=;
|
||||
// snprintf(wsc.graphtitle,sizeof(wsc.graphtitle),graph->text->label);
|
||||
websrv_scopedata_msg_t *msg=NULL;
|
||||
|
||||
websrv_nf_getdata(graph->graph, layer, &msg);
|
||||
msg->header.msgtype=SCOPEMSG_TYPE_DATA ;
|
||||
msg->header.chartid=graph->chartid;
|
||||
msg->header.datasetid=graph->datasetid;
|
||||
msg->header.msgseg=0;
|
||||
msg->header.update= 1;
|
||||
websrv_scope_senddata(len,(msg->header.chartid==SCOPEMSG_DATAID_LLR)?2:4, msg);
|
||||
#else
|
||||
fl_redraw_object(graph->graph);
|
||||
|
||||
if ( NoAutoScale && graph->iteration%NoAutoScale == 0) {
|
||||
@@ -223,14 +326,19 @@ static void oai_xygraph(OAIgraph_t *graph, float *x, float *y, int len, int laye
|
||||
|
||||
setRange(graph, minX-5, maxX+5, minY-5, maxY+5);
|
||||
}
|
||||
|
||||
#endif
|
||||
graph->iteration++;
|
||||
|
||||
|
||||
}
|
||||
|
||||
static void genericWaterFall (OAIgraph_t *graph, scopeSample_t *values, const int datasize, const int divisions, const char *label) {
|
||||
if ( values == NULL )
|
||||
return;
|
||||
|
||||
#ifdef WEBSRVSCOPE
|
||||
websrv_scopedata_msg_t *msgp[sizeof(water_colors)/sizeof(FL_COLOR)];
|
||||
websrv_get_WF_buffers(graph,msgp);
|
||||
#endif
|
||||
fl_winset(FL_ObjWin(graph->graph));
|
||||
const int samplesPerPixel=datasize/graph->w;
|
||||
int displayPart=graph->waterFallh-ScaleZone;
|
||||
@@ -266,10 +374,14 @@ static void genericWaterFall (OAIgraph_t *graph, scopeSample_t *values, const in
|
||||
|
||||
if (val > avg*100 )
|
||||
col=3;
|
||||
|
||||
#ifdef WEBSRVSCOPE
|
||||
websrv_setpoint(pix,graph->iteration%displayPart,msgp[col]);
|
||||
#else
|
||||
fl_point(pix, graph->iteration%displayPart, water_colors[col]);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifndef WEBSRVSCOPE
|
||||
if (graph->initDone==false) {
|
||||
for ( int i=0; i < graph->waterFallh; i++ )
|
||||
for ( int j = 0 ; j < graph->w ; j++ )
|
||||
@@ -283,6 +395,12 @@ static void genericWaterFall (OAIgraph_t *graph, scopeSample_t *values, const in
|
||||
}
|
||||
|
||||
fl_set_object_label_f(graph->text, "%s, avg I/Q pow: %4.1f", label, 0/*sqrt(avg)*/);
|
||||
#else
|
||||
for(int i=0; i<sizeof(water_colors)/sizeof(FL_COLOR);i++){
|
||||
msgp[i]->header.msgseg=graph->iteration%displayPart;
|
||||
websrv_scope_senddata(msgp[i]->data_xy[0],2, msgp[i]);
|
||||
}
|
||||
#endif
|
||||
graph->iteration++;
|
||||
}
|
||||
|
||||
@@ -295,7 +413,6 @@ static void genericPowerPerAntena(OAIgraph_t *graph, const int nb_ant, const sc
|
||||
for (int i=0; i<len; i++) {
|
||||
values[i] = SquaredNorm(data[ant][i]);
|
||||
}
|
||||
|
||||
oai_xygraph(graph,time,values, len, ant, 10);
|
||||
}
|
||||
}
|
||||
@@ -365,20 +482,24 @@ static void frequencyResponse (OAIgraph_t *graph, PHY_VARS_gNB *phy_vars_gnb, RU
|
||||
static void puschLLR (OAIgraph_t *graph, scopeData_t *p, int nb_UEs) {
|
||||
//int Qm = 2;
|
||||
int coded_bits_per_codeword =3*8*6144+12; // (8*((3*8*6144)+12)); // frame_parms->N_RB_UL*12*Qm*frame_parms->symbols_per_tti;
|
||||
|
||||
|
||||
for (int ue=0; ue<nb_UEs; ue++) {
|
||||
if ( p->gNB->pusch_vars &&
|
||||
p->gNB->pusch_vars[ue] &&
|
||||
p->gNB->pusch_vars[ue]->llr ) {
|
||||
int16_t *pusch_llr = (int16_t *)p->gNB->pusch_vars[ue]->llr;
|
||||
float *llr, *bit;
|
||||
int nx=coded_bits_per_codeword;
|
||||
#ifdef WEBSRVSCOPE
|
||||
nx=websrv_cpllrbuff_tomsg(graph,pusch_llr , coded_bits_per_codeword, ue,0,0);
|
||||
#else
|
||||
oai_xygraph_getbuff(graph, &bit, &llr, coded_bits_per_codeword, ue);
|
||||
|
||||
for (int i=0; i<coded_bits_per_codeword; i++) {
|
||||
llr[i] = (float) pusch_llr[i];
|
||||
}
|
||||
|
||||
oai_xygraph(graph,bit,llr,coded_bits_per_codeword,ue,10);
|
||||
#endif
|
||||
oai_xygraph(graph,bit,llr,nx,ue,10);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -386,22 +507,25 @@ static void puschLLR (OAIgraph_t *graph, scopeData_t *p, int nb_UEs) {
|
||||
static void puschIQ (OAIgraph_t *graph, scopeData_t *p, int nb_UEs) {
|
||||
NR_DL_FRAME_PARMS *frame_parms=&p->gNB->frame_parms;
|
||||
int sz=frame_parms->N_RB_UL*12*frame_parms->symbols_per_slot;
|
||||
|
||||
int newsz=sz;
|
||||
for (int ue=0; ue<nb_UEs; ue++) {
|
||||
if ( p->gNB->pusch_vars &&
|
||||
p->gNB->pusch_vars[ue] &&
|
||||
p->gNB->pusch_vars[ue]->rxdataF_comp &&
|
||||
p->gNB->pusch_vars[ue]->rxdataF_comp[0] ) {
|
||||
scopeSample_t *pusch_comp = (scopeSample_t *) p->gNB->pusch_vars[ue]->rxdataF_comp[0];
|
||||
float *I, *Q;
|
||||
scopeSample_t *pusch_comp = (scopeSample_t *) p->gNB->pusch_vars[ue]->rxdataF_comp[0];
|
||||
float *I, *Q;
|
||||
#ifdef WEBSRVSCOPE
|
||||
newsz = websrv_cpiqbuff_tomsg(graph,pusch_comp ,sz, 0,0);
|
||||
#else
|
||||
oai_xygraph_getbuff(graph, &I, &Q, sz, ue);
|
||||
|
||||
for (int k=0; k<sz; k++ ) {
|
||||
I[k] = pusch_comp[k].r;
|
||||
Q[k] = pusch_comp[k].i;
|
||||
}
|
||||
#endif
|
||||
|
||||
oai_xygraph(graph,I,Q,sz,ue,10);
|
||||
oai_xygraph(graph,I,Q,newsz,ue,10);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -450,7 +574,7 @@ static void puschThroughtput (OAIgraph_t *graph, scopeData_t *p, int nb_UEs) {
|
||||
// fl_set_xyplot_ybounds(form->pusch_tput,0,ymax);
|
||||
*/
|
||||
}
|
||||
static OAI_phy_scope_t *create_phy_scope_gnb(void) {
|
||||
STATICFORXSCOPE OAI_phy_scope_t *create_phy_scope_gnb(void) {
|
||||
FL_OBJECT *obj;
|
||||
OAI_phy_scope_t *fdui = calloc(( sizeof *fdui ),1);
|
||||
// Define form
|
||||
@@ -463,6 +587,8 @@ static OAI_phy_scope_t *create_phy_scope_gnb(void) {
|
||||
// Received signal
|
||||
fdui->graph[0] = gNBcommonGraph( gNBWaterFall, WATERFALL, 0, curY, 400, 100,
|
||||
"Received Signal (Time-Domain, one frame)", FL_RED );
|
||||
fdui->graph[0].chartid=SCOPEMSG_DATAID_WF; //tells websrv frontend to use WF chart for displaying
|
||||
fdui->graph[0].datasetid=0; // not used for WF
|
||||
// Time-domain channel response
|
||||
fdui->graph[1] = gNBcommonGraph( timeResponse, FL_NORMAL_XYPLOT, 410, curY, 400, 100,
|
||||
"SRS Frequency Response (samples, abs)", FL_RED );
|
||||
@@ -473,14 +599,21 @@ static OAI_phy_scope_t *create_phy_scope_gnb(void) {
|
||||
"Channel Frequency domain (RE, one frame)", FL_RED );
|
||||
fl_get_object_bbox(fdui->graph[2].graph,&x, &y,&w, &h);
|
||||
curY+=h+20;
|
||||
fdui->graph[2].chartid=SCOPEMSG_DATAID_WF; //tells websrv frontend to use WF chart for displaying
|
||||
fdui->graph[2].datasetid=0; // not used for WF
|
||||
// LLR of PUSCH
|
||||
fdui->graph[3] = gNBcommonGraph( puschLLR, FL_POINTS_XYPLOT, 0, curY, 500, 200,
|
||||
"PUSCH Log-Likelihood Ratios (LLR, mag)", FL_YELLOW );
|
||||
fdui->graph[3].chartid=SCOPEMSG_DATAID_LLR; //tells websrv frontend to use LLR chart for displaying
|
||||
fdui->graph[3].datasetid=0; //tells websrv frontend to use dataset index 0 in LLR chart
|
||||
// I/Q PUSCH comp
|
||||
fdui->graph[4] = gNBcommonGraph( puschIQ, FL_POINTS_XYPLOT, 500, curY, 300, 200,
|
||||
"PUSCH I/Q of MF Output", FL_YELLOW );
|
||||
fl_get_object_bbox(fdui->graph[3].graph,&x, &y,&w, &h);
|
||||
curY+=h;
|
||||
|
||||
fdui->graph[4].chartid=SCOPEMSG_DATAID_IQ; //tells websrv frontend to use constellation chart for displaying
|
||||
fdui->graph[4].datasetid=0; //tells websrv frontend to use dataset 0 of constellation chart
|
||||
// I/Q PUCCH comp (format 1)
|
||||
fdui->graph[5] = gNBcommonGraph( pucchEnergy, FL_POINTS_XYPLOT, 0, curY, 300, 100,
|
||||
"PUCCH1 Energy (SR)", FL_YELLOW );
|
||||
@@ -490,17 +623,20 @@ static OAI_phy_scope_t *create_phy_scope_gnb(void) {
|
||||
"PUCCH I/Q of MF Output", FL_YELLOW );
|
||||
fl_get_object_bbox(fdui->graph[6].graph,&x, &y,&w, &h);
|
||||
curY+=h;
|
||||
fdui->graph[6].chartid=SCOPEMSG_DATAID_IQ; //tells websrv frontend to use constellation chart for displaying
|
||||
fdui->graph[6].datasetid=1; //tells websrv frontend to use dataset 1 of constellation chart
|
||||
// Throughput on PUSCH
|
||||
fdui->graph[7] = gNBcommonGraph( puschThroughtput, FL_NORMAL_XYPLOT, 0, curY, 500, 100,
|
||||
"PUSCH Throughput [frame]/[kbit/s]", FL_WHITE );
|
||||
fdui->graph[8].graph=NULL;
|
||||
fl_end_form( );
|
||||
fdui->phy_scope->fdui = fdui;
|
||||
if (fdui->phy_scope)
|
||||
fdui->phy_scope->fdui = fdui;
|
||||
fl_show_form (fdui->phy_scope, FL_PLACE_HOTSPOT, FL_FULLBORDER, "LTE UL SCOPE gNB");
|
||||
return fdui;
|
||||
}
|
||||
static const int scope_enb_num_ue = 1;
|
||||
void phy_scope_gNB(OAI_phy_scope_t *form,
|
||||
STATICFORXSCOPE void phy_scope_gNB(OAI_phy_scope_t *form,
|
||||
scopeData_t *p,
|
||||
int UE_id) {
|
||||
static OAI_phy_scope_t *rememberForm=NULL;
|
||||
@@ -516,13 +652,15 @@ void phy_scope_gNB(OAI_phy_scope_t *form,
|
||||
int i=0;
|
||||
|
||||
while (form->graph[i].graph) {
|
||||
form->graph[i].gNBfunct(form->graph+i, p, UE_id);
|
||||
if (form->graph[i].enabled)
|
||||
form->graph[i].gNBfunct(form->graph+i, p, UE_id);
|
||||
i++;
|
||||
}
|
||||
|
||||
//fl_check_forms();
|
||||
}
|
||||
|
||||
#ifndef WEBSRVSCOPE
|
||||
static void *scope_thread_gNB(void *arg) {
|
||||
scopeData_t *p=(scopeData_t *) arg;
|
||||
size_t stksize=0;
|
||||
@@ -547,6 +685,7 @@ static void *scope_thread_gNB(void *arg) {
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void copyRxdataF(int32_t *data, int slot, void *scopeData) {
|
||||
scopeData_t *scope=(scopeData_t *)scopeData;
|
||||
@@ -555,7 +694,7 @@ static void copyRxdataF(int32_t *data, int slot, void *scopeData) {
|
||||
scope->gNB->frame_parms.samples_per_slot_wCP*sizeof(int32_t));
|
||||
}
|
||||
|
||||
void gNBinitScope(scopeParms_t *p) {
|
||||
STATICFORXSCOPE void gNBinitScope(scopeParms_t *p) {
|
||||
AssertFatal(p->gNB->scopeData=malloc(sizeof(scopeData_t)),"");
|
||||
scopeData_t *scope=(scopeData_t *) p->gNB->scopeData;
|
||||
scope->argc=p->argc;
|
||||
@@ -564,8 +703,11 @@ void gNBinitScope(scopeParms_t *p) {
|
||||
scope->gNB=p->gNB;
|
||||
scope->slotFunc=copyRxdataF;
|
||||
AssertFatal(scope->liveData= calloc(p->gNB->frame_parms.samples_per_frame_wCP*sizeof(int32_t),1),"");
|
||||
#ifndef WEBSRVSCOPE
|
||||
pthread_t forms_thread;
|
||||
threadCreate(&forms_thread, scope_thread_gNB, p->gNB->scopeData, "scope", -1, OAI_PRIORITY_RT_LOW);
|
||||
#endif
|
||||
|
||||
}
|
||||
static void ueWaterFall (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
|
||||
// Received signal in time domain of receive antenna 0
|
||||
@@ -639,6 +781,7 @@ static void uePbchFrequencyResp (OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue
|
||||
oai_xygraph(graph,freq,chest_f_abs,frame_parms->ofdm_symbol_size,0,10);
|
||||
}
|
||||
*/
|
||||
|
||||
static void uePbchLLR (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
|
||||
// PBCH LLRs
|
||||
if ( !data[pbchLlr])
|
||||
@@ -649,33 +792,45 @@ static void uePbchLLR (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_
|
||||
// We take the first antenna only for now
|
||||
int16_t *llrs = (int16_t *) (data[pbchLlr]+1);
|
||||
float *llr_pbch, *bit_pbch;
|
||||
int nx=sz;
|
||||
#ifdef WEBSRVSCOPE
|
||||
nx=websrv_cpllrbuff_tomsg(graph, llrs, sz, UE_id,0,0);
|
||||
#else
|
||||
oai_xygraph_getbuff(graph, &bit_pbch, &llr_pbch, sz, 0);
|
||||
|
||||
for (int i=0; i<sz; i++) {
|
||||
llr_pbch[i] = llrs[i];
|
||||
}
|
||||
|
||||
oai_xygraph(graph,bit_pbch,llr_pbch,sz,0,10);
|
||||
#endif
|
||||
oai_xygraph(graph,bit_pbch,llr_pbch,nx,0,10);
|
||||
}
|
||||
|
||||
static void uePbchIQ (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
|
||||
// PBCH I/Q of MF Output
|
||||
if (!data[pbchRxdataF_comp])
|
||||
return;
|
||||
|
||||
|
||||
|
||||
scopeSample_t *pbch_comp = (scopeSample_t *) (data[pbchRxdataF_comp]+1);
|
||||
const int sz=data[pbchRxdataF_comp]->lineSz;
|
||||
int newsz=sz;
|
||||
float *I, *Q;
|
||||
#ifdef WEBSRVSCOPE
|
||||
newsz = websrv_cpiqbuff_tomsg(graph,pbch_comp ,sz, 0,0);
|
||||
#else
|
||||
|
||||
oai_xygraph_getbuff(graph, &I, &Q, sz, 0);
|
||||
|
||||
for (int i=0; i<sz; i++) {
|
||||
I[i]=pbch_comp[i].r;
|
||||
Q[i]=pbch_comp[i].i;
|
||||
}
|
||||
|
||||
oai_xygraph(graph, I, Q, sz, 0, true);
|
||||
#endif
|
||||
oai_xygraph(graph, I, Q, newsz, 0, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void uePcchLLR (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
|
||||
// PDCCH LLRs
|
||||
if (!data[pdcchLlr])
|
||||
@@ -685,15 +840,19 @@ static void uePcchLLR (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_
|
||||
//int Qm = 2;
|
||||
const int sz=data[pdcchLlr]->lineSz;
|
||||
float *llr, *bit;
|
||||
oai_xygraph_getbuff(graph, &bit, &llr, sz, 0);
|
||||
|
||||
int nx=sz;
|
||||
int16_t *pdcch_llr = (int16_t *)(data[pdcchLlr]+1);
|
||||
|
||||
#ifdef WEBSRVSCOPE
|
||||
nx=websrv_cpllrbuff_tomsg(graph, pdcch_llr, sz, UE_id,0,0);
|
||||
#else
|
||||
oai_xygraph_getbuff(graph, &bit, &llr, sz, 0);
|
||||
|
||||
for (int i=0; i<sz; i++) {
|
||||
llr[i] = (float) pdcch_llr[i];
|
||||
}
|
||||
|
||||
oai_xygraph(graph,bit,llr,sz,0,10);
|
||||
#endif
|
||||
oai_xygraph(graph,bit,llr,nx,0,10);
|
||||
}
|
||||
static void uePcchIQ (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
|
||||
// PDCCH I/Q of MF Output
|
||||
@@ -701,20 +860,26 @@ static void uePcchIQ (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_U
|
||||
return;
|
||||
|
||||
const int sz=data[pdcchRxdataF_comp]->lineSz;
|
||||
int newsz=sz;
|
||||
//const int antennas=data[pdcchRxdataF_comp]->colSz;
|
||||
// We take the first antenna only for now
|
||||
float *I, *Q;
|
||||
oai_xygraph_getbuff(graph, &I, &Q, sz, 0);
|
||||
|
||||
scopeSample_t *pdcch_comp = (scopeSample_t *) (data[pdcchRxdataF_comp]+1);
|
||||
#ifdef WEBSRVSCOPE
|
||||
newsz = websrv_cpiqbuff_tomsg(graph,pdcch_comp ,sz, 0,0);
|
||||
#else
|
||||
oai_xygraph_getbuff(graph, &I, &Q, sz, 0);
|
||||
|
||||
for (int i=0; i<sz; i++) {
|
||||
I[i] = pdcch_comp[i].r;
|
||||
Q[i] = pdcch_comp[i].i;
|
||||
}
|
||||
|
||||
oai_xygraph(graph,I,Q,sz,0,10);
|
||||
#endif
|
||||
oai_xygraph(graph,I,Q,newsz,0,10);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void uePdschLLR (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
|
||||
// PDSCH LLRs
|
||||
if (!phy_vars_ue->pdsch_vars[0][eNB_id]->llr[0])
|
||||
@@ -723,24 +888,31 @@ static void uePdschLLR (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR
|
||||
int num_re = 4500;
|
||||
int Qm = 2;
|
||||
int coded_bits_per_codeword = num_re*Qm;
|
||||
int newsz=0;
|
||||
float *llr, *bit;
|
||||
|
||||
#ifndef WEBSRVSCOPE
|
||||
oai_xygraph_getbuff(graph, &bit, &llr, coded_bits_per_codeword*RX_NB_TH_MAX, 0);
|
||||
#endif
|
||||
int base=0;
|
||||
|
||||
for (int thr=0 ; thr < RX_NB_TH_MAX ; thr ++ ) {
|
||||
int16_t *pdsch_llr = (int16_t *) phy_vars_ue->pdsch_vars[thr][eNB_id]->llr[0]; // stream 0
|
||||
|
||||
#ifdef WEBSRVSCOPE
|
||||
newsz += websrv_cpllrbuff_tomsg(graph, pdsch_llr,coded_bits_per_codeword, 0,thr,RX_NB_TH_MAX);
|
||||
#else
|
||||
for (int i=0; i<coded_bits_per_codeword; i++) {
|
||||
llr[base+i] = (float) pdsch_llr[i];
|
||||
bit[base+i] = (float) base+i;
|
||||
}
|
||||
|
||||
newsz += coded_bits_per_codeword;
|
||||
#endif
|
||||
base+=coded_bits_per_codeword;
|
||||
}
|
||||
|
||||
AssertFatal(base <= coded_bits_per_codeword*RX_NB_TH_MAX, "");
|
||||
//fl_set_xyplot_xbounds(form->pdsch_llr,0,coded_bits_per_codeword);
|
||||
oai_xygraph(graph,bit,llr,base,0,10);
|
||||
oai_xygraph(graph,bit,llr,newsz,0,10);
|
||||
}
|
||||
static void uePdschIQ (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
|
||||
// PDSCH I/Q of MF Output
|
||||
@@ -749,25 +921,31 @@ static void uePdschIQ (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_
|
||||
|
||||
NR_DL_FRAME_PARMS *frame_parms = &phy_vars_ue->frame_parms;
|
||||
int sz=7*2*frame_parms->N_RB_DL*12; // size of the malloced buffer
|
||||
int newsz=0;
|
||||
float *I, *Q;
|
||||
oai_xygraph_getbuff(graph, &I, &Q, sz*RX_NB_TH_MAX, 0);
|
||||
int base=0;
|
||||
|
||||
#ifndef WEBSRVSCOPE
|
||||
oai_xygraph_getbuff(graph, &I, &Q, sz*RX_NB_TH_MAX, 0);
|
||||
memset(I+base, 0, sz*RX_NB_TH_MAX * sizeof(*I));
|
||||
memset(Q+base, 0, sz*RX_NB_TH_MAX * sizeof(*Q));
|
||||
|
||||
#endif
|
||||
for (int thr=0 ; thr < RX_NB_TH_MAX ; thr ++ ) {
|
||||
scopeSample_t *pdsch_comp = (scopeSample_t *) phy_vars_ue->pdsch_vars[thr][eNB_id]->rxdataF_comp0[0];
|
||||
|
||||
#ifdef WEBSRVSCOPE
|
||||
newsz += websrv_cpiqbuff_tomsg(graph, pdsch_comp,sz, 0,base);
|
||||
#else
|
||||
for (int s=0; s<sz; s++) {
|
||||
I[s+base] += pdsch_comp[s].r;
|
||||
Q[s+base] += pdsch_comp[s].i;
|
||||
}
|
||||
|
||||
newsz += sz;
|
||||
#endif
|
||||
base+=sz;
|
||||
}
|
||||
|
||||
AssertFatal(base <= sz*RX_NB_TH_MAX, "");
|
||||
oai_xygraph(graph,I,Q,sz*RX_NB_TH_MAX,0,10);
|
||||
oai_xygraph(graph,I,Q,newsz,0,10);
|
||||
}
|
||||
static void uePdschThroughput (scopeGraphData_t **data, OAIgraph_t *graph, PHY_VARS_NR_UE *phy_vars_ue, int eNB_id, int UE_id) {
|
||||
/*
|
||||
@@ -792,7 +970,7 @@ static void uePdschThroughput (scopeGraphData_t **data, OAIgraph_t *graph, PHY_
|
||||
fl_set_xyplot_ybounds(form->pdsch_tput,0,tput_ue_max[UE_id]);
|
||||
*/
|
||||
}
|
||||
static OAI_phy_scope_t *create_phy_scope_nrue( int ID ) {
|
||||
STATICFORXSCOPE OAI_phy_scope_t *create_phy_scope_nrue( int ID ) {
|
||||
FL_OBJECT *obj;
|
||||
OAI_phy_scope_t *fdui = calloc(( sizeof *fdui ),1);
|
||||
// Define form
|
||||
@@ -805,6 +983,8 @@ static OAI_phy_scope_t *create_phy_scope_nrue( int ID ) {
|
||||
// Received signal
|
||||
fdui->graph[0] = nrUEcommonGraph(ueWaterFall,
|
||||
WATERFALL, 0, curY, 400, 100, "Received Signal (Time-Domain, one frame)", FL_RED );
|
||||
fdui->graph[0].chartid=SCOPEMSG_DATAID_WF; //tells websrv frontend to use WF chart for displaying
|
||||
fdui->graph[0].datasetid=0; // not used for WF
|
||||
// Time-domain channel response
|
||||
fdui->graph[1] = nrUEcommonGraph(ueChannelResponse,
|
||||
FL_NORMAL_XYPLOT, 400, curY, 400, 100, "Channel Impulse Response (samples, abs)", FL_RED );
|
||||
@@ -815,31 +995,45 @@ static OAI_phy_scope_t *create_phy_scope_nrue( int ID ) {
|
||||
WATERFALL, 0, curY, 800, 100, "Channel Frequency (RE, one slot)", FL_RED );
|
||||
fl_get_object_bbox(fdui->graph[2].graph,&x, &y,&w, &h);
|
||||
curY+=h+20;
|
||||
fdui->graph[2].chartid=SCOPEMSG_DATAID_WF; //tells websrv frontend to use WF chart for displaying
|
||||
fdui->graph[2].datasetid=0; // not used for WF
|
||||
// LLR of PBCH
|
||||
fdui->graph[3] = nrUEcommonGraph(uePbchLLR,
|
||||
FL_POINTS_XYPLOT, 0, curY, 500, 100, "PBCH Log-Likelihood Ratios (LLR, mag)", FL_GREEN );
|
||||
fl_set_xyplot_xgrid(fdui->graph[3].graph,FL_GRID_MAJOR);
|
||||
fdui->graph[3].chartid=SCOPEMSG_DATAID_LLR; //tells websrv frontend to use LLR chart for displaying
|
||||
fdui->graph[3].datasetid=0; //tells websrv frontend to use dataset index 0 in LLR chart
|
||||
// I/Q PBCH comp
|
||||
fdui->graph[4] = nrUEcommonGraph(uePbchIQ,
|
||||
FL_POINTS_XYPLOT, 500, curY, 300, 100, "PBCH I/Q of MF Output", FL_GREEN );
|
||||
fl_get_object_bbox(fdui->graph[3].graph,&x, &y,&w, &h);
|
||||
curY+=h;
|
||||
curY+=h;
|
||||
fdui->graph[4].chartid=SCOPEMSG_DATAID_IQ; //tells websrv frontend to use constellation chart for displaying
|
||||
fdui->graph[4].datasetid=0; //tells websrv frontend which dataset to use in the window
|
||||
// LLR of PDCCH
|
||||
fdui->graph[5] = nrUEcommonGraph(uePcchLLR,
|
||||
FL_POINTS_XYPLOT, 0, curY, 500, 100, "PDCCH Log-Likelihood Ratios (LLR, mag)", FL_CYAN );
|
||||
fdui->graph[5].chartid=SCOPEMSG_DATAID_LLR; //tells websrv frontend to use LLR chart for displaying
|
||||
fdui->graph[5].datasetid=1; //tells websrv frontend to use dataset index 1 in LLR chart
|
||||
// I/Q PDCCH comp
|
||||
fdui->graph[6] = nrUEcommonGraph(uePcchIQ,
|
||||
FL_POINTS_XYPLOT, 500, curY, 300, 100, "PDCCH I/Q of MF Output", FL_CYAN );
|
||||
fl_get_object_bbox(fdui->graph[5].graph,&x, &y,&w, &h);
|
||||
curY+=h;
|
||||
fdui->graph[6].chartid=SCOPEMSG_DATAID_IQ; //tells websrv frontend to use constellation chart for displaying
|
||||
fdui->graph[6].datasetid=1; //tells websrv frontend which dataset to use in the window
|
||||
// LLR of PDSCH
|
||||
fdui->graph[7] = nrUEcommonGraph(uePdschLLR,
|
||||
FL_POINTS_XYPLOT, 0, curY, 500, 200, "PDSCH Log-Likelihood Ratios (LLR, mag)", FL_YELLOW );
|
||||
fdui->graph[7].chartid=SCOPEMSG_DATAID_LLR; //tells websrv frontend to use LLR chart for displaying
|
||||
fdui->graph[7].datasetid=2; //tells websrv frontend to use dataset index 2 in LLR chart
|
||||
// I/Q PDSCH comp
|
||||
fdui->graph[8] = nrUEcommonGraph(uePdschIQ,
|
||||
FL_POINTS_XYPLOT, 500, curY, 300, 200, "PDSCH I/Q of MF Output", FL_YELLOW );
|
||||
fl_get_object_bbox(fdui->graph[8].graph,&x, &y,&w, &h);
|
||||
curY+=h;
|
||||
fdui->graph[8].chartid=SCOPEMSG_DATAID_IQ; //tells websrv frontend to use constellation chart for displaying
|
||||
fdui->graph[8].datasetid=2; //tells websrv frontend which dataset to use in the window
|
||||
// Throughput on PDSCH
|
||||
fdui->graph[9] = nrUEcommonGraph(uePdschThroughput,
|
||||
FL_NORMAL_XYPLOT, 0, curY, 500, 100, "PDSCH Throughput [frame]/[kbit/s]", FL_WHITE );
|
||||
@@ -856,20 +1050,20 @@ static OAI_phy_scope_t *create_phy_scope_nrue( int ID ) {
|
||||
fl_hide_object(fdui->button_0);
|
||||
#endif
|
||||
fl_end_form( );
|
||||
fdui->phy_scope->fdui = fdui;
|
||||
if (fdui->phy_scope)
|
||||
fdui->phy_scope->fdui = fdui;
|
||||
char buf[100];
|
||||
sprintf(buf,"NR DL SCOPE UE %d", ID);
|
||||
fl_show_form (fdui->phy_scope, FL_PLACE_HOTSPOT, FL_FULLBORDER, buf);
|
||||
return fdui;
|
||||
}
|
||||
|
||||
void phy_scope_nrUE(scopeGraphData_t **UEliveData,
|
||||
OAI_phy_scope_t *form,
|
||||
STATICFORXSCOPE void phy_scope_nrUE( OAI_phy_scope_t *form,
|
||||
PHY_VARS_NR_UE *phy_vars_ue,
|
||||
int eNB_id,
|
||||
int UE_id) {
|
||||
static OAI_phy_scope_t *remeberForm=NULL;
|
||||
|
||||
scopeGraphData_t **UEliveData = (( scopeData_t *)phy_vars_ue->scopeData)->liveData;
|
||||
if (form==NULL)
|
||||
form=remeberForm;
|
||||
else
|
||||
@@ -881,13 +1075,15 @@ void phy_scope_nrUE(scopeGraphData_t **UEliveData,
|
||||
int i=0;
|
||||
|
||||
while (form->graph[i].graph) {
|
||||
form->graph[i].nrUEfunct(UEliveData, form->graph+i, phy_vars_ue, eNB_id, UE_id);
|
||||
if ( form->graph[i].enabled)
|
||||
form->graph[i].nrUEfunct(UEliveData, form->graph+i, phy_vars_ue, eNB_id, UE_id);
|
||||
i++;
|
||||
}
|
||||
|
||||
//fl_check_forms();
|
||||
}
|
||||
|
||||
#ifndef WEBSRVSCOPE
|
||||
static void *nrUEscopeThread(void *arg) {
|
||||
PHY_VARS_NR_UE *ue=(PHY_VARS_NR_UE *)arg;
|
||||
size_t stksize;
|
||||
@@ -901,8 +1097,7 @@ static void *nrUEscopeThread(void *arg) {
|
||||
|
||||
while (!oai_exit) {
|
||||
fl_freeze_form(form_nrue->phy_scope);
|
||||
phy_scope_nrUE((( scopeData_t *)ue->scopeData)->liveData,
|
||||
form_nrue,
|
||||
phy_scope_nrUE(form_nrue,
|
||||
ue,
|
||||
0,0);
|
||||
fl_unfreeze_form(form_nrue->phy_scope);
|
||||
@@ -912,8 +1107,9 @@ static void *nrUEscopeThread(void *arg) {
|
||||
|
||||
pthread_exit((void *)arg);
|
||||
}
|
||||
#endif
|
||||
|
||||
void UEcopyData(PHY_VARS_NR_UE *ue, enum UEdataType type, void *dataIn, int elementSz, int colSz, int lineSz) {
|
||||
static void UEcopyData(PHY_VARS_NR_UE *ue, enum UEdataType type, void *dataIn, int elementSz, int colSz, int lineSz) {
|
||||
// Local static copy of the scope data bufs
|
||||
// The active data buf is alterned to avoid interference between the Scope thread (display) and the Rx thread (data input)
|
||||
// Index of "2" could be set to the number of Rx threads + 1
|
||||
@@ -954,13 +1150,15 @@ void UEcopyData(PHY_VARS_NR_UE *ue, enum UEdataType type, void *dataIn, int elem
|
||||
}
|
||||
}
|
||||
|
||||
void nrUEinitScope(PHY_VARS_NR_UE *ue) {
|
||||
STATICFORXSCOPE void nrUEinitScope(PHY_VARS_NR_UE *ue) {
|
||||
AssertFatal(ue->scopeData=malloc(sizeof(scopeData_t)),"");
|
||||
scopeData_t *scope=(scopeData_t *) ue->scopeData;
|
||||
scope->copyData=UEcopyData;
|
||||
AssertFatal(scope->liveData=calloc(sizeof(scopeGraphData_t *), UEdataTypeNumberOfItems),"");
|
||||
#ifndef WEBSRVSCOPE
|
||||
pthread_t forms_thread;
|
||||
threadCreate(&forms_thread, nrUEscopeThread, ue, "scope", -1, OAI_PRIORITY_RT_LOW);
|
||||
#endif
|
||||
}
|
||||
|
||||
void nrscope_autoinit(void *dataptr) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user