wiliot-certificate 4.5.0a2__py3-none-any.whl → 4.5.0a4__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 (65) hide show
  1. certificate/cert_common.py +35 -18
  2. certificate/cert_config.py +6 -6
  3. certificate/cert_data_sim.py +12 -9
  4. certificate/cert_defines.py +6 -0
  5. certificate/cert_gw_sim.py +3 -3
  6. certificate/cert_mqtt.py +5 -4
  7. certificate/cert_results.py +42 -32
  8. certificate/cert_utils.py +9 -10
  9. certificate/certificate.py +7 -5
  10. certificate/certificate_cli.py +9 -12
  11. certificate/certificate_eth_test_list.txt +3 -2
  12. certificate/certificate_sanity_test_list.txt +3 -2
  13. certificate/certificate_test_list.txt +3 -3
  14. certificate/tests/cloud_connectivity/acl_test/acl_test.py +13 -15
  15. certificate/tests/cloud_connectivity/brg_ota_test/brg_ota_test.json +1 -1
  16. certificate/tests/cloud_connectivity/channel_scan_behaviour_test/channel_scan_behaviour_test.py +2 -2
  17. certificate/tests/cloud_connectivity/connection_test/connection_test.py +4 -13
  18. certificate/tests/cloud_connectivity/deduplication_test/deduplication_test.py +1 -2
  19. certificate/tests/cloud_connectivity/downlink_test/downlink_test.py +1 -4
  20. certificate/tests/cloud_connectivity/ext_adv_stress_test/ext_adv_stress_test.py +12 -6
  21. certificate/tests/cloud_connectivity/registration_test/registration_test_cli.py +1 -1
  22. certificate/tests/cloud_connectivity/stress_test/stress_test.py +12 -7
  23. certificate/tests/cloud_connectivity/uplink_ext_adv_test/uplink_ext_adv_test.py +1 -2
  24. certificate/tests/cloud_connectivity/uplink_test/uplink_test.py +26 -20
  25. certificate/tests/datapath/event_ble5_test/event_ble5_test.json +1 -1
  26. certificate/tests/datapath/event_ble5_test/event_ble5_test.py +7 -13
  27. certificate/tests/datapath/event_test/event_test.json +1 -1
  28. certificate/tests/datapath/event_test/event_test.py +5 -10
  29. certificate/tests/datapath/pacer_interval_ble5_test/pacer_interval_ble5_test.py +4 -4
  30. certificate/tests/datapath/pkt_filter_ble5_chl21_test/pkt_filter_ble5_chl21_test.py +5 -5
  31. certificate/tests/datapath/pkt_filter_ble5_test/pkt_filter_ble5_test.py +5 -5
  32. certificate/tests/datapath/pkt_filter_brg2gw_ext_adv_test/pkt_filter_brg2gw_ext_adv_test.py +10 -8
  33. certificate/tests/datapath/rx_rate_gen2_test/rx_rate_gen2_test.py +1 -1
  34. certificate/tests/energy2400/signal_indicator_ble5_test/signal_indicator_ble5_test.py +4 -3
  35. certificate/tests/energy2400/signal_indicator_ext_adv_test/signal_indicator_ext_adv_test.json +8 -9
  36. certificate/tests/energy2400/signal_indicator_ext_adv_test/signal_indicator_ext_adv_test.py +113 -271
  37. certificate/tests/energy2400/signal_indicator_test/signal_indicator_test.py +1 -1
  38. certificate/tests/sensors/ext_sensor_test/ext_sensor_test.py +4 -9
  39. common/api_if/api_validation.py +6 -0
  40. common/web/templates/generator.html +141 -79
  41. common/web/web_utils.py +78 -56
  42. gui_certificate/server.py +255 -70
  43. gui_certificate/templates/cert_run.html +128 -98
  44. {wiliot_certificate-4.5.0a2.dist-info → wiliot_certificate-4.5.0a4.dist-info}/METADATA +6 -11
  45. {wiliot_certificate-4.5.0a2.dist-info → wiliot_certificate-4.5.0a4.dist-info}/RECORD +49 -65
  46. certificate/ag/wlt_types_ag_jsons/brg2brg_ota.json +0 -211
  47. certificate/ag/wlt_types_ag_jsons/brg2gw_hb.json +0 -894
  48. certificate/ag/wlt_types_ag_jsons/brg2gw_hb_sleep.json +0 -184
  49. certificate/ag/wlt_types_ag_jsons/calibration.json +0 -490
  50. certificate/ag/wlt_types_ag_jsons/custom.json +0 -614
  51. certificate/ag/wlt_types_ag_jsons/datapath.json +0 -900
  52. certificate/ag/wlt_types_ag_jsons/energy2400.json +0 -670
  53. certificate/ag/wlt_types_ag_jsons/energySub1g.json +0 -691
  54. certificate/ag/wlt_types_ag_jsons/externalSensor.json +0 -727
  55. certificate/ag/wlt_types_ag_jsons/interface.json +0 -1095
  56. certificate/ag/wlt_types_ag_jsons/powerManagement.json +0 -1439
  57. certificate/ag/wlt_types_ag_jsons/side_info_sensor.json +0 -105
  58. certificate/ag/wlt_types_ag_jsons/signal_indicator_data.json +0 -77
  59. certificate/ag/wlt_types_ag_jsons/unified_echo_ext_pkt.json +0 -126
  60. certificate/ag/wlt_types_ag_jsons/unified_echo_pkt.json +0 -175
  61. certificate/ag/wlt_types_ag_jsons/unified_sensor_pkt.json +0 -65
  62. {wiliot_certificate-4.5.0a2.dist-info → wiliot_certificate-4.5.0a4.dist-info}/WHEEL +0 -0
  63. {wiliot_certificate-4.5.0a2.dist-info → wiliot_certificate-4.5.0a4.dist-info}/entry_points.txt +0 -0
  64. {wiliot_certificate-4.5.0a2.dist-info → wiliot_certificate-4.5.0a4.dist-info}/licenses/LICENSE +0 -0
  65. {wiliot_certificate-4.5.0a2.dist-info → wiliot_certificate-4.5.0a4.dist-info}/top_level.txt +0 -0
@@ -21,24 +21,53 @@
21
21
  <h1 style="color:#00AB83">{{ title }}</h1>
22
22
  <hr>
23
23
  <form class="form-horizontal" id="form" action="">
24
+ <div class="row mb-3">
25
+ <div class="col-md-6">
26
+ <label for="filter_category" class="form-label"><strong>Filter by Type:</strong></label>
27
+ <select class="form-select" id="filter_category" name="filter_category" onchange="applyFilters()">
28
+ <option value="">All Types</option>
29
+ {% for cat in categories %}
30
+ <option value="{{ cat }}" {% if cat == filter_category or (not filter_category and cat == 'Module') %}selected{% endif %}>{{ cat }}</option>
31
+ {% endfor %}
32
+ </select>
33
+ </div>
34
+ <div class="col-md-6">
35
+ <label for="filter_version" class="form-label"><strong>Filter by API Version:</strong></label>
36
+ <select class="form-select" id="filter_version" name="filter_version" onchange="applyFilters()">
37
+ <option value="">All Versions</option>
38
+ {% for ver in api_versions %}
39
+ <option value="{{ ver }}" {% if ver == filter_version or (not filter_version and ver|int == API_VERSION_LATEST) %}selected{% endif %}>V{{ ver }}</option>
40
+ {% endfor %}
41
+ </select>
42
+ </div>
43
+ </div>
44
+ <hr>
24
45
  <div id="builder_form">
25
46
  {% for n in pkt_types_list %}
26
- <div class="form-check form-check-inline">
27
- <input class="form-check-input" type="radio" name="radios" value="{{ n }}" onclick="show();">
28
- <label class="form-check-label" for="{{ n }}">{{ n }}</label>
47
+ <div class="form-check form-check-inline pkt-radio"
48
+ data-category="{{ pkt_metadata.get(n, {}).get('category', 'Other') }}"
49
+ data-version="{{ pkt_metadata.get(n, {}).get('api_version', '') }}">
50
+ <input class="form-check-input" type="radio" name="radios" value="{{ n }}" id="radio_{{ n }}"
51
+ {% if n == wanted_pkt_type %}checked{% endif %} onclick="show();">
52
+ <label class="form-check-label" for="radio_{{ n }}">{{ n }}</label>
29
53
  </div>
30
54
  {% endfor %}
31
55
  {% for n,p in pkt_types.items() %}
32
- <div id="{{ n }}_form">
56
+ <div id="{{ n }}_form" style="display: none;">
33
57
  <hr>
34
58
  {% for k,v in p.__dict__.items() %}
35
59
  <div class="row">
36
60
  <label for="{{ n }}_{{ k }}" class="col-sm-2">{{ k }}:</label>
37
- {% if k == 'brg_mac' or k == 'pkt_size' or k == 'ad_type' or k == 'uuid_msb' or k == 'uuid_lsb' or k == 'group_id' %}
38
- <input type="text" class="col-sm-4" name="{{ n }}_{{ k }}" value="{{ '0x'+('%0x' % v).upper() }}">
61
+ {% set field_name = n + '_' + k %}
62
+ {% set saved_value = form_values.get(field_name, '') %}
63
+ {% if saved_value %}
64
+ {% set display_value = saved_value %}
65
+ {% elif k == 'brg_mac' or k == 'pkt_size' or k == 'ad_type' or k == 'uuid_msb' or k == 'uuid_lsb' or k == 'group_id' %}
66
+ {% set display_value = '0x' + ('%0x' % v).upper() %}
39
67
  {% else %}
40
- <input type="text" class="col-sm-4" name="{{ n }}_{{ k }}" value="{{ v }}">
68
+ {% set display_value = v %}
41
69
  {% endif %}
70
+ <input type="text" class="col-sm-4" name="{{ n }}_{{ k }}" value="{{ display_value }}">
42
71
  </div>
43
72
  {% endfor %}
44
73
  </div>
@@ -63,86 +92,119 @@
63
92
  <br>
64
93
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
65
94
  <script>
66
- function show()
67
- {
68
- const form = document.getElementById('form');
69
- const group_id_brg2gw = {{ GROUP_ID_BRG2GW }};
70
- const group_id_gw2brg = {{ GROUP_ID_GW2BRG }};
71
- const group_id_side_info_sensor = {{ GROUP_ID_SIDE_INFO_SENSOR }};
72
- const group_id_side_info = {{ GROUP_ID_SIDE_INFO }};
73
- const group_id_signal_indicator = {{ GROUP_ID_SIGNAL_INDICATOR }};
74
- const group_id_unified_v0 = {{ GROUP_ID_UNIFIED_PKT_V0 }};
75
- const group_id_unified_v1 = {{ GROUP_ID_UNIFIED_PKT_V1 }};
76
- const group_id_unified_v2 = {{ GROUP_ID_UNIFIED_PKT_V2 }};
77
- const group_id_unified_ext_v0 = {{ GROUP_ID_BLE5_EXTENDED_V0 }};
78
- const group_id_unified_ext_v1 = {{ GROUP_ID_BLE5_EXTENDED_V1 }};
79
- var radios = document.getElementsByName("radios");
80
- var pktTypes = {{ pkt_types_list | tojson | safe }};
81
- console.log(pktTypes)
82
- var forms = [];
83
-
84
- for (var i = 0; i < pktTypes.length; i++) {
85
- forms[i] = document.getElementById(pktTypes[i]+"_form");
86
- if (pktTypes[i] != "Hdr"){
87
- forms[i].style.display = 'none';
95
+ var pktMetadata = {{ pkt_metadata | tojson | safe }};
96
+
97
+ function applyFilters() {
98
+ const categoryFilter = document.getElementById('filter_category').value;
99
+ const versionFilter = document.getElementById('filter_version').value;
100
+ const radioContainers = document.querySelectorAll('.pkt-radio');
101
+ radioContainers.forEach(function(container) {
102
+ const category = container.getAttribute('data-category');
103
+ const version = container.getAttribute('data-version');
104
+ const radio = container.querySelector('input[type="radio"]');
105
+ let show = true;
106
+ // Filter by category
107
+ if (categoryFilter && category !== categoryFilter) {
108
+ show = false;
88
109
  }
89
- }
90
-
91
- for(var i = 0; i < radios.length; i++) {
92
- radios[i].onclick = function() {
93
- for(var j = 0; j < radios.length; j++) {
94
- forms[j].style.display = 'none';
110
+ // Filter by version
111
+ if (versionFilter && version !== versionFilter) {
112
+ show = false;
95
113
  }
96
- if (this.value == "DataHdr"){
97
- document.getElementById("Hdr_form").style.display = 'none';
98
- }
99
- else{
100
- document.getElementById("Hdr_form").style.display = 'block';
101
- var default_value = 0;
102
- if (this.value.includes("Brg2Gw")){
103
- default_value = group_id_brg2gw;
104
- }
105
- else if (this.value.includes("Gw2Brg")){
106
- default_value = group_id_gw2brg;
107
- }
108
- else if (this.value.includes("SignalIndicator")){
109
- default_value = group_id_signal_indicator;
110
- }
111
- else if (this.value.includes("SideInfoSensor")){
112
- default_value = group_id_side_info_sensor;
113
- }
114
- else if (this.value.includes("SideInfo")){
115
- default_value = group_id_side_info;
116
- }
117
- else if (this.value.includes("Unified")){
118
- if (this.value.includes("V0")){
119
- if (this.value.includes("Ext")){
120
- default_value = group_id_unified_ext_v0
121
- }
122
- else {
123
- default_value = group_id_unified_v0
124
- }
125
- }
126
- else if (this.value.includes("V1")){
127
- if (this.value.includes("Ext")){
128
- default_value = group_id_unified_ext_v1
129
- }
130
- else {
131
- default_value = group_id_unified_v1
132
- }
133
- }
134
- else if (this.value.includes("V2")){
135
- default_value = group_id_unified_v2
136
- }
114
+ // Show/hide the container
115
+ if (show) {
116
+ container.style.display = 'inline-block';
117
+ } else {
118
+ container.style.display = 'none';
119
+ // Uncheck if hidden
120
+ if (radio.checked) {
121
+ radio.checked = false;
137
122
  }
123
+ }
124
+ });
125
+ }
126
+
127
+ const form = document.getElementById('form');
128
+ const group_id_brg2gw = {{ GROUP_ID_BRG2GW }};
129
+ const group_id_gw2brg = {{ GROUP_ID_GW2BRG }};
130
+ const group_id_side_info_sensor = {{ GROUP_ID_SIDE_INFO_SENSOR }};
131
+ const group_id_side_info = {{ GROUP_ID_SIDE_INFO }};
132
+ const group_id_signal_indicator = {{ GROUP_ID_SIGNAL_INDICATOR }};
133
+ const group_id_unified_v0 = {{ GROUP_ID_UNIFIED_PKT_V0 }};
134
+ const group_id_unified_v1 = {{ GROUP_ID_UNIFIED_PKT_V1 }};
135
+ const group_id_unified_v2 = {{ GROUP_ID_UNIFIED_PKT_V2 }};
136
+ const group_id_unified_ext_v0 = {{ GROUP_ID_BLE5_EXTENDED_V0 }};
137
+ const group_id_unified_ext_v1 = {{ GROUP_ID_BLE5_EXTENDED_V1 }};
138
+ var pktTypes = {{ pkt_types_list | tojson | safe }};
139
+
140
+ function showPktForm(pktType) {
141
+ // Hide all packet forms
142
+ pktTypes.forEach(function(type) {
143
+ var formEl = document.getElementById(type + "_form");
144
+ if (formEl) formEl.style.display = 'none';
145
+ });
146
+
147
+ // Show appropriate header and packet form
148
+ if (pktType && pktType.includes("Unified")) {
149
+ document.getElementById("Hdr_form").style.display = 'none';
150
+ document.getElementById("DataHdr_form").style.display = 'block';
151
+ var default_value = 0;
152
+ if (pktType.includes("V0")) {
153
+ default_value = pktType.includes("Ext") ? group_id_unified_ext_v0 : group_id_unified_v0;
154
+ } else if (pktType.includes("V1")) {
155
+ default_value = pktType.includes("Ext") ? group_id_unified_ext_v1 : group_id_unified_v1;
156
+ } else if (pktType.includes("V2")) {
157
+ default_value = group_id_unified_v2;
158
+ }
159
+ if (form.elements.DataHdr_group_id_major) {
160
+ form.elements.DataHdr_group_id_major.value = "0x" + default_value.toString(16).toUpperCase();
161
+ }
162
+ } else {
163
+ document.getElementById("Hdr_form").style.display = 'block';
164
+ document.getElementById("DataHdr_form").style.display = 'none';
165
+ var default_value = 0;
166
+ if (pktType && pktType.includes("Brg2Gw")) {
167
+ default_value = group_id_brg2gw;
168
+ } else if (pktType && pktType.includes("Gw2Brg")) {
169
+ default_value = group_id_gw2brg;
170
+ } else if (pktType && pktType.includes("SignalIndicator")) {
171
+ default_value = group_id_signal_indicator;
172
+ } else if (pktType && pktType.includes("SideInfoSensor")) {
173
+ default_value = group_id_side_info_sensor;
174
+ } else if (pktType && pktType.includes("SideInfo")) {
175
+ default_value = group_id_side_info;
176
+ }
177
+ if (form.elements.Hdr_group_id) {
138
178
  form.elements.Hdr_group_id.value = "0x" + default_value.toString(16).toUpperCase();
139
179
  }
140
- console.log(this.value)
141
- document.getElementById(this.value + "_form").style.display = 'block';
142
180
  }
181
+
182
+ // Show the selected packet form
183
+ if (pktType) {
184
+ var pktForm = document.getElementById(pktType + "_form");
185
+ if (pktForm) pktForm.style.display = 'block';
186
+ }
187
+ }
188
+
189
+ function show() {
190
+ // Set up click handlers
191
+ var radios = document.getElementsByName("radios");
192
+ for (var i = 0; i < radios.length; i++) {
193
+ radios[i].onclick = function() {
194
+ showPktForm(this.value);
195
+ };
196
+ }
197
+
198
+ // Show form for checked radio (if any)
199
+ for (var i = 0; i < radios.length; i++) {
200
+ if (radios[i].checked) {
201
+ showPktForm(radios[i].value);
202
+ break;
203
+ }
143
204
  }
144
205
  }
145
- show();
206
+ applyFilters(); // Apply filters on page load
207
+ show(); // Initialize form display (will show selected packet form if one is checked)
146
208
  </script>
147
209
  </body>
148
210
  </html>
common/web/web_utils.py CHANGED
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
  from flask import render_template, request
3
- import inspect
4
3
  import argparse
5
4
  import json
6
5
  import re
@@ -28,7 +27,7 @@ FDM = "FDM"
28
27
  CERT_WEB = "CERTIFICATE WEB"
29
28
 
30
29
  # Defines to update jinja_env globals
31
- GLOBAL_DEFINES_TO_UPDATE = {k:v for k,v in ag.__dict__.items() if 'GROUP_ID' in k}
30
+ GLOBAL_DEFINES_TO_UPDATE = {k:v for k,v in ag.__dict__.items() if 'GROUP_ID' in k or 'API_VERSION_LATEST' in k}
32
31
 
33
32
  TestSchema = Dict[str, Any]
34
33
  ModuleSchema = Dict[str, Any]
@@ -63,46 +62,90 @@ def utils_parser(app):
63
62
  def utils_generator(app):
64
63
  title = "Packet Generator"
65
64
  output = ""
66
- wanted_pkt_type = request.args.get('radios')
67
- pkt = None
68
- dont_generate = ["Generic", "Gw2BrgHb", "PktTypesMask", "WltPkt"]
65
+ def parse_int_input(x, default=0):
66
+ x = str(x).strip()
67
+ if x.startswith('0x') or x.startswith('0X'):
68
+ return int(x, 16)
69
+ try:
70
+ return int(x)
71
+ except ValueError:
72
+ return default
73
+
74
+ def build_and_dump_pkt(pkt_type):
75
+ pkt_template = getattr(ag, pkt_type)()
76
+ pkt_params = {
77
+ k: parse_int_input(request.args.get(f"{pkt_type}_{k}"), pkt_template.__dict__.get(k, 0))
78
+ for k in pkt_template.__dict__.keys()
79
+ }
80
+ pkt = getattr(ag, pkt_type)(**pkt_params)
81
+ try:
82
+ return pkt.dump()
83
+ except Exception as e:
84
+ return f"<br>Error encoding {pkt_type} (bit structure error): {str(e)}<br>"
85
+
86
+ # Build packet types dictionary
87
+ dont_generate_strs = ["Cfg", "SideInfo"]
69
88
  wlt_pkt_types = {
70
- name: cls()
71
- for name, cls in inspect.getmembers(ag, inspect.isclass)
72
- if cls.__module__ == ag.__name__ and name not in dont_generate
89
+ 'Hdr': ag.Hdr(),
90
+ 'DataHdr': ag.DataHdr(),
91
+ **{cls.__name__: cls() for cls in ag.WLT_PKT_TYPES if not any([dg in cls.__name__ for dg in dont_generate_strs])}
73
92
  }
74
- # Find selected pkt type
75
- for name ,p in wlt_pkt_types.items():
76
- if name == wanted_pkt_type:
77
- pkt = p
78
- break
79
93
 
80
- # Handle Hdr
81
- hdr = wlt_pkt_types['Hdr']
82
- wlt_pkt_types = {'Hdr': hdr, **{k: v for k, v in wlt_pkt_types.items() if k != 'Hdr'}} # Put 'Hdr' first
83
- hdr_string = ""
84
- for k in hdr.__dict__.keys():
85
- if not request.args.get(f"Hdr_{k}"):
86
- hdr_string += "{}={},".format(k, str(hdr.__dict__[k]))
94
+ # Categorize packets and extract API versions
95
+ import re
96
+ pkt_metadata = {}
97
+ for pkt_name in list(wlt_pkt_types.keys())[2:]: # Skip Hdr and DataHdr
98
+ # Determine category
99
+ if pkt_name.startswith('Unified'):
100
+ category = 'Unified'
101
+ elif pkt_name.startswith('Module'):
102
+ category = 'Module'
103
+ elif pkt_name.startswith('Action'):
104
+ category = 'Action'
105
+ elif pkt_name.startswith('Brg2Brg'):
106
+ category = 'Brg2Brg'
107
+ elif pkt_name.startswith('Brg2Gw'):
108
+ category = 'Brg2Gw'
109
+ elif pkt_name.startswith('Sensor'):
110
+ category = 'Sensor'
87
111
  else:
88
- hdr_string += "{}={},".format(k, str(request.args.get(f"Hdr_{k}")))
89
- hdr = eval_pkt(f"Hdr({hdr_string[:-1]})")
90
- output += hdr.dump()
112
+ category = 'Other'
113
+
114
+ # Extract API version
115
+ version_match = re.search(r'V(\d+)$', pkt_name)
116
+ api_version = version_match.group(1) if version_match else None
117
+
118
+ pkt_metadata[pkt_name] = {
119
+ 'category': category,
120
+ 'api_version': api_version
121
+ }
122
+
123
+ # Get unique categories and API versions for filters
124
+ categories = sorted(set(m['category'] for m in pkt_metadata.values()))
125
+ api_versions = sorted(set(m['api_version'] for m in pkt_metadata.values() if m['api_version']),
126
+ key=lambda x: int(x) if x else 0, reverse=True)
127
+
128
+ wanted_pkt_type = request.args.get('radios')
129
+ if wanted_pkt_type:
130
+ # Determine header type and build header
131
+ hdr_type = 'DataHdr' if 'Unified' in wanted_pkt_type else 'Hdr'
132
+ output += build_and_dump_pkt(hdr_type)
133
+ output += build_and_dump_pkt(wanted_pkt_type)
134
+
135
+ # Get filter values to preserve them
136
+ filter_category = request.args.get('filter_category', '')
137
+ filter_version = request.args.get('filter_version', '')
138
+
139
+ # Get all form values to preserve them (convert MultiDict to regular dict, taking first value)
140
+ form_values = {k: v for k, v in request.args.items()}
91
141
 
92
- # Handle selected pkt
93
- if pkt:
94
- pkt_string = ""
95
- for k in pkt.__dict__.keys():
96
- if not request.args.get(f"{type(pkt).__name__}_{k}"):
97
- pkt_string += "{}={},".format(k, str(0))
98
- else:
99
- pkt_string += "{}={},".format(k, str(request.args.get(f"{type(pkt).__name__}_{k}")))
100
- pkt = eval_pkt(f"{type(pkt).__name__}({pkt_string[0:-1]})")
101
- output += pkt.dump()
102
142
  print(output)
103
143
  return render_template('generator.html',
104
144
  app=app, title=title, output=txt2html(output),
105
- pkt_types=wlt_pkt_types, pkt_types_list=[k for k in wlt_pkt_types.keys() if k != 'Hdr'])
145
+ pkt_types=wlt_pkt_types, pkt_types_list=list(wlt_pkt_types.keys())[2:],
146
+ pkt_metadata=pkt_metadata, categories=categories, api_versions=api_versions,
147
+ wanted_pkt_type=wanted_pkt_type, form_values=form_values,
148
+ filter_category=filter_category, filter_version=filter_version)
106
149
 
107
150
  def utils_tag2brg(app):
108
151
  title = "Tag to Bridge Packet Converter"
@@ -295,26 +338,6 @@ def _read_json_safe(p: Path) -> Dict[str, Any]:
295
338
  except Exception:
296
339
  return {}
297
340
 
298
- def _tooltip(meta: Dict[str, Any], fallback: str) -> str:
299
- if not meta:
300
- return fallback
301
- # prefer human fields; keep it short
302
- parts: List[str] = []
303
- # for k in ("name", "title"):
304
- # if isinstance(meta.get(k), str):
305
- # parts.append(meta[k].strip())
306
- for k in ("description", "desc", "objective", "purpose"):
307
- if isinstance(meta.get(k), str):
308
- parts.append(meta[k].strip())
309
- break
310
- if parts:
311
- return "\n".join(parts[:2])
312
- try:
313
- s = json.dumps(meta, ensure_ascii=False, separators=(",", ":"))
314
- return s[:240] + ("…" if len(s) > 240 else "")
315
- except Exception:
316
- return fallback
317
-
318
341
  def scan_tests_dir(root: str | Path) -> Dict[str, Any]:
319
342
  """
320
343
  Expect: <root>/<module>/<test>/{<test>.py, <test>.json}
@@ -359,13 +382,12 @@ def scan_tests_dir(root: str | Path) -> Dict[str, Any]:
359
382
  "id": tid,
360
383
  "module": mod_dir.name,
361
384
  "name": test_dir.name,
362
- # "label": test_dir.name.replace("_", " "),
363
385
  "label": meta.get('name', test_dir.name.replace("_", " ")),
364
386
  "dir": str(test_dir),
365
387
  "py": str(py) if py else "",
366
388
  "json_path": str(j) if j else "",
367
389
  "meta": meta,
368
- "tooltip": _tooltip(meta, test_dir.name),
390
+ "tooltip": meta.get('purpose', ""),
369
391
  }
370
392
  tests.append(item)
371
393
  if py: