wiliot-certificate 1.3.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 (51) hide show
  1. gw_certificate/__init__.py +0 -0
  2. gw_certificate/ag/ut_defines.py +361 -0
  3. gw_certificate/ag/wlt_types.py +85 -0
  4. gw_certificate/ag/wlt_types_ag.py +5310 -0
  5. gw_certificate/ag/wlt_types_data.py +64 -0
  6. gw_certificate/api/extended_api.py +1547 -0
  7. gw_certificate/api_if/__init__.py +0 -0
  8. gw_certificate/api_if/api_validation.py +40 -0
  9. gw_certificate/api_if/gw_capabilities.py +18 -0
  10. gw_certificate/common/analysis_data_bricks.py +1455 -0
  11. gw_certificate/common/debug.py +63 -0
  12. gw_certificate/common/utils.py +219 -0
  13. gw_certificate/common/utils_defines.py +102 -0
  14. gw_certificate/common/wltPb_pb2.py +72 -0
  15. gw_certificate/common/wltPb_pb2.pyi +227 -0
  16. gw_certificate/gw_certificate.py +138 -0
  17. gw_certificate/gw_certificate_cli.py +70 -0
  18. gw_certificate/interface/ble_simulator.py +91 -0
  19. gw_certificate/interface/ble_sniffer.py +189 -0
  20. gw_certificate/interface/if_defines.py +35 -0
  21. gw_certificate/interface/mqtt.py +469 -0
  22. gw_certificate/interface/packet_error.py +22 -0
  23. gw_certificate/interface/pkt_generator.py +720 -0
  24. gw_certificate/interface/uart_if.py +193 -0
  25. gw_certificate/interface/uart_ports.py +20 -0
  26. gw_certificate/templates/results.html +241 -0
  27. gw_certificate/templates/stage.html +22 -0
  28. gw_certificate/templates/table.html +6 -0
  29. gw_certificate/templates/test.html +38 -0
  30. gw_certificate/tests/__init__.py +11 -0
  31. gw_certificate/tests/actions.py +131 -0
  32. gw_certificate/tests/bad_crc_to_PER_quantization.csv +51 -0
  33. gw_certificate/tests/connection.py +181 -0
  34. gw_certificate/tests/downlink.py +174 -0
  35. gw_certificate/tests/generic.py +161 -0
  36. gw_certificate/tests/registration.py +288 -0
  37. gw_certificate/tests/static/__init__.py +0 -0
  38. gw_certificate/tests/static/connection_defines.py +9 -0
  39. gw_certificate/tests/static/downlink_defines.py +9 -0
  40. gw_certificate/tests/static/generated_packet_table.py +209 -0
  41. gw_certificate/tests/static/packet_table.csv +10051 -0
  42. gw_certificate/tests/static/references.py +4 -0
  43. gw_certificate/tests/static/uplink_defines.py +20 -0
  44. gw_certificate/tests/throughput.py +244 -0
  45. gw_certificate/tests/uplink.py +683 -0
  46. wiliot_certificate-1.3.0a1.dist-info/LICENSE +21 -0
  47. wiliot_certificate-1.3.0a1.dist-info/METADATA +113 -0
  48. wiliot_certificate-1.3.0a1.dist-info/RECORD +51 -0
  49. wiliot_certificate-1.3.0a1.dist-info/WHEEL +5 -0
  50. wiliot_certificate-1.3.0a1.dist-info/entry_points.txt +2 -0
  51. wiliot_certificate-1.3.0a1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,227 @@
