Compare commits

...

347 Commits

Author SHA1 Message Date
El Mghazli Yacine
1ef5fafe89 logger component 2022-10-10 10:15:51 +02:00
frtabu
9f6e46bd8a Add backend memory cleanup when stopping scope, minor enhancements frontend interface 2022-10-07 20:01:01 +02:00
El Mghazli Yacine
7372378732 detail 2022-10-06 18:38:45 +02:00
El Mghazli Yacine
71d20e0543 removed logger components 2022-10-06 18:03:42 +02:00
El Mghazli Yacine
33fd2343ce removed charts folder 2022-10-06 18:02:05 +02:00
El Mghazli Yacine
48ef96959d removed scop2 and ws2 2022-10-06 17:07:29 +02:00
El Mghazli Yacine
c73ce11828 Merge branch 'rxws' of https://gitlab.eurecom.fr/oai/openairinterface5g into rxws 2022-10-06 15:15:46 +02:00
El Mghazli Yacine
66546b7ad0 send message fixed 2022-10-06 15:15:25 +02:00
El Mghazli Yacine
11087fff0f details 2022-10-06 15:15:25 +02:00
El Mghazli Yacine
ffa13d99e5 scope2 working like scope 2022-10-06 15:15:25 +02:00
yacine
ccae22f648 serdes to be tested 2022-10-06 15:15:25 +02:00
yacine
4e932c9248 build OK 2022-10-06 15:15:25 +02:00
yacine
0c9dd4d04a scope2 2022-10-06 15:12:23 +02:00
El Mghazli Yacine
67a5bfaa43 initial commit WIP 2022-10-06 15:12:23 +02:00
El Mghazli Yacine
501192a1f8 send message fixed 2022-10-06 15:12:06 +02:00
frtabu
83d00d4c21 scope waterfall enhancements 2022-10-06 15:09:38 +02:00
El Mghazli Yacine
12504106eb details 2022-10-04 15:15:48 +02:00
El Mghazli Yacine
3bb957722f scope2 working like scope 2022-10-04 12:58:41 +02:00
yacine
ae2abbc6e2 serdes to be tested 2022-10-02 23:42:22 +02:00
yacine
544c723372 build OK 2022-10-01 01:10:29 +02:00
yacine
1e34ab443c Merge branch 'rxws' of https://gitlab.eurecom.fr/oai/openairinterface5g into rxws 2022-10-01 00:51:03 +02:00
yacine
519b38817a scope2 2022-10-01 00:50:41 +02:00
El Mghazli Yacine
3da668576c initial commit WIP 2022-10-01 00:50:41 +02:00
yacine
ce46cab866 scope2 2022-10-01 00:48:51 +02:00
frtabu
553c6377aa waterfall implem going on.... 2022-09-30 19:13:29 +02:00
El Mghazli Yacine
b42694254f initial commit WIP 2022-09-30 19:07:20 +02:00
frtabu
50821cace3 switch to 16bits value for LLR data, start watterfall view implem 2022-09-29 19:40:52 +02:00
frtabu
2fb9752bff llr view enhancements 2022-09-28 19:33:42 +02:00
frtabu
7b57a3d4ed hardening websocket connection 2022-09-28 16:23:44 +02:00
frtabu
d81b670ac2 scope: llr view debug 2022-09-22 16:41:46 +02:00
yacine nokia
461ba045b1 use env backend for ws:// 2022-09-20 15:32:27 +02:00
frtabu
03a8ff2913 init fix scope availibility Pb 2022-09-20 14:31:59 +02:00
frtabu
6e85528582 scope interface modifications 2022-09-20 12:17:37 +02:00
frtabu
81ba755984 more scope updates 2022-09-19 11:25:35 +02:00
frtabu
e8ca483c56 scope front-end and back-end fixes and enhancements 2022-09-13 19:48:17 +02:00
frtabu
f8eba8400d scope chart display enhancements 2022-09-09 18:52:05 +02:00
frtabu
6d2bd1025f Add frontend chart options to improve GUI and perf 2022-09-07 19:50:56 +02:00
frtabu
36c0cf0133 scope data transfer debug 2022-09-06 19:47:57 +02:00
frtabu
7b31678bed fix frontend bug and add signal selection 2022-09-05 19:33:04 +02:00
frtabu
b2ba88667a scope gui enhancement, implement backend scope interface suport via regular http, websocket reserved for scope data transfers , frontend broken, init Pb 2022-08-26 17:14:41 +02:00
frtabu
0aca093122 start Restructure scope support code to move to regular http request control protocol, websocket being used for data 2022-08-24 19:38:36 +02:00
frtabu
73f5496ffa scope iq transfer implem and test 2022-08-23 19:19:53 +02:00
frtabu
f809e4689c angular update and ng2-charts integration completed 2022-07-26 19:15:00 +02:00
frtabu
5ae59cc394 Angular update 2022-07-26 15:16:02 +02:00
frtabu
def1ebc661 angular update 2022-07-26 13:18:22 +02:00
frtabu
b57963dd2a remove package_lock.json from git 2022-07-26 13:12:59 +02:00
frtabu
d33ae02d39 Angular update and start importing ng2-charts 2022-07-26 12:45:38 +02:00
frtabu
86ba2b0822 angular update 2022-07-25 17:55:41 +02:00
frtabu
4bfc929bad angular update 2022-07-25 15:33:48 +02:00
frtabu
db81a3f0b9 angular update 2022-07-25 15:31:18 +02:00
frtabu
abf14c0bb4 angular update 2022-07-25 15:28:26 +02:00
frtabu
a1d4514ae0 angular update 2022-07-25 15:26:05 +02:00
frtabu
d813fb8470 angular upgrade 2022-07-25 15:23:01 +02:00
frtabu
e2615dd886 angular update 2022-07-25 15:21:04 +02:00
frtabu
cc24755a34 angular update 2022-07-25 14:33:20 +02:00
frtabu
ca45cbb2b7 Angular update 2022-07-25 14:27:54 +02:00
frtabu
deb2a453c6 angular update for chart lib 2022-07-25 14:19:49 +02:00
frtabu
5a08d8109c websrv softscope interface, implement data messages 2022-07-22 19:54:08 +02:00
frtabu
65c858757d scope implem going on 2022-07-11 20:11:07 +02:00
frtabu
55003cafd2 Merge branch 'websrv' of https://gitlab.eurecom.fr/oai/openairinterface5g into websrv 2022-07-11 00:53:53 +02:00
frtabu
57a7488e85 Fix modifiable string bug and minor GUI update 2022-07-11 00:38:08 +02:00
yacine nokia
da85cf5183 Merge branch 'websrv' of gitlab.eurecom.fr:oai/openairinterface5g into websrv 2022-07-09 20:12:32 +02:00
yacine nokia
32a6dd85c2 fix string issue 2022-07-09 20:12:15 +02:00
frtabu
d9216265dc Merge branch 'websrv' of https://gitlab.eurecom.fr/oai/openairinterface5g into websrv
after rebase onto develop.
2022-07-08 20:00:53 +02:00
yacine nokia
99ecd83133 firefox debugger 2022-07-08 12:28:14 +02:00
frtabu
c4958e50e8 vertical scroll bar in grid tile 2022-07-08 12:19:07 +02:00
frtabu
7b16f7e863 Fix build error when libulfius not found (remove REQUIRED keyword in CMakelist) 2022-07-08 12:17:44 +02:00
El Mghazli Yacine
e0c7980df5 websrv working 2022-07-08 12:17:44 +02:00
frtabu
4b002029fb Remove telnet server dependency 2022-07-08 12:17:44 +02:00
frtabu
27d626dc48 exclude authentication code from websrv.c to workaround ubuntu18 compilation Pb 2022-07-08 12:17:44 +02:00
frtabu
4fa9e2b39b merge channel algo validation frontend and implement simuType variable type in backend - doesn't work and breaks modifiable string which are no more modifiable 2022-07-08 12:17:44 +02:00
El Mghazli Yacine
fb5c768159 predefs$ 2022-07-08 12:13:24 +02:00
El Mghazli Yacine
0966ceb70b predefs$ 2022-07-08 12:12:28 +02:00
El Mghazli Yacine
b75d4f90b7 loglvl 2022-07-08 12:07:09 +02:00
El Mghazli Yacine
8cbe53b82b paramCtrl 2022-07-08 12:03:30 +02:00
El Mghazli Yacine
13e17339d3 removed paramFC 2022-07-08 11:53:57 +02:00
El Mghazli Yacine
6d457fcb56 removed paramFC 2022-07-08 11:47:22 +02:00
yacine nokia
748d4cb49a localhost dev env 2022-07-08 11:46:16 +02:00
El Mghazli Yacine
1e77b753ff fixed table set 2022-07-08 11:33:50 +02:00
El Mghazli Yacine
2964ef7e3e paramCtrl 2022-07-08 11:33:50 +02:00
El Mghazli Yacine
7f69632bbb removed paramFC 2022-07-08 11:33:50 +02:00
frtabu
17af1562a8 scope implem ongoing.. 2022-07-08 11:33:50 +02:00
frtabu
c3378c7f84 Scope implem going on , front and back 2022-07-08 11:33:50 +02:00
El Mghazli Yacine
38dca6387f cleanup api (details) 2022-07-08 11:33:50 +02:00
frtabu
4a42ff54df log level validation , backend implem 2022-07-08 11:33:50 +02:00
El Mghazli Yacine
22d4495c20 !!!! major refactoring !!!! 2022-07-08 11:33:50 +02:00
frtabu
3b93a9c5cb minor fixes in scope frontend/backend 2022-07-08 11:33:50 +02:00
frtabu
6407df90e2 websocket binary message and scope interface 2022-07-08 11:33:50 +02:00
frtabu
d75da26833 start implem websocket interface with frontend 2022-07-08 11:33:50 +02:00
El Mghazli Yacine
b0f0106cc7 better rows with pristine 2022-07-08 11:33:50 +02:00
El Mghazli Yacine
2690cbd08e ws server address 2022-07-08 11:33:50 +02:00
El Mghazli Yacine
868f9d9042 ng serve OK 2022-07-08 11:33:50 +02:00
El Mghazli Yacine
ad0dcfe287 ng serve OK 2022-07-08 11:33:50 +02:00
El Mghazli Yacine
7bd44da548 initial commit - build NOK 2022-07-08 11:33:50 +02:00
El Mghazli Yacine
185e5934b7 pristine 2022-07-08 11:33:50 +02:00
frtabu
24802ee18b updatable result (frontend) 2022-07-08 11:33:50 +02:00
frtabu
8916cfdfd2 implement updatte results in frontend, ongoing 2022-07-08 11:33:49 +02:00
frtabu
b135c1dd8f start fixing https implem in backend 2022-07-08 11:33:49 +02:00
frtabu
6e61c8a05a Add access to parsed config file 2022-07-08 11:33:49 +02:00
frtabu
c957882da8 backend and frontend implementation of downloading config file 2022-07-08 11:33:49 +02:00
frtabu
7404ad7a5c commands with params... working now 2022-07-08 11:33:49 +02:00
frtabu
4e8fa01a25 frontend dialog test 2022-07-08 11:33:49 +02:00
frtabu
ec0d3fde1c command with parameters... 2022-07-08 11:33:49 +02:00
frtabu
58618be544 on going with command with parameter 2022-07-08 11:33:49 +02:00
frtabu
7e7c097942 going on with command parameter 2022-07-08 11:33:49 +02:00
frtabu
94796957e8 commands with a parameter, going on.... 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
55d9200a48 trying questions 2022-07-08 11:33:49 +02:00
frtabu
d815edebe7 command with parameter going on 2022-07-08 11:33:49 +02:00
frtabu
f1d18b4edb command with param implementation in frontend, beginning 2022-07-08 11:33:49 +02:00
frtabu
06355e9cf1 rontend Question Dialog 2022-07-08 11:33:49 +02:00
frtabu
9bcf269856 Implementing commands with question like when modifying a channel model (ask which model) 2022-07-08 11:33:49 +02:00
frtabu
9bfcbe7faa implemebtation of softmodem threads commands 2022-07-08 11:33:49 +02:00
frtabu
fa92bf1a79 Dialog enhancements in frontend 2022-07-08 11:33:49 +02:00
frtabu
a348ada211 fix log modifications in backend, allow Dialog resize and move 2022-07-08 11:33:49 +02:00
frtabu
e0f76e6ca6 implement log options modif in backend 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
351f069adc window.location.port 2022-07-08 11:33:49 +02:00
frtabu
64f84cdd6a implementation of command modifying module parameters, step 1, decode json sent by fronthaul 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
8bd1b3b53a boolean as string for setRow 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
2aef88960f http://' + window.location.hostname + ':8090' 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
51d6fd7ec2 v1 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
f2d43636bf better fields and buttons 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
dbeda2d33f table disapears when no rows 2022-07-08 11:33:49 +02:00
frtabu
5d9f91997e Modify status info , add redir log commands 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
210429f388 set disabled if all row is not modifiable 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
dc40e0dca1 fixed cmd results tile now disapears 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
fe8f418e7a fixed boolean enabled 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
98de3b9049 fixed 2 posts 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
55d83f6045 button and readonly 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
6882b63991 fixed rawIndex 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
45896f2583 row.paramsFA.controls 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
ed6b13c759 IRow2 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
eee4d3fc07 setRow$ in API 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
b8f64823bf setParam$ 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
d37952f94d setParam$ 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
12e2eded38 confirm button ok 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
d910e92669 height 80% display 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
9e2f2db88c table OK 2022-07-08 11:33:49 +02:00
frtabu
85929b7933 fix empty array format 2022-07-08 11:33:49 +02:00
frtabu
5673a31814 fix new IRespCmd json format in backend 2022-07-08 11:33:49 +02:00
frtabu
7da80421ff table response implementation fix (backend) 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
35e4f29cf6 display fix 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
c1abfcfc50 confirm dialog 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
b2e8b78a01 dynamic table WIP 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
53e160728a cleanup rowCtrl 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
522f2ad140 dynamic tables 2022-07-08 11:33:49 +02:00
frtabu
4a26a44ee0 Table Response implementation in backend 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
59722d1a91 confirm dialog before sending cmd 2022-07-08 11:33:49 +02:00
frtabu
218a524cd2 display array fix 2022-07-08 11:33:49 +02:00
frtabu
f44f105cb3 more commands implementation in backend 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
32ff4b33f1 "prod": "ng serve --configuration=production" 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
bf0492ba87 table display 2022-07-08 11:33:49 +02:00
frtabu
7a9c13422a fix json format in log response 2022-07-08 11:33:49 +02:00
frtabu
3e3b33e94c test table reslt with log command 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
58ae227080 tables and new API with IColumn and IRow 2022-07-08 11:33:49 +02:00
frtabu
81a671a4f3 implementation of log command 2022-07-08 11:33:49 +02:00
frtabu
039c6d24bc Adapt command response structure 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
ec80ef106f details 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
60f17d4d45 ilogs 2022-07-08 11:33:49 +02:00
frtabu
5e86379be4 implement mechanism for simple string response 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
62789c5161 openDialog 2022-07-08 11:33:49 +02:00
frtabu
bede89b233 command processing implementation 2022-07-08 11:33:49 +02:00
frtabu
4a29496988 better variable type processing 2022-07-08 11:33:49 +02:00
frtabu
a809a8c722 modify telnet server structure to support command execution from the webserver 2022-07-08 11:33:49 +02:00
frtabu
ad3ce4ddb6 json boolean fix 2022-07-08 11:33:49 +02:00
frtabu
72df29f225 fix backend commands response body 2022-07-08 11:33:49 +02:00
frtabu
42ab0247bf backend implementation of variable modifications 2022-07-08 11:33:49 +02:00
frtabu
a439a0cce0 Add OPTIONS http method support, used before POST by frontend. 2022-07-08 11:33:49 +02:00
frtabu
f7c9075337 change endpoint registration mechanism 2022-07-08 11:33:49 +02:00
frtabu
15fc3ccce0 More debug messages in backend 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
f2500d9297 fixed cmds display 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
72bc9baf2e fixed API variables calls 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
6cef7a8f67 APIv2.2 2022-07-08 11:33:49 +02:00
Raymond Knopp
b32524dd74 Adapt to new frontend interface 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
a45dd3b8bc yacine s'adapte grace a sa flaxibilité legendaire 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
a981bb382b APIv2.2 2022-07-08 11:33:49 +02:00
frtabu
f17aba0f5e config module fixes and websrv modif 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
ca0398c5bc APIv2.1 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
ab6bb2534e webserv APIv2 (frontend part) 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
c875ce639c working with first API version 2022-07-08 11:33:49 +02:00
frtabu
23a1d8d9bc fix config file generation to sip command line only options 2022-07-08 11:33:49 +02:00
frtabu
c0f6b39da5 config module enhancement,step 2 2022-07-08 11:33:49 +02:00
frtabu
095be9fe6a config module enhancement, next step 2022-07-08 11:33:49 +02:00
frtabu
82ebc9fa7b config module enhancement (dump of running config): start implem. 2022-07-08 11:33:49 +02:00
Raymond Knopp
526720cb24 add websrv as optional lib and frontend as independant target in oai build 2022-07-08 11:33:49 +02:00
frtabu
433dbcd41a websrv backend implementation next step 2022-07-08 11:33:49 +02:00
frtabu
20b3210062 json format sync with frontend 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
b959af152d backend env 2022-07-08 11:33:49 +02:00
frtabu
e5335a0188 Tested init with front end 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
1387bdc16e api first test 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
d2dfe830e7 display ok 2022-07-08 11:33:49 +02:00
frtabu
3da8b0e5eb Add https support and front-end url support 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
de0e075381 rudiments ok 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
826b405d47 ng build OK 2022-07-08 11:33:49 +02:00
frtabu
d761f67bcf go on websrv implem 2022-07-08 11:33:49 +02:00
frtabu
975835804d Initial implem web interface for telnet server 2022-07-08 11:33:49 +02:00
El Mghazli Yacine
b467fd2c68 updated tslib 2022-07-08 11:30:26 +02:00
El Mghazli Yacine
daec2ba3ea initial commit 2022-07-08 11:30:26 +02:00
El Mghazli Yacine
e705c88dd5 cleanup 2022-07-08 11:30:26 +02:00
El Mghazli Yacine
7121bf6d05 updated angular successfully 2022-07-08 11:30:26 +02:00
El Mghazli Yacine
1228608cc2 ng update 2022-07-08 11:30:26 +02:00
El Mghazli Yacine
47ac986d8c ng update 2022-07-08 11:30:26 +02:00
El Mghazli Yacine
8ea378eea8 ngx initial commit before upgrade 2022-07-08 11:30:26 +02:00
El Mghazli Yacine
2c688f230f Merge branch 'websrv' of gitlab.eurecom.fr:oai/openairinterface5g into websrv 2022-07-08 10:49:03 +02:00
El Mghazli Yacine
cbec64c28f websrv working 2022-07-08 10:49:00 +02:00
frtabu
1c352f3138 vertical scroll bar in grid tile 2022-07-08 01:43:00 +02:00
frtabu
5e964d660f Fix build error when libulfius not found (remove REQUIRED keyword in CMakelist) 2022-07-07 19:52:13 +02:00
frtabu
2e66c87f4d Remove telnet server dependency 2022-07-06 11:49:29 +02:00
frtabu
82a771dd61 exclude authentication code from websrv.c to workaround ubuntu18 compilation Pb 2022-07-06 10:13:45 +02:00
frtabu
c99f52207d merge channel algo validation frontend and implement simuType variable type in backend - doesn't work and breaks modifiable string which are no more modifiable 2022-07-05 13:35:05 +02:00
El Mghazli Yacine
3555edff76 Merge branch 'validators' of gitlab.eurecom.fr:oai/openairinterface5g into validators 2022-07-04 17:02:21 +02:00
El Mghazli Yacine
6723fc531b predefs$ 2022-07-04 17:02:11 +02:00
El Mghazli Yacine
26cc637d1b predefs$ 2022-07-04 16:57:42 +02:00
frtabu
e63f1454b3 Merge branch 'websrv' of https://gitlab.eurecom.fr/oai/openairinterface5g into websrv 2022-07-04 16:45:53 +02:00
El Mghazli Yacine
c69d980613 loglvl 2022-07-01 15:02:18 +02:00
frtabu
4c4917442f Merge branch 'websrv' of https://gitlab.eurecom.fr/oai/openairinterface5g into websrv 2022-07-01 14:14:42 +02:00
frtabu
bfb06225a5 Merge branch 'websrv' of https://gitlab.eurecom.fr/oai/openairinterface5g into websrv 2022-07-01 14:13:33 +02:00
El Mghazli Yacine
d62892ff8a Merge branch 'validators' of gitlab.eurecom.fr:oai/openairinterface5g into validators 2022-07-01 11:51:50 +02:00
El Mghazli Yacine
78ed7d4570 fixed table set 2022-07-01 11:47:57 +02:00
El Mghazli Yacine
d969a124ee paramCtrl 2022-06-30 17:09:45 +02:00
El Mghazli Yacine
b68e614850 removed paramFC 2022-06-30 17:09:45 +02:00
El Mghazli Yacine
17a4dea3e1 paramCtrl 2022-06-30 16:45:55 +02:00
frtabu
43130b687e scope implem ongoing.. 2022-06-30 00:21:27 +02:00
frtabu
fdf2728d02 Scope implem going on , front and back 2022-06-29 23:07:25 +02:00
frtabu
2783acb933 Scope implem going on , front and back 2022-06-29 20:22:16 +02:00
El Mghazli Yacine
e78e720a46 Merge branch 'validators' of gitlab.eurecom.fr:oai/openairinterface5g into validators 2022-06-28 12:13:49 +02:00
El Mghazli Yacine
2d9bac6359 removed paramFC 2022-06-28 12:13:39 +02:00
El Mghazli Yacine
2ebb462a8e removed paramFC 2022-06-28 12:10:07 +02:00
frtabu
8ea5fd7894 Merge branch 'websrv' of https://gitlab.eurecom.fr/oai/openairinterface5g into websrv 2022-06-28 11:38:36 +02:00
frtabu
c939a9cdf5 log level validation , backend implem 2022-06-28 11:35:45 +02:00
El Mghazli Yacine
31c9a03e83 cleanup api (details) 2022-06-28 11:19:28 +02:00
El Mghazli Yacine
b7b41fefc0 !!!! major refactoring !!!! 2022-06-27 18:30:54 +02:00
frtabu
29c5129b0a minor fixes in scope frontend/backend 2022-06-27 00:34:03 +02:00
frtabu
3b0f050a33 websocket binary message and scope interface 2022-06-26 02:11:03 +02:00
frtabu
dc6dd67827 start implem websocket interface with frontend 2022-06-24 19:42:33 +02:00
El Mghazli Yacine
9f10bfebf2 better rows with pristine 2022-06-23 16:32:37 +02:00
El Mghazli Yacine
b34a44a4f9 ws server address 2022-06-23 14:40:53 +02:00
El Mghazli Yacine
0b7201244a ng serve OK 2022-06-22 23:06:18 +02:00
El Mghazli Yacine
28bd9ea4bc ng serve OK 2022-06-22 23:06:05 +02:00
El Mghazli Yacine
044ed48fc7 initial commit - build NOK 2022-06-22 19:39:04 +02:00
El Mghazli Yacine
1b071df0bc pristine 2022-06-22 10:13:05 +02:00
frtabu
45967c8b77 updatable result (frontend) 2022-06-17 18:04:55 +02:00
frtabu
fc758069fb implement updatte results in frontend, ongoing 2022-06-17 18:04:55 +02:00
frtabu
9be7b12654 start fixing https implem in backend 2022-06-17 18:04:55 +02:00
frtabu
160abd88cc Add access to parsed config file 2022-06-17 18:04:55 +02:00
frtabu
e2eebc7a28 backend and frontend implementation of downloading config file 2022-06-17 18:04:55 +02:00
frtabu
a52adaa95e commands with params... working now 2022-06-17 18:04:55 +02:00
frtabu
ced3078e30 frontend dialog test 2022-06-17 18:04:55 +02:00
frtabu
b7d2840e04 command with parameters... 2022-06-17 18:04:55 +02:00
frtabu
0a88f1a2d8 on going with command with parameter 2022-06-17 18:04:55 +02:00
frtabu
4855b4102d going on with command parameter 2022-06-17 18:04:55 +02:00
frtabu
2dac45f1a3 commands with a parameter, going on.... 2022-06-17 18:04:55 +02:00
El Mghazli Yacine
e73acbc776 trying questions 2022-06-17 18:04:55 +02:00
frtabu
21d0fdb92a command with parameter going on 2022-06-17 18:04:55 +02:00
frtabu
68e2cb7800 command with param implementation in frontend, beginning 2022-06-17 18:04:55 +02:00
frtabu
de24e2da1e rontend Question Dialog 2022-06-17 18:04:55 +02:00
frtabu
bc4eb38eec Implementing commands with question like when modifying a channel model (ask which model) 2022-06-17 18:04:55 +02:00
frtabu
8600b7815d implemebtation of softmodem threads commands 2022-06-17 18:04:55 +02:00
frtabu
8bee5da854 Dialog enhancements in frontend 2022-06-17 18:04:55 +02:00
frtabu
40bfb7b07a fix log modifications in backend, allow Dialog resize and move 2022-06-17 18:04:55 +02:00
frtabu
46c1dd511a implement log options modif in backend 2022-06-17 18:04:55 +02:00
El Mghazli Yacine
d9cd942f3b window.location.port 2022-06-17 18:04:55 +02:00
frtabu
147fb74085 implementation of command modifying module parameters, step 1, decode json sent by fronthaul 2022-06-17 18:04:55 +02:00
El Mghazli Yacine
7546e614c7 boolean as string for setRow 2022-06-17 18:04:55 +02:00
El Mghazli Yacine
9a58bae112 http://' + window.location.hostname + ':8090' 2022-06-17 18:04:55 +02:00
El Mghazli Yacine
9e99113aa1 v1 2022-06-17 18:04:55 +02:00
El Mghazli Yacine
ea4d82bc31 better fields and buttons 2022-06-17 18:04:55 +02:00
El Mghazli Yacine
94efccba21 table disapears when no rows 2022-06-17 18:04:55 +02:00
frtabu
28c688b16f Modify status info , add redir log commands 2022-06-17 18:04:55 +02:00
El Mghazli Yacine
6d991456a9 set disabled if all row is not modifiable 2022-06-17 18:04:55 +02:00
El Mghazli Yacine
bb840b7f7e fixed cmd results tile now disapears 2022-06-17 18:04:55 +02:00
El Mghazli Yacine
9fecc570f8 fixed boolean enabled 2022-06-17 18:04:55 +02:00
El Mghazli Yacine
1a5da9a1aa fixed 2 posts 2022-06-17 18:04:55 +02:00
El Mghazli Yacine
adb13cd4d4 button and readonly 2022-06-17 18:04:55 +02:00
El Mghazli Yacine
9f8ebe86f4 fixed rawIndex 2022-06-17 18:04:55 +02:00
El Mghazli Yacine
46329a4f2d row.paramsFA.controls 2022-06-17 18:04:55 +02:00
El Mghazli Yacine
1f4dcc9fda IRow2 2022-06-17 18:04:55 +02:00
El Mghazli Yacine
7dda3dde08 setRow$ in API 2022-06-17 18:04:55 +02:00
El Mghazli Yacine
e108aaa7db setParam$ 2022-06-17 18:04:55 +02:00
El Mghazli Yacine
7da12ca861 setParam$ 2022-06-17 18:04:55 +02:00
El Mghazli Yacine
2b987e8e00 confirm button ok 2022-06-17 18:04:55 +02:00
El Mghazli Yacine
c2106e49d8 height 80% display 2022-06-17 18:04:55 +02:00
El Mghazli Yacine
0b473838fa table OK 2022-06-17 18:04:55 +02:00
frtabu
3e15c59e00 fix empty array format 2022-06-17 18:04:55 +02:00
frtabu
9aa6e61197 fix new IRespCmd json format in backend 2022-06-17 18:04:55 +02:00
frtabu
4c58063268 table response implementation fix (backend) 2022-06-17 18:04:55 +02:00
El Mghazli Yacine
25f1b46647 display fix 2022-06-17 18:04:55 +02:00
El Mghazli Yacine
68ae77a79e confirm dialog 2022-06-17 18:04:55 +02:00
El Mghazli Yacine
a4ad969553 dynamic table WIP 2022-06-17 18:04:55 +02:00
El Mghazli Yacine
415e9e8495 cleanup rowCtrl 2022-06-17 18:04:55 +02:00
El Mghazli Yacine
5a5d8670ea dynamic tables 2022-06-17 18:04:55 +02:00
frtabu
9e20b2464f Table Response implementation in backend 2022-06-17 18:04:55 +02:00
El Mghazli Yacine
ea5c125dbd confirm dialog before sending cmd 2022-06-17 18:04:55 +02:00
frtabu
66646cd17a display array fix 2022-06-17 18:04:54 +02:00
frtabu
81b0df6f1d more commands implementation in backend 2022-06-17 18:04:54 +02:00
El Mghazli Yacine
e9dadf1706 "prod": "ng serve --configuration=production" 2022-06-17 18:04:54 +02:00
El Mghazli Yacine
f9e45856fc table display 2022-06-17 18:04:54 +02:00
frtabu
0f9fcb3f0a fix json format in log response 2022-06-17 18:04:54 +02:00
frtabu
17af664374 test table reslt with log command 2022-06-17 18:04:54 +02:00
El Mghazli Yacine
da3bda8bbb tables and new API with IColumn and IRow 2022-06-17 18:04:54 +02:00
frtabu
a5d66d1f4a implementation of log command 2022-06-17 18:04:54 +02:00
frtabu
4b289a53df Adapt command response structure 2022-06-17 18:04:54 +02:00
El Mghazli Yacine
d5eb164a20 details 2022-06-17 18:04:54 +02:00
El Mghazli Yacine
fec25da5e7 ilogs 2022-06-17 18:04:54 +02:00
frtabu
4ec7d0a824 implement mechanism for simple string response 2022-06-17 18:04:54 +02:00
El Mghazli Yacine
a873c0080a openDialog 2022-06-17 18:04:54 +02:00
frtabu
06f905853a command processing implementation 2022-06-17 18:04:54 +02:00
frtabu
179b8f57b3 better variable type processing 2022-06-17 18:04:54 +02:00
frtabu
9b1e5db390 modify telnet server structure to support command execution from the webserver 2022-06-17 18:04:54 +02:00
frtabu
32e5f13002 json boolean fix 2022-06-17 18:04:54 +02:00
frtabu
4497800fe1 fix backend commands response body 2022-06-17 18:04:54 +02:00
frtabu
6846459ea5 backend implementation of variable modifications 2022-06-17 18:04:54 +02:00
frtabu
f7dafc4771 Add OPTIONS http method support, used before POST by frontend. 2022-06-17 18:04:54 +02:00
frtabu
ee11159d5d change endpoint registration mechanism 2022-06-17 18:04:54 +02:00
frtabu
c0a3b39688 More debug messages in backend 2022-06-17 18:04:54 +02:00
El Mghazli Yacine
679dcae557 fixed cmds display 2022-06-17 18:04:54 +02:00
El Mghazli Yacine
b14c4b2aa6 fixed API variables calls 2022-06-17 18:04:54 +02:00
El Mghazli Yacine
92c534dcaa APIv2.2 2022-06-17 18:04:54 +02:00
Raymond Knopp
07fb404e33 Adapt to new frontend interface 2022-06-17 18:04:54 +02:00
El Mghazli Yacine
a010b4d89f yacine s'adapte grace a sa flaxibilité legendaire 2022-06-17 18:04:54 +02:00
El Mghazli Yacine
b340427363 APIv2.2 2022-06-17 18:04:54 +02:00
frtabu
654a2b7f63 config module fixes and websrv modif 2022-06-17 18:04:54 +02:00
El Mghazli Yacine
b0cb9b8615 APIv2.1 2022-06-17 18:04:54 +02:00
El Mghazli Yacine
9bb75d82cf webserv APIv2 (frontend part) 2022-06-17 18:04:54 +02:00
El Mghazli Yacine
535239a5ae working with first API version 2022-06-17 18:04:54 +02:00
frtabu
89b2a966ba fix config file generation to sip command line only options 2022-06-17 18:04:54 +02:00
frtabu
29e2a542dd config module enhancement,step 2 2022-06-17 18:04:54 +02:00
frtabu
98a736c9d7 config module enhancement, next step 2022-06-17 18:04:54 +02:00
frtabu
8404af8d18 config module enhancement (dump of running config): start implem. 2022-06-17 18:04:54 +02:00
Raymond Knopp
7572d8cb46 add websrv as optional lib and frontend as independant target in oai build 2022-06-17 18:04:54 +02:00
frtabu
db43c3e12e websrv backend implementation next step 2022-06-17 18:04:54 +02:00
frtabu
ea066dc0dc json format sync with frontend 2022-06-17 18:04:54 +02:00
El Mghazli Yacine
c0037b1f98 backend env 2022-06-17 18:04:54 +02:00
frtabu
29aa6407be Tested init with front end 2022-06-17 18:04:54 +02:00
El Mghazli Yacine
f6d4c77e75 api first test 2022-06-17 18:04:54 +02:00
El Mghazli Yacine
c0e019efbd display ok 2022-06-17 18:04:54 +02:00
frtabu
ded656208d Add https support and front-end url support 2022-06-17 18:04:54 +02:00
El Mghazli Yacine
9c36a8ebc2 rudiments ok 2022-06-17 18:04:54 +02:00
El Mghazli Yacine
1dda299dc3 ng build OK 2022-06-17 18:04:54 +02:00
frtabu
c8d1b99e42 go on websrv implem 2022-06-17 18:04:54 +02:00
frtabu
b274f3c8d3 Initial implem web interface for telnet server 2022-06-17 18:04:54 +02:00
El Mghazli Yacine
1d7775875e updated tslib 2022-06-17 18:04:54 +02:00
El Mghazli Yacine
3a560604ec initial commit 2022-06-17 18:04:54 +02:00
El Mghazli Yacine
f677cc34b3 cleanup 2022-06-17 18:04:54 +02:00
El Mghazli Yacine
cdab201890 updated angular successfully 2022-06-17 18:04:54 +02:00
El Mghazli Yacine
20186aec26 ng update 2022-06-17 18:04:54 +02:00
El Mghazli Yacine
79fc8623e3 ng update 2022-06-17 18:04:54 +02:00
El Mghazli Yacine
c104b12302 ngx initial commit before upgrade 2022-06-17 18:04:54 +02:00
106 changed files with 37307 additions and 395 deletions

31
.vscode/launch.json vendored
View File

@@ -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
}
]
}
]
}

View File

@@ -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)

View File

@@ -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=()

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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");

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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")

View File

@@ -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},
};

View File

@@ -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);

View File

@@ -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;

View File

@@ -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 */

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View 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)

View 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)

View 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)

View 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.

View 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

View 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
View 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

View File

@@ -0,0 +1 @@
# SoftModemNgx

View 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"
]
}
}
}
}
}
}

View 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
}
}));
}
};

View 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
}
}));
}
};

View 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));
});
});

View 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();
}
}

View 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"]
}
}

File diff suppressed because it is too large Load Diff

View 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"
}
}
}

View 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);
}

View 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);
}

View 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 { }

View File

@@ -0,0 +1,11 @@
.mat-tab-group {
margin-bottom: 10px;
}
.mat-ink-bar{
height: 8px !important;
}
.mat-tab-label-active
{
color: #000000 !important;
}

View 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>

View 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' : '';
}
}

View 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 { }

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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!)));
}
}

View File

@@ -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>

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -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();
}
}
}

View File

@@ -0,0 +1,5 @@
<div id="logDisplay">
<ul>
<li *ngFor="let msg of messages">{{JSON.stringify(msg)}}</li>
</ul>
</div>

View File

@@ -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();
});
});

View File

@@ -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)
)
}
}

View File

@@ -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>

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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 }} &nbsp; &nbsp; </label>
</div>
</mat-card-content>
</mat-card>
</mat-grid-tile>
<!-- left full heigth, narrow width area, for global scope controls -->
<mat-grid-tile [colspan]="1" [rowspan]="8">
<mat-card class="dashboard-card">
<mat-card-header>
<mat-card-title>Scope control</mat-card-title>
</mat-card-header>
<mat-card-content class="dashboard-card-content">
<button mat-mini-fab color={{startstop_color}} (click)="startorstop()">{{startstop}}</button>
<br><br>
<div class="scope-slider-div">
<label id="scope-slider-refrate-labelid" class="scope-slider-label">refresh rate</label>
<label class="scope-slider-value-label"> {{rfrate}} </label>
</div>
<mat-slider class="scope-slider" min=".1" max="10" step="0.1" [value]="rfrate"
aria-labelledby="scope-slider-refrate-labelid" vertical="true" thumbLabel="true"
[(ngModel)]="rfrate" (change)="OnRefrateChange()" >
</mat-slider>
<br><br>
<mat-list-item class="sigselect" role="listitem">
<mat-form-field class="select-form">
<mat-select placeholder="sig select" name="SigSelect" class="SigSelect" [value]="selected_sig.target_id" (selectionChange)="SigChanged($event.value)" >
<mat-option *ngFor="let targetid of sig_list" [value]="targetid.target_id" >
{{targetid.target_id}}
</mat-option>
</mat-select>
</mat-form-field>
</mat-list-item>
<br><br>
<mat-slide-toggle [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>

View File

@@ -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);
}
}

View 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);
}
}

View 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 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"
}
}

View File

@@ -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);
}
}

View File

@@ -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
}
}

View 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
}
}

View 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"
}
}

View File

@@ -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';

View File

@@ -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 },
];

View File

@@ -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();
}
}),
);
}
}

View 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));
}
}

View File

@@ -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();
}
)
}
};

View File

@@ -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);
}
}

View File

@@ -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!' });
// }
}

View 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 + '/'
};

View 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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View 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>

View 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));

View 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
*/

View 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;
}

View 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);

View 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"]
}

View 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
}
}

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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")

View 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];
}

File diff suppressed because it is too large Load Diff

View 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;
}

View 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;
}

View File

@@ -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;
}

View File

@@ -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");
}
}

View File

@@ -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);

View File

@@ -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"};

View File

@@ -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