ce1a2862e01decc69d0662dfeea398c9c0df70da
[o-du/phy.git] / fhi_lib / test / master.py
1 #!/usr/bin/python\r
2 #######################################################################\r
3 #\r
4 # <COPYRIGHT_TAG>\r
5 #\r
6 #######################################################################\r
7 \r
8 """This script run test cases with O-DU and O-RU\r
9 """\r
10 import logging\r
11 import sys\r
12 import argparse\r
13 import re\r
14 import subprocess\r
15 import os\r
16 import shutil\r
17 from itertools import dropwhile\r
18 from datetime import datetime\r
19 import json\r
20 \r
21 nNumRbsPerSymF1 = [\r
22     #  5MHz    10MHz   15MHz   20 MHz  25 MHz  30 MHz  40 MHz  50MHz   60 MHz  70 MHz  80 MHz   90 MHz  100 MHz\r
23         [25,    52,     79,     106,    133,    160,    216,    270,    0,         0,      0,      0,      0],         # Numerology 0 (15KHz)\r
24         [11,    24,     38,     51,     65,     78,     106,    133,    162,       0,    217,    245,    273],         # Numerology 1 (30KHz)\r
25         [0,     11,     18,     24,     31,     38,     51,     65,     79,        0,    107,    121,    135]          # Numerology 2 (60KHz)\r
26 ]\r
27 \r
28 nNumRbsPerSymF2 = [\r
29     # 50Mhz  100MHz  200MHz   400MHz\r
30     [66,    132,    264,     0],       # Numerology 2 (60KHz)\r
31     [32,    66,     132,     264]      # Numerology 3 (120KHz)\r
32 ]\r
33 \r
34 \r
35 nRChBwOptions_keys = ['5','10','15','20', '25', '30', '40', '50', '60','70', '80', '90', '100', '200', '400']\r
36 nRChBwOptions_values = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14]\r
37 nRChBwOptions = dict(zip(nRChBwOptions_keys, nRChBwOptions_values))\r
38 \r
39 nRChBwOptions_keys_mu2and3 = ['50', '100', '200', '400']\r
40 nRChBwOptions_values_mu2and3 = [0,1,2,3]\r
41 nRChBwOptions_mu2and3 = dict(zip(nRChBwOptions_keys_mu2and3, nRChBwOptions_values_mu2and3))\r
42 \r
43 # table of all test cases\r
44 #                 (cat, mu, bw, test case)\r
45 all_test_cases =   [(0,   0,  5,   0),\r
46                   (0,   0,  10,  0),\r
47                   (0,   0,  20,  0),\r
48                   (0,   1,  100, 0),\r
49                   (0,   3,  100, 0),\r
50                   (1,   1,  100, 0)]\r
51 #Cat B\r
52 """ all_test_cases  =  [(1,   1,  100, 0),\r
53                     (1,   1,  100, 1),\r
54                     (1,   1,  100, 101),\r
55                     (1,   1,  100, 102),\r
56                     (1,   1,  100, 103),\r
57                     (1,   1,  100, 104),\r
58                     (1,   1,  100, 105),\r
59                     #(1,   1,  100, 106), 25G not enough\r
60                     (1,   1,  100, 107),\r
61                     (1,   1,  100, 108),\r
62                     #(1,   1,  100, 109), 25G not enough\r
63                     (1,   1,  100, 201),\r
64                     #(1,   1,  100, 202), 25G not enough\r
65                     (1,   1,  100, 203),\r
66                     (1,   1,  100, 204),\r
67                     (1,   1,  100, 205),\r
68                     (1,   1,  100, 206),\r
69                     (1,   1,  100, 211),\r
70                     #(1,   1,  100, 212), 25G not enough\r
71                     (1,   1,  100, 213),\r
72                     (1,   1,  100, 214),\r
73                     (1,   1,  100, 215),\r
74                     (1,   1,  100, 216)\r
75 ]\r
76  """\r
77 dic_dir = dict({0:'DL', 1:'UL'})\r
78 dic_xu  = dict({0:'o-du', 1:'o-ru'})\r
79 \r
80 def init_logger(console_level, logfile_level):\r
81     """Initializes console and logfile logger with given logging levels"""\r
82     # File logger\r
83     logging.basicConfig(filename="runtests.log",\r
84                         filemode='w',\r
85                         format="%(asctime)s: %(levelname)s: %(message)s",\r
86                         level=logfile_level)\r
87     # Console logger\r
88     logger = logging.getLogger()\r
89     handler = logging.StreamHandler()\r
90     handler.setLevel(console_level)\r
91     formatter = logging.Formatter("%(levelname)s: %(message)s")\r
92     handler.setFormatter(formatter)\r
93     logger.addHandler(handler)\r
94 \r
95 def parse_args(args):\r
96     """Configures parser and parses command line configuration"""\r
97     # Parser configuration\r
98     parser = argparse.ArgumentParser(description="Run test cases: category numerology bandwidth test_num")\r
99 \r
100     parser.add_argument("--cat", type=int, default=0, help="Category: 0 (A) or 1 (B)", metavar="cat", dest="category")\r
101     parser.add_argument("--mu", type=int, default=0, help="numerology [0,1,3]", metavar="num", dest="numerology")\r
102     parser.add_argument("--bw",  type=int, default=20, help="bandwidth [5,10,20,100]", metavar="bw", dest="bandwidth")\r
103     parser.add_argument("--testcase", type=int, default=0, help="test case number", metavar="testcase", dest="testcase")\r
104 \r
105     # Parse arguments\r
106     options = parser.parse_args(args)\r
107     #parser.print_help()\r
108     logging.debug("Options: category=%d num=%d bw=%d testcase=%d",\r
109                   options.category, options.numerology, options.bandwidth, options.testcase)\r
110     return options\r
111 \r
112 def is_comment(s):\r
113     """ function to check if a line\r
114          starts with some character.\r
115          Here # for comment\r
116     """\r
117     # return true if a line starts with #\r
118     return s.startswith('#')\r
119 \r
120 class GetOutOfLoops( Exception ):\r
121     pass\r
122 \r
123 def compare_resuts(cat, mu, bw, tcase, xran_path, test_cfg, direction):\r
124     res = 0\r
125 \r
126     if mu < 3:\r
127         nDlRB = nNumRbsPerSymF1[mu][nRChBwOptions.get(str(nDLBandwidth))]\r
128         nUlRB = nNumRbsPerSymF1[mu][nRChBwOptions.get(str(nULBandwidth))]\r
129     elif (mu >=2) & (mu <= 3):\r
130         nDlRB = nNumRbsPerSymF2[mu - 2][nRChBwOptions_mu2and3.get(str(nDLBandwidth))]\r
131         nUlRB = nNumRbsPerSymF2[mu - 2][nRChBwOptions_mu2and3.get(str(nULBandwidth))]\r
132         print(nDlRB, nUlRB)\r
133     else:\r
134         print("Incorrect arguments\n")\r
135         res = -1\r
136         return res\r
137 \r
138     if 'compression' in globals():\r
139         comp = compression\r
140     else:\r
141         comp = 0\r
142 \r
143     print("compare results: {} [compression {}]\n".format(dic_dir.get(direction), comp))\r
144 \r
145     #if cat == 1:\r
146     #    print("WARNING: Skip checking IQs and BF Weights for CAT B for now\n");\r
147     #    return res\r
148 \r
149     #get slot config\r
150     if nFrameDuplexType == 1:\r
151         SlotConfig = []\r
152         for i in range(nTddPeriod):\r
153             if i == 0:\r
154                 SlotConfig.insert(i, sSlotConfig0)\r
155             elif i == 1:\r
156                 SlotConfig.insert(i, sSlotConfig1)\r
157             elif i == 2:\r
158                 SlotConfig.insert(i, sSlotConfig2)\r
159             elif i == 3:\r
160                 SlotConfig.insert(i, sSlotConfig3)\r
161             elif i == 4:\r
162                 SlotConfig.insert(i, sSlotConfig4)\r
163             elif i == 5:\r
164                 SlotConfig.insert(i, sSlotConfig5)\r
165             elif i == 6:\r
166                 SlotConfig.insert(i, sSlotConfig6)\r
167             elif i == 7:\r
168                 SlotConfig.insert(i, sSlotConfig7)\r
169             elif i == 8:\r
170                 SlotConfig.insert(i, sSlotConfig8)\r
171             elif i == 9:\r
172                 SlotConfig.insert(i, sSlotConfig9)\r
173             else :\r
174                 raise Exception('i should not exceed nTddPeriod %d. The value of i was: {}'.format(nTddPeriod, i))\r
175         #print(SlotConfig, type(sSlotConfig0))\r
176     try:\r
177 \r
178         if (direction == 1) & (cat == 1): #UL\r
179             flowId = ccNum*antNumUL\r
180         else:\r
181             flowId = ccNum*antNum\r
182 \r
183         for i in range(0, flowId):\r
184             #read ref and test files\r
185             tst = []\r
186             ref = []\r
187             if direction == 0:\r
188                 # DL\r
189                 nRB = nDlRB\r
190                 file_tst = xran_path+"/app/logs/"+"o-ru-rx_log_ant"+str(i)+".txt"\r
191                 file_ref = xran_path+"/app/logs/"+"o-du-play_ant"+str(i)+".txt"\r
192             elif direction == 1:\r
193                 # UL\r
194                 nRB = nUlRB\r
195                 file_tst = xran_path+"/app/logs/"+"o-du-rx_log_ant"+str(i)+".txt"\r
196                 file_ref = xran_path+"/app/logs/"+"o-ru-play_ant"+str(i)+".txt"\r
197             else:\r
198                 raise Exception('Direction is not supported %d'.format(direction))\r
199 \r
200             print("test result   :", file_tst)\r
201             print("test reference:", file_ref)\r
202             if os.path.exists(file_tst):\r
203                 try:\r
204                     file_tst = open(file_tst, 'r')\r
205                 except OSError:\r
206                     print ("Could not open/read file:", file_tst)\r
207                     sys.exit()\r
208             else:\r
209                 print(file_tst, "doesn't exist")\r
210                 res = -1\r
211                 return res\r
212             if os.path.exists(file_ref):\r
213                 try:\r
214                     file_ref = open(file_ref, 'r')\r
215                 except OSError:\r
216                     print ("Could not open/read file:", file_ref)\r
217                     sys.exit()\r
218             else:\r
219                 print(file_tst, "doesn't exist")\r
220                 res = -1\r
221                 return res\r
222 \r
223             tst = file_tst.readlines()\r
224             ref = file_ref.readlines()\r
225 \r
226             print(len(tst))\r
227             print(len(ref))\r
228 \r
229             file_tst.close();\r
230             file_ref.close();\r
231 \r
232             print(numSlots)\r
233 \r
234             for slot_idx in range(0, numSlots):\r
235                 for sym_idx in range(0, 14):\r
236                     if nFrameDuplexType==1:\r
237                         #skip sym if TDD\r
238                         if direction == 0:\r
239                             #DL\r
240                             sym_dir = SlotConfig[slot_idx%nTddPeriod][sym_idx]\r
241                             if(sym_dir != 0):\r
242                                 continue\r
243                         elif direction == 1:\r
244                             #UL\r
245                             sym_dir = SlotConfig[slot_idx%nTddPeriod][sym_idx]\r
246                             if(sym_dir != 1):\r
247                                 continue\r
248 \r
249                     #print("Check:","[",i,"]", slot_idx, sym_idx)\r
250                     for line_idx in range(0, nRB*12):\r
251                         offset = (slot_idx*nRB*12*14) + sym_idx*nRB*12 + line_idx\r
252                         line_tst = tst[offset].rstrip()\r
253                         line_ref = ref[offset].rstrip()\r
254                         if comp == 1:\r
255                             # discard LSB bits as BFP compression is not Bit Exact\r
256                             tst_i_value = int(line_tst.split(" ")[0]) & 0xFF80\r
257                             tst_q_value = int(line_tst.split(" ")[1]) & 0xFF80\r
258                             ref_i_value = int(line_ref.split(" ")[0]) & 0xFF80\r
259                             ref_q_value = int(line_ref.split(" ")[1]) & 0xFF80\r
260 \r
261                             print("check:","ant:[",i,"]:",offset, slot_idx, sym_idx, line_idx,":","tst: ", tst_i_value, " ", tst_q_value, " " , "ref: ", ref_i_value, " ", ref_q_value, " ")\r
262                             if (tst_i_value != ref_i_value) or  (tst_q_value != ref_q_value) :\r
263                                 print("FAIL:","ant:[",i,"]:",offset, slot_idx, sym_idx, line_idx,":","tst: ", tst_i_value, " ", tst_q_value, " " , "ref: ", ref_i_value, " ", ref_q_value, " ")\r
264                                 res = -1\r
265                                 raise GetOutOfLoops\r
266                         else:\r
267                             #if line_idx == 0:\r
268                                 #print("Check:", offset,"[",i,"]", slot_idx, sym_idx,":",line_tst, line_ref)\r
269                             if line_ref != line_tst:\r
270                                 print("FAIL:","ant:[",i,"]:",offset, slot_idx, sym_idx, line_idx,":","tst:", line_tst, "ref:", line_ref)\r
271                                 res = -1\r
272                                 raise GetOutOfLoops\r
273     except GetOutOfLoops:\r
274         pass\r
275 \r
276     return res\r
277 \r
278 def parse_dat_file(cat, mu, bw, tcase, xran_path, test_cfg):\r
279     #parse config files\r
280     logging.info("parse config files %s\n", test_cfg[0])\r
281     lineList = list()\r
282     sep = '#'\r
283     with open(test_cfg[0],'r') as fh:\r
284         for curline in dropwhile(is_comment, fh):\r
285             my_line = curline.rstrip().split(sep, 1)[0].strip()\r
286             if my_line:\r
287                 lineList.append(my_line)\r
288     global_env = {}\r
289     local_env = {}\r
290 \r
291     for line in lineList:\r
292         exe_line = line.replace(":", ",")\r
293         if exe_line.find("/") > 0 :\r
294             exe_line = exe_line.replace('./', "'")\r
295             exe_line = exe_line+"'"\r
296 \r
297         code = compile(str(exe_line), '<string>', 'exec')\r
298         exec (code, global_env, local_env)\r
299 \r
300     for k, v in local_env.items():\r
301         globals()[k] = v\r
302         print(k, v)\r
303 \r
304     return local_env\r
305 \r
306 def make_copy_mlog(cat, mu, bw, tcase, xran_path):\r
307     res = 0\r
308 \r
309     src_bin = xran_path+"/app/mlog-o-du-c0.bin"\r
310     src_csv = xran_path+"/app/mlog-o-du-hist.csv"\r
311     dst_bin = xran_path+"/app/mlog-o-du-c0-cat"+str(cat)+"-mu"+str(mu)+"-bw"+str(bw)+"-tcase"+str(tcase)+".bin"\r
312     dst_csv = xran_path+"/app/mlog-o-du-hist-cat"+str(cat)+"-mu"+str(mu)+"-bw"+str(bw)+"-tcase"+str(tcase)+".csv"\r
313 \r
314     try:\r
315         d_bin  = shutil.copyfile(src_bin, dst_bin)\r
316         d_csv  = shutil.copyfile(src_csv, dst_csv)\r
317     except IOError:\r
318         logging.info("MLog is not present\n")\r
319         res = 1\r
320     else:\r
321         logging.info("Mlog was copied\n")\r
322 \r
323     print("Destination path:", d_bin)\r
324     print("Destination path:", d_csv)\r
325 \r
326     d_bin  = shutil.copyfile(src_bin, dst_bin)\r
327     d_csv  = shutil.copyfile(src_csv, dst_csv)\r
328 \r
329     #print("After copying file:")\r
330     #print(os.listdir(xran_path+"/app/"))\r
331 \r
332     #print("Destination path:", d_bin)\r
333     #print("Destination path:", d_csv)\r
334 \r
335     src_bin = xran_path+"/app/mlog-o-ru-c0.bin"\r
336     src_csv = xran_path+"/app/mlog-o-ru-hist.csv"\r
337     dst_bin = xran_path+"/app/mlog-o-ru-c0-cat"+str(cat)+"-mu"+str(mu)+"-bw"+str(bw)+"-tcase"+str(tcase)+".bin"\r
338     dst_csv = xran_path+"/app/mlog-o-ru-hist-cat"+str(cat)+"-mu"+str(mu)+"-bw"+str(bw)+"-tcase"+str(tcase)+".csv"\r
339 \r
340     d_bin  = shutil.copyfile(src_bin, dst_bin)\r
341     d_csv  = shutil.copyfile(src_csv, dst_csv)\r
342 \r
343     #print("After copying file:")\r
344     #print(os.listdir(xran_path+"/app/"))\r
345 \r
346     #print("Destination path:", d_bin)\r
347     #print("Destination path:", d_csv)\r
348 \r
349     try:\r
350         d_bin  = shutil.copyfile(src_bin, dst_bin)\r
351         d_csv  = shutil.copyfile(src_csv, dst_csv)\r
352     except IOError:\r
353         logging.info("MLog is not present\n")\r
354         res = 1\r
355     else:\r
356         logging.info("Mlog was copied\n")\r
357 \r
358     #print("After copying file:")\r
359     #print(os.listdir(xran_path+"/app/"))\r
360 \r
361     #print("Destination path:", d_bin)\r
362     #print("Destination path:", d_csv)\r
363 \r
364     return res\r
365 \r
366 \r
367 def run_tcase(cat, mu, bw, tcase, xran_path):\r
368     if cat == 1:\r
369         test_config = xran_path+"/app/usecase/cat_b/mu{0:d}_{1:d}mhz".format(mu, bw)\r
370     elif cat == 0 :\r
371         test_config = xran_path+"/app/usecase/mu{0:d}_{1:d}mhz".format(mu, bw)\r
372     else:\r
373         print("Incorrect arguments\n")\r
374 \r
375     if(tcase > 0) :\r
376         test_config = test_config+"/"+str(tcase)\r
377 \r
378     app = xran_path+"/app/build/sample-app"\r
379 \r
380     logging.debug("run: %s %s", app, test_config)\r
381     logging.debug("Started script: master.py, XRAN path %s", xran_path)\r
382 \r
383     test_cfg = []\r
384     #TODO: add detection of ETH ports\r
385     eth_cp_dev = ["0000:22:02.1", "0000:22:0a.1"]\r
386     eth_up_dev = ["0000:22:02.0", "0000:22:0a.0"]\r
387 \r
388     test_cfg.append(test_config+"/config_file_o_du.dat")\r
389     test_cfg.append(test_config+"/config_file_o_ru.dat")\r
390 \r
391     wd = os.getcwd()\r
392     os.chdir(xran_path+"/app/")\r
393 \r
394     processes     = []\r
395     logfile_xu    = []\r
396     log_file_name = []\r
397 \r
398     os.system('rm -rf ./logs')\r
399 \r
400     for i in range(2):\r
401         log_file_name.append("sampleapp_log_{}_cat_{}_mu{}_{}mhz_tst_{}.log".format(dic_xu.get(i),cat, mu, bw, tcase))\r
402         with open(log_file_name[i], "w") as f:\r
403             #, stdout=f, stderr=f\r
404             p = subprocess.Popen([app, test_cfg[i], eth_up_dev[i], eth_cp_dev[i]], stdout=f, stderr=f)\r
405             logfile_xu.insert(i, f)\r
406         processes.append((p, logfile_xu[i]))\r
407 \r
408     logging.info("Running O-DU and O-RU see output in: %s %s\n", logfile_xu[0].name, logfile_xu[1].name)\r
409     for p, f in processes:\r
410         p.wait()\r
411         p.communicate()[0]\r
412         if p.returncode != 0:\r
413             print("Application {} failed p.returncode:{}".format(dic_xu.get(i), p.returncode))\r
414             print("FAIL")\r
415             logging.info("FAIL\n")\r
416             logging.shutdown()\r
417             sys.exit(p.returncode)\r
418 \r
419         f.close()\r
420 \r
421     logging.info("O-DU and O-RU are done\n")\r
422 \r
423     make_copy_mlog(cat, mu, bw, tcase, xran_path)\r
424 \r
425     usecase_cfg = parse_dat_file(cat, mu, bw, tcase, xran_path, test_cfg)\r
426 \r
427     res = compare_resuts(cat, mu, bw, tcase, xran_path, test_cfg, 0)\r
428     if res != 0:\r
429         os.chdir(wd)\r
430         print("FAIL")\r
431         return res\r
432 \r
433     res = compare_resuts(cat, mu, bw, tcase, xran_path, test_cfg, 1)\r
434     if res != 0:\r
435         os.chdir(wd)\r
436         print("FAIL")\r
437         return res\r
438 \r
439     os.chdir(wd)\r
440     print("PASS")\r
441     return res\r
442 \r
443 def main():\r
444     test_results = []\r
445     test_executed_total = 0\r
446     run_total = 0\r
447     cat   = 0\r
448     mu    = 0\r
449     bw    = 0\r
450     tcase = 0\r
451     """Processes input files to produce IACA files"""\r
452     # Find path to XRAN\r
453     xran_path = os.getenv("XRAN_DIR")\r
454 \r
455     # Set up logging with given level (DEBUG, INFO, ERROR) for console end logfile\r
456     init_logger(logging.INFO, logging.DEBUG)\r
457     logging.info("Started script: master.py, XRAN path %s", xran_path)\r
458 \r
459     # Parse input arguments\r
460     if len(sys.argv) == 1 :\r
461         run_total = len(all_test_cases)\r
462         print(run_total)\r
463         print("Run All test cases {}\n".format(run_total))\r
464     else:\r
465         options = parse_args(sys.argv[1:])\r
466         cat     = options.category\r
467         mu      = options.numerology\r
468         bw      = options.bandwidth\r
469         tcase   = options.testcase\r
470 \r
471 \r
472     if (run_total):\r
473         for test_run_ix in range(0, run_total):\r
474             cat     = all_test_cases[test_run_ix][0]\r
475             mu      = all_test_cases[test_run_ix][1]\r
476             bw      = all_test_cases[test_run_ix][2]\r
477             tcase   = all_test_cases[test_run_ix][3]\r
478 \r
479             res = run_tcase(cat, mu, bw, tcase, xran_path)\r
480             if (res != 0):\r
481                 test_results.append((cat, mu, bw, tcase,'FAIL'))\r
482                 break;\r
483 \r
484             test_results.append((cat, mu, bw, tcase,'PASS'))\r
485     else:\r
486         res = run_tcase(cat, mu, bw, tcase, xran_path)\r
487         if (res != 0):\r
488             test_results.append((cat, mu, bw, tcase,'FAIL'))\r
489         test_results.append((cat, mu, bw, tcase,'PASS'))\r
490 \r
491     with open('testresult.txt', 'w') as reshandle:\r
492         json.dump(test_results, reshandle)\r
493 \r
494     return res\r
495 \r
496 if __name__ == '__main__':\r
497     START_TIME = datetime.now()\r
498     res = main()\r
499     END_TIME = datetime.now()\r
500     logging.debug("Start time: %s, end time: %s", START_TIME, END_TIME)\r
501     logging.info("Execution time: %s", END_TIME - START_TIME)\r
502     logging.shutdown()\r
503     sys.exit(res)\r