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