1
+ from google.protobuf.internal import containers as _containers
2
+ from google.protobuf import descriptor as _descriptor
3
+ from google.protobuf import message as _message
4
+ from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union
5
+
6
+ DESCRIPTOR: _descriptor.FileDescriptor
7
+
8
+ class Location(_message.Message):
9
+ __slots__ = ("lat", "lng")
10
+ LAT_FIELD_NUMBER: _ClassVar[int]
11
+ LNG_FIELD_NUMBER: _ClassVar[int]
12
+ lat: float
13
+ lng: float
14
+ def __init__(self, lat: _Optional[float] = ..., lng: _Optional[float] = ...) -> None: ...
15
+
16
+ class Value(_message.Message):
17
+ __slots__ = ("integerValue", "numberValue", "stringValue", "boolValue")
18
+ INTEGERVALUE_FIELD_NUMBER: _ClassVar[int]
19
+ NUMBERVALUE_FIELD_NUMBER: _ClassVar[int]
20
+ STRINGVALUE_FIELD_NUMBER: _ClassVar[int]
21
+ BOOLVALUE_FIELD_NUMBER: _ClassVar[int]
22
+ integerValue: int
23
+ numberValue: float
24
+ stringValue: str
25
+ boolValue: bool
26
+ def __init__(self, integerValue: _Optional[int] = ..., numberValue: _Optional[float] = ..., stringValue: _Optional[str] = ..., boolValue: bool = ...) -> None: ...
27
+
28
+ class GatewayData(_message.Message):
29
+ __slots__ = ("gatewayId", "timestamp", "packets", "location")
30
+ class Packet(_message.Message):
31
+ __slots__ = ("payload", "timestamp", "sequenceId", "rssi", "aliasBridgeId")
32
+ PAYLOAD_FIELD_NUMBER: _ClassVar[int]
33
+ TIMESTAMP_FIELD_NUMBER: _ClassVar[int]
34
+ SEQUENCEID_FIELD_NUMBER: _ClassVar[int]
35
+ RSSI_FIELD_NUMBER: _ClassVar[int]
36
+ ALIASBRIDGEID_FIELD_NUMBER: _ClassVar[int]
37
+ payload: bytes
38
+ timestamp: int
39
+ sequenceId: int
40
+ rssi: int
41
+ aliasBridgeId: str
42
+ def __init__(self, payload: _Optional[bytes] = ..., timestamp: _Optional[int] = ..., sequenceId: _Optional[int] = ..., rssi: _Optional[int] = ..., aliasBridgeId: _Optional[str] = ...) -> None: ...
43
+ GATEWAYID_FIELD_NUMBER: _ClassVar[int]
44
+ TIMESTAMP_FIELD_NUMBER: _ClassVar[int]
45
+ PACKETS_FIELD_NUMBER: _ClassVar[int]
46
+ LOCATION_FIELD_NUMBER: _ClassVar[int]
47
+ gatewayId: str
48
+ timestamp: int
49
+ packets: _containers.RepeatedCompositeFieldContainer[GatewayData.Packet]
50
+ location: Location
51
+ def __init__(self, gatewayId: _Optional[str] = ..., timestamp: _Optional[int] = ..., packets: _Optional[_Iterable[_Union[GatewayData.Packet, _Mapping]]] = ..., location: _Optional[_Union[Location, _Mapping]] = ...) -> None: ...
52
+
53
+ class UplinkMessage(_message.Message):
54
+ __slots__ = ("gatewayStatus", "gatewayInfo", "gatewayLogs", "actionStatus")
55
+ GATEWAYSTATUS_FIELD_NUMBER: _ClassVar[int]
56
+ GATEWAYINFO_FIELD_NUMBER: _ClassVar[int]
57
+ GATEWAYLOGS_FIELD_NUMBER: _ClassVar[int]
58
+ ACTIONSTATUS_FIELD_NUMBER: _ClassVar[int]
59
+ gatewayStatus: GatewayStatus
60
+ gatewayInfo: GatewayInfo
61
+ gatewayLogs: GatewayLogs
62
+ actionStatus: ActionStatus
63
+ def __init__(self, gatewayStatus: _Optional[_Union[GatewayStatus, _Mapping]] = ..., gatewayInfo: _Optional[_Union[GatewayInfo, _Mapping]] = ..., gatewayLogs: _Optional[_Union[GatewayLogs, _Mapping]] = ..., actionStatus: _Optional[_Union[ActionStatus, _Mapping]] = ...) -> None: ...
64
+
65
+ class GatewayStatus(_message.Message):
66
+ __slots__ = ("gatewayId", "gatewayType", "downlinkSupported", "bridgeOtaUpgradeSupported", "apiVersion", "version", "bleSwVersion", "interfaceSwVersion", "location", "config", "bleAddress")
67
+ class ConfigEntry(_message.Message):
68
+ __slots__ = ("key", "value")
69
+ KEY_FIELD_NUMBER: _ClassVar[int]
70
+ VALUE_FIELD_NUMBER: _ClassVar[int]
71
+ key: str
72
+ value: Value
73
+ def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[Value, _Mapping]] = ...) -> None: ...
74
+ GATEWAYID_FIELD_NUMBER: _ClassVar[int]
75
+ GATEWAYTYPE_FIELD_NUMBER: _ClassVar[int]
76
+ DOWNLINKSUPPORTED_FIELD_NUMBER: _ClassVar[int]
77
+ BRIDGEOTAUPGRADESUPPORTED_FIELD_NUMBER: _ClassVar[int]
78
+ APIVERSION_FIELD_NUMBER: _ClassVar[int]
79
+ VERSION_FIELD_NUMBER: _ClassVar[int]
80
+ BLESWVERSION_FIELD_NUMBER: _ClassVar[int]
81
+ INTERFACESWVERSION_FIELD_NUMBER: _ClassVar[int]
82
+ LOCATION_FIELD_NUMBER: _ClassVar[int]
83
+ CONFIG_FIELD_NUMBER: _ClassVar[int]
84
+ BLEADDRESS_FIELD_NUMBER: _ClassVar[int]
85
+ gatewayId: str
86
+ gatewayType: str
87
+ downlinkSupported: bool
88
+ bridgeOtaUpgradeSupported: bool
89
+ apiVersion: int
90
+ version: str
91
+ bleSwVersion: str
92
+ interfaceSwVersion: str
93
+ location: Location
94
+ config: _containers.MessageMap[str, Value]
95
+ bleAddress: str
96
+ def __init__(self, gatewayId: _Optional[str] = ..., gatewayType: _Optional[str] = ..., downlinkSupported: bool = ..., bridgeOtaUpgradeSupported: bool = ..., apiVersion: _Optional[int] = ..., version: _Optional[str] = ..., bleSwVersion: _Optional[str] = ..., interfaceSwVersion: _Optional[str] = ..., location: _Optional[_Union[Location, _Mapping]] = ..., config: _Optional[_Mapping[str, Value]] = ..., bleAddress: _Optional[str] = ...) -> None: ...
97
+
98
+ class GatewayInfo(_message.Message):
99
+ __slots__ = ("entries",)
100
+ class EntriesEntry(_message.Message):
101
+ __slots__ = ("key", "value")
102
+ KEY_FIELD_NUMBER: _ClassVar[int]
103
+ VALUE_FIELD_NUMBER: _ClassVar[int]
104
+ key: str
105
+ value: Value
106
+ def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[Value, _Mapping]] = ...) -> None: ...
107
+ ENTRIES_FIELD_NUMBER: _ClassVar[int]
108
+ entries: _containers.MessageMap[str, Value]
109
+ def __init__(self, entries: _Optional[_Mapping[str, Value]] = ...) -> None: ...
110
+
111
+ class GatewayLogs(_message.Message):
112
+ __slots__ = ("logs",)
113
+ LOGS_FIELD_NUMBER: _ClassVar[int]
114
+ logs: _containers.RepeatedScalarFieldContainer[str]
115
+ def __init__(self, logs: _Optional[_Iterable[str]] = ...) -> None: ...
116
+
117
+ class ActionStatus(_message.Message):
118
+ __slots__ = ("action", "status")
119
+ ACTION_FIELD_NUMBER: _ClassVar[int]
120
+ STATUS_FIELD_NUMBER: _ClassVar[int]
121
+ action: int
122
+ status: int
123
+ def __init__(self, action: _Optional[int] = ..., status: _Optional[int] = ...) -> None: ...
124
+
125
+ class DownlinkMessage(_message.Message):
126
+ __slots__ = ("txPacket", "gatewayAction", "bridgeUpgrade", "gatewayConfig", "customBroker", "customMessage")
127
+ TXPACKET_FIELD_NUMBER: _ClassVar[int]
128
+ GATEWAYACTION_FIELD_NUMBER: _ClassVar[int]
129
+ BRIDGEUPGRADE_FIELD_NUMBER: _ClassVar[int]
130
+ GATEWAYCONFIG_FIELD_NUMBER: _ClassVar[int]
131
+ CUSTOMBROKER_FIELD_NUMBER: _ClassVar[int]
132
+ CUSTOMMESSAGE_FIELD_NUMBER: _ClassVar[int]
133
+ txPacket: TxPacket
134
+ gatewayAction: GatewayAction
135
+ bridgeUpgrade: BridgeUpgrade
136
+ gatewayConfig: GatewayConfig
137
+ customBroker: CustomBroker
138
+ customMessage: CustomMessage
139
+ def __init__(self, txPacket: _Optional[_Union[TxPacket, _Mapping]] = ..., gatewayAction: _Optional[_Union[GatewayAction, _Mapping]] = ..., bridgeUpgrade: _Optional[_Union[BridgeUpgrade, _Mapping]] = ..., gatewayConfig: _Optional[_Union[GatewayConfig, _Mapping]] = ..., customBroker: _Optional[_Union[CustomBroker, _Mapping]] = ..., customMessage: _Optional[_Union[CustomMessage, _Mapping]] = ...) -> None: ...
140
+
141
+ class TxPacket(_message.Message):
142
+ __slots__ = ("payload", "maxRetries", "maxDurationMs")
143
+ PAYLOAD_FIELD_NUMBER: _ClassVar[int]
144
+ MAXRETRIES_FIELD_NUMBER: _ClassVar[int]
145
+ MAXDURATIONMS_FIELD_NUMBER: _ClassVar[int]
146
+ payload: bytes
147
+ maxRetries: int
148
+ maxDurationMs: int
149
+ def __init__(self, payload: _Optional[bytes] = ..., maxRetries: _Optional[int] = ..., maxDurationMs: _Optional[int] = ...) -> None: ...
150
+
151
+ class GatewayAction(_message.Message):
152
+ __slots__ = ("action",)
153
+ ACTION_FIELD_NUMBER: _ClassVar[int]
154
+ action: str
155
+ def __init__(self, action: _Optional[str] = ...) -> None: ...
156
+
157
+ class BridgeUpgrade(_message.Message):
158
+ __slots__ = ("rebootPacket", "txMaxDurationMs", "txMaxRetries", "bridgeId", "versionUuid", "upgradeBlSd", "imageDirUrl")
159
+ REBOOTPACKET_FIELD_NUMBER: _ClassVar[int]
160
+ TXMAXDURATIONMS_FIELD_NUMBER: _ClassVar[int]
161
+ TXMAXRETRIES_FIELD_NUMBER: _ClassVar[int]
162
+ BRIDGEID_FIELD_NUMBER: _ClassVar[int]
163
+ VERSIONUUID_FIELD_NUMBER: _ClassVar[int]
164
+ UPGRADEBLSD_FIELD_NUMBER: _ClassVar[int]
165
+ IMAGEDIRURL_FIELD_NUMBER: _ClassVar[int]
166
+ rebootPacket: bytes
167
+ txMaxDurationMs: int
168
+ txMaxRetries: int
169
+ bridgeId: str
170
+ versionUuid: str
171
+ upgradeBlSd: bool
172
+ imageDirUrl: str
173
+ def __init__(self, rebootPacket: _Optional[bytes] = ..., txMaxDurationMs: _Optional[int] = ..., txMaxRetries: _Optional[int] = ..., bridgeId: _Optional[str] = ..., versionUuid: _Optional[str] = ..., upgradeBlSd: bool = ..., imageDirUrl: _Optional[str] = ...) -> None: ...
174
+
175
+ class GatewayConfig(_message.Message):
176
+ __slots__ = ("version", "bleSwVersion", "interfaceSwVersion", "location", "config")
177
+ class ConfigEntry(_message.Message):
178
+ __slots__ = ("key", "value")
179
+ KEY_FIELD_NUMBER: _ClassVar[int]
180
+ VALUE_FIELD_NUMBER: _ClassVar[int]
181
+ key: str
182
+ value: Value
183
+ def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[Value, _Mapping]] = ...) -> None: ...
184
+ VERSION_FIELD_NUMBER: _ClassVar[int]
185
+ BLESWVERSION_FIELD_NUMBER: _ClassVar[int]
186
+ INTERFACESWVERSION_FIELD_NUMBER: _ClassVar[int]
187
+ LOCATION_FIELD_NUMBER: _ClassVar[int]
188
+ CONFIG_FIELD_NUMBER: _ClassVar[int]
189
+ version: str
190
+ bleSwVersion: str
191
+ interfaceSwVersion: str
192
+ location: Location
193
+ config: _containers.MessageMap[str, Value]
194
+ def __init__(self, version: _Optional[str] = ..., bleSwVersion: _Optional[str] = ..., interfaceSwVersion: _Optional[str] = ..., location: _Optional[_Union[Location, _Mapping]] = ..., config: _Optional[_Mapping[str, Value]] = ...) -> None: ...
195
+
196
+ class CustomBroker(_message.Message):
197
+ __slots__ = ("customBroker", "port", "brokerUrl", "username", "password", "updateTopic", "statusTopic", "dataTopic")
198
+ CUSTOMBROKER_FIELD_NUMBER: _ClassVar[int]
199
+ PORT_FIELD_NUMBER: _ClassVar[int]
200
+ BROKERURL_FIELD_NUMBER: _ClassVar[int]
201
+ USERNAME_FIELD_NUMBER: _ClassVar[int]
202
+ PASSWORD_FIELD_NUMBER: _ClassVar[int]
203
+ UPDATETOPIC_FIELD_NUMBER: _ClassVar[int]
204
+ STATUSTOPIC_FIELD_NUMBER: _ClassVar[int]
205
+ DATATOPIC_FIELD_NUMBER: _ClassVar[int]
206
+ customBroker: bool
207
+ port: int
208
+ brokerUrl: str
209
+ username: str
210
+ password: str
211
+ updateTopic: str
212
+ statusTopic: str
213
+ dataTopic: str
214
+ def __init__(self, customBroker: bool = ..., port: _Optional[int] = ..., brokerUrl: _Optional[str] = ..., username: _Optional[str] = ..., password: _Optional[str] = ..., updateTopic: _Optional[str] = ..., statusTopic: _Optional[str] = ..., dataTopic: _Optional[str] = ...) -> None: ...
215
+
216
+ class CustomMessage(_message.Message):
217
+ __slots__ = ("entries",)
218
+ class EntriesEntry(_message.Message):
219
+ __slots__ = ("key", "value")
220
+ KEY_FIELD_NUMBER: _ClassVar[int]
221
+ VALUE_FIELD_NUMBER: _ClassVar[int]
222
+ key: str
223
+ value: Value
224
+ def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[Value, _Mapping]] = ...) -> None: ...
225
+ ENTRIES_FIELD_NUMBER: _ClassVar[int]
226
+ entries: _containers.MessageMap[str, Value]
227
+ def __init__(self, entries: _Optional[_Mapping[str, Value]] = ...) -> None: ...
@@ -0,0 +1,138 @@
1
+ # External Imports
2
+ import time
3
+ import datetime
4
+ import os
5
+ import webbrowser
6
+ from jinja2 import Environment, FileSystemLoader
7
+ import pkg_resources
8
+ import time
9
+ import importlib.metadata
10
+ from wiliot_core.utils.utils import WiliotDir
11
+
12
+ # Internal Imports
13
+ from gw_certificate.common.analysis_data_bricks import initialize_logger
14
+ from gw_certificate.common.debug import debug_print
15
+ from gw_certificate.interface.ble_sniffer import BLESniffer
16
+ from gw_certificate.interface.uart_if import UARTError, UARTInterface
17
+ from gw_certificate.interface.ble_simulator import BLESimulator
18
+ from gw_certificate.interface.mqtt import MqttClient
19
+ from gw_certificate.tests import *
20
+ from gw_certificate.interface.uart_ports import get_uart_ports
21
+ from gw_certificate.api_if.gw_capabilities import GWCapabilities
22
+ from gw_certificate.tests import TESTS_NO_UART
23
+
24
+ GW_CERT_VERSION = importlib.metadata.version("wiliot-certificate")
25
+
26
+ class GWCertificateError(Exception):
27
+ pass
28
+
29
+
30
+ class TemplateEngine:
31
+ def __init__(self):
32
+ self.template_dir = pkg_resources.resource_filename(__name__, 'templates')
33
+ self.env = Environment(loader=FileSystemLoader(self.template_dir))
34
+
35
+ def get_template(self, template_name):
36
+ return self.env.get_template(template_name)
37
+
38
+ def render_template(self, template_name, **kwargs):
39
+ template = self.get_template(template_name)
40
+ return template.render(**kwargs)
41
+
42
+
43
+ class GWCertificate:
44
+ def __init__(self, gw_id, owner_id, tests:list = TESTS_DEFAULT, topic_suffix='', update_fw=False, stress_pps=None, aggregation_time=0, env='prod'):
45
+ # Runtime
46
+ self.env_dirs = WiliotDir()
47
+ self.current_datetime = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
48
+ self.certificate_dir = os.path.join(self.env_dirs.get_wiliot_root_app_dir(), 'gw-certificate', self.current_datetime)
49
+ self.env_dirs.create_dir(self.certificate_dir)
50
+ self.start_timestamp = initialize_logger(self.certificate_dir)
51
+ self.logger_filepath = os.path.join(self.certificate_dir, f'{self.start_timestamp}.log')
52
+ self.mqtt_logger_filepath = os.path.join(self.certificate_dir, f'{self.start_timestamp}_mqtt.log')
53
+ self.sniffer_logger_filepath = os.path.join(self.certificate_dir, f'{self.start_timestamp}_sniffer.log')
54
+ self.result_html_path = os.path.join(self.certificate_dir, f'results_{self.current_datetime}.html')
55
+ self.template_engine = TemplateEngine()
56
+ self.env = env
57
+
58
+ # Version
59
+ self.gw_cert_version = GW_CERT_VERSION
60
+
61
+ # GW & specific tests related
62
+ self.gw_id = gw_id
63
+ self.owner_id = owner_id
64
+ self.topic_suffix = topic_suffix
65
+ self.mqttc = MqttClient(gw_id, owner_id, self.mqtt_logger_filepath, topic_suffix=topic_suffix, broker='eclipse')
66
+ self.gw_capabilities = GWCapabilities()
67
+ self.stress_pps = stress_pps
68
+ self.aggregation_time = aggregation_time
69
+
70
+ # UART-related. Require only when running tests that need it
71
+ self.use_uart = not all(test in TESTS_NO_UART for test in tests)
72
+ if self.use_uart:
73
+ self.uart = None
74
+ self.uart_comports = get_uart_ports()
75
+ debug_print(f'UART Ports:{self.uart_comports}')
76
+ if len(self.uart_comports) < 1:
77
+ raise GWCertificateError('A Wiliot tester board must be connected to USB!')
78
+
79
+ for port in self.uart_comports:
80
+ try:
81
+ # TODO Fix UARTInterface firmware update and then call it with the flag set to True
82
+ self.uart = UARTInterface(port, update_fw=update_fw)
83
+ break
84
+ except UARTError as e:
85
+ debug_print(f'Port: {port} - {e}')
86
+ if type(self.uart) is not UARTInterface:
87
+ raise GWCertificateError("Cannot initialize any port!")
88
+ self.ble_sim = BLESimulator(self.uart)
89
+ self.sniffer = BLESniffer(self.uart, logger_filepath=self.sniffer_logger_filepath)
90
+
91
+ # Tests
92
+ self.tests = [t(**self.__dict__) for t in tests]
93
+ debug_print(f'Running Tests: {self.tests}')
94
+
95
+ def run_tests(self):
96
+ debug_print("Sleeping 20 seconds after mqtt connect")
97
+ time.sleep(20)
98
+
99
+ # capabilities_received = ConnectionTest in self.tests
100
+ for test in self.tests:
101
+ # if capabilities_received:
102
+ # if (type(test) == DownlinkTest and self.gw_capabilities.downlinkSupported == False):
103
+ # debug_print(f'# Skipping {type(test)} since it is not a supported capability. #')
104
+ # continue
105
+ test.run()
106
+ test.end_test()
107
+
108
+ def create_results_html(self):
109
+ with open(self.logger_filepath, 'r') as f:
110
+ log = f.read().split('\n')
111
+ with open(self.mqtt_logger_filepath, 'r') as f:
112
+ mqtt_log = f.read().split('\n')
113
+ if self.use_uart:
114
+ with open(self.sniffer_logger_filepath, 'r') as f:
115
+ sniffer_log = f.read().split('\n')
116
+ else:
117
+ sniffer_log = []
118
+
119
+
120
+ html = self.template_engine.render_template('results.html', tests=self.tests,
121
+ log = log,
122
+ mqtt_log = mqtt_log,
123
+ sniffer_log = sniffer_log,
124
+ gw_id = self.gw_id,
125
+ version = self.gw_cert_version,
126
+ datetime = self.current_datetime)
127
+ with open(self.result_html_path, 'w', encoding="utf-8") as f:
128
+ f.write(html)
129
+ debug_print("Test Finished. Results HTML Saved: " + self.result_html_path)
130
+ webbrowser.open('file://' + os.path.realpath(self.result_html_path))
131
+
132
+ if __name__ == "__main__":
133
+ from api_secrets import *
134
+ gw_id = 'gw_id'
135
+ owner_id = 'owner_id'
136
+ gwc = GWCertificate(gw_id=gw_id,owner_id=owner_id, tests=[ConnectionTest, DownlinkTest, UplinkTest, StressTest])
137
+ gwc.run_tests()
138
+ gwc.create_results_html()
@@ -0,0 +1,70 @@
1
+
2
+ import time
3
+ from argparse import ArgumentParser
4
+
5
+ from gw_certificate.gw_certificate import GWCertificate, GW_CERT_VERSION
6
+ from gw_certificate.tests import TESTS, TESTS_DEFAULT
7
+ from gw_certificate.tests.throughput import STRESS_DEFAULT_PPS
8
+ from gw_certificate.tests.registration import REG_CERT_OWNER_ID, RegistrationTest
9
+ from gw_certificate.tests.uplink import UplinkTest
10
+ from gw_certificate.tests.throughput import StressTest
11
+
12
+ def filter_tests(tests_names):
13
+ chosen_tests = []
14
+ if tests_names == []:
15
+ return TESTS_DEFAULT
16
+ for test_class in TESTS:
17
+ for test_name in tests_names:
18
+ if test_name in test_class.__name__.lower() and test_class not in chosen_tests:
19
+ chosen_tests.append(test_class)
20
+ return chosen_tests
21
+
22
+ def main():
23
+ usage = (
24
+ "usage: wlt-gw-certificate [-h] -owner OWNER -gw GW\n"
25
+ f" [-tests {{connection, uplink, downlink, stress}}] [-update] [-pps {STRESS_DEFAULT_PPS}]\n"
26
+ " [-agg AGG] [-env {prod, test, dev}]"
27
+ )
28
+
29
+ parser = ArgumentParser(prog='wlt-gw-certificate',
30
+ description=f'Gateway Certificate {GW_CERT_VERSION} - CLI Tool to test Wiliot GWs', usage=usage)
31
+
32
+ required = parser.add_argument_group('required arguments')
33
+ required.add_argument('-gw', type=str, help="Gateway ID", required=True)
34
+ optional = parser.add_argument_group('optional arguments')
35
+ optional.add_argument('-owner', type=str, help="Owner ID", required=False, default=REG_CERT_OWNER_ID)
36
+ optional.add_argument('-suffix', type=str, help="Topic suffix", default='', required=False)
37
+ optional.add_argument('-tests', type=str, choices=['registration', 'connection', 'uplink', 'downlink', 'actions', 'stress'],
38
+ help="Tests to run. Registration omitted by default.", required=False, nargs='+', default=[])
39
+ optional.add_argument('-update', action='store_true', help='Update test board firmware', default=False, required=False)
40
+ optional.add_argument('-pps', type=int, help='Single packets-per-second rate to simulate in the stress test',
41
+ choices=STRESS_DEFAULT_PPS, default=None, required=False)
42
+ optional.add_argument('-agg', type=int, help='Aggregation time [seconds] the Uplink stages wait before processing results',
43
+ default=0, required=False)
44
+ optional.add_argument('-env', type=str, help='Environment for the registration test (Internal usage)',
45
+ choices=['prod', 'test', 'dev'], default='prod', required=False)
46
+ args = parser.parse_args()
47
+
48
+ tests = filter_tests(args.tests)
49
+ topic_suffix = '' if args.suffix == '' else '-'+args.suffix
50
+
51
+ if args.pps != None and StressTest not in tests:
52
+ parser.error("Packets per second (-pps) flag can only be used when 'stress' is included in test list (e.g. -tests stress)")
53
+ if args.agg != 0 and UplinkTest not in tests:
54
+ parser.error("Aggregation time (-agg) flag can only be used when 'uplink' is included in test list (e.g. -tests uplink)")
55
+ if args.owner == REG_CERT_OWNER_ID and not all(test == RegistrationTest for test in tests):
56
+ print(f"Note: using default owner ID (-owner) - {REG_CERT_OWNER_ID}..")
57
+ time.sleep(2)
58
+ # parser.error("The -owner flag is required when running any test other than the RegistrationTest.")
59
+
60
+ gwc = GWCertificate(gw_id=args.gw, owner_id=args.owner, topic_suffix=topic_suffix, tests=tests, update_fw=args.update,
61
+ stress_pps=args.pps, aggregation_time=args.agg, env=args.env)
62
+ gwc.run_tests()
63
+ gwc.create_results_html()
64
+
65
+ def main_cli():
66
+ main()
67
+
68
+ if __name__ == '__main__':
69
+ main()
70
+
@@ -0,0 +1,91 @@
1
+ import datetime
2
+ import time
3
+
4
+ from gw_certificate.interface.uart_if import UARTInterface
5
+ from gw_certificate.common.debug import debug_print
6
+ from gw_certificate.interface.if_defines import *
7
+
8
+ class BLESimulator():
9
+ def __init__(self, uart:UARTInterface):
10
+ self.uart = uart
11
+ self.sim_mode = False
12
+
13
+ def set_sim_mode(self, sim_mode):
14
+ self.uart.flush()
15
+ mode_dict = {True: 1, False: 0}
16
+ self.sim_mode = sim_mode
17
+ self.uart.reset_gw()
18
+ self.uart.write_ble_command(f"!ble_sim_init {mode_dict[sim_mode]}")
19
+ if not sim_mode:
20
+ self.uart.reset_gw()
21
+ time.sleep(3)
22
+
23
+ def send_packet(self, raw_packet, duplicates=DEFAULT_DUPLICATES, output_power=DEFAULT_OUTPUT_POWER, channel=SEND_ALL_ADV_CHANNELS, delay=DEFAULT_DELAY):
24
+ assert self.sim_mode is True, 'BLE Sim not initialized!'
25
+ if len(raw_packet) == 62:
26
+ # Add ADVA
27
+ raw_packet = DEFAULT_ADVA + raw_packet
28
+ if len(raw_packet) != 74:
29
+ raise ValueError('Raw Packet must be 62/74 chars long!')
30
+ self.uart.write_ble_command(f"!ble_sim {str(raw_packet)} {str(duplicates)} {str(output_power)} {str(channel)} {str(delay)}")
31
+ if delay > 0:
32
+ diff = time.perf_counter()
33
+ time.sleep((delay/1000) * duplicates)
34
+ diff = time.perf_counter() - diff
35
+ debug_print(f'Desired Delay: {(delay/1000) * duplicates} Actual Delay {diff}')
36
+
37
+
38
+ def send_data_si_pair(self, data_packet, si_packet, duplicates, output_power=DEFAULT_OUTPUT_POWER, delay=DEFAULT_DELAY, packet_error=None):
39
+ if packet_error is None:
40
+ packet_error = [True for i in range (duplicates * 2)]
41
+ # debug_print(packet_error)
42
+ # print(f'delay {delay}')
43
+ packet_to_send = data_packet
44
+ def switch_packet(packet_to_send):
45
+ if packet_to_send == data_packet:
46
+ return si_packet
47
+ else:
48
+ return data_packet
49
+ for dup in range(duplicates * 2):
50
+ diff = time.perf_counter()
51
+ if packet_error[dup]:
52
+ debug_print(f'Sending Packet {dup}')
53
+ self.send_packet(packet_to_send, duplicates=1, output_power=output_power, channel=SEND_ALL_ADV_CHANNELS, delay=0)
54
+ else:
55
+ debug_print(f'Dropping Packet {dup}')
56
+ time.sleep(delay/1000)
57
+ diff = time.perf_counter() - diff
58
+ debug_print(f'Desired Delay: {delay/1000} Actual Delay {diff}')
59
+ packet_to_send = switch_packet(packet_to_send)
60
+
61
+ def trigger_by_time_stamp(self, ts):
62
+ if ts == None:
63
+ return
64
+ current_time = datetime.datetime.timestamp(datetime.datetime.now()) * 1000
65
+ time_difference = ts-current_time
66
+ print(f"The test will start in: {time_difference/1000} secondes")
67
+ time.sleep(time_difference/1000)
68
+
69
+ def send_brg_network_pkts(self, pkts, duplicates, output_power=DEFAULT_OUTPUT_POWER):
70
+ num_brgs = len(pkts)
71
+ total_pkts_to_send = num_brgs * 2 * duplicates
72
+ for pkt_idx in range(total_pkts_to_send):
73
+ brg_idx = pkt_idx % 3
74
+ pkt = pkts[brg_idx]
75
+ pkt_idx_per_brg = pkt_idx // num_brgs
76
+ if not bool(pkt_idx % 2):
77
+ packet_to_send = pkt['data_packet']
78
+ else:
79
+ packet_to_send = pkt['si_packet']
80
+ packet_error = pkt['packet_error']
81
+ if packet_error[pkt_idx_per_brg]:
82
+ debug_print(f'BRG {pkt["bridge_id"]}: Sending Packet {pkt_idx_per_brg}')
83
+ self.send_packet(packet_to_send, duplicates=1, output_power=output_power,
84
+ channel=SEND_ALL_ADV_CHANNELS, delay=0)
85
+ else:
86
+ debug_print(f'BRG {pkt["bridge_id"]}: Dropping Packet {pkt_idx_per_brg}')
87
+ diff = time.perf_counter()
88
+ delay = pkt['time_delay']
89
+ time.sleep(delay/1000)
90
+ diff = time.perf_counter() - diff
91
+ debug_print(f'Desired Delay: {delay/1000} Actual Delay {diff}')