wiliot-certificate 4.4.3__py3-none-any.whl → 4.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (297) hide show
  1. {brg_certificate → certificate}/ag/wlt_cmd_if.html +10 -4
  2. {brg_certificate → certificate}/ag/wlt_types_ag.py +1878 -519
  3. certificate/cert_common.py +1488 -0
  4. certificate/cert_config.py +480 -0
  5. {brg_certificate → certificate}/cert_data_sim.py +134 -46
  6. {brg_certificate → certificate}/cert_defines.py +129 -128
  7. {brg_certificate → certificate}/cert_gw_sim.py +183 -53
  8. {brg_certificate → certificate}/cert_mqtt.py +179 -64
  9. {brg_certificate → certificate}/cert_prints.py +35 -33
  10. {brg_certificate → certificate}/cert_protobuf.py +15 -6
  11. {brg_certificate → certificate}/cert_results.py +240 -64
  12. certificate/cert_utils.py +634 -0
  13. certificate/certificate.py +205 -0
  14. certificate/certificate_cli.py +76 -0
  15. certificate/certificate_eth_test_list.txt +76 -0
  16. certificate/certificate_sanity_test_list.txt +66 -0
  17. certificate/certificate_test_list.txt +76 -0
  18. {brg_certificate → certificate}/tests/calibration/interval_test/interval_test.json +3 -2
  19. {brg_certificate → certificate}/tests/calibration/interval_test/interval_test.py +7 -6
  20. certificate/tests/calibration/output_power_test/output_power_test.json +23 -0
  21. certificate/tests/calibration/output_power_test/output_power_test.py +39 -0
  22. {brg_certificate → certificate}/tests/calibration/pattern_test/pattern_test.json +2 -1
  23. {brg_certificate → certificate}/tests/calibration/pattern_test/pattern_test.py +20 -15
  24. certificate/tests/cloud_connectivity/acl_ext_adv_test/acl_ext_adv_test.json +15 -0
  25. certificate/tests/cloud_connectivity/acl_ext_adv_test/acl_ext_adv_test.py +140 -0
  26. certificate/tests/cloud_connectivity/acl_test/acl_test.json +15 -0
  27. certificate/tests/cloud_connectivity/acl_test/acl_test.py +96 -0
  28. certificate/tests/cloud_connectivity/brg_ota_test/brg_ota_test.json +19 -0
  29. certificate/tests/cloud_connectivity/brg_ota_test/brg_ota_test.py +41 -0
  30. certificate/tests/cloud_connectivity/channel_scan_behaviour_test/channel_scan_behaviour_test.json +19 -0
  31. certificate/tests/cloud_connectivity/channel_scan_behaviour_test/channel_scan_behaviour_test.py +215 -0
  32. certificate/tests/cloud_connectivity/connection_test/connection_test.json +18 -0
  33. certificate/tests/cloud_connectivity/connection_test/connection_test.py +67 -0
  34. certificate/tests/cloud_connectivity/deduplication_test/deduplication_test.json +15 -0
  35. certificate/tests/cloud_connectivity/deduplication_test/deduplication_test.py +80 -0
  36. certificate/tests/cloud_connectivity/downlink_test/downlink_test.json +21 -0
  37. certificate/tests/cloud_connectivity/downlink_test/downlink_test.py +201 -0
  38. certificate/tests/cloud_connectivity/ext_adv_stress_test/ext_adv_stress_test.json +17 -0
  39. certificate/tests/cloud_connectivity/ext_adv_stress_test/ext_adv_stress_test.py +104 -0
  40. certificate/tests/cloud_connectivity/reboot_test/reboot_test.json +18 -0
  41. certificate/tests/cloud_connectivity/reboot_test/reboot_test.py +59 -0
  42. certificate/tests/cloud_connectivity/registration_test/registration_test.json +20 -0
  43. certificate/tests/cloud_connectivity/registration_test/registration_test.py +384 -0
  44. certificate/tests/cloud_connectivity/registration_test/registration_test_cli.py +90 -0
  45. certificate/tests/cloud_connectivity/stress_test/stress_test.json +17 -0
  46. certificate/tests/cloud_connectivity/stress_test/stress_test.py +101 -0
  47. certificate/tests/cloud_connectivity/uplink_ext_adv_test/uplink_ext_adv_test.json +25 -0
  48. certificate/tests/cloud_connectivity/uplink_ext_adv_test/uplink_ext_adv_test.py +92 -0
  49. certificate/tests/cloud_connectivity/uplink_test/uplink_test.json +20 -0
  50. certificate/tests/cloud_connectivity/uplink_test/uplink_test.py +169 -0
  51. {brg_certificate → certificate}/tests/datapath/aging_test/aging_test.json +2 -1
  52. certificate/tests/datapath/aging_test/aging_test.py +142 -0
  53. certificate/tests/datapath/event_ble5_test/event_ble5_test.json +17 -0
  54. certificate/tests/datapath/event_ble5_test/event_ble5_test.py +89 -0
  55. certificate/tests/datapath/event_test/event_test.json +17 -0
  56. certificate/tests/datapath/event_test/event_test.py +80 -0
  57. {brg_certificate → certificate}/tests/datapath/num_of_tags_test/num_of_tags_test.json +4 -3
  58. {brg_certificate → certificate}/tests/datapath/num_of_tags_test/num_of_tags_test.py +19 -13
  59. certificate/tests/datapath/output_power_test/output_power_test.json +23 -0
  60. {brg_certificate → certificate}/tests/datapath/output_power_test/output_power_test.py +17 -6
  61. {brg_certificate → certificate}/tests/datapath/pacer_interval_ble5_test/pacer_interval_ble5_test.json +2 -1
  62. {brg_certificate → certificate}/tests/datapath/pacer_interval_ble5_test/pacer_interval_ble5_test.py +13 -11
  63. {brg_certificate → certificate}/tests/datapath/pacer_interval_test/pacer_interval_test.json +2 -1
  64. {brg_certificate → certificate}/tests/datapath/pacer_interval_test/pacer_interval_test.py +9 -7
  65. {brg_certificate → certificate}/tests/datapath/pattern_test/pattern_test.json +3 -2
  66. {brg_certificate → certificate}/tests/datapath/pattern_test/pattern_test.py +18 -6
  67. certificate/tests/datapath/pkt_filter_ble5_chl21_test/pkt_filter_ble5_chl21_test.json +20 -0
  68. certificate/tests/datapath/pkt_filter_ble5_chl21_test/pkt_filter_ble5_chl21_test.py +61 -0
  69. {brg_certificate → certificate}/tests/datapath/pkt_filter_ble5_test/pkt_filter_ble5_test.json +2 -1
  70. {brg_certificate → certificate}/tests/datapath/pkt_filter_ble5_test/pkt_filter_ble5_test.py +15 -14
  71. certificate/tests/datapath/pkt_filter_brg2gw_ext_adv_test/pkt_filter_brg2gw_ext_adv_test.json +19 -0
  72. certificate/tests/datapath/pkt_filter_brg2gw_ext_adv_test/pkt_filter_brg2gw_ext_adv_test.py +85 -0
  73. {brg_certificate → certificate}/tests/datapath/pkt_filter_gen3_test/pkt_filter_gen3_test.json +2 -1
  74. {brg_certificate → certificate}/tests/datapath/pkt_filter_gen3_test/pkt_filter_gen3_test.py +10 -9
  75. {brg_certificate → certificate}/tests/datapath/pkt_filter_test/pkt_filter_test.json +2 -1
  76. {brg_certificate → certificate}/tests/datapath/pkt_filter_test/pkt_filter_test.py +10 -9
  77. {brg_certificate → certificate}/tests/datapath/rssi_threshold_test/rssi_threshold_test.json +3 -2
  78. {brg_certificate → certificate}/tests/datapath/rssi_threshold_test/rssi_threshold_test.py +9 -8
  79. brg_certificate/tests/datapath/output_power_test/output_power_test.json → certificate/tests/datapath/rx_channel_hopping_test/rx_channel_hopping_test.json +6 -4
  80. certificate/tests/datapath/rx_channel_hopping_test/rx_channel_hopping_test.py +77 -0
  81. {brg_certificate → certificate}/tests/datapath/rx_channel_test/rx_channel_test.json +3 -2
  82. {brg_certificate → certificate}/tests/datapath/rx_channel_test/rx_channel_test.py +7 -6
  83. {brg_certificate → certificate}/tests/datapath/rx_rate_gen2_test/rx_rate_gen2_test.json +8 -7
  84. {brg_certificate → certificate}/tests/datapath/rx_rate_gen2_test/rx_rate_gen2_test.py +113 -73
  85. {brg_certificate → certificate}/tests/datapath/rx_rate_gen3_test/rx_rate_gen3_test.json +8 -7
  86. {brg_certificate → certificate}/tests/datapath/rx_rate_gen3_test/rx_rate_gen3_test.py +112 -72
  87. {brg_certificate → certificate}/tests/datapath/stress_gen3_test/stress_gen3_test.json +4 -3
  88. {brg_certificate → certificate}/tests/datapath/stress_gen3_test/stress_gen3_test.py +15 -11
  89. {brg_certificate → certificate}/tests/datapath/stress_test/stress_test.json +4 -3
  90. {brg_certificate → certificate}/tests/datapath/stress_test/stress_test.py +15 -11
  91. {brg_certificate → certificate}/tests/datapath/tx_repetition_test/tx_repetition_test.json +3 -1
  92. {brg_certificate → certificate}/tests/datapath/tx_repetition_test/tx_repetition_test.py +14 -13
  93. certificate/tests/edge_mgmt/action_blink_test/action_blink_test.json +15 -0
  94. certificate/tests/edge_mgmt/action_blink_test/action_blink_test.py +24 -0
  95. certificate/tests/edge_mgmt/action_get_battery_sensor_test/action_get_battery_sensor_test.json +15 -0
  96. certificate/tests/edge_mgmt/action_get_battery_sensor_test/action_get_battery_sensor_test.py +43 -0
  97. certificate/tests/edge_mgmt/action_get_module_test/action_get_module_test.json +15 -0
  98. certificate/tests/edge_mgmt/action_get_module_test/action_get_module_test.py +42 -0
  99. certificate/tests/edge_mgmt/action_get_pof_data_test/action_get_pof_data_test.json +15 -0
  100. certificate/tests/edge_mgmt/action_get_pof_data_test/action_get_pof_data_test.py +44 -0
  101. certificate/tests/edge_mgmt/action_gw_hb_test/action_gw_hb_test.json +16 -0
  102. certificate/tests/edge_mgmt/action_gw_hb_test/action_gw_hb_test.py +42 -0
  103. certificate/tests/edge_mgmt/action_reboot_test/action_reboot_test.json +15 -0
  104. certificate/tests/edge_mgmt/action_reboot_test/action_reboot_test.py +49 -0
  105. certificate/tests/edge_mgmt/action_restore_defaults_test/action_restore_defaults_test.json +15 -0
  106. certificate/tests/edge_mgmt/action_restore_defaults_test/action_restore_defaults_test.py +102 -0
  107. certificate/tests/edge_mgmt/action_send_hb_test/action_send_hb_test.json +15 -0
  108. certificate/tests/edge_mgmt/action_send_hb_test/action_send_hb_test.py +45 -0
  109. {brg_certificate → certificate}/tests/edge_mgmt/periodic_msgs_test/periodic_msgs_test.json +3 -2
  110. {brg_certificate → certificate}/tests/edge_mgmt/periodic_msgs_test/periodic_msgs_test.py +22 -11
  111. {brg_certificate → certificate}/tests/energy2400/duty_cycle_test/duty_cycle_test.json +2 -1
  112. {brg_certificate → certificate}/tests/energy2400/duty_cycle_test/duty_cycle_test.py +7 -6
  113. certificate/tests/energy2400/output_power_test/output_power_test.json +23 -0
  114. {brg_certificate → certificate}/tests/energy2400/output_power_test/output_power_test.py +17 -6
  115. {brg_certificate → certificate}/tests/energy2400/pattern_test/pattern_test.json +2 -1
  116. {brg_certificate → certificate}/tests/energy2400/pattern_test/pattern_test.py +7 -6
  117. certificate/tests/energy2400/signal_indicator_ble5_test/signal_indicator_ble5_test.json +26 -0
  118. certificate/tests/energy2400/signal_indicator_ble5_test/signal_indicator_ble5_test.py +379 -0
  119. certificate/tests/energy2400/signal_indicator_ext_adv_test/signal_indicator_ext_adv_test.json +20 -0
  120. certificate/tests/energy2400/signal_indicator_ext_adv_test/signal_indicator_ext_adv_test.py +173 -0
  121. certificate/tests/energy2400/signal_indicator_test/signal_indicator_test.json +24 -0
  122. certificate/tests/energy2400/signal_indicator_test/signal_indicator_test.py +350 -0
  123. {brg_certificate → certificate}/tests/energy_sub1g/duty_cycle_test/duty_cycle_test.json +2 -1
  124. {brg_certificate → certificate}/tests/energy_sub1g/duty_cycle_test/duty_cycle_test.py +7 -6
  125. {brg_certificate → certificate}/tests/energy_sub1g/pattern_test/pattern_test.json +2 -1
  126. {brg_certificate → certificate}/tests/energy_sub1g/pattern_test/pattern_test.py +7 -6
  127. {brg_certificate → certificate}/tests/pwr_mgmt/pwr_mgmt_test/pwr_mgmt_test.json +2 -1
  128. {brg_certificate → certificate}/tests/pwr_mgmt/pwr_mgmt_test/pwr_mgmt_test.py +10 -10
  129. {brg_certificate → certificate}/tests/sensors/ext_sensor_test/ext_sensor_test.json +5 -4
  130. certificate/tests/sensors/ext_sensor_test/ext_sensor_test.py +450 -0
  131. certificate/wlt_types.py +122 -0
  132. {gw_certificate → common}/api_if/202/status.json +6 -0
  133. {gw_certificate → common}/api_if/203/status.json +6 -0
  134. {gw_certificate → common}/api_if/204/status.json +6 -0
  135. common/api_if/206/data.json +85 -0
  136. common/api_if/206/status.json +69 -0
  137. common/api_if/api_validation.py +91 -0
  138. common/web/templates/generator.html +210 -0
  139. common/web/templates/index.html +20 -0
  140. common/web/templates/menu.html +54 -0
  141. common/web/templates/parser.html +53 -0
  142. {brg_certificate/ag → common/web/templates}/wlt_types.html +1216 -191
  143. common/web/web_utils.py +399 -0
  144. {brg_certificate → common}/wltPb_pb2.py +14 -12
  145. {gw_certificate/common → common}/wltPb_pb2.pyi +16 -2
  146. gui_certificate/gui_certificate_cli.py +14 -0
  147. gui_certificate/server.py +1267 -0
  148. gui_certificate/templates/cert_run.html +1273 -0
  149. wiliot_certificate-4.5.0.dist-info/METADATA +99 -0
  150. wiliot_certificate-4.5.0.dist-info/RECORD +168 -0
  151. {wiliot_certificate-4.4.3.dist-info → wiliot_certificate-4.5.0.dist-info}/WHEEL +1 -1
  152. wiliot_certificate-4.5.0.dist-info/entry_points.txt +5 -0
  153. wiliot_certificate-4.5.0.dist-info/top_level.txt +3 -0
  154. brg_certificate/ag/energous_v0_defines.py +0 -925
  155. brg_certificate/ag/energous_v1_defines.py +0 -931
  156. brg_certificate/ag/energous_v2_defines.py +0 -925
  157. brg_certificate/ag/energous_v3_defines.py +0 -925
  158. brg_certificate/ag/energous_v4_defines.py +0 -925
  159. brg_certificate/ag/fanstel_lan_v0_defines.py +0 -925
  160. brg_certificate/ag/fanstel_lte_v0_defines.py +0 -925
  161. brg_certificate/ag/fanstel_wifi_v0_defines.py +0 -925
  162. brg_certificate/ag/minew_lte_v0_defines.py +0 -925
  163. brg_certificate/ag/wlt_types_ag_jsons/brg2brg_ota.json +0 -142
  164. brg_certificate/ag/wlt_types_ag_jsons/brg2gw_hb.json +0 -785
  165. brg_certificate/ag/wlt_types_ag_jsons/brg2gw_hb_sleep.json +0 -139
  166. brg_certificate/ag/wlt_types_ag_jsons/calibration.json +0 -394
  167. brg_certificate/ag/wlt_types_ag_jsons/custom.json +0 -515
  168. brg_certificate/ag/wlt_types_ag_jsons/datapath.json +0 -672
  169. brg_certificate/ag/wlt_types_ag_jsons/energy2400.json +0 -550
  170. brg_certificate/ag/wlt_types_ag_jsons/energySub1g.json +0 -595
  171. brg_certificate/ag/wlt_types_ag_jsons/externalSensor.json +0 -598
  172. brg_certificate/ag/wlt_types_ag_jsons/interface.json +0 -938
  173. brg_certificate/ag/wlt_types_ag_jsons/powerManagement.json +0 -1234
  174. brg_certificate/ag/wlt_types_ag_jsons/side_info_sensor.json +0 -105
  175. brg_certificate/ag/wlt_types_ag_jsons/signal_indicator_data.json +0 -77
  176. brg_certificate/ag/wlt_types_ag_jsons/unified_echo_ext_pkt.json +0 -61
  177. brg_certificate/ag/wlt_types_ag_jsons/unified_echo_pkt.json +0 -110
  178. brg_certificate/brg_certificate.py +0 -225
  179. brg_certificate/brg_certificate_cli.py +0 -63
  180. brg_certificate/cert_common.py +0 -923
  181. brg_certificate/cert_config.py +0 -402
  182. brg_certificate/cert_utils.py +0 -362
  183. brg_certificate/certificate_bcc_sanity_test_list.txt +0 -40
  184. brg_certificate/certificate_bcc_test_list.txt +0 -48
  185. brg_certificate/certificate_sanity_test_list.txt +0 -43
  186. brg_certificate/certificate_test_list.txt +0 -53
  187. brg_certificate/config/eclipse.json +0 -10
  188. brg_certificate/config/hivemq.json +0 -10
  189. brg_certificate/config/mosquitto.json +0 -10
  190. brg_certificate/config/mosquitto.md +0 -95
  191. brg_certificate/config/wiliot-dev.json +0 -10
  192. brg_certificate/restore_brg.py +0 -61
  193. brg_certificate/tests/calibration/output_power_test/output_power_test.json +0 -16
  194. brg_certificate/tests/calibration/output_power_test/output_power_test.py +0 -28
  195. brg_certificate/tests/datapath/aging_test/aging_test.py +0 -143
  196. brg_certificate/tests/datapath/pacer_interval_tags_count_test/pacer_interval_tags_count_test.json +0 -16
  197. brg_certificate/tests/datapath/pacer_interval_tags_count_test/pacer_interval_tags_count_test.py +0 -73
  198. brg_certificate/tests/datapath/tx_repetition_algo_test/tx_repetition_algo_test.json +0 -17
  199. brg_certificate/tests/datapath/tx_repetition_algo_test/tx_repetition_algo_test.py +0 -118
  200. brg_certificate/tests/edge_mgmt/actions_test/actions_test.json +0 -14
  201. brg_certificate/tests/edge_mgmt/actions_test/actions_test.py +0 -396
  202. brg_certificate/tests/edge_mgmt/brg2brg_ota_ble5_test/brg2brg_ota_ble5_test.json +0 -20
  203. brg_certificate/tests/edge_mgmt/brg2brg_ota_ble5_test/brg2brg_ota_ble5_test.py +0 -94
  204. brg_certificate/tests/edge_mgmt/brg2brg_ota_test/brg2brg_ota_test.json +0 -19
  205. brg_certificate/tests/edge_mgmt/brg2brg_ota_test/brg2brg_ota_test.py +0 -87
  206. brg_certificate/tests/edge_mgmt/leds_test/leds_test.json +0 -17
  207. brg_certificate/tests/edge_mgmt/leds_test/leds_test.py +0 -223
  208. brg_certificate/tests/edge_mgmt/ota_test/ota_test.json +0 -17
  209. brg_certificate/tests/edge_mgmt/ota_test/ota_test.py +0 -128
  210. brg_certificate/tests/energy2400/output_power_test/output_power_test.json +0 -16
  211. brg_certificate/tests/energy2400/signal_indicator_ble5_10_250k_test/signal_indicator_ble5_10_250k_test.json +0 -20
  212. brg_certificate/tests/energy2400/signal_indicator_ble5_10_250k_test/signal_indicator_ble5_10_250k_test.py +0 -321
  213. brg_certificate/tests/energy2400/signal_indicator_ble5_10_500k_test/signal_indicator_ble5_10_500k_test.json +0 -20
  214. brg_certificate/tests/energy2400/signal_indicator_ble5_10_500k_test/signal_indicator_ble5_10_500k_test.py +0 -321
  215. brg_certificate/tests/energy2400/signal_indicator_sub1g_2_4_test/signal_indicator_sub1g_2_4_test.json +0 -20
  216. brg_certificate/tests/energy2400/signal_indicator_sub1g_2_4_test/signal_indicator_sub1g_2_4_test.py +0 -141
  217. brg_certificate/tests/energy2400/signal_indicator_test/signal_indicator_test.json +0 -20
  218. brg_certificate/tests/energy2400/signal_indicator_test/signal_indicator_test.py +0 -276
  219. brg_certificate/tests/energy_sub1g/signal_indicator_functionality_test/signal_indicator_functionality_test.json +0 -20
  220. brg_certificate/tests/energy_sub1g/signal_indicator_functionality_test/signal_indicator_functionality_test.py +0 -390
  221. brg_certificate/tests/energy_sub1g/signal_indicator_test/signal_indicator_test.json +0 -16
  222. brg_certificate/tests/energy_sub1g/signal_indicator_test/signal_indicator_test.py +0 -28
  223. brg_certificate/tests/sensors/ext_sensor_test/ext_sensor_test.py +0 -305
  224. brg_certificate/wltPb_pb2.pyi +0 -234
  225. brg_certificate/wlt_types.py +0 -113
  226. gw_certificate/ag/ut_defines.py +0 -364
  227. gw_certificate/ag/wlt_types.py +0 -85
  228. gw_certificate/ag/wlt_types_ag.py +0 -5310
  229. gw_certificate/ag/wlt_types_data.py +0 -64
  230. gw_certificate/api/extended_api.py +0 -23
  231. gw_certificate/api_if/200/data.json +0 -106
  232. gw_certificate/api_if/200/status.json +0 -47
  233. gw_certificate/api_if/201/data.json +0 -98
  234. gw_certificate/api_if/201/status.json +0 -53
  235. gw_certificate/api_if/205/logs.json +0 -12
  236. gw_certificate/api_if/api_validation.py +0 -38
  237. gw_certificate/api_if/gw_capabilities.py +0 -54
  238. gw_certificate/cert_results.py +0 -145
  239. gw_certificate/common/analysis_data_bricks.py +0 -60
  240. gw_certificate/common/debug.py +0 -42
  241. gw_certificate/common/serialization_formatter.py +0 -93
  242. gw_certificate/common/utils.py +0 -8
  243. gw_certificate/common/utils_defines.py +0 -15
  244. gw_certificate/common/wltPb_pb2.py +0 -84
  245. gw_certificate/gw_certificate.py +0 -154
  246. gw_certificate/gw_certificate_cli.py +0 -87
  247. gw_certificate/interface/4.4.93_app.zip +0 -0
  248. gw_certificate/interface/4.4.93_sd_bl_app.zip +0 -0
  249. gw_certificate/interface/ble_simulator.py +0 -61
  250. gw_certificate/interface/ble_sniffer.py +0 -189
  251. gw_certificate/interface/flash_fw.py +0 -90
  252. gw_certificate/interface/if_defines.py +0 -36
  253. gw_certificate/interface/mqtt.py +0 -563
  254. gw_certificate/interface/nrfutil-linux +0 -0
  255. gw_certificate/interface/nrfutil-mac +0 -0
  256. gw_certificate/interface/nrfutil.exe +0 -0
  257. gw_certificate/interface/pkt_generator.py +0 -594
  258. gw_certificate/interface/uart_if.py +0 -236
  259. gw_certificate/interface/uart_ports.py +0 -20
  260. gw_certificate/templates/results.html +0 -241
  261. gw_certificate/templates/stage.html +0 -22
  262. gw_certificate/templates/table.html +0 -6
  263. gw_certificate/templates/test.html +0 -38
  264. gw_certificate/tests/__init__.py +0 -10
  265. gw_certificate/tests/actions.py +0 -289
  266. gw_certificate/tests/bad_crc_to_PER_quantization.csv +0 -51
  267. gw_certificate/tests/connection.py +0 -188
  268. gw_certificate/tests/downlink.py +0 -172
  269. gw_certificate/tests/generic.py +0 -238
  270. gw_certificate/tests/registration.py +0 -340
  271. gw_certificate/tests/static/__init__.py +0 -0
  272. gw_certificate/tests/static/connection_defines.py +0 -9
  273. gw_certificate/tests/static/downlink_defines.py +0 -9
  274. gw_certificate/tests/static/generated_packet_table.py +0 -195
  275. gw_certificate/tests/static/packet_table.csv +0 -10067
  276. gw_certificate/tests/static/references.py +0 -5
  277. gw_certificate/tests/static/uplink_defines.py +0 -14
  278. gw_certificate/tests/throughput.py +0 -240
  279. gw_certificate/tests/uplink.py +0 -853
  280. wiliot_certificate-4.4.3.dist-info/METADATA +0 -211
  281. wiliot_certificate-4.4.3.dist-info/RECORD +0 -210
  282. wiliot_certificate-4.4.3.dist-info/entry_points.txt +0 -3
  283. wiliot_certificate-4.4.3.dist-info/top_level.txt +0 -3
  284. {brg_certificate → certificate}/__init__.py +0 -0
  285. {gw_certificate → common}/api_if/202/data.json +0 -0
  286. {gw_certificate/api_if/200 → common/api_if/202}/logs.json +0 -0
  287. {gw_certificate → common}/api_if/203/data.json +0 -0
  288. {gw_certificate/api_if/201 → common/api_if/203}/logs.json +0 -0
  289. {gw_certificate → common}/api_if/204/data.json +0 -0
  290. {gw_certificate/api_if/202 → common/api_if/204}/logs.json +0 -0
  291. {gw_certificate → common}/api_if/205/data.json +0 -0
  292. {gw_certificate/api_if/203 → common/api_if/205}/logs.json +0 -0
  293. {gw_certificate → common}/api_if/205/status.json +0 -0
  294. {gw_certificate/api_if/204 → common/api_if/206}/logs.json +0 -0
  295. {gw_certificate → common/api_if}/__init__.py +0 -0
  296. {gw_certificate/api_if → gui_certificate}/__init__.py +0 -0
  297. {wiliot_certificate-4.4.3.dist-info → wiliot_certificate-4.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,1488 @@
1
+ from certificate.cert_prints import *
2
+ from certificate.cert_defines import *
3
+ from certificate.wlt_types import *
4
+ import certificate.cert_utils as cert_utils
5
+ import certificate.cert_config as cert_config
6
+ import certificate.cert_mqtt as cert_mqtt
7
+ import certificate.cert_data_sim as cert_data_sim
8
+ import datetime
9
+ # from ut_te import ut_rtsa
10
+ import pandas as pd
11
+ import os
12
+ import plotly.express as px
13
+ import math, random
14
+
15
+ DEFAULT_HDR = ag.Hdr(group_id=ag.GROUP_ID_GW2BRG)
16
+
17
+ # Returns a 12 chars long hex string
18
+ int2mac_get = lambda int_val: f"{int_val:012X}"
19
+
20
+ # Returns a 12 chars long masked alias_id from hex string
21
+ STATIC_RANDOM_ADDR_MASK = 0xC00000000000
22
+ hex2alias_id_get = lambda id_str: int2mac_get(int(id_str, 16) | STATIC_RANDOM_ADDR_MASK)
23
+
24
+ # Returns True if running from PyPi package, else False
25
+ is_cert_running = lambda : not (CERT_VERSION == LOCAL_DEV)
26
+
27
+
28
+ def name_to_val(name):
29
+ return globals()[name]
30
+
31
+
32
+ def test_prolog(test, flush_mqtt=True):
33
+ """
34
+ kicks off the test:
35
+ - sets test start time
36
+ - checks to see if brg is DB for DB-only tests
37
+ - setups spectrum analyzer configuration if needed
38
+
39
+ :param WltTest test: test to be started
40
+ :return test: returns the test
41
+ """
42
+ test.start_time = datetime.datetime.now()
43
+
44
+ test_run_print(test)
45
+
46
+ # Clean all exsisting mqtt's before starting
47
+ if flush_mqtt:
48
+ test.flush_all_mqtt_packets()
49
+
50
+ #TODO - remove/check status later on in the test
51
+ test.set_phase_rc(PRE_CONFIG, rc=test.rc)
52
+ test.add_phase_reason(PRE_CONFIG, reason=test.reason)
53
+ #
54
+
55
+ return test
56
+
57
+ def test_epilog(test, revert_brgs=False, revert_gws=False, modules=[], brg1_modules=[], ble5=False,
58
+ flush_mqtt=True):
59
+ """
60
+ closes off the test:
61
+ - sets test end time and duration
62
+ - reverts gw/brgs/both to defaults
63
+ - prints test results
64
+
65
+ :param WltTest test: test to be finished
66
+ :param bool revert_brgs: reverts brgs to defaults (default ep and config), defaults to False
67
+ :param bool revert_gws: reverts gws to defaults (default config), defaults to False
68
+ :return test: returns the test
69
+ """
70
+ # TODO - REMOVE when rc is re-designed
71
+ if test.get_phase_by_name(TEST_BODY):
72
+ test.set_phase_rc(TEST_BODY, test.rc)
73
+ test.add_phase_reason(TEST_BODY, test.reason)
74
+
75
+ test.reset_result()
76
+ test.set_phase_rc(RESTORE_CONFIG, TEST_PASSED)
77
+
78
+ if revert_brgs:
79
+ res2 = DONE
80
+ test, res = cert_config.config_brg_defaults(test, modules=modules, ble5=ble5)
81
+ # TODO - REMOVE when rc is re-designed
82
+ test.set_phase_rc(RESTORE_CONFIG, test.rc)
83
+ test.reset_result()
84
+ #
85
+ if test.brg1 and test.multi_brg:
86
+ brg1_modules = modules if not brg1_modules else brg1_modules
87
+ test, res2 = cert_config.config_brg_defaults(test, modules=brg1_modules, ble5=ble5, target=BRG1)
88
+ # TODO - REMOVE when rc is re-designed
89
+ test.set_phase_rc(RESTORE_CONFIG, test.rc)
90
+ test.reset_result()
91
+ #
92
+ if res == NO_RESPONSE or res2 == NO_RESPONSE:
93
+ txt = "Failed: Revert BRGs to defaults"
94
+ utPrint(txt, "RED")
95
+ test.add_phase_reason(RESTORE_CONFIG, txt)
96
+
97
+ if revert_gws:
98
+ test, res = cert_config.config_gw_defaults(test)
99
+ # TODO - REMOVE when rc is re-designed
100
+ test.set_phase_rc(RESTORE_CONFIG, test.rc)
101
+ test.reset_result()
102
+ #
103
+ if res == NO_RESPONSE:
104
+ txt = "Failed: Revert GW to defaults"
105
+ utPrint(txt, "RED")
106
+ test.add_phase_reason(RESTORE_CONFIG, txt)
107
+ if flush_mqtt:
108
+ test.flush_all_mqtt_packets()
109
+ test.end_time = datetime.datetime.now()
110
+ test.duration = str(test.end_time - test.start_time).split(".")[0]
111
+
112
+ # patch for nightly pipeline - as long as brg ver is updated, continue
113
+ if ("ota_test" in test.module_name and not "brg2brg" in test.module_name and
114
+ (BRG_VER_SUCCESS in test.get_phase_reason(TEST_BODY) or WANTED_VER_SAME in test.get_phase_reason(TEST_BODY))
115
+ and test.get_phase_rc(TEST_BODY) == TEST_FAILED):
116
+ print("Setting rc to TEST_PASSED for pipeline after BRG OTA succeeded")
117
+ test.set_phase_rc(TEST_BODY, TEST_PASSED)
118
+ test.set_phase_rc(RESTORE_CONFIG, TEST_PASSED)
119
+
120
+ test_epilog_print(test)
121
+ return test
122
+
123
+ def get_gw_versions(test, target=DUT):
124
+ """
125
+ returns gw ble and wifi versions
126
+
127
+ :param WltTest test: test (with gw) to be checked
128
+ :return dict[str, str]: dictionary with BLE_VERSION and WIFI_VERSION
129
+ """
130
+ mqttc = test.get_mqttc_by_target(target)
131
+ mqttc.flush_pkts()
132
+ cert_config.gw_info_action(test)
133
+ found = False
134
+ gw_ble_version, gw_wifi_version = "", ""
135
+ start_time = datetime.datetime.now()
136
+ while not found:
137
+ for p in cert_mqtt.get_all_status_pkts(mqttc):
138
+ if GW_INFO in p:
139
+ print("Config pkts:")
140
+ print_pkt(p)
141
+ if test.tester.protobuf:
142
+ gw_ble_version = p[GW_INFO][ENTRIES][BLE_VERSION][STR_VAL]
143
+ gw_wifi_version = p[GW_INFO][ENTRIES][WIFI_VERSION][STR_VAL]
144
+ else:
145
+ gw_ble_version = p[GW_INFO][BLE_VERSION]
146
+ gw_wifi_version = p[GW_INFO][WIFI_VERSION]
147
+ print(f"current versions: wifi {gw_wifi_version} ble {gw_ble_version}")
148
+ found = True
149
+ print_update_wait()
150
+ if (datetime.datetime.now() - start_time).seconds > DEFAULT_GW_FIELD_UPDATE_TIMEOUT:
151
+ test.rc = TEST_FAILED
152
+ test.add_reason(f"{GW_INFO} not found after {DEFAULT_BRG_FIELD_UPDATE_TIMEOUT} seconds!")
153
+ break
154
+ return {BLE_VERSION:gw_ble_version, WIFI_VERSION:gw_wifi_version}
155
+
156
+ def get_gw_geolocation(test, target=DUT):
157
+ """
158
+ returns gw latitude and longitude from a gw_info action
159
+
160
+ :param WltTest test: test (with gw) to be checked
161
+ :return dict[str, float]: dictionary with GW_LATITUDE and GW_LONGITUDE
162
+ """
163
+ mqttc = test.get_mqttc_by_target(target)
164
+ mqttc.flush_pkts()
165
+ cert_config.gw_info_action(test)
166
+ found = False
167
+ gw_lat, gw_lng = 0.0, 0.0
168
+ start_time = datetime.datetime.now()
169
+ while not found:
170
+ for p in cert_mqtt.get_all_status_pkts(mqttc):
171
+ if GW_INFO in p:
172
+ print_pkt(p)
173
+ if test.protobuf:
174
+ gw_lat = p[GW_INFO][ENTRIES][GW_LATITUDE][NUM_VAL]
175
+ gw_lng = p[GW_INFO][ENTRIES][GW_LONGITUDE][NUM_VAL]
176
+ else:
177
+ gw_lat = p[GW_INFO][GW_LATITUDE]
178
+ gw_lng = p[GW_INFO][GW_LONGITUDE]
179
+ print(f"gw_lat:{gw_lat} \ngw_lng:{gw_lng}")
180
+ found = True
181
+ print_update_wait()
182
+ if (datetime.datetime.now() - start_time).seconds > DEFAULT_GW_FIELD_UPDATE_TIMEOUT:
183
+ test.rc = TEST_FAILED
184
+ test.add_reason(f"{GW_INFO} not found after {DEFAULT_BRG_FIELD_UPDATE_TIMEOUT} seconds!")
185
+ break
186
+ return test, {GW_LATITUDE:gw_lat, GW_LONGITUDE:gw_lng}
187
+
188
+ def get_gw_info(test, print=True, target=DUT):
189
+ """
190
+ gets gw info json dict from a gw_info action
191
+
192
+ :param WltTest test: test with gw that it's info will be retreived
193
+ :return str/dict[str, str]: json info dict from an info pkt OR a NO_RESPONSE str
194
+ """
195
+ gw = test.dut if target == DUT else test.tester
196
+ mqttc = test.get_mqttc_by_target(target)
197
+ mqttc.flush_pkts()
198
+ # Always send gw info in both JSON and protobuf
199
+ cert_config.gw_info_action(test, target=target)
200
+ gw.protobuf = not gw.protobuf
201
+ cert_config.gw_info_action(test, target=target)
202
+ gw.protobuf = not gw.protobuf
203
+
204
+ start_time = datetime.datetime.now()
205
+ while (datetime.datetime.now() - start_time).seconds < DEFAULT_GW_FIELD_UPDATE_TIMEOUT:
206
+ for p in cert_mqtt.get_all_status_pkts(mqttc):
207
+ if GW_INFO in p:
208
+ if print:
209
+ print_pkt(p)
210
+ return p
211
+ print_update_wait()
212
+ return NO_RESPONSE
213
+
214
+ def get_logs(test, target=DUT):
215
+ """
216
+ gets logs info json dict from a gw_logs action
217
+
218
+ :param WltTest test: test with gw that it's info will be retreived
219
+ :return str/dict[str, str]: json info dict from an info pkt OR a NO_RESPONSE str
220
+ """
221
+ mqttc = test.get_mqttc_by_target(target)
222
+ mqttc.flush_pkts()
223
+ cert_config.gw_logs_action(test)
224
+ start_time = datetime.datetime.now()
225
+ while (datetime.datetime.now() - start_time).seconds < DEFAULT_GW_FIELD_UPDATE_TIMEOUT:
226
+ for p in cert_mqtt.get_all_status_pkts(mqttc):
227
+ if GET_LOGS:
228
+ print_pkt(p)
229
+ return p
230
+ print_update_wait()
231
+ return NO_RESPONSE
232
+
233
+ def get_brg_cfg_pkts(test, last=False, cfg_info=False, target=DUT):
234
+ """
235
+ gets brg cfg data pkts (payload)
236
+
237
+ :param WltTest test: test to be scanned (it's first brg is the default brg to be scanned for)
238
+ :param bool last: set to True to get only the last pkt caught, defaults to False
239
+ :param bool cfg_info: set to True to get cfg info sent by the brg (msg_type=1 instead of 5 which is the default for this function), defaults to False
240
+ :param int brg_mac: specific brg_mac in case we want to get cfg pkts for a specific brg different than the default, defaults to 0
241
+ :param bool module: Indicates we look for a module pkt as ack for config change
242
+ :return str/list[str]: cfg pkts payloads list/last cfg pkt payload received
243
+ """
244
+ pkts = []
245
+ msg_type = ag.BRG_MGMT_MSG_TYPE_CFG_SET
246
+ if cfg_info:
247
+ msg_type = ag.BRG_MGMT_MSG_TYPE_CFG_INFO
248
+
249
+ mqttc = test.get_mqttc_by_target(target)
250
+ brg = cert_config.get_brg_by_target(test, target)
251
+ for p in cert_mqtt.get_brg2gw_mgmt_pkts(mqttc, brg):
252
+ brg2gw_cfg = p[MGMT_PKT].pkt
253
+ if type(brg2gw_cfg).__name__ in [module.__name__ for module in brg.modules]:
254
+ if brg2gw_cfg.msg_type == msg_type:
255
+ pkts += [p[PAYLOAD]]
256
+ if pkts and last:
257
+ return pkts[-1]
258
+ return pkts
259
+
260
+ time_in_sec = lambda t : t.seconds + t.microseconds / 1000000
261
+
262
+ # Pandas DataFrame documentation: https://pandas.pydata.org/docs/reference/frame.html
263
+
264
+ def get_all_brg_pkts(test):
265
+ utPrint(f"Collecting all BRG pkts", "BLUE")
266
+ return cert_mqtt.get_unified_data_pkts(test, only_active_brg=True)
267
+
268
+ def get_all_brgs_pkts(test):
269
+ utPrint(f"Collecting all BRG pkts", "BLUE")
270
+ return cert_mqtt.get_unified_data_pkts(test, only_active_brg=False)
271
+
272
+ def get_pkts_data_frame(test, gw_data=False, brg_data=False, per_pkt_type=False):
273
+ pkts = []
274
+ tags_last_pkt_cntr = {}
275
+ tags_received_per_src = {}
276
+ tbc = None
277
+ nfpkt = None
278
+ event_flag = None
279
+ event_ctr = None
280
+ gw_pkts = 0
281
+ brg_pkts = 0
282
+ all_data = {TIMESTAMP:[],TAG_ID:[],SRC_ID:[],NFPKT:[],EVENT_CTR:[],EVENT_FLAG:[],TBC:[],PACKET_CNTR:[],PKT_CNTR_DIFF:[],CER:[],RSSI:[],BRG_LATENCY:[],PAYLOAD:[],SEQUENCE_ID:[],GW_ID:[], PACKET_TYPE:[]}
283
+ if gw_data:
284
+ pkts += cert_mqtt.get_internal_brg_unified_data_pkts(test)
285
+ if brg_data:
286
+ if test.brg1 and test.multi_brg:
287
+ pkts += get_all_brg_pkts(test)
288
+ test.active_brg = test.brg1
289
+ pkts += get_all_brg_pkts(test)
290
+ test.active_brg = test.dut.internal_brg if cert_config.is_gw(test.dut) else test.dut
291
+ else:
292
+ pkts += get_all_brg_pkts(test)
293
+ for p in pkts:
294
+ # Protection from pkts of type "test_mode" from old tags
295
+ if type(p[DECODED_DATA][PACKET_TYPE]) == str or p[DECODED_DATA][PACKET_TYPE] == None:
296
+ print(f"Skipped packet {p}")
297
+ continue
298
+ if per_pkt_type:
299
+ tag_id = p[DECODED_DATA][TAG_ID] + "_" + str(p[DECODED_DATA][PACKET_TYPE])
300
+ else:
301
+ tag_id = p[DECODED_DATA][TAG_ID]
302
+
303
+ if UNIFIED_PKT in p:
304
+ src_id = p[ALIAS_BRIDGE_ID]
305
+ rssi = p[UNIFIED_PKT].pkt.rssi
306
+ brg_latency = p[UNIFIED_PKT].pkt.brg_latency
307
+ if isinstance(p[UNIFIED_PKT].pkt, ag.UnifiedEchoPktV0):
308
+ nfpkt = p[UNIFIED_PKT].pkt.nfpkt
309
+ if isinstance(p[UNIFIED_PKT].pkt, ag.UnifiedEchoPktV1) or isinstance(p[UNIFIED_PKT].pkt, ag.UnifiedEchoExtPktV0):
310
+ tbc = p[UNIFIED_PKT].pkt.tbc
311
+ nfpkt = p[UNIFIED_PKT].pkt.nfpkt
312
+ if isinstance(p[UNIFIED_PKT].pkt, ag.UnifiedEchoPktV2) or isinstance(p[UNIFIED_PKT].pkt, ag.UnifiedEchoExtPktV1):
313
+ tbc = p[UNIFIED_PKT].pkt.tbc
314
+ event_flag = p[UNIFIED_PKT].pkt.event_flag
315
+ event_ctr = p[UNIFIED_PKT].pkt.event_ctr
316
+
317
+ all_data[TIMESTAMP] += [p[TIMESTAMP]]
318
+ all_data[TAG_ID] += [tag_id]
319
+ all_data[GW_ID] += [p[GW_ID]]
320
+ all_data[SRC_ID] += [src_id]
321
+ all_data[NFPKT] += [nfpkt]
322
+ all_data[TBC] += [tbc]
323
+ all_data[EVENT_FLAG] += [event_flag]
324
+ all_data[EVENT_CTR] += [event_ctr]
325
+ all_data[PACKET_CNTR] += [p[DECODED_DATA][PACKET_CNTR]]
326
+ all_data[RSSI] += [rssi]
327
+ all_data[BRG_LATENCY] += [brg_latency]
328
+ all_data[PAYLOAD] += [p[PAYLOAD]]
329
+ all_data[SEQUENCE_ID] += [p[SEQUENCE_ID]]
330
+ all_data[PACKET_TYPE] += [p[DECODED_DATA][PACKET_TYPE]]
331
+
332
+ # handling pkt_cntr_diff
333
+ pkt_cntr_diff = (p[DECODED_DATA][PACKET_CNTR] - tags_last_pkt_cntr[tag_id])%255 if tag_id and tag_id in tags_received_per_src and src_id and src_id in tags_received_per_src[tag_id] else None
334
+ all_data[PKT_CNTR_DIFF] += [pkt_cntr_diff]
335
+ cer = 1-(nfpkt/pkt_cntr_diff) if (pkt_cntr_diff and nfpkt != None) else None
336
+ all_data[CER] += [cer]
337
+
338
+ # saving last pkt_cntr per tag
339
+ tags_last_pkt_cntr[tag_id] = p[DECODED_DATA][PACKET_CNTR]
340
+
341
+ # saving all srcs a tag was received from
342
+ if tag_id and src_id:
343
+ if tag_id not in tags_received_per_src:
344
+ tags_received_per_src[tag_id] = [src_id]
345
+ elif not src_id in tags_received_per_src[tag_id]:
346
+ tags_received_per_src[tag_id] += [src_id]
347
+
348
+ if gw_data:
349
+ if src_id == test.internal_id_alias():
350
+ gw_pkts += 1
351
+ if brg_data:
352
+ if src_id != test.internal_id_alias():
353
+ brg_pkts += 1
354
+
355
+ if gw_data:
356
+ print(f"Found {gw_pkts} gw_tags_pkts")
357
+ if brg_data:
358
+ print(f"Found {brg_pkts} brg_tags_pkts")
359
+
360
+ df = pd.DataFrame.from_dict(all_data)
361
+ df = df.sort_values(by=TIMESTAMP)
362
+ return df
363
+
364
+ def data_scan(test, gw_data=False, brg_data=False, scan_time=0, per_pkt_type=False, pkt_filter_cfg=0, flush_pkts=True, first_pkt_is_start_time=False, target=DUT):
365
+ # MQTT scan
366
+ mqttc = test.get_mqttc_by_target(target)
367
+ if flush_pkts:
368
+ mqttc.flush_pkts()
369
+ start_time = datetime.datetime.now()
370
+ if scan_time:
371
+ mqtt_scan_start(test, scan_time)
372
+ chars = ["|", "/", "-", "\\"]
373
+ start_time = datetime.datetime.now()
374
+ i = 0
375
+ while not test.rc:
376
+ cur_duration = (datetime.datetime.now() - start_time).seconds
377
+ if cur_duration >= scan_time:
378
+ break
379
+ if pipeline_running():
380
+ sys.stdout.write(".")
381
+ else:
382
+ sys.stdout.write("\r"+chars[i%4]*20+" "+str(cur_duration)+" "+chars[i%4]*20+" {} pkts captured".format(len(cert_mqtt.get_all_data_pkts(mqttc))))
383
+ sys.stdout.flush()
384
+ time.sleep(0.25)
385
+ i += 1
386
+ print("\n")
387
+
388
+ if per_pkt_type:
389
+ cert_mqtt.dump_pkts(test, log=str(pkt_filter_cfg))
390
+ if pkt_filter_cfg == ag.PKT_FILTER_RANDOM_FIRST_ARRIVING_PKT:
391
+ # When PKT_FILTER_RANDOM_FIRST_ARRIVING_PKT we don't want to split the tags to be per pkt_type
392
+ per_pkt_type = False
393
+ df = get_pkts_data_frame(test, gw_data=gw_data, brg_data=brg_data, per_pkt_type=per_pkt_type)
394
+ if not df.empty:
395
+ df['gw_id'] = test.internal_id_alias()
396
+ if first_pkt_is_start_time:
397
+ start_time = min(df[TIMESTAMP])
398
+ df[TIMESTAMP_DELTA] = (df[TIMESTAMP]- start_time) / 1000
399
+ else:
400
+ df[TIMESTAMP_DELTA] = (df[TIMESTAMP] / 1000) - start_time.timestamp()
401
+ return df
402
+
403
+ def pacing_analysis(test, pacer_interval, df, pkt_filter_cfg=ag.PKT_FILTER_RANDOM_FIRST_ARRIVING_PKT, num_of_pixels=0, is_ble5_test=False, ext_adv_brg2gw=False,
404
+ event_time_unit=ag.BRG_DEFAULT_EVENT_TIME_UNIT, phase=""):
405
+ ROUND = 3
406
+
407
+ # Validate pkts amount
408
+ if df[TAG_ID].nunique() == 0:
409
+ if pkt_filter_cfg == ag.PKT_FILTER_DISABLE_FORWARDING:
410
+ print("Packets echo disabled and no packets were found accordingly")
411
+ else:
412
+ test.rc = TEST_FAILED
413
+ test.add_reason("No packets found!\nMake sure you have an energizing BRG around you.")
414
+ print(test.reason)
415
+ return test
416
+ elif pkt_filter_cfg == ag.PKT_FILTER_DISABLE_FORWARDING:
417
+ test.rc = TEST_FAILED
418
+ test.add_reason("Packets were found while packets echo is turned off!")
419
+ print(test.reason)
420
+ return test
421
+
422
+ # Verify received pkt types are correct when cfg is not PKT_FILTER_RANDOM_FIRST_ARRIVING_PKT
423
+ if pkt_filter_cfg != ag.PKT_FILTER_RANDOM_FIRST_ARRIVING_PKT:
424
+ for pkt_type in list(df[PACKET_TYPE].unique()):
425
+ if ((pkt_filter_cfg & (1 << pkt_type)) == 0
426
+ and not (is_ble5_test and (test.internal_brg or test.dut_is_combo() or ext_adv_brg2gw) and pkt_type == ag.PKT_TYPE_BLE5_EXTENDED_TEMP_ADVANCED)):
427
+ test.rc = TEST_FAILED
428
+ test.add_reason(f"Tag is of packet type {pkt_type} which is turned off in packet_types_mask configuration!")
429
+ return test
430
+
431
+ # Verify the tags count according to simulation data and pkt_filter_cfg
432
+ tags_count = len(list(df[TAG_ID].unique()))
433
+ if test.data == DATA_SIMULATION and num_of_pixels:
434
+ if is_ble5_test and (test.dut_is_combo() or ext_adv_brg2gw):
435
+ # In ble5 bcc packet type 2 extended uploaded as is without splitting to ble4 packets
436
+ expected_tags_count = num_of_pixels
437
+ elif pkt_filter_cfg == ag.PKT_FILTER_TEMP_AND_ADVANCED_PKTS or pkt_filter_cfg == ag.PKT_FILTER_TEMP_AND_DEBUG_PKTS:
438
+ expected_tags_count = num_of_pixels * 2
439
+ elif pkt_filter_cfg == ag.PKT_FILTER_TEMP_ADVANCED_AND_DEBUG_PKTS:
440
+ expected_tags_count = num_of_pixels * 3
441
+ else:
442
+ expected_tags_count = num_of_pixels
443
+ if tags_count != expected_tags_count:
444
+ test.rc = TEST_FAILED
445
+ test.add_reason(f"Expected {expected_tags_count} pixels but found {tags_count}!")
446
+ print(test.reason)
447
+ return test
448
+
449
+ # verify there is no event flag when not event test
450
+ event_test = True if "event" in phase else False
451
+ if not event_test and (df['event_flag'] == 1).any():
452
+ test.rc = TEST_FAILED
453
+ print(f"Got event flag when not testing events!")
454
+ test.add_reason(f"Got event flag when not testing events!")
455
+
456
+ # Verify the tags event pacing
457
+ tag_event = True if "new_tag" not in phase else False
458
+ if event_test:
459
+
460
+ if event_time_unit == ag.EVENT_TIME_UNIT_SECONDS:
461
+ pacing_window = TEST_EVENT_WINDOW_SEC_CFG if not tag_event else TEST_TAG_EVENT_WINDOW_CFG
462
+ elif event_time_unit == ag.EVENT_TIME_UNIT_MINUTES:
463
+ pacing_window = TEST_EVENT_WINDOW_MIN_CFG * 60
464
+ elif event_time_unit == ag.EVENT_TIME_UNIT_HOURS:
465
+ pacing_window = TEST_EVENT_WINDOW_HR_CFG * 3600
466
+
467
+ # In seconds phase we also test the dynamic pacer interval
468
+ if "rssi" in phase:
469
+ event_pacing = DATA_SIM_RSSI_EVENT_TESTING_DELAY_SEC
470
+ elif "seconds" in phase:
471
+ event_pacing = DATA_SIM_EVENT_PACER_INTERVAL_TESTING
472
+ else:
473
+ event_pacing = DATA_SIM_EVENT_TESTING_DELAY_SEC
474
+
475
+ expected_event_pkt_count = math.ceil(pacing_window / event_pacing)
476
+
477
+ if "rssi" in phase:
478
+ if not test.sterile_run:
479
+ # In non-sterile runs - rssi is less stable and can trigger more events
480
+ max_count_threshold = float('inf')
481
+ else:
482
+ # In rssi movement events the alpha filter takes about 13 packets to stabilize
483
+ max_count_threshold = 1 + (RSSI_EVENT_PKTS_TO_STABILIZE / expected_event_pkt_count)
484
+ else:
485
+ max_count_threshold = PACER_INTERVAL_CEIL_THRESHOLD
486
+
487
+ for tag in list(df[TAG_ID].unique()):
488
+ event_pkts = df.query('tag_id == @tag and event_flag == 1')
489
+ avg_event_pacer = round(event_pkts.timestamp.diff().mean(skipna=True)/1000, ROUND)
490
+
491
+ if not (PACER_INTERVAL_THRESHOLD <= len(event_pkts) / expected_event_pkt_count <= max_count_threshold):
492
+ test.rc = TEST_FAILED
493
+ msg = f"Packet count for dynamic tag {tag} is wrong! expected_event_pkt_count = {expected_event_pkt_count}, received pkt count = {len(event_pkts)}"
494
+ test.add_reason(msg)
495
+ utPrint(msg, "RED")
496
+ if not (PACER_INTERVAL_THRESHOLD <= avg_event_pacer / event_pacing <= PACER_INTERVAL_CEIL_THRESHOLD):
497
+ test.rc = TEST_FAILED
498
+ msg = f"Tag {tag} has a wrong avg time diff. diff_time {list(event_pkts.timestamp.diff().div(1000))}, avg_event_pacer={avg_event_pacer} exceeds threshold!"
499
+ test.add_reason(msg)
500
+ utPrint(msg, "RED")
501
+
502
+ # Verify the tags pacer interval (without event)
503
+ failed_tags = 0
504
+ max_received_pkts = max([len(df.query('tag_id == @tag and event_flag == 0')) for tag in list(df[TAG_ID].unique())])
505
+ for tag in list(df[TAG_ID].unique()):
506
+ if tag_event:
507
+ # No need to test regular pacing in tag event test
508
+ break
509
+ pkts = df.query('tag_id == @tag and event_flag == 0')
510
+ avg_pacer = round(pkts.timestamp.diff().mean(skipna=True)/1000, ROUND)
511
+ print(f"Tag: {tag} avg_pacer={avg_pacer} num_of_pkts={len(pkts)}")
512
+ if ((avg_pacer / pacer_interval) < PACER_INTERVAL_THRESHOLD_HIGH and (pacer_interval - avg_pacer) > 1):
513
+ failed_tags += 1
514
+ test.rc = TEST_FAILED
515
+ msg = f"Tag {tag} with diff_time {list(pkts.timestamp.diff().div(1000))}, avg_pacer={avg_pacer} exceeds {PACER_INTERVAL_THRESHOLD_HIGH} minimum threshold!"
516
+ utPrint(msg, "RED")
517
+ test.add_reason(msg)
518
+ # Pass the test with real tags when less than 5% tag failed
519
+ if test.data != DATA_SIMULATION and failed_tags / tags_count < 0.05:
520
+ test.rc = TEST_PASSED
521
+
522
+ if test.data == DATA_SIMULATION and (avg_pacer / pacer_interval) > PACER_INTERVAL_CEIL_THRESHOLD:
523
+ if max_received_pkts == len(pkts):
524
+ # we fail the tag only if it received all expected pkts and pacer caluculation is valid
525
+ failed_tags += 1
526
+ msg = f"Tag {tag} with diff_time {list(pkts.timestamp.diff().div(1000))}, avg_pacer={avg_pacer} exceeds {PACER_INTERVAL_CEIL_THRESHOLD} maximum threshold!"
527
+ utPrint(msg, "RED")
528
+ test.add_reason(msg)
529
+ else:
530
+ print(f"Tag {tag} received only {len(pkts)} pkts out of {max_received_pkts}, avg_pacer failed but skipping pacer ceil validation")
531
+ if failed_tags / tags_count > 0.2: # Fail the test on ceil threshold only when more than 20% tag failed
532
+ test.reason = f"{failed_tags}/{tags_count} tags with wrong time diff"
533
+ test.rc = TEST_FAILED
534
+
535
+ return test
536
+
537
+
538
+ def brg2brg_ota_init(test, is_bl_ota=False):
539
+
540
+ VERSIONS_SAME = "Both bridges FW versions are the same!"
541
+ BL_VERSIONS_SAME = "Both bridges Bootloader versions are the same!"
542
+ BOARDS_MISMATCH = "Bridges are of different board types!"
543
+
544
+ # Initialize bridges
545
+ # TODO - REMOVE CERT_CONFIG IMPORT
546
+ brg0 = test.dut.internal_brg if cert_config.is_gw(test.dut) else test.dut
547
+ brg1 = test.brg1
548
+
549
+ # Protections from same version & different board types
550
+ if not is_bl_ota and brg0.version == brg1.version:
551
+ utPrint(VERSIONS_SAME, "RED")
552
+ test.rc = TEST_FAILED
553
+ test.add_reason(VERSIONS_SAME)
554
+ if is_bl_ota and brg0.bl_version == brg1.bl_version:
555
+ utPrint(BL_VERSIONS_SAME, "RED")
556
+ test.rc = TEST_FAILED
557
+ test.add_reason(BL_VERSIONS_SAME)
558
+ if brg0.board_type != brg1.board_type:
559
+ utPrint(BOARDS_MISMATCH, "RED")
560
+ test.rc = TEST_FAILED
561
+ test.add_reason(BOARDS_MISMATCH)
562
+
563
+ # Active bridge will be the source bridge
564
+ if is_bl_ota:
565
+ test.active_brg = brg0 if brg0.bl_version > brg1.bl_version else brg1
566
+ else:
567
+ test.active_brg = brg0 if brg0.version > brg1.version else brg1
568
+
569
+ return test
570
+
571
+
572
+ def send_brg2brg_ota_msg(test, src_brg, dest_brg, is_bl_ota=False, target=DUT):
573
+
574
+ utPrint(f"Source {"bootloader" if is_bl_ota else "firmware"} bridge version: {src_brg.bl_version if is_bl_ota else src_brg.version}. "
575
+ f"Destination bridge {"bootloader" if is_bl_ota else "firmware"} version: {dest_brg.bl_version if is_bl_ota else dest_brg.version}", "BLUE")
576
+
577
+ # Send BRG2BRG_OTA message to source bridge
578
+ functionality_run_print(f"BRG2BRG OTA - Source Bridge MAC: {src_brg.id_str}, Destination Bridge MAC: {dest_brg.id_str}")
579
+ brg2brg_ota_pkt = eval_pkt(f'Brg2BrgOtaV{test.active_brg.api_version}')(src_brg_mac=src_brg.id_int,
580
+ dest_brg_mac=dest_brg.id_int,
581
+ seq_id=test.get_seq_id(),
582
+ bootloader=is_bl_ota)
583
+ brg2brg_ota_pkt_downlink = WltPkt(hdr=ag.Hdr(group_id=ag.GROUP_ID_GW2BRG), pkt=brg2brg_ota_pkt)
584
+ # BRG OTA - Flush pkts ONLY before starting to avoid deletion of needed GW Logs
585
+ test.get_mqttc_by_target(target).flush_pkts()
586
+ cert_config.gw_downlink(test, raw_tx_data=brg2brg_ota_pkt_downlink.dump(), target=target)
587
+
588
+ # Get version of the destination bridge
589
+ test.active_brg = dest_brg
590
+ # expected_hash=1 due to different cfgs and versions between builds
591
+ test = reboot_config_analysis(test=test, expected_hash=1, ble_version=src_brg.version if not is_bl_ota else None,
592
+ bl_version=src_brg.bl_version if is_bl_ota else None, timeout=VER_UPDATE_TIMEOUT)
593
+ # Update back to original bridge version if test failed:
594
+ if test.rc == TEST_FAILED:
595
+ cert_config.brg_ota(test, ble_version=test.gw_orig_versions[BLE_VERSION], search_ack=False)
596
+
597
+ return test
598
+
599
+
600
+ def run_event_test_phase(test, phase, datapath_module, values, scan_time, event_time_unit, ble5_test=False,
601
+ pkt_filter_cfg=ag.PKT_FILTER_RANDOM_FIRST_ARRIVING_PKT):
602
+ # Generic runner for events tests
603
+
604
+ fields = [BRG_EVENT_TIME_UNIT, BRG_EVENT_WINDOW, BRG_RX_CHANNEL, BRG_EVENT_TRIGGER, BRG_EVENT_PACER_INTERVAL]
605
+ scan_time_multiplier = 1
606
+ delay = DATA_SIM_EVENT_TESTING_DELAY_MS
607
+ if "rssi" in phase:
608
+ test.sterile_run = is_quiet_setup_running()
609
+ delay = DATA_SIM_RSSI_EVENT_TESTING_DELAY_MS
610
+ scan_time_multiplier = (1 / 3)
611
+ fields += [BRG_RSSI_MOVEMENT_THRESHOLD]
612
+
613
+ ble5 = ble5_test and test.dut_is_bridge() # ble5 only for bridge dut (with combo we don't need to wait)
614
+ test = cert_config.brg_configure(test, fields=fields, values=values, module=datapath_module, ble5=ble5)[0]
615
+ test.set_phase_rc(phase, test.rc)
616
+ test.add_phase_reason(phase, test.reason)
617
+ if test.rc == TEST_FAILED and test.exit_on_param_failure:
618
+ return test
619
+
620
+ num_of_pixels = 0
621
+ if test.data == DATA_SIMULATION:
622
+ num_of_pixels = 1
623
+ if ble5_test:
624
+ pixel_sim_thread = cert_data_sim.DataSimThread(test=test, num_of_pixels=num_of_pixels, duplicates=6,
625
+ delay=delay, pkt_types=[2],
626
+ pixels_type=GEN3_EXTENDED)
627
+ else:
628
+ pixel_sim_thread = cert_data_sim.DataSimThread(test=test, num_of_pixels=num_of_pixels, duplicates=6,
629
+ delay=delay, pkt_types=[0],
630
+ pixels_type=GEN3)
631
+ pixel_sim_thread.start()
632
+ else:
633
+ test.rc = TEST_FAILED
634
+ warning_txt = "This test should be ran with data simulator!"
635
+ utPrint(warning_txt, "RED")
636
+ test.add_reason(warning_txt)
637
+ return test
638
+
639
+ df = data_scan(test, scan_time=scan_time * scan_time_multiplier, pkt_filter_cfg=pkt_filter_cfg, brg_data=(not test.internal_brg),
640
+ gw_data=test.internal_brg, per_pkt_type=True)
641
+
642
+ if "rssi" in phase:
643
+ # Configure data simulator to change output power for rssi change test
644
+ test = cert_config.brg_configure(test, module=datapath_module, fields=[BRG_OUTPUT_POWER, BRG_RX_CHANNEL], values=[ag.OUTPUT_POWER_2_4_MAX_MINUS_26], wait=False, target=TESTER)[0]
645
+ df_ext = data_scan(test, scan_time=scan_time * (1 - scan_time_multiplier), pkt_filter_cfg=pkt_filter_cfg, brg_data=(not test.internal_brg),
646
+ gw_data=test.internal_brg, per_pkt_type=True)
647
+ df = pd.concat([df, df_ext]).sort_values(by=TIMESTAMP)
648
+ test = cert_config.brg_configure(test, module=datapath_module, fields=[BRG_OUTPUT_POWER, BRG_RX_CHANNEL], values=[ag.BRG_DEFAULT_DATAPATH_OUTPUT_POWER], target=TESTER)[0]
649
+
650
+ if test.data == DATA_SIMULATION:
651
+ pixel_sim_thread.stop()
652
+ time.sleep(5)
653
+
654
+ cert_mqtt.dump_pkts(test, log=phase)
655
+ display_data(df, event_flag=True, event_ctr=True, name_prefix=f"{phase}_", dir=test.dir)
656
+
657
+ test = pacing_analysis(test, df=df, pkt_filter_cfg=pkt_filter_cfg, pacer_interval=ag.BRG_DEFAULT_PACER_INTERVAL,
658
+ num_of_pixels=num_of_pixels, event_time_unit=event_time_unit, is_ble5_test=ble5_test, phase=phase)
659
+ test.set_phase_rc(phase, test.rc)
660
+ test.add_phase_reason(phase, test.reason)
661
+ return test
662
+
663
+ def reboot_config_analysis(test, expected_hash, timeout=ACTION_LONG_TIMEOUT, ble_version=None, bl_version=None, target=DUT):
664
+ utPrint("Analyzing Reboot", "BLUE")
665
+ # start with a 5 sec wait time before searching interface to allow the BRG to reboot
666
+ time.sleep(5)
667
+
668
+ start_time = datetime.datetime.now()
669
+ seq_ids = []
670
+ found = {ag.MODULE_IF : False, ag.MODULE_DATAPATH: False}
671
+ received_hash = 0
672
+ mqttc = test.get_mqttc_by_target(target)
673
+ # Flush data pkts only to keep the GW logs which are in status topic
674
+ mqttc.flush_data_pkts()
675
+
676
+ while not all(found.values()):
677
+ # scan for ModuleIf and ModuleDatapath pkts of all api versions to support api version change on update
678
+ # ModuleDatapath arrival shows that the BLE really rebooted
679
+ if_pkts_list = [eval_pkt(f'ModuleIfV{i}') for i in range(ag.API_VERSION_V9, ag.API_VERSION_LATEST+1)]
680
+ datapath_pkts_list = [eval_pkt(f'ModuleDatapathV{i}') for i in range(ag.API_VERSION_V9, ag.API_VERSION_LATEST+1)]
681
+ pkts = cert_mqtt.get_brg2gw_mgmt_pkts(mqttc, test.active_brg, mgmt_types=if_pkts_list+datapath_pkts_list)
682
+ for p in pkts:
683
+ if p[SEQUENCE_ID] not in seq_ids:
684
+ seq_ids.append(p[SEQUENCE_ID])
685
+ module_pkt = p[MGMT_PKT].pkt
686
+ if not found[module_pkt.module_type]:
687
+ print("\nGot {} packet after {} sec!".format(type(module_pkt).__name__, (datetime.datetime.now() - start_time).seconds))
688
+ print(module_pkt)
689
+ if module_pkt.module_type == ag.MODULE_IF:
690
+ test.active_brg.api_version = module_pkt.api_version
691
+ print(f"received ModuleIfV{test.active_brg.api_version} pkt:")
692
+ # get received cfg_hash & expected cfg_hash
693
+ received_hash = module_pkt.cfg_hash
694
+ print(f"\nexpected cfg_hash: {hex(expected_hash)}")
695
+ print(f"received cfg_hash: {hex(received_hash)}")
696
+ # brg version update (OTA) analysis
697
+ if ble_version:
698
+ brg_version = f"{module_pkt.major_ver}.{module_pkt.minor_ver}.{module_pkt.patch_ver}"
699
+ print(f"\nBRG version: {brg_version}, expected version: {ble_version}")
700
+ # compare wanted version to received version
701
+ if brg_version == ble_version:
702
+ test.active_brg.version = brg_version
703
+ test.add_reason(BRG_VER_SUCCESS)
704
+ # ALSO compare received cfg_hash to expected cfg_hash
705
+ # expected_hash will be 1 if api_version was updated
706
+ if received_hash == expected_hash or expected_hash == 1:
707
+ found[module_pkt.module_type] = True
708
+ elif bl_version:
709
+ brg_bl_version = module_pkt.bl_version
710
+ print(f"\nBRG bootloader version: {brg_bl_version}, expected bootloader version: {bl_version}")
711
+ # compare wanted version to received version
712
+ if brg_bl_version == bl_version:
713
+ test.active_brg.bl_version = brg_bl_version
714
+ test.add_reason(BRG_BL_VER_SUCCESS)
715
+ found[module_pkt.module_type] = True
716
+ # analysis of any other reboot actions with no version update (relevant only for api version 8 or higher)
717
+ # compare received cfg_hash to expected cfg_hash
718
+ elif received_hash == expected_hash:
719
+ found[module_pkt.module_type] = True
720
+ else:
721
+ found[module_pkt.module_type] = True
722
+ print_update_wait()
723
+
724
+ if (datetime.datetime.now() - start_time).seconds > timeout:
725
+ test.rc = TEST_FAILED
726
+ unfound = [f'{ag.MODULES_DICT[m]}{test.active_brg.api_version}' for m in found if not found[m]]
727
+ test.add_reason(f"{unfound} not received in {timeout} sec.")
728
+ break
729
+ return test
730
+
731
+ def scan_for_mgmt_pkts(test, mgmt_type, target=DUT):
732
+
733
+ start_time = datetime.datetime.now()
734
+ # Search for module packets
735
+ found = False
736
+ ret_pkts = []
737
+ mqttc = test.get_mqttc_by_target(target)
738
+ while DEFAULT_BRG_FIELD_UPDATE_TIMEOUT > (datetime.datetime.now() - start_time).seconds:
739
+ print_update_wait()
740
+ pkts_collected = cert_mqtt.get_brg2gw_mgmt_pkts(mqttc, test.active_brg, mgmt_types=mgmt_type)
741
+ if pkts_collected:
742
+ seq_ids = []
743
+ for p in pkts_collected:
744
+ if seq_ids == [] or p[SEQUENCE_ID] not in seq_ids:
745
+ seq_ids.append(p[SEQUENCE_ID])
746
+ ret_pkts.append(p)
747
+ found = True
748
+ break
749
+ if not found:
750
+ test.rc = TEST_FAILED
751
+ error = f"Didn't receive {mgmt_type[0].__name__} pkt after {DEFAULT_BRG_FIELD_UPDATE_TIMEOUT} seconds!"
752
+ test.add_reason(error)
753
+ utPrint(error, "RED")
754
+ return test, ret_pkts
755
+
756
+ # Actions test functions
757
+ # modules should receive a list of module names to look for - identical to their actual classes' names!
758
+ def scan_for_modules(test, modules=[]):
759
+ modules = test.active_brg.modules if not modules else modules
760
+ found = {module.__name__: False for module in modules}
761
+ start_time = datetime.datetime.now()
762
+
763
+ # Search for packets
764
+ while not all(found.values()):
765
+ for module in found:
766
+ pkts = cert_mqtt.get_brg2gw_mgmt_pkts(
767
+ test.get_mqttc_by_target(DUT),
768
+ test.active_brg,
769
+ mgmt_types=[eval_pkt(module)],
770
+ )
771
+ if pkts and not found[module]:
772
+ found[module] = True
773
+ print("\nGot {} packet after {} sec!".format(module, (datetime.datetime.now() - start_time).seconds))
774
+ print(pkts[-1][MGMT_PKT].pkt)
775
+ print_update_wait()
776
+ if (datetime.datetime.now() - start_time).seconds > DEFAULT_BRG_FIELD_UPDATE_TIMEOUT:
777
+ test.rc = TEST_FAILED
778
+ err_print = ','.join([module for module, value in found.items() if not value])
779
+ test.add_reason("Didn't receive {} after {} seconds!".format(err_print, DEFAULT_BRG_FIELD_UPDATE_TIMEOUT))
780
+ break
781
+ return test
782
+
783
+
784
+ def search_action_ack(test, action_id, **kwargs):
785
+ test, mgmt_pkts = scan_for_mgmt_pkts(test, mgmt_type=[eval_pkt(f'{ag.ACTIONS_DICT[action_id]}{test.active_brg.api_version}')])
786
+ if test.rc == TEST_FAILED:
787
+ return test
788
+ print("\nReceived ACK pkts:")
789
+ for p in mgmt_pkts:
790
+ print(p[MGMT_PKT].pkt)
791
+ pkt = cert_config.get_default_brg_pkt(test,
792
+ pkt_type=eval_pkt(f'{ag.ACTIONS_DICT[action_id]}{test.active_brg.api_version}'),
793
+ **kwargs).pkt
794
+ if p[MGMT_PKT].pkt == pkt:
795
+ utPrint("Received ACK for action", "GREEN")
796
+ return test
797
+ test.rc = TEST_FAILED
798
+ test.add_reason(f"Didn't find action ACK for action id {action_id} {ag.ACTIONS_DICT[action_id]}")
799
+ return test
800
+
801
+
802
+ def get_brg_non_default_module_pkt(test, module):
803
+ if 'Energy2400' in module.__name__:
804
+ if test.active_brg.board_type in [ag.BOARD_TYPE_ENERGOUS_V0, ag.BOARD_TYPE_ENERGOUS_V2,
805
+ ag.BOARD_TYPE_ENERGOUS_V3, ag.BOARD_TYPE_ENERGOUS_V4]:
806
+ # Increase output power offset by 1
807
+ op_offset = BRG_NON_DEFAULT_OP_OFFSET_2_4 + 1
808
+ else:
809
+ op_offset = BRG_NON_DEFAULT_OP_OFFSET_2_4
810
+ return cert_config.get_default_brg_pkt(test, pkt_type=test.active_brg.energy2400,
811
+ duty_cycle=BRG_NON_DEFAULT_DUTY_CYCLE,
812
+ output_power=op_offset, pattern=BRG_NON_DEFAULT_EP_2_4,
813
+ signal_indicator_cycle=BRG_NON_DEFAULT_SIGNAL_INDICATOR_CYCLE_2_4,
814
+ signal_indicator_rep=BRG_NON_DEFAULT_SIGNAL_INDICATOR_REP_2_4)
815
+ elif 'EnergySub1G' in module.__name__:
816
+ return cert_config.get_default_brg_pkt(test, pkt_type=test.active_brg.energy_sub1g,
817
+ duty_cycle=BRG_NON_DEFAULT_DUTY_CYCLE,
818
+ signal_indicator_cycle=BRG_NON_DEFAULT_SIGNAL_INDICATOR_CYCLE_SUB1G,
819
+ signal_indicator_rep=BRG_NON_DEFAULT_SIGNAL_INDICATOR_REP_SUB1G)
820
+ elif 'PwrMgmt' in module.__name__:
821
+ return cert_config.get_default_brg_pkt(test, pkt_type=test.active_brg.pwr_mgmt,
822
+ dynamic_keep_alive_scan=BRG_NON_DEFAULT_PWR_MGMT_KEEP_ALIVE_SCAN)
823
+ elif 'Custom' in module.__name__:
824
+ return cert_config.get_default_brg_pkt(test, pkt_type=test.active_brg.custom,
825
+ motion_sensitivity_threshold=LIS2DW12_NON_DEFAULT_STATE_THRESHOLD,
826
+ s2d_transition_time=LIS2DW12_NON_DEFAULT_WAKE_UP_DURATION,
827
+ d2s_transition_time=LIS2DW12_NON_DEFAULT_SLEEP_DURATION)
828
+ elif 'Datapath' in module.__name__:
829
+ if test.active_brg.board_type in [ag.BOARD_TYPE_ENERGOUS_V0, ag.BOARD_TYPE_ENERGOUS_V2,
830
+ ag.BOARD_TYPE_ENERGOUS_V3, ag.BOARD_TYPE_ENERGOUS_V4]:
831
+ # Increase output power offset by 1
832
+ op_offset = BRG_NON_DEFAULT_OP_OFFSET_2_4 + 1
833
+ else:
834
+ op_offset = BRG_NON_DEFAULT_OP_OFFSET_2_4
835
+ return cert_config.get_default_brg_pkt(test, pkt_type=test.active_brg.datapath,
836
+ tx_repetition=BRG_NON_DEFAULT_TX_REPETITION,
837
+ pkt_filter=BRG_NON_DEFAULT_PKT_FILTER,
838
+ output_power=op_offset,
839
+ pattern=ag.DATAPATH_PATTERN_EU_PATTERN,
840
+ pacer_interval=BRG_NON_DEFAULT_PACER_INTERVAL,
841
+ rssi_threshold=BRG_NON_DEFAULT_RSSI_THRESHOLD,
842
+ event_time_unit=BRG_NON_DEFAULT_EVENT_TIME_UNIT,
843
+ event_window=BRG_NON_DEFAULT_EVENT_WINDOW,
844
+ event_trigger=BRG_NON_DEFAULT_EVENT_TRIGGER,
845
+ event_pacer_interval=BRG_NON_DEFAULT_EVENT_PACER_INTERVAL,
846
+ rssi_movement_threshold=BRG_NON_DEFAULT_RSSI_MOVEMENT_THRESHOLD)
847
+ elif 'Calibration' in module.__name__:
848
+ if test.active_brg.board_type in [ag.BOARD_TYPE_ENERGOUS_V0, ag.BOARD_TYPE_ENERGOUS_V2,
849
+ ag.BOARD_TYPE_ENERGOUS_V3, ag.BOARD_TYPE_ENERGOUS_V4]:
850
+ # Increase output power offset by 1
851
+ op_offset = BRG_NON_DEFAULT_CALIB_OUTPUT_POWER_OFFSET + 1
852
+ else:
853
+ op_offset = BRG_NON_DEFAULT_CALIB_OUTPUT_POWER_OFFSET
854
+ return cert_config.get_default_brg_pkt(test, pkt_type=test.active_brg.calibration,
855
+ output_power=op_offset,
856
+ interval=BRG_NON_DEFAULT_CALIB_INTERVAL,
857
+ pattern=BRG_NON_DEFAULT_CALIB_PATTERN)
858
+ elif 'ExtSensors' in module.__name__:
859
+ return cert_config.get_default_brg_pkt(test, pkt_type=test.active_brg.sensors,
860
+ sensor0=ag.EXTERNAL_SENSORS_MINEWS1,
861
+ sensor1=ag.EXTERNAL_SENSORS_ZEBRA_PRINTER,
862
+ sensor2=ag.EXTERNAL_SENSORS_ERM_SMART_MS,
863
+ rssi_threshold=BRG_NON_DEFAULT_RSSI_THRESHOLD,
864
+ sub1g_rssi_threshold=BRG_NON_DEFAULT_RSSI_THRESHOLD)
865
+ return None
866
+
867
+
868
+ def brg_non_default_modules_cfg(test):
869
+ for module in test.active_brg.modules:
870
+ cfg_pkt = get_brg_non_default_module_pkt(test, module)
871
+ if cfg_pkt:
872
+ utPrint(f"Configuring {module.__name__} non-default cfg", "BLUE")
873
+ test = cert_config.brg_configure(test=test, cfg_pkt=cfg_pkt)[0]
874
+ if test.rc == TEST_FAILED and test.exit_on_param_failure:
875
+ test.add_reason(f"{module.__name__} non-default cfg pkt was not found after {DEFAULT_BRG_FIELD_UPDATE_TIMEOUT} sec!")
876
+ return test
877
+ return test
878
+
879
+ # Plotly graphing libraries documentation: https://plotly.com/python/
880
+
881
+ def display_data(df, csv=True, pkt_cntr_diff=False, cer_per_tag=False, tbc=False, rssi=False, ttfp=False, event_flag=False, event_ctr=False,
882
+ start_time=None, name_prefix="", dir=""):
883
+ print("\nGenerating data analysis graphs and CSV file\n")
884
+ df[DATETIME] = df[TIMESTAMP].apply(lambda x: datetime.datetime.fromtimestamp(x/1e3))
885
+ df = df.sort_values(by=DATETIME)
886
+ symbol_sequence = ["hourglass", "bowtie", "cross", "x"]
887
+ all_graphs = []
888
+ ttfp_graph = None
889
+ # insert new start_time to override timestamp_delta from data_scan()
890
+ if start_time:
891
+ df[TIMESTAMP_DELTA] = (df[TIMESTAMP] / 1000) - start_time.timestamp()
892
+ if rssi:
893
+ rssi_graph = px.scatter(df, title=RSSI, x=DATETIME, y=RSSI, color=TAG_ID, symbol=SRC_ID, symbol_sequence=symbol_sequence)
894
+ rssi_graph.update_traces(marker=dict(size=12, line=dict(width=2, color='DarkSlateGrey')), selector=dict(mode='markers'))
895
+ all_graphs.append(rssi_graph)
896
+ if pkt_cntr_diff:
897
+ pkt_cntr_diff_graph = px.scatter(df, title=PKT_CNTR_DIFF, x=DATETIME, y=PKT_CNTR_DIFF, color=TAG_ID, symbol=SRC_ID, symbol_sequence=symbol_sequence)
898
+ pkt_cntr_diff_graph.update_traces(marker=dict(size=12, line=dict(width=2, color='DarkSlateGrey')), selector=dict(mode='markers'))
899
+ all_graphs.append(pkt_cntr_diff_graph)
900
+ if cer_per_tag:
901
+ cer_per_tag_graph = px.scatter(df, title=CER, x=DATETIME, y=CER, color=TAG_ID, symbol=SRC_ID, symbol_sequence=symbol_sequence)
902
+ cer_per_tag_graph.update_traces(marker=dict(size=12, line=dict(width=2, color='DarkSlateGrey')), selector=dict(mode='markers'))
903
+ all_graphs.append(cer_per_tag_graph)
904
+ if tbc:
905
+ tbc_graph = px.scatter(df, title=TBC, x=DATETIME, y=TBC, color=TAG_ID, symbol=SRC_ID, symbol_sequence=symbol_sequence)
906
+ tbc_graph.update_traces(marker=dict(size=12, line=dict(width=2, color='DarkSlateGrey')), selector=dict(mode='markers'))
907
+ all_graphs.append(tbc_graph)
908
+ if event_flag:
909
+ event_flag_graph = px.scatter(df, title=EVENT_FLAG, x=DATETIME, y=EVENT_FLAG, color=TAG_ID, symbol=SRC_ID, symbol_sequence=symbol_sequence)
910
+ event_flag_graph.update_traces(marker=dict(size=12, line=dict(width=2, color='DarkSlateGrey')), selector=dict(mode='markers'))
911
+ all_graphs.append(event_flag_graph)
912
+ if event_ctr:
913
+ event_ctr_graph = px.scatter(df, title=EVENT_CTR, x=DATETIME, y=EVENT_CTR, color=TAG_ID, symbol=SRC_ID, symbol_sequence=symbol_sequence)
914
+ event_ctr_graph.update_traces(marker=dict(size=12, line=dict(width=2, color='DarkSlateGrey')), selector=dict(mode='markers'))
915
+ all_graphs.append(event_ctr_graph)
916
+ if ttfp:
917
+ data = {TIMESTAMP_DELTA:[], TAGS_COUNT:[], NEW_TAGS:[]}
918
+ tags_count = []
919
+ # iterate all integers from 0 to the largest timestamp_delta as values for X
920
+ for i in range(int(math.ceil(df[TIMESTAMP_DELTA].iloc[-1]))+1):
921
+ new_tags = []
922
+ # for every timestamp_delta value (i) add all NEW tags received in that timestamp_delta
923
+ for row in df.query('timestamp_delta < @i').itertuples(index=False):
924
+ if not row.tag_id in tags_count and not row.tag_id in new_tags:
925
+ new_tags += [row.tag_id]
926
+ tags_count += new_tags
927
+ data[TIMESTAMP_DELTA] += ([i])
928
+ data[TAGS_COUNT] += [len(tags_count)]
929
+ data[NEW_TAGS] += [new_tags]
930
+ ttfp_graph = px.line(pd.DataFrame(data), x=TIMESTAMP_DELTA, y=TAGS_COUNT, title=TTFP,hover_data=[TIMESTAMP_DELTA,TAGS_COUNT,NEW_TAGS], markers=True)
931
+ all_graphs.append(ttfp_graph)
932
+ #generate
933
+ file_path = os.path.join(ARTIFACTS_DIR, dir, f"{name_prefix}data_graphs.html")
934
+ if not os.path.exists(os.path.dirname(file_path)):
935
+ os.makedirs(os.path.dirname(file_path))
936
+ with open(file_path, 'w') as f:
937
+ for g in all_graphs:
938
+ f.write(g.to_html(full_html=False, include_plotlyjs='cdn', include_mathjax='cdn'))
939
+ f.write("<br>")
940
+ if csv:
941
+ df.to_csv(os.path.join(ARTIFACTS_DIR, dir, f"{name_prefix}all_data.csv"), index=False)
942
+
943
+ return ttfp_graph
944
+
945
+ def single_log_search(test, s, found, fail_on_find=False, print_logs=True, additional_log="", target=DUT):
946
+ mqttc = test.get_mqttc_by_target(target)
947
+ res = False
948
+ for p in cert_mqtt.get_all_status_pkts(mqttc):
949
+ if GW_LOGS in p:
950
+ if cert_config.get_protobuf_by_target(test, target) and p[GW_LOGS]:
951
+ # handle protobuf structure (when GW_LOGS is not empty)
952
+ logs = p[GW_LOGS][LOGS]
953
+ else:
954
+ logs = p[GW_LOGS]
955
+ for log in logs:
956
+ if any([s in log]) and any([additional_log in log]) and (log not in found):
957
+ print(f"Log: {log}, Additional Log: {additional_log}")
958
+ found += [log]
959
+ res = True
960
+ if fail_on_find:
961
+ if test.rc == TEST_PASSED:
962
+ test= test.add_reason("Test functionality passed")
963
+ test.add_reason(f"Found {s}")
964
+ test.rc = TEST_FAILED
965
+ print(found)
966
+ return test, res, found
967
+ if print_logs:
968
+ print_pkt(s)
969
+ return test, res, found
970
+
971
+ def gw_logs_search(test, strings, scan_time=GW_LOG_PERIOD+5, print_logs=False, fail_on_find=False):
972
+ """searching for specific logs in mqtt status topic in GW_LOGS field
973
+
974
+ :param WltTest test: test running
975
+ :param [str] strings: list of logs to search
976
+ :param int scan_time: time to scan for logs, defaults to GW_LOG_PERIOD+5
977
+ :return WltTest: test with updated results
978
+ """
979
+ start_time = datetime.datetime.now()
980
+ print(f"Searching for {strings} log in MQTT status topic.\nFail on find is set to {fail_on_find}")
981
+ found = []
982
+ while (len(strings) > len(found)):
983
+ for s in strings:
984
+ test, res, found = single_log_search(test, s, found, fail_on_find, print_logs)
985
+ if res:
986
+ break
987
+ if (datetime.datetime.now() - start_time).seconds >= scan_time:
988
+ if not fail_on_find:
989
+ test.add_reason(f"Didnt find logs in [{scan_time}] seconds")
990
+ print(test.reason)
991
+ test.rc = TEST_FAILED
992
+ break
993
+ if test.rc == TEST_PASSED:
994
+ if not fail_on_find:
995
+ print(f"SUCCESS found all [{strings}]")
996
+ else:
997
+ print(f"SUCCESS Didnt find [{strings}]")
998
+ return test
999
+
1000
+ def gw_action_status_search(test, action_idx, status_code, target=DUT):
1001
+ """searching for action returned status code in mqtt status topic in ACTION field
1002
+
1003
+ :param WltTest test: test running
1004
+ :param int action_idx: sent action index
1005
+ :param int status_code: expected status code for action
1006
+ :return WltTest: test with updated results
1007
+ """
1008
+ mqttc = test.get_mqttc_by_target(target)
1009
+ start_time = datetime.datetime.now()
1010
+ print(f"Searching for action idx ({action_idx}) update log in MQTT status topic")
1011
+ while (datetime.datetime.now() - start_time).seconds < GW_LOG_PERIOD:
1012
+ for p in cert_mqtt.get_all_status_pkts(mqttc):
1013
+ # JSON
1014
+ if ((ACTION in p) and (p[ACTION] == action_idx) and
1015
+ (STATUS_CODE_STR in p) and (p[STATUS_CODE_STR] == status_code) and
1016
+ (STEP not in p or p[STEP] == FINAL_OTA_STEP)):
1017
+ return test
1018
+ # Protobuf - when succeed status is not sent
1019
+ if ((ACTION_STATUS in p) and (p[ACTION_STATUS][ACTION] == action_idx) and
1020
+ STATUS_CODE not in p[ACTION_STATUS] and
1021
+ (STEP not in p[ACTION_STATUS] or p[ACTION_STATUS][STEP] == FINAL_OTA_STEP)):
1022
+ return test
1023
+ test.add_reason(f"ActionStatus message (idx={action_idx} status={status_code}) not found in {GW_LOG_PERIOD} sec")
1024
+ print(test.reason)
1025
+ test.rc = TEST_FAILED
1026
+ return test
1027
+
1028
+ def get_gw_logs_packets(test, last=False, print_log=True, target=DUT):
1029
+ """
1030
+ gets gw logs pkts
1031
+ :param WltTest test: test with gw that it's info will be retreived
1032
+ :param bool last: set to True to get only the last pkt caught, defaults to False
1033
+ :return pkt/list[pkt]: logs pkts list/last status pkt received
1034
+ """
1035
+ mqttc = test.get_mqttc_by_target(target)
1036
+ cert_config.gw_logs_action(test)
1037
+ pkts = []
1038
+ for p in cert_mqtt.get_all_status_pkts(mqttc):
1039
+ if GW_LOGS in p:
1040
+ if print_log:
1041
+ print("GW logs packet:\n", p[GW_LOGS])
1042
+ logs = p[GW_LOGS][LOGS] if test.tester.protobuf else p[GW_LOGS]
1043
+ pkts += [log for log in logs]
1044
+ if pkts and last:
1045
+ return pkts[len(pkts)-1]
1046
+ return pkts
1047
+
1048
+ def get_gw_type(mqttc):
1049
+ messages = cert_mqtt.get_all_status_pkts(mqttc)
1050
+ for msg in messages:
1051
+ if GW_STATUS in msg: # protobuf
1052
+ return msg[GW_STATUS][GW_TYPE], msg
1053
+ if GW_CONF in msg: # JSON
1054
+ return msg[GW_TYPE], msg
1055
+ return None, None
1056
+
1057
+ def get_module_if_pkt(test):
1058
+ cert_config.send_brg_action(test, ag.ACTION_GET_MODULE, interface=1)
1059
+ mgmt_type = [m for m in ag.MODULES_LIST if "ModuleIf" in m.__name__]
1060
+ test, pkts = scan_for_mgmt_pkts(test, mgmt_type=mgmt_type)
1061
+
1062
+ if test.rc == TEST_FAILED:
1063
+ return test, NO_RESPONSE
1064
+ else:
1065
+ print(pkts[-1][MGMT_PKT].pkt)
1066
+ return test, pkts[-1][MGMT_PKT].pkt
1067
+
1068
+ def get_gw_api_version(mqttc):
1069
+ messages = cert_mqtt.get_all_status_pkts(mqttc)
1070
+ for msg in messages:
1071
+ if GW_CONF in msg: # JSON
1072
+ return msg[GW_CONF][GW_API_VERSION]
1073
+ if GW_STATUS in msg: # protobuf
1074
+ return msg[GW_STATUS][GW_API_VERSION]
1075
+ return None
1076
+
1077
+ def get_cfg_hash(test):
1078
+ utPrint(f"Fetching BRG cfg hash for BRG {test.active_brg.id_str}", "BLUE")
1079
+ test, module_if_pkt = get_module_if_pkt(test)
1080
+ if test.rc == TEST_FAILED:
1081
+ return test, 0
1082
+ else:
1083
+ return test, module_if_pkt.cfg_hash
1084
+
1085
+
1086
+ def brg_restore_defaults_check(test, target=DUT):
1087
+ print("Starting Restore Defaults Check")
1088
+ start_time = datetime.datetime.now()
1089
+ found = False
1090
+ revived = False
1091
+ output = ""
1092
+ while not found:
1093
+ last_pkt = get_brg_cfg_pkts(test=test, cfg_info=True, last=True, target=target)
1094
+ if last_pkt:
1095
+ print(f"Got pkt after {(datetime.datetime.now() - start_time).seconds} sec!")
1096
+ wlt_pkt = WltPkt(last_pkt)
1097
+ print(f"SUCCESS: Found pkt from brg: {wlt_pkt}")
1098
+ found = True # exit
1099
+ revived = True
1100
+ output = "SUCCESS: brg is alive and restored to defaults!"
1101
+ if (datetime.datetime.now() - start_time).seconds > ACTION_LONG_TIMEOUT:
1102
+ print(f"FAILURE: Can't find bridge! Didn't get config pkt after {ACTION_LONG_TIMEOUT} seconds!")
1103
+ break
1104
+ print_update_wait()
1105
+ return test, revived, output
1106
+
1107
+ # Pwr Mgmt
1108
+ def brg_pwr_mgmt_turn_on(test):
1109
+ utPrint("Sending pwr_mgmt static mode configuration - 30 seconds ON, 60 seconds SLEEP!", "BLUE")
1110
+ module = test.active_brg.pwr_mgmt
1111
+ # send pwr mgmt module packet
1112
+ wltpkt = WltPkt(hdr=DEFAULT_HDR, pkt=module(module_type=ag.MODULE_PWR_MGMT, msg_type=ag.BRG_MGMT_MSG_TYPE_CFG_SET,
1113
+ api_version=ag.API_VERSION_LATEST,seq_id=random.randrange(99),
1114
+ brg_mac=test.active_brg.id_int, static_on_duration=30, static_sleep_duration=60,
1115
+ dynamic_leds_on=0,dynamic_keep_alive_period=0, dynamic_keep_alive_scan=0,
1116
+ dynamic_on_duration=0,dynamic_sleep_duration=0))
1117
+ test = cert_config.brg_configure(test=test, cfg_pkt=wltpkt, module=module)[0]
1118
+
1119
+ if test.rc == TEST_FAILED:
1120
+ test.add_reason("Turning pwr mgmt ON failed, Didn't receive GW MEL pwr mgmt ON pkt")
1121
+ else:
1122
+ utPrint("SUCCESS! pwr mgmt static mode turned on!", "GREEN")
1123
+ return test, wltpkt
1124
+
1125
+ def brg_pwr_mgmt_turn_off(test):
1126
+ utPrint("Turning pwr mgmt OFF - sending default configuration!", "BLUE")
1127
+ module = test.active_brg.pwr_mgmt
1128
+ start_time = datetime.datetime.now()
1129
+ wltpkt = WltPkt(hdr=DEFAULT_HDR, pkt=module(module_type=ag.MODULE_PWR_MGMT, msg_type=ag.BRG_MGMT_MSG_TYPE_CFG_SET,
1130
+ api_version=ag.API_VERSION_LATEST,seq_id=random.randrange(99),
1131
+ brg_mac=test.active_brg.id_int,static_leds_on=1,
1132
+ static_keep_alive_period=0,static_keep_alive_scan=0,
1133
+ static_on_duration=0,static_sleep_duration=0,
1134
+ dynamic_leds_on=0,dynamic_keep_alive_period=0,
1135
+ dynamic_keep_alive_scan=0,dynamic_on_duration=0,dynamic_sleep_duration=0))
1136
+ found = NOT_FOUND
1137
+ while found != DONE:
1138
+ test, found = cert_config.brg_configure(test=test, cfg_pkt=wltpkt, module=module, wait=False)
1139
+ if ((datetime.datetime.now() - start_time).seconds > (ag.PWR_MGMT_DEFAULTS_KEEP_ALIVE_PERIOD + 1)):
1140
+ test.add_reason(f"Didn't receive GW MEL pwr mgmt OFF ack after {ag.PWR_MGMT_DEFAULTS_KEEP_ALIVE_PERIOD + 1} seconds")
1141
+ test.rc = TEST_FAILED
1142
+ break
1143
+ print_update_wait()
1144
+ if found == DONE:
1145
+ utPrint(f"FOUND off pkt after {(datetime.datetime.now() - start_time)} secs", "GREEN")
1146
+ utPrint("SUCCESS! pwr mgmt static mode turned off!", "GREEN")
1147
+ return test
1148
+
1149
+ # LEDs tests funcs
1150
+ def check_input_n_try_again(value):
1151
+ # check for valid input char - only 'Y', 'y', 'N' or 'n'
1152
+ while value.lower() != 'y' and value.lower() != 'n':
1153
+ utPrint("Wrong input, Please try Again!\n", "RED")
1154
+ value = input()
1155
+ return value
1156
+
1157
+ # Executed only when the received value is 'n'!
1158
+ def value_check_if_y(test, received_value, stage):
1159
+ # check if the received value is different from the expected value
1160
+ if 'y' != received_value.lower():
1161
+ test.rc = TEST_FAILED
1162
+ test.add_reason(f"{stage} failed")
1163
+ return test
1164
+
1165
+
1166
+ ##########################################
1167
+ # Signal Indicator functions
1168
+ ##########################################
1169
+ def dual_polarization_ant_boards_get():
1170
+ return [ag.BOARD_TYPE_MINEW_SINGLE_BAND_V0, ag.BOARD_TYPE_MINEW_DUAL_BAND_V0,
1171
+ ag.BOARD_TYPE_ENERGOUS_V2, ag.BOARD_TYPE_ERM_V0, ag.BOARD_TYPE_ERM_V1,
1172
+ ag.BOARD_TYPE_MINEW_POE_V0]
1173
+
1174
+ def exp_sig_ind_pkts(tx_brg, rx_brg, cycles):
1175
+ if tx_brg.board_type in dual_polarization_ant_boards_get():
1176
+ tx_brg_ant_polarization_num = 2
1177
+ else:
1178
+ tx_brg_ant_polarization_num = 1
1179
+ if rx_brg.board_type in dual_polarization_ant_boards_get():
1180
+ rx_brg_ant_polarization_num = 2
1181
+ else:
1182
+ rx_brg_ant_polarization_num = 1
1183
+
1184
+ expected = cycles * tx_brg_ant_polarization_num * rx_brg_ant_polarization_num
1185
+ # Allow missing 1 pkt
1186
+ return [expected - 1, expected]
1187
+
1188
+ def exp_sig_ind_pkts2(tx_brg, rx_brg, cycles):
1189
+ if tx_brg.board_type in dual_polarization_ant_boards_get():
1190
+ tx_brg_ant_polarization_num = 2
1191
+ else:
1192
+ tx_brg_ant_polarization_num = 1
1193
+ if rx_brg.board_type in dual_polarization_ant_boards_get():
1194
+ rx_brg_ant_polarization_num = 2
1195
+ else:
1196
+ rx_brg_ant_polarization_num = 1
1197
+
1198
+ expected = cycles * tx_brg_ant_polarization_num * rx_brg_ant_polarization_num
1199
+ return expected
1200
+
1201
+ def sig_ind_pkts_fail_analysis(tx_brg, rx_brg, cycles, received_pkts):
1202
+
1203
+ expected = exp_sig_ind_pkts2(tx_brg, rx_brg, cycles)
1204
+ print(f"Expected pkts: {expected}, Received pkts: {len(received_pkts)}")
1205
+ # Allow missing 25% max
1206
+ if int(0.75 * expected) <= len(received_pkts) <= int(1.25 * expected):
1207
+ return False
1208
+ return True
1209
+
1210
+ def get_all_sig_ind_pkts(test=None, rx_brg=None, tx_brg=None):
1211
+ if rx_brg == test.brg1:
1212
+ all_sensor_packets = cert_mqtt.get_all_brg1_ext_sensor_pkts(test=test, is_unified=True, target=TESTER)
1213
+ elif rx_brg == (test.dut.internal_brg if cert_config.is_gw(test.dut) else test.dut):
1214
+ all_sensor_packets = cert_mqtt.get_all_sensor_pkts(test=test, is_unified=True, target=DUT)
1215
+ signal_ind_pkts = []
1216
+ for p in all_sensor_packets:
1217
+ sensor_id = (f"{p[UNIFIED_SENSOR_PKT].pkt.sensor_ad_type:02X}"
1218
+ f"{p[UNIFIED_SENSOR_PKT].pkt.sensor_uuid_msb:02X}"
1219
+ f"{p[UNIFIED_SENSOR_PKT].pkt.sensor_uuid_lsb:02X}")
1220
+ if (sensor_id == f"{ag.SENSOR_SERVICE_ID_SIGNAL_INDICATOR:06X}" and
1221
+ p[ALIAS_BRIDGE_ID] == rx_brg.id_alias and f"{p[UNIFIED_SENSOR_PKT].pkt.sensor_mac:012X}" == tx_brg.id_alias):
1222
+ signal_ind_pkts.append(p)
1223
+ return signal_ind_pkts
1224
+
1225
+ def output_power_check(test, received_signal_ind_pkts, tx_brg_):
1226
+ output_power_default = tx_brg_.max_output_power_dbm - tx_brg_.datapath().output_power
1227
+
1228
+ for p in received_signal_ind_pkts:
1229
+ if p[UNIFIED_SENSOR_PKT].pkt.signal_indicator_payload.output_power != output_power_default:
1230
+ test.rc = TEST_FAILED
1231
+ test.add_reason("output power of internal brg is incorrect!\n"
1232
+ f"got:{p[UNIFIED_SENSOR_PKT].pkt.signal_indicator_payload.output_power}, expected: {output_power_default}\n")
1233
+ return test
1234
+
1235
+ def rssi_check(test, received_signal_ind_pkts):
1236
+ threshold_rssi = [0, 80]
1237
+ for p in received_signal_ind_pkts:
1238
+ if not threshold_rssi[0] < p[UNIFIED_SENSOR_PKT].pkt.rssi < threshold_rssi[1]:
1239
+ test.rc = TEST_FAILED
1240
+ test.add_reason("rssi value is wrong, out of 0 to 80 ")
1241
+
1242
+ return test
1243
+
1244
+
1245
+ def rx_tx_antenna_check(test, received_signal_ind_pkts, tx_brg_, rx_brg_, cycles):
1246
+
1247
+ # Allow to miss 1 packet or get 1 extra packet
1248
+ expected = range(int(cycles * 0.5), cycles + 2)
1249
+
1250
+ received = len(get_polar_signal_ind_pkt(received_signal_ind_pkts, rx_ant=0, tx_ant=0))
1251
+ if received not in expected:
1252
+ test.rc = TEST_FAILED
1253
+ test.add_reason(f"rx_ant=0 tx_ant=0 expected={cycles} received={received}")
1254
+
1255
+ if tx_brg_.board_type in dual_polarization_ant_boards_get():
1256
+ received = len(get_polar_signal_ind_pkt(received_signal_ind_pkts, rx_ant=0, tx_ant=1))
1257
+ if received not in expected:
1258
+ test.rc = TEST_FAILED
1259
+ test.add_reason(f"rx_ant=0 tx_ant=1 expected={cycles} received={received}")
1260
+
1261
+ if rx_brg_.board_type in dual_polarization_ant_boards_get():
1262
+ received = len(get_polar_signal_ind_pkt(received_signal_ind_pkts, rx_ant=1, tx_ant=0))
1263
+ if received not in expected:
1264
+ test.rc = TEST_FAILED
1265
+ test.add_reason(f"rx_ant=1 tx_ant=0 expected={cycles} received={received}")
1266
+
1267
+ if (rx_brg_.board_type in dual_polarization_ant_boards_get() and
1268
+ tx_brg_.board_type in dual_polarization_ant_boards_get()):
1269
+ received = len(get_polar_signal_ind_pkt(received_signal_ind_pkts, rx_ant=1, tx_ant=1))
1270
+ if received not in expected:
1271
+ test.rc = TEST_FAILED
1272
+ test.add_reason(f"rx_ant=1 tx_ant=1 expected={cycles} received={received}")
1273
+ return test
1274
+
1275
+
1276
+ def get_polar_signal_ind_pkt(pkts, rx_ant, tx_ant):
1277
+ return [p for p in pkts if p[UNIFIED_SENSOR_PKT].pkt.signal_indicator_payload.rx_antenna ==
1278
+ rx_ant and p[UNIFIED_SENSOR_PKT].pkt.signal_indicator_payload.tx_antenna == tx_ant]
1279
+
1280
+
1281
+ def output_power_supported(board_type, output_power):
1282
+ if board_type in [ag.BOARD_TYPE_FANSTEL_WIFI_V0, ag.BOARD_TYPE_FANSTEL_LAN_V0]:
1283
+ return output_power in FANSTEL_GW_SUPPORTED_VALUES
1284
+ elif board_type in ENERGOUS_BOARD_TYPES_GROUP:
1285
+ return output_power in ENERGOUS_GROUP_SUPPORTED_VALUES
1286
+ elif board_type == ag.BOARD_TYPE_ENERGOUS_V1:
1287
+ return output_power in ENERGOUS_V1_SUPPORTED_VALUES
1288
+ else:
1289
+ return output_power in ag.OUTPUT_POWER_2_4_SUPPORTED_VALUES
1290
+
1291
+
1292
+ def validate_received_packets(pkts_array):
1293
+ for pkt in pkts_array:
1294
+ # Check all required fields are present
1295
+ if TIMESTAMP not in pkt:
1296
+ return False, "timestamp field is missing in some of the packets"
1297
+ if ALIAS_BRIDGE_ID not in pkt:
1298
+ return False, "alias_bridge_id field is missing in some of the packets"
1299
+ if PAYLOAD not in pkt:
1300
+ return False, "payload field is missing in some of the packets"
1301
+ if SEQUENCE_ID not in pkt:
1302
+ return False, "sequence_id field is missing in some of the packets"
1303
+ # Check that the payload length is either 62 or 74 hex characters (equevelnt to 31 or 37 bytes)
1304
+ if len(pkt[PAYLOAD]) != 62 and len(pkt[PAYLOAD]) !=74:
1305
+ return False, f"Payload length is invalid for packet {pkt[PAYLOAD]}"
1306
+ return True, ""
1307
+
1308
+
1309
+ def wiliot_pkts_validation(test, all_messages_in_test, all_data_pkts):
1310
+ PHASE_NAME = "Wiliot packets validation"
1311
+ phase_run_print(PHASE_NAME)
1312
+ wiliot_pkts_validation_phase = cert_utils.Phase(PHASE_NAME, rc=TEST_PASSED)
1313
+
1314
+ if len(all_messages_in_test) == 0:
1315
+ wiliot_pkts_validation_phase.rc = TEST_FAILED
1316
+ wiliot_pkts_validation_phase.reason = "No packets to validate"
1317
+
1318
+ if wiliot_pkts_validation_phase.rc != TEST_FAILED:
1319
+ wiliot_pkts_validation_phase = timestamps_validation(wiliot_pkts_validation_phase, all_messages_in_test, test.dut.upload_wait_time)
1320
+
1321
+ if wiliot_pkts_validation_phase.rc != TEST_FAILED:
1322
+ wiliot_pkts_validation_phase = seq_id_validation(wiliot_pkts_validation_phase, all_data_pkts)
1323
+
1324
+ field_functionality_pass_fail_print(wiliot_pkts_validation_phase, PHASE_NAME)
1325
+ test.add_phase(wiliot_pkts_validation_phase)
1326
+ return test
1327
+
1328
+
1329
+ def seq_id_validation(wiliot_pkts_validation_phase, all_data_pkts):
1330
+ # Dedup packet if it is aggregated packets with the same sequenceId
1331
+ all_data_pkts = dedup_aggregated_packets(all_data_pkts)
1332
+ required_sequenceId = 0
1333
+ # check that for every packet received the sequenceId is incremental:
1334
+ for idx, pkt in enumerate(all_data_pkts):
1335
+ # check that there is sequenceId in all packets
1336
+ if SEQUENCE_ID not in pkt:
1337
+ wiliot_pkts_validation_phase.rc = TEST_FAILED
1338
+ wiliot_pkts_validation_phase.reason = f'No sequenceId in packet {pkt[PAYLOAD]}.'
1339
+ break
1340
+ if idx == 0:
1341
+ required_sequenceId = all_data_pkts[0][SEQUENCE_ID]
1342
+ pkt_sequenceId = pkt[SEQUENCE_ID]
1343
+ if pkt_sequenceId != required_sequenceId:
1344
+ wiliot_pkts_validation_phase.rc = TEST_FAILED
1345
+ wiliot_pkts_validation_phase.reason = (f'SequenceId is not incremental. Expected {required_sequenceId}, received {pkt_sequenceId}, '
1346
+ 'this may be caused by packets drops in the wifi chip side')
1347
+ break
1348
+ required_sequenceId += 1
1349
+ if len(all_data_pkts) == 0:
1350
+ wiliot_pkts_validation_phase.rc = TEST_FAILED
1351
+ wiliot_pkts_validation_phase.reason = "No packets to validate"
1352
+ return wiliot_pkts_validation_phase
1353
+
1354
+
1355
+ def dedup_aggregated_packets(all_data_pkts):
1356
+ deduped_packets = []
1357
+ for pkt in all_data_pkts:
1358
+ if AGGREGATED_PAYLOAD not in pkt:
1359
+ deduped_packets.append(pkt)
1360
+ else:
1361
+ # Check if a packet with the same AGGREGATED_PAYLOAD already exists
1362
+ already_exists = any(existing_pkt[AGGREGATED_PAYLOAD] == pkt[AGGREGATED_PAYLOAD]
1363
+ for existing_pkt in deduped_packets
1364
+ if AGGREGATED_PAYLOAD in existing_pkt)
1365
+ if not already_exists:
1366
+ deduped_packets.append(pkt)
1367
+
1368
+ return deduped_packets
1369
+
1370
+
1371
+ def timestamps_validation(wiliot_pkts_validation_phase, all_messages_in_test, upload_wait_time):
1372
+ previous_ts = 0
1373
+ for full_pkt in all_messages_in_test:
1374
+ if full_pkt.mqtt_timestamp - full_pkt.body[TIMESTAMP] >= 10000:
1375
+ wiliot_pkts_validation_phase.rc = TEST_FAILED
1376
+ wiliot_pkts_validation_phase.reason = f'More then 10 seconds GW publication and MQTT receive time'
1377
+ return wiliot_pkts_validation_phase
1378
+
1379
+ if PACKETS in full_pkt.body_ex:
1380
+ for idx, inner_pkt in enumerate(full_pkt.body_ex[PACKETS]):
1381
+ if TIMESTAMP not in inner_pkt:
1382
+ wiliot_pkts_validation_phase.rc = TEST_FAILED
1383
+ wiliot_pkts_validation_phase.reason = f"No timestamps in inner packet, for pkt {inner_pkt}"
1384
+ return wiliot_pkts_validation_phase
1385
+ if idx == 0:
1386
+ if full_pkt.body[TIMESTAMP] - inner_pkt[TIMESTAMP] >= 10000 + upload_wait_time and \
1387
+ previous_ts < inner_pkt[TIMESTAMP]:
1388
+ wiliot_pkts_validation_phase.rc = TEST_FAILED
1389
+ wiliot_pkts_validation_phase.reason = f'More then {10 + upload_wait_time}'
1390
+ f' seconds between publication and inner message,for ts {inner_pkt[TIMESTAMP]}'
1391
+ return wiliot_pkts_validation_phase
1392
+ previous_ts = inner_pkt[TIMESTAMP]
1393
+ else:
1394
+ if inner_pkt[TIMESTAMP] < previous_ts:
1395
+ wiliot_pkts_validation_phase.rc = TEST_FAILED
1396
+ wiliot_pkts_validation_phase.reason = f'Timestamp is not incremental for inner packet {inner_pkt[PAYLOAD]}'
1397
+ return wiliot_pkts_validation_phase
1398
+ previous_ts = inner_pkt[TIMESTAMP]
1399
+ return wiliot_pkts_validation_phase
1400
+
1401
+ ################### Stress test helper functions ###################
1402
+
1403
+ def generate_graph_stress_test(test, results, test_pkts_received):
1404
+ # Create DataFrame from results list [pps1, percentage1, pps2, percentage2, ...]
1405
+ graph_data = []
1406
+ for i in range(0, len(results), 2):
1407
+ if i + 1 < len(results):
1408
+ graph_data.append({'pkts_per_sec': results[i], 'received_pps': results[i + 1]})
1409
+
1410
+ graph_df = pd.DataFrame(graph_data)
1411
+ html_file_path = os.path.join(ARTIFACTS_DIR, test.dir, 'stress_graph.html')
1412
+
1413
+ # First graph: percentage received vs packets per second
1414
+ fig1 = px.line(graph_df, x='pkts_per_sec', y='received_pps',
1415
+ title='Packets Per Second Uploaded vs Packets Per Second Advertised',
1416
+ labels={'pkts_per_sec': 'Advertised PPS', 'received_pps': 'Uploaded PPS'},
1417
+ markers=True, text='received_pps')
1418
+
1419
+ # Set y-axis to [0 - highest_pps] scale
1420
+ fig1.update_yaxes(range=[0, results[-2]])
1421
+
1422
+ # Position text labels next to the markers
1423
+ fig1.update_traces(textposition="top right")
1424
+
1425
+ # Second graph: sequenceID vs time
1426
+ seq_id_fig = None
1427
+ seq_id_data = []
1428
+ for pkt in test_pkts_received:
1429
+ if SEQUENCE_ID in pkt and TIMESTAMP in pkt:
1430
+ seq_id_data.append({
1431
+ SEQUENCE_ID: pkt[SEQUENCE_ID],
1432
+ TIMESTAMP: pkt[TIMESTAMP]
1433
+ })
1434
+
1435
+ if seq_id_data:
1436
+ seq_id_df = pd.DataFrame(seq_id_data)
1437
+ seq_id_df = seq_id_df.sort_values(by=TIMESTAMP)
1438
+ # Convert timestamp to datetime for better display
1439
+ seq_id_df['datetime'] = seq_id_df[TIMESTAMP].apply(lambda x: datetime.datetime.fromtimestamp(x/1e3))
1440
+ seq_id_fig = px.scatter(seq_id_df, x='datetime', y=SEQUENCE_ID,
1441
+ title='Sequence ID vs Time',
1442
+ labels={'datetime': 'Time', SEQUENCE_ID: 'Sequence ID'})
1443
+
1444
+ # Write both graphs to HTML file
1445
+ with open(html_file_path, 'w') as f:
1446
+ f.write(fig1.to_html(full_html=False, include_plotlyjs='cdn', include_mathjax='cdn'))
1447
+ f.write("<br>")
1448
+ if seq_id_fig:
1449
+ f.write(seq_id_fig.to_html(full_html=False, include_plotlyjs='cdn', include_mathjax='cdn'))
1450
+
1451
+
1452
+ def stress_analysis(test, pps, sent_pkts, received_pkts):
1453
+
1454
+ _sent_pkts = [p[12:] for p in sent_pkts]
1455
+ sent_df = pd.DataFrame(_sent_pkts, columns=[PACKETS])
1456
+ received_df = pd.DataFrame(received_pkts, columns=[PACKETS])
1457
+
1458
+ # No need to drop duplicates, in the stress test each packet is sent only once
1459
+ merged_df = pd.merge(sent_df, received_df, on=PACKETS, how='inner')
1460
+ pkts_sent_count = len(sent_df)
1461
+ pkts_received_count = len(merged_df)
1462
+
1463
+ # Prints and calculations
1464
+ percentage_received = round(pkts_received_count * 100 / pkts_sent_count)
1465
+ utPrint(f'Sent: {pkts_sent_count}, Received: {pkts_received_count} ({percentage_received}%)', "BLUE")
1466
+ received_pps = pps * percentage_received / 100
1467
+
1468
+ # PASS/FAIL logic
1469
+ if percentage_received < 1: # If less than 1% of the packets were received, fail the test
1470
+ test.set_phase_rc(str(pps), TEST_FAILED)
1471
+ test.add_phase_reason(str(pps), f"{percentage_received}% of the packets were scanned & uploaded by the gateway")
1472
+ else:
1473
+ test.set_phase_rc(str(pps), TEST_PASSED)
1474
+ test.add_phase_reason(str(pps), f"received pps: {received_pps} ({percentage_received}% of packets)")
1475
+
1476
+ return test, received_pps
1477
+
1478
+
1479
+ def generate_adv_payload(test_indicator, unique_pkt=False):
1480
+ # Keep last 4 bytes as zeroes so for incrementing them in FW 'ble_sim'
1481
+ adva = hex2alias_id_get(get_random_hex_str(12))
1482
+ unique_pkt_byte = get_random_hex_str(2) if unique_pkt == False else "00"
1483
+ payload = (ag.Hdr(group_id=ag.GROUP_ID_UNIFIED_PKT_V2).dump() + test_indicator +
1484
+ get_random_hex_str(32) + unique_pkt_byte + "00000000")
1485
+ return adva + payload
1486
+
1487
+ def change_endianness(hex_str: str) -> str:
1488
+ return ''.join(f"{b:02X}" for b in bytes.fromhex(hex_str)[::-1])