[6320] | 1 | # -*- coding: utf-8 -*- |
---|
| 2 | """ |
---|
| 3 | ------------------------------------------------------------------------------ |
---|
| 4 | Mango 802.11 Reference Design Experiments Framework |
---|
| 5 | - Information Struct classes |
---|
| 6 | ------------------------------------------------------------------------------ |
---|
| 7 | License: Copyright 2019 Mango Communications, Inc. All rights reserved. |
---|
| 8 | Use and distribution subject to terms in LICENSE.txt |
---|
| 9 | ------------------------------------------------------------------------------ |
---|
| 10 | |
---|
| 11 | This module provides class definitions for information classes. |
---|
| 12 | |
---|
| 13 | Classes (see below for more information): |
---|
| 14 | Info() -- Base class for information classes |
---|
| 15 | StationInfo() -- Base class for station information |
---|
| 16 | NetworkInfo() -- Base class for network information |
---|
| 17 | CountsInfo() -- Base class for information on node counts |
---|
| 18 | |
---|
| 19 | """ |
---|
| 20 | import struct |
---|
| 21 | import sys |
---|
| 22 | |
---|
| 23 | import wlan_exp.util as util |
---|
| 24 | |
---|
| 25 | __all__ = ['NodeInfo', 'StationInfo', 'NetworkInfo', 'TxRxCounts'] |
---|
| 26 | |
---|
| 27 | # Fix to support Python 2.x and 3.x |
---|
| 28 | if sys.version[0]=="3": long=None |
---|
| 29 | |
---|
| 30 | |
---|
| 31 | #------------------------------------------------------------------------- |
---|
| 32 | # Field definitions for Information structures |
---|
| 33 | # |
---|
| 34 | # The info_field_defs is a dictionary of field defintions whose key is the |
---|
| 35 | # field name and whose value is an ordered list of tuples of the form: |
---|
| 36 | # (field name, struct format, numpy format, description) |
---|
| 37 | # |
---|
| 38 | # where: |
---|
| 39 | # field name - Text name of the field |
---|
| 40 | # struct format - Python struct format identifier |
---|
| 41 | # numpy format - Numpy format identifier |
---|
| 42 | # description - Text description of the field |
---|
| 43 | # |
---|
| 44 | # These tuples can be extended by sub-classes, but the first four fields are |
---|
| 45 | # required in order to efficiently serialize / deserialize information objects |
---|
| 46 | # for communication over the transport. |
---|
| 47 | # |
---|
| 48 | # These definitions match the corresponding definitions in the 802.11 |
---|
| 49 | # Reference Design Experiments Framework in C. |
---|
| 50 | # |
---|
| 51 | #------------------------------------------------------------------------- |
---|
| 52 | |
---|
| 53 | # The Tx/Rx counts fields are used in the STATION_INFO and TXRX_COUNTS structs |
---|
| 54 | # The C code always returns STATION_INFO structs with embedded counts |
---|
| 55 | # The Python node API still supports "retrieving" counts indepdent of the |
---|
| 56 | # station_info list. The TXRX_COUNTS struct below is used to create the |
---|
| 57 | # standalone counts structs returned by the node method |
---|
| 58 | station_info_counts_fields = [ |
---|
| 59 | ('data_num_rx_bytes', 'Q', 'uint64', 'Number of non-duplicate bytes received in DATA packets from remote node'), |
---|
| 60 | ('data_num_rx_bytes_total', 'Q', 'uint64', 'Total number of bytes received in DATA packets from remote node'), |
---|
| 61 | ('data_num_tx_bytes_success', 'Q', 'uint64', 'Total number of bytes successfully transmitted in DATA packets to remote node'), |
---|
| 62 | ('data_num_tx_bytes_total', 'Q', 'uint64', 'Total number of bytes transmitted (successfully or not) in DATA packets to remote node'), |
---|
| 63 | ('data_num_rx_packets', 'I', 'uint32', 'Number of non-duplicate DATA packets received from remote node'), |
---|
| 64 | ('data_num_rx_packets_total', 'I', 'uint32', 'Total number of DATA packets received from remote node'), |
---|
| 65 | ('data_num_tx_packets_success', 'I', 'uint32', 'Total number of DATA packets successfully transmitted to remote node'), |
---|
| 66 | ('data_num_tx_packets_total', 'I', 'uint32', 'Total number of DATA packets transmitted (successfully or not) to remote node'), |
---|
| 67 | ('data_num_tx_attempts', 'Q', 'uint64', 'Total number of attempts of DATA packets to remote node (includes re-transmissions)'), |
---|
| 68 | ('mgmt_num_rx_bytes', 'Q', 'uint64', 'Number of bytes non-duplicate received in management packets from remote node'), |
---|
| 69 | ('mgmt_num_rx_bytes_total', 'Q', 'uint64', 'Total number of bytes received in management packets from remote node'), |
---|
| 70 | ('mgmt_num_tx_bytes_success', 'Q', 'uint64', 'Total number of bytes successfully transmitted in management packets to remote node'), |
---|
| 71 | ('mgmt_num_tx_bytes_total', 'Q', 'uint64', 'Total number of bytes transmitted (successfully or not) in management packets to remote node'), |
---|
| 72 | ('mgmt_num_rx_packets', 'I', 'uint32', 'Number of non-duplicate management packets received from remote node'), |
---|
| 73 | ('mgmt_num_rx_packets_total', 'I', 'uint32', 'Total number of management packets received from remote node'), |
---|
| 74 | ('mgmt_num_tx_packets_success', 'I', 'uint32', 'Total number of management packets successfully transmitted to remote node'), |
---|
| 75 | ('mgmt_num_tx_packets_total', 'I', 'uint32', 'Total number of management packets transmitted (successfully or not) to remote node'), |
---|
| 76 | ('mgmt_num_tx_attempts', 'Q', 'uint64', 'Total number of attempts management packets to remote node (includes re-transmissions)') |
---|
| 77 | ] |
---|
| 78 | |
---|
| 79 | info_field_defs = { |
---|
| 80 | |
---|
| 81 | # Framework NODE_INFO carries fields shared by all platforms |
---|
| 82 | 'NODE_INFO': [ |
---|
| 83 | ('platform_id', 'I', 'uint32', 'Platform ID, unique integer assigned to each supported hardware platform'), |
---|
| 84 | ('platform_config', 'I', 'uint32', 'Platform config flags, values are platform-specific'), |
---|
| 85 | ('serial_number', 'I', 'uint32', 'Numeric part of node serial number, full serial number format is platform-specific'), |
---|
| 86 | ('wlan_exp_version', 'I', 'uint32', 'Version of wlan_exp code running on node'), |
---|
| 87 | ('node_id', 'I', 'uint32', 'Node ID for wlan_exp transport messages, typically assigned during wlan_exp init handshake'), |
---|
| 88 | ('wlan_mac_addr', '6s', '6uint8', 'MAC address of node\'s wireless interface'), |
---|
| 89 | ('high_sw_id', 'B', 'uint8', 'ID for MAC software application in CPU High'), |
---|
| 90 | ('low_sw_id', 'B', 'uint8', 'ID for MAC software application in CPU Low'), |
---|
| 91 | ('high_sw_config', 'I', 'uint32', 'Config flags for high MAC aplication/framework'), |
---|
| 92 | ('low_sw_config', 'I', 'uint32', 'Config flags for low MAC aplication/framework'), |
---|
| 93 | ('wlan_exp_eth_mtu', 'H', 'uint16', 'MTU in bytes of wlan_exp Ethernet interface'), |
---|
| 94 | ('scheduler_interval', 'H', 'uint16', 'Minimum interval for scheduled events, in usec'), |
---|
| 95 | ('high_compilation_date', '12s', '12uint8', 'Compilation date string for high application'), |
---|
| 96 | ('high_compilation_time', '12s', '12uint8', 'Compilation time string for high application'), |
---|
| 97 | ('low_compilation_date', '12s', '12uint8', 'Compilation date string for low application'), |
---|
| 98 | ('low_compilation_time', '12s', '12uint8', 'Compilation time string for low application'), |
---|
| 99 | ], |
---|
| 100 | |
---|
| 101 | 'STATION_INFO' : [ |
---|
| 102 | ('mac_addr', '6s', '6uint8', 'MAC address of station'), |
---|
| 103 | ('id', 'H', 'uint16', 'Identification Index for this station'), |
---|
| 104 | ('host_name', '20s', '20uint8', 'String hostname (19 chars max), taken from DHCP DISCOVER packets'), |
---|
| 105 | ('flags', 'B', 'uint8', 'Station flags'), |
---|
| 106 | ('ps_state', 'B', 'uint8', 'Power save state'), |
---|
| 107 | ('capabiltiies', 'H', 'uint16', 'Capabilities'), |
---|
| 108 | ('latest_rx_timestamp', 'Q', 'uint64', 'Value of System Time in microseconds of last successful Rx from device'), |
---|
| 109 | ('latest_txrx_timestamp', 'Q', 'uint64', 'Value of System Time in microseconds of last successful Rx from device or Tx to device'), |
---|
| 110 | ('latest_rx_seq', 'H', 'uint16', 'Sequence number of last packet received from device'), |
---|
| 111 | ('qid', 'H', 'uint16', 'Queue ID used by this station'), |
---|
| 112 | ('num_tx_queued', 'I', 'uint32', 'Number of queued transmissions'), |
---|
| 113 | ('tx_phy_mcs_data', 'B', 'uint8', 'Current PHY MCS index in [0:7] for new transmissions to device'), |
---|
| 114 | ('tx_phy_mode_data', 'B', 'uint8', 'Current PHY mode for new transmissions to deivce'), |
---|
| 115 | ('tx_phy_antenna_mode_data', 'B', 'uint8', 'Current PHY antenna mode in [1:4] for new transmissions to device'), |
---|
| 116 | ('tx_phy_power_data', 'b', 'int8', 'Current Tx power in dBm for new transmissions to device'), |
---|
| 117 | ('tx_mac_flags_data', 'B', 'uint8', 'Flags for Tx MAC config for new transmissions to device'), |
---|
| 118 | ('padding1', '3x', '3uint8', ''), |
---|
| 119 | ('tx_phy_mcs_mgmt', 'B', 'uint8', 'Current PHY MCS index in [0:7] for new transmissions to device'), |
---|
| 120 | ('tx_phy_mode_mgmt', 'B', 'uint8', 'Current PHY mode for new transmissions to deivce'), |
---|
| 121 | ('tx_phy_antenna_mode_mgmt', 'B', 'uint8', 'Current PHY antenna mode in [1:4] for new transmissions to device'), |
---|
| 122 | ('tx_phy_power_mgmt', 'b', 'int8', 'Current Tx power in dBm for new transmissions to device'), |
---|
| 123 | ('tx_mac_flags_mgmt', 'B', 'uint8', 'Flags for Tx MAC config for new transmissions to device'), |
---|
| 124 | ('padding2', '3x', '3uint8', ''), |
---|
| 125 | ] + station_info_counts_fields, |
---|
| 126 | |
---|
| 127 | 'BSS_CONFIG_COMMON' : [ |
---|
| 128 | ('bssid', '6s', '6uint8', 'BSS ID'), |
---|
| 129 | ('channel', 'B', 'uint8', 'Primary channel'), |
---|
| 130 | ('channel_type', 'B', 'uint8', 'Channel Type'), |
---|
| 131 | ('ssid', '33s', '33uint8', 'SSID (32 chars max)'), |
---|
| 132 | ('ht_capable', 'B', 'uint8', 'Support for HTMF Tx/Rx'), |
---|
| 133 | ('beacon_interval', 'H', 'uint16', 'Beacon interval - In time units of 1024 us'), |
---|
| 134 | ('dtim_period', 'B', 'uint8', 'DTIM Period - In units of beacon intervals'), |
---|
| 135 | ('reserved', '3x', '3uint8', '')], |
---|
| 136 | |
---|
| 137 | 'NETWORK_INFO' : [ |
---|
| 138 | # BSS_CONFIG_COMMON Fields to be inserted here! |
---|
| 139 | ('flags', 'I', 'uint32', 'Bit Flags'), |
---|
| 140 | ('capabilities', 'I', 'uint32', 'Supported capabilities of the BSS'), |
---|
| 141 | ('latest_beacon_rx_time', 'Q', 'uint64', 'Value of System Time in microseconds of last beacon Rx'), |
---|
| 142 | ('latest_beacon_rx_power', 'b', 'int8', 'Last observed beacon Rx Power (dBm)'), |
---|
| 143 | ('padding1', '3x', '3uint8', '')], |
---|
| 144 | |
---|
| 145 | 'BSS_CONFIG_UPDATE' : [ |
---|
| 146 | ('update_mask', 'I', 'uint32', 'Bit mask indicating which fields were updated')], |
---|
| 147 | # BSS_CONFIG_COMMON Fields to be inserted here! |
---|
| 148 | |
---|
| 149 | 'TXRX_COUNTS' : [ |
---|
| 150 | ('retrieval_timestamp', 'Q', 'uint64', 'Value of System Time in microseconds when structure retrieved from the node'), |
---|
| 151 | ('mac_addr', '6s', '6uint8', 'MAC address of remote node whose counts are recorded here'), |
---|
| 152 | ] + station_info_counts_fields, |
---|
| 153 | } |
---|
| 154 | |
---|
| 155 | |
---|
| 156 | info_struct_len_reqs = { |
---|
| 157 | 'TXRX_COUNTS': 128, |
---|
| 158 | 'BSS_CONFIG_UPDATE': 52 |
---|
| 159 | } |
---|
| 160 | |
---|
| 161 | info_consts_defs = { |
---|
| 162 | 'STATION_INFO' : util.consts_dict({ |
---|
| 163 | 'flags' : util.consts_dict({ |
---|
| 164 | 'KEEP' : 0x01, |
---|
| 165 | 'DISABLE_ASSOC_CHECK' : 0x02 |
---|
| 166 | }), |
---|
| 167 | 'ps_state' : util.consts_dict({ |
---|
| 168 | 'DOZE' : 0x01 |
---|
| 169 | }), |
---|
| 170 | 'capabilities' : util.consts_dict({ |
---|
| 171 | 'HT_CAPABLE' : 0x0001 |
---|
| 172 | }), |
---|
| 173 | 'tx_phy_mode' : util.phy_modes, |
---|
| 174 | 'tx_mac_flags' : util.consts_dict() |
---|
| 175 | }), |
---|
| 176 | |
---|
| 177 | 'NETWORK_INFO' : util.consts_dict({ |
---|
| 178 | 'flags' : util.consts_dict({ |
---|
| 179 | 'KEEP' : 0x00000001, |
---|
| 180 | }), |
---|
| 181 | 'channel_type' : util.consts_dict({ |
---|
| 182 | 'BW20' : 0x0000, |
---|
| 183 | 'BW40_SEC_BELOW' : 0x0001, |
---|
| 184 | 'BW40_SEC_ABOVE' : 0x0002, |
---|
| 185 | }), |
---|
| 186 | 'capabilities' : util.consts_dict({ |
---|
| 187 | 'ESS' : 0x0001, |
---|
| 188 | 'IBSS' : 0x0002, |
---|
| 189 | 'PRIVACY' : 0x0010, |
---|
| 190 | }) |
---|
| 191 | }), |
---|
| 192 | |
---|
| 193 | 'BSS_CONFIG_UPDATE' : util.consts_dict({ |
---|
| 194 | 'update_mask' : util.consts_dict({ |
---|
| 195 | 'BSSID' : 0x00000001, |
---|
| 196 | 'CHANNEL' : 0x00000002, |
---|
| 197 | 'SSID' : 0x00000004, |
---|
| 198 | 'BEACON_INTERVAL' : 0x00000008, |
---|
| 199 | 'HT_CAPABLE' : 0x00000010, |
---|
| 200 | 'DTIM_PERIOD' : 0x00000020, |
---|
| 201 | }), |
---|
| 202 | 'channel_type' : util.consts_dict({ |
---|
| 203 | 'BW20' : 0x0000, |
---|
| 204 | 'BW40_SEC_BELOW' : 0x0001, |
---|
| 205 | 'BW40_SEC_ABOVE' : 0x0002, |
---|
| 206 | }) |
---|
| 207 | }), |
---|
| 208 | } |
---|
| 209 | |
---|
| 210 | |
---|
| 211 | |
---|
| 212 | # ----------------------------------------------------------------------------- |
---|
| 213 | # Information Structure Base Class |
---|
| 214 | # ----------------------------------------------------------------------------- |
---|
| 215 | |
---|
| 216 | class InfoStruct(dict): |
---|
| 217 | """Base class for structured information classes |
---|
| 218 | |
---|
| 219 | This class provides the basic methods for setting / accessing information |
---|
| 220 | within the info struct object. It also provides the base methods for |
---|
| 221 | serializing / deserializing the object for communication by the transport. |
---|
| 222 | |
---|
| 223 | An InfoStruct object represents structured data that is passed to/from the |
---|
| 224 | MAC C code. In the C code these objects are represented by struct defintions. |
---|
| 225 | The fields defined for an InfoStruct object must match the fields in the |
---|
| 226 | corresponding C struct. Conceptually these InfoStruct objects are very similar |
---|
| 227 | to log entries; they even share the log entry syntax for defining fields. By |
---|
| 228 | defining InfoStruct objects here, however, wlan_exp scripts can control and |
---|
| 229 | retrieve parameters encoded in non-log structs on the node without relying |
---|
| 230 | on any of wlan_exp's logging framework. The primary benefit of this separation |
---|
| 231 | is removing the numpy dependency when dealing with the non-log version of |
---|
| 232 | the info structures described here. |
---|
| 233 | """ |
---|
| 234 | _field_name = None # Internal name for the info_field_defs entry |
---|
| 235 | _fields_struct_fmt = None # Internal string of field formats, in struct module format |
---|
| 236 | _consts = None # Internal container for user-defined, type-specific constants |
---|
| 237 | |
---|
| 238 | def __init__(self, field_sets=None, field_defs=None): |
---|
| 239 | super(InfoStruct, self).__init__() |
---|
| 240 | |
---|
| 241 | # field_sets: scalar or list of strings matching keys in the global info_field_defs |
---|
| 242 | # field_defs: list of field definitions, for structs whose fields are not in the global info_field_defs |
---|
| 243 | # Code below supports both arguments being not-None, in case caller wants to create InfoStruct |
---|
| 244 | # based on global defs with extra fields appeneded |
---|
| 245 | |
---|
| 246 | if field_sets is not None: |
---|
| 247 | if(type(field_sets) is str): |
---|
| 248 | field_sets = [field_sets] |
---|
| 249 | |
---|
| 250 | # Initialize variables |
---|
| 251 | self._consts = util.consts_dict() |
---|
| 252 | self._field_defs = [] |
---|
| 253 | for fs in field_sets: |
---|
| 254 | if(fs not in info_field_defs.keys()): |
---|
| 255 | msg = "Field set name {0} does not exist in info_field_defs.".format(fs) |
---|
| 256 | raise AttributeError(msg) |
---|
| 257 | |
---|
| 258 | self.append_field_defs(info_field_defs[fs]) |
---|
| 259 | |
---|
| 260 | if(fs in info_consts_defs.keys()): |
---|
| 261 | self.append_const_defs(info_consts_defs[fs]) |
---|
| 262 | |
---|
| 263 | # Update the struct format string, used by pack/unpack/calcsize below |
---|
| 264 | self._fields_struct_fmt = ' '.join(self.get_field_struct_formats()) |
---|
| 265 | |
---|
| 266 | # Check the final struct size using the name of the last field set |
---|
| 267 | last_field_set = field_sets[-1] |
---|
| 268 | if(last_field_set in info_struct_len_reqs.keys()): |
---|
| 269 | # A struct length value was provided - confirm the field defs match this length |
---|
| 270 | def_size = self.sizeof() |
---|
| 271 | req_size = info_struct_len_reqs[last_field_set] |
---|
| 272 | |
---|
| 273 | if(def_size != req_size): |
---|
| 274 | msg = 'Struct size definition mismatch - {0} field defs have size {1} vs required size {2}'.format(last_field_set, def_size, req_size) |
---|
| 275 | raise AttributeError(msg) |
---|
| 276 | |
---|
| 277 | if field_defs is not None: |
---|
| 278 | # Caller provided field definitions directly - adopt these as-is |
---|
| 279 | self.append_field_defs(field_defs) |
---|
| 280 | |
---|
| 281 | # Update the struct format string, used by pack/unpack/calcsize below |
---|
| 282 | #self._fields_struct_fmt = ' '.join(self.get_field_struct_formats()) |
---|
| 283 | # The '=' qualifier mimics the __packed__ attribute in C |
---|
| 284 | self._fields_struct_fmt = '=' + ' '.join(self.get_field_struct_formats()) |
---|
| 285 | |
---|
| 286 | # Add and initialize all the fields in the info_field_defs |
---|
| 287 | for field in self._field_defs: |
---|
| 288 | if 'x' not in field[1]: |
---|
| 289 | self[field[0]] = None |
---|
| 290 | |
---|
| 291 | |
---|
| 292 | |
---|
| 293 | # ------------------------------------------------------------------------- |
---|
| 294 | # Helper methods for the Info Type |
---|
| 295 | # ------------------------------------------------------------------------- |
---|
| 296 | def append_field_defs(self, new_field_defs): |
---|
| 297 | try: |
---|
| 298 | # Existing field defs - concatenate lists |
---|
| 299 | self._field_defs = self._field_defs + new_field_defs |
---|
| 300 | except TypeError: |
---|
| 301 | # No existing field defs |
---|
| 302 | self._field_defs = new_field_defs |
---|
| 303 | |
---|
| 304 | def append_const_defs(self, new_const_defs): |
---|
| 305 | for k in new_const_defs.keys(): |
---|
| 306 | self._consts[k] = new_const_defs[k] |
---|
| 307 | |
---|
| 308 | def get_field_names(self): |
---|
| 309 | """Get the field names. |
---|
| 310 | |
---|
| 311 | Returns: |
---|
| 312 | names (list of str): List of string field names for the entry |
---|
| 313 | """ |
---|
| 314 | return [f[0] for f in self.get_field_defs()] |
---|
| 315 | |
---|
| 316 | |
---|
| 317 | def get_field_struct_formats(self): |
---|
| 318 | """Get the Python struct format of the fields. |
---|
| 319 | |
---|
| 320 | Returns: |
---|
| 321 | formats (list of str): List of Python struct formats for the fields |
---|
| 322 | """ |
---|
| 323 | return [f[1] for f in self.get_field_defs()] |
---|
| 324 | |
---|
| 325 | |
---|
| 326 | def get_field_defs(self): |
---|
| 327 | """Get the field definitions. |
---|
| 328 | |
---|
| 329 | Returns: |
---|
| 330 | fields (list of tuple): List of tuples that describe each field |
---|
| 331 | """ |
---|
| 332 | return self._field_defs |
---|
| 333 | |
---|
| 334 | def get_consts(self): |
---|
| 335 | """Get all constants defined in the info struct object as a dictionary |
---|
| 336 | |
---|
| 337 | Returns: |
---|
| 338 | values (dict): All constant values in the object |
---|
| 339 | """ |
---|
| 340 | return self._consts.copy() |
---|
| 341 | |
---|
| 342 | |
---|
| 343 | def get_const(self, name): |
---|
| 344 | """Get a constant defined in the info struct object |
---|
| 345 | |
---|
| 346 | Returns: |
---|
| 347 | value (int or str): Value associated with the constant |
---|
| 348 | """ |
---|
| 349 | if (name in self._consts.keys()): |
---|
| 350 | return self._consts[name] |
---|
| 351 | else: |
---|
| 352 | msg = "Constant {0} does not exist ".format(name) |
---|
| 353 | msg += "in {0}".format(self.__class__.__name__) |
---|
| 354 | raise AttributeError(msg) |
---|
| 355 | |
---|
| 356 | |
---|
| 357 | def sizeof(self): |
---|
| 358 | """Return the size of the object when being transmitted / received by |
---|
| 359 | the transport |
---|
| 360 | """ |
---|
| 361 | return struct.calcsize(self._fields_struct_fmt) |
---|
| 362 | |
---|
| 363 | |
---|
| 364 | # ------------------------------------------------------------------------- |
---|
| 365 | # Utility methods for the InfoStruct object |
---|
| 366 | # ------------------------------------------------------------------------- |
---|
| 367 | def serialize(self): |
---|
| 368 | """Packs object into a data buffer |
---|
| 369 | |
---|
| 370 | Returns: |
---|
| 371 | data (bytearray): Bytearray of packed binary data |
---|
| 372 | """ |
---|
| 373 | # Pack the object into a single data buffer |
---|
| 374 | ret_val = b'' |
---|
| 375 | fields = [] |
---|
| 376 | field_type = [] |
---|
| 377 | tmp_values = [] |
---|
| 378 | used_field = [] |
---|
| 379 | |
---|
| 380 | for field in self._field_defs: |
---|
| 381 | if 'x' not in field[1]: |
---|
| 382 | fields.append(field[0]) |
---|
| 383 | try: |
---|
| 384 | tmp_values.append(self[field[0]]) |
---|
| 385 | field_type.append(type(self[field[0]])) |
---|
| 386 | used_field.append(True) |
---|
| 387 | except KeyError: |
---|
| 388 | tmp_values.append(0) |
---|
| 389 | field_type.append(None) |
---|
| 390 | used_field.append(False) |
---|
| 391 | |
---|
| 392 | try: |
---|
| 393 | ret_val += struct.pack(self._fields_struct_fmt, *tmp_values) |
---|
| 394 | except struct.error as err: |
---|
| 395 | print("Error serializing structure:\n\t{0}".format(err)) |
---|
| 396 | print("Serialize Structure:") |
---|
| 397 | print(fields) |
---|
| 398 | print(field_type) |
---|
| 399 | print(used_field) |
---|
| 400 | print(tmp_values) |
---|
| 401 | print(self._fields_struct_fmt) |
---|
| 402 | raise RuntimeError("See above print statements to debug error.") |
---|
| 403 | |
---|
| 404 | |
---|
| 405 | if (self.sizeof()) != len(ret_val): |
---|
| 406 | msg = "WARNING: Sizes do not match.\n" |
---|
| 407 | msg += " Expected {0} bytes".format(self.sizeof()) |
---|
| 408 | msg += " Buffer is {0} bytes".format(len(ret_val)) |
---|
| 409 | print(msg) |
---|
| 410 | |
---|
| 411 | return ret_val |
---|
| 412 | |
---|
| 413 | |
---|
| 414 | def deserialize(self, buf): |
---|
| 415 | """Unpacks a buffer of data into the object |
---|
| 416 | |
---|
| 417 | Args: |
---|
| 418 | buf (bytearray): Array of bytes containing the values of an information object |
---|
| 419 | |
---|
| 420 | Returns: |
---|
| 421 | (Info Object): Each Info object in the list has been filled in with the corresponding |
---|
| 422 | data from the buffer. |
---|
| 423 | """ |
---|
| 424 | all_names = self.get_field_names() |
---|
| 425 | all_fmts = self.get_field_struct_formats() |
---|
| 426 | object_size = self.sizeof() |
---|
| 427 | |
---|
| 428 | try: |
---|
| 429 | data_tuple = struct.unpack(self._fields_struct_fmt, buf[0:object_size]) |
---|
| 430 | |
---|
| 431 | # Filter out names for fields ignored during unpacking |
---|
| 432 | names = [n for (n,f) in zip(all_names, all_fmts) if 'x' not in f] |
---|
| 433 | |
---|
| 434 | # Populate object with data |
---|
| 435 | for i, name in enumerate(names): |
---|
| 436 | self[name] = data_tuple[i] |
---|
| 437 | |
---|
| 438 | except struct.error as err: |
---|
| 439 | print("Error unpacking buffer with len {0}: {1}".format(len(buf), err)) |
---|
| 440 | |
---|
| 441 | |
---|
| 442 | |
---|
| 443 | # ------------------------------------------------------------------------- |
---|
| 444 | # Internal methods for the InfoStruct object |
---|
| 445 | # ------------------------------------------------------------------------- |
---|
| 446 | def _update_field_defs(self): |
---|
| 447 | """Internal method to update meta-data about the fields.""" |
---|
| 448 | # Update the fields format used by struct unpack/calcsize |
---|
| 449 | self._fields_struct_fmt = ' '.join(self.get_field_struct_formats()) |
---|
| 450 | |
---|
| 451 | |
---|
| 452 | def __str__(self): |
---|
| 453 | """Pretty print info struct object""" |
---|
| 454 | msg = "{0}\n".format(self.__class__.__name__) |
---|
| 455 | |
---|
| 456 | for field in self._field_defs: |
---|
| 457 | if 'x' not in field[1]: |
---|
| 458 | msg += " {0:30s} = {1}\n".format(field[0], self[field[0]]) |
---|
| 459 | |
---|
| 460 | return msg |
---|
| 461 | |
---|
| 462 | # Allow attribute (ie ".") notation to access contents of dictionary |
---|
| 463 | def __getattr__(self, name): |
---|
| 464 | if name in self: |
---|
| 465 | return self[name] |
---|
| 466 | |
---|
| 467 | def __setattr__(self, name, value): |
---|
| 468 | if name in self: |
---|
| 469 | self[name] = value |
---|
| 470 | else: |
---|
| 471 | super(InfoStruct, self).__setattr__(name, value) |
---|
| 472 | |
---|
| 473 | def __add__(self, x): |
---|
| 474 | """Define an addition operator for Infostruct to build a new InfoStruct |
---|
| 475 | from two other InfoStructs. Neither input struct is modified. The return |
---|
| 476 | is a new InfoStruct instance with all the fields and values of the two inputs.""" |
---|
| 477 | |
---|
| 478 | # Verify no overlapping field names |
---|
| 479 | for f1_name in self.get_field_names(): |
---|
| 480 | if f1_name in x.get_field_names(): |
---|
| 481 | raise Exception('ERROR: cannot combine InfoStructs with duplicate field name {0}'.format(f1_name)) |
---|
| 482 | |
---|
| 483 | # Define the new sturct type, with |
---|
| 484 | r = InfoStruct(field_defs=self._field_defs + x._field_defs) |
---|
| 485 | |
---|
| 486 | # Copy values from the input structs to the new combined struct |
---|
| 487 | for f1_name in self.get_field_names(): |
---|
| 488 | r[f1_name] = self[f1_name] |
---|
| 489 | |
---|
| 490 | for f2_name in x.get_field_names(): |
---|
| 491 | r[f2_name] = x[f2_name] |
---|
| 492 | |
---|
| 493 | return r |
---|
| 494 | |
---|
| 495 | # End Class |
---|
| 496 | |
---|
| 497 | class NodeInfo(InfoStruct): |
---|
| 498 | """Class for framework NODE_INFO struct retrieved during init.""" |
---|
| 499 | |
---|
| 500 | def __init__(self): |
---|
| 501 | super(NodeInfo, self).__init__(field_sets='NODE_INFO') |
---|
| 502 | |
---|
| 503 | def serialize(self): |
---|
| 504 | print("Error: serialize() is not supported for NodeInfo.") |
---|
| 505 | raise NotImplementedError |
---|
| 506 | |
---|
| 507 | def deserialize(self, buf): |
---|
| 508 | import ctypes |
---|
| 509 | |
---|
| 510 | # Use superclass for raw deserialization using the struct field |
---|
| 511 | # formats specified for NODE_INFO above |
---|
| 512 | super(NodeInfo, self).deserialize(buf) |
---|
| 513 | |
---|
| 514 | # Then convert some fields to more useable Python types |
---|
| 515 | |
---|
| 516 | # compilation_date/time fields are null-terminated ASCII strings |
---|
| 517 | # Check last character is 0, then pass to util to convert to string |
---|
| 518 | # Return empty string if last character is not null (i.e. C screwed up) |
---|
| 519 | for f in ['high_compilation_date', 'high_compilation_time', |
---|
| 520 | 'low_compilation_date', 'low_compilation_time',]: |
---|
| 521 | |
---|
| 522 | if self[f][-1] == 0: |
---|
| 523 | # Valid null termination - decode ASCII bytes to Python string |
---|
| 524 | # C pads the strings with extra null characters to align |
---|
| 525 | # struct fields. The rstrip() call removes all trailing nulls |
---|
| 526 | self[f] = self[f].rstrip(b'\00').decode('utf-8') |
---|
| 527 | else: |
---|
| 528 | # Invalid termination - return empty string |
---|
| 529 | print('WARNING: NodeInfo deserialize ignored invalid string for {0}: {1}'.format(f, self[f])) |
---|
| 530 | self[f] = u'' |
---|
| 531 | |
---|
| 532 | # Convert the MAC address byte array to u64 |
---|
| 533 | self['wlan_mac_addr'] = util.byte_str_to_mac_addr(self['wlan_mac_addr']) |
---|
| 534 | |
---|
| 535 | # End Class |
---|
| 536 | |
---|
| 537 | # ----------------------------------------------------------------------------- |
---|
| 538 | # TX/RX Counts Class |
---|
| 539 | # ----------------------------------------------------------------------------- |
---|
| 540 | |
---|
| 541 | class TxRxCounts(InfoStruct): |
---|
| 542 | """Class for TX/RX counts.""" |
---|
| 543 | |
---|
| 544 | def __init__(self): |
---|
| 545 | super(TxRxCounts, self).__init__(field_sets='TXRX_COUNTS') |
---|
| 546 | |
---|
| 547 | # To populate the TxRxCounts with information, use the |
---|
| 548 | # deserialize() function on a proper buffer of data |
---|
| 549 | |
---|
| 550 | |
---|
| 551 | def serialize(self): |
---|
| 552 | # serialize() is currently not supported for TxRxCounts. This |
---|
| 553 | # is due to the fact that TxRxCounts information should only come |
---|
| 554 | # directly from the node and should not be set to the node. |
---|
| 555 | # |
---|
| 556 | print("Error: serialize() is not supported for TxRxCounts.") |
---|
| 557 | raise NotImplementedError |
---|
| 558 | |
---|
| 559 | # If serialize() needs to be supported in future version for |
---|
| 560 | # TxRxCounts, below is the code to use: |
---|
| 561 | # |
---|
| 562 | # # Convert MAC address to byte string for transmit |
---|
| 563 | # mac_addr_tmp = self['mac_addr'] |
---|
| 564 | # self['mac_addr'] = util.str_to_mac_addr(self['mac_addr']) |
---|
| 565 | # self['mac_addr'] = util.mac_addr_to_byte_str(self['mac_addr']) |
---|
| 566 | # |
---|
| 567 | # ret_val = super(TxRxCounts, self).serialize() |
---|
| 568 | # |
---|
| 569 | # # Revert MAC address to a colon delimited string |
---|
| 570 | # self['mac_addr'] = mac_addr_tmp |
---|
| 571 | # |
---|
| 572 | # return ret_val |
---|
| 573 | |
---|
| 574 | |
---|
| 575 | def deserialize(self, buf): |
---|
| 576 | super(TxRxCounts, self).deserialize(buf) |
---|
| 577 | |
---|
| 578 | if (False): |
---|
| 579 | msg = "TX/RX Counts Data Buffer:\n" |
---|
| 580 | msg += util.buffer_to_str(buf) |
---|
| 581 | print(msg) |
---|
| 582 | |
---|
| 583 | # Clean up the values |
---|
| 584 | # - Convert the MAC Address to a colon delimited string |
---|
| 585 | self['mac_addr'] = util.byte_str_to_mac_addr(self['mac_addr']) |
---|
| 586 | #self['mac_addr'] = util.mac_addr_to_str(self['mac_addr']) |
---|
| 587 | |
---|
| 588 | |
---|
| 589 | # End Class |
---|
| 590 | |
---|
| 591 | |
---|
| 592 | |
---|
| 593 | # ----------------------------------------------------------------------------- |
---|
| 594 | # Station Info Class |
---|
| 595 | # ----------------------------------------------------------------------------- |
---|
| 596 | |
---|
| 597 | class StationInfo(InfoStruct): |
---|
| 598 | """Class for Station Information.""" |
---|
| 599 | |
---|
| 600 | def __init__(self): |
---|
| 601 | super(StationInfo, self).__init__(field_sets='STATION_INFO') |
---|
| 602 | |
---|
| 603 | # To populate the TxRxCounts with information, use the |
---|
| 604 | # deserialize() function on a proper buffer of data |
---|
| 605 | |
---|
| 606 | |
---|
| 607 | def serialize(self): |
---|
| 608 | # serialize() is currently not supported for StationInfo. This |
---|
| 609 | # is due to the fact that StationInfo information should only come |
---|
| 610 | # directly from the node and should not be set to the node. |
---|
| 611 | # |
---|
| 612 | print("Error: serialize() is not supported for StationInfo.") |
---|
| 613 | raise NotImplementedError |
---|
| 614 | |
---|
| 615 | # If serialize() needs to be supported in future version for |
---|
| 616 | # StationInfo, below is the code to use: |
---|
| 617 | # |
---|
| 618 | # # Convert MAC address to byte string for transmit |
---|
| 619 | # mac_addr_tmp = self['mac_addr'] |
---|
| 620 | # self['mac_addr'] = util.str_to_mac_addr(self['mac_addr']) |
---|
| 621 | # self['mac_addr'] = util.mac_addr_to_byte_str(self['mac_addr']) |
---|
| 622 | # |
---|
| 623 | # ret_val = super(StationInfo, self).serialize() |
---|
| 624 | # |
---|
| 625 | # # Revert MAC address to a colon delimited string |
---|
| 626 | # self['mac_addr'] = mac_addr_tmp |
---|
| 627 | # |
---|
| 628 | # return ret_val |
---|
| 629 | |
---|
| 630 | |
---|
| 631 | def deserialize(self, buf): |
---|
| 632 | super(StationInfo, self).deserialize(buf) |
---|
| 633 | |
---|
| 634 | if (False): |
---|
| 635 | msg = "Station Info Data buffer:\n" |
---|
| 636 | msg += util.buffer_to_str(buf) |
---|
| 637 | print(msg) |
---|
| 638 | |
---|
| 639 | # Clean up the values |
---|
| 640 | # - Remove extra characters in the SSID |
---|
| 641 | # - Convert the MAC Address to a colon delimited string |
---|
| 642 | if (self['host_name'][0] == '\x00'): |
---|
| 643 | self['host_name'] = '\x00' |
---|
| 644 | else: |
---|
| 645 | import ctypes |
---|
| 646 | self['host_name'] = ctypes.c_char_p(self['host_name']).value |
---|
| 647 | |
---|
| 648 | self['mac_addr'] = util.byte_str_to_mac_addr(self['mac_addr']) |
---|
| 649 | #self['mac_addr'] = util.mac_addr_to_str(self['mac_addr']) |
---|
| 650 | |
---|
| 651 | |
---|
| 652 | # End Class |
---|
| 653 | |
---|
| 654 | # ----------------------------------------------------------------------------- |
---|
| 655 | # Network Info Class |
---|
| 656 | # ----------------------------------------------------------------------------- |
---|
| 657 | class NetworkInfo(InfoStruct): |
---|
| 658 | """Class for Network Information, represents information about wireless network |
---|
| 659 | observed by hardware nodes. |
---|
| 660 | """ |
---|
| 661 | def __init__(self): |
---|
| 662 | # Constructor has no arguments since NetworkInfo objects are only |
---|
| 663 | # created by deserializing bytes from the hardware node |
---|
| 664 | |
---|
| 665 | # Initialize the field definitions with BSS_CONFIG fields first |
---|
| 666 | super(NetworkInfo, self).__init__(field_sets=['BSS_CONFIG_COMMON', 'NETWORK_INFO']) |
---|
| 667 | |
---|
| 668 | def serialize(self): |
---|
| 669 | print("Error: serialize() is not supported for NetworkInfo") |
---|
| 670 | raise NotImplementedError |
---|
| 671 | |
---|
| 672 | |
---|
| 673 | def deserialize(self, buf): |
---|
| 674 | super(NetworkInfo, self).deserialize(buf) |
---|
| 675 | |
---|
| 676 | if (False): |
---|
| 677 | msg = "Network Info Data buffer:\n" |
---|
| 678 | msg += util.buffer_to_str(buf) |
---|
| 679 | print(msg) |
---|
| 680 | |
---|
| 681 | # Clean up the values |
---|
| 682 | # - Remove extra characters in the SSID |
---|
| 683 | # - Convert the BSS ID to a colon delimited string for storage |
---|
| 684 | # |
---|
| 685 | # A BSSID is a 48-bit integer and can be treated like a MAC |
---|
| 686 | # address in the wlan_exp framework (ie all the MAC address |
---|
| 687 | # utility functions can be used on it.) |
---|
| 688 | # |
---|
| 689 | import ctypes |
---|
| 690 | self['ssid'] = ctypes.c_char_p(self['ssid']).value |
---|
| 691 | # If the SSID is not a string already (which happens in Python3) |
---|
| 692 | # then decode the bytes class assuming a UTF-8 encoding |
---|
| 693 | if type(self['ssid']) is not str: |
---|
| 694 | self['ssid'] = self['ssid'].decode('utf-8') |
---|
| 695 | |
---|
| 696 | self['bssid'] = util.byte_str_to_mac_addr(self['bssid']) |
---|
| 697 | self['bssid'] = util.mac_addr_to_str(self['bssid']) |
---|
| 698 | |
---|
| 699 | |
---|
| 700 | # End Class |
---|
| 701 | |
---|
| 702 | |
---|
| 703 | # ----------------------------------------------------------------------------- |
---|
| 704 | # BSS Config Class |
---|
| 705 | # ----------------------------------------------------------------------------- |
---|
| 706 | class BSSConfig(InfoStruct): |
---|
| 707 | """Represents the BSS Config struct in hardware. This struct is created only |
---|
| 708 | in Python. The NetworkInfo and BSSConfigUpdate structs should be used for |
---|
| 709 | communicating BSS details with hardware nodes" |
---|
| 710 | """ |
---|
| 711 | def __init__(self): |
---|
| 712 | super(BSSConfig, self).__init__(field_sets='BSS_CONFIG_COMMON') |
---|
| 713 | |
---|
| 714 | def serialize(self): |
---|
| 715 | raise NotImplementedError('serialize() is not supported for BSSConfig') |
---|
| 716 | |
---|
| 717 | def deserialize(self): |
---|
| 718 | raise NotImplementedError('deserialize() is not supported for BSSConfig') |
---|
| 719 | |
---|
| 720 | # ----------------------------------------------------------------------------- |
---|
| 721 | # BSS Config Update Class |
---|
| 722 | # ----------------------------------------------------------------------------- |
---|
| 723 | |
---|
| 724 | class BSSConfigUpdate(InfoStruct): |
---|
| 725 | """Class for updating Basic Service Set (BSS) Configuration Information |
---|
| 726 | |
---|
| 727 | Attributes: |
---|
| 728 | bssid (int): 48-bit ID of the BSS either as a integer; A value of |
---|
| 729 | None will remove current BSS on the node (similar to |
---|
| 730 | node.reset(bss=True)); A value of False will not update the current |
---|
| 731 | bssid |
---|
| 732 | ssid (str): SSID string (Must be 32 characters or less); A value of |
---|
| 733 | None will not update the current SSID |
---|
| 734 | channel (int): Channel on which the BSS operates; A value of None will |
---|
| 735 | not update the current channel |
---|
| 736 | beacon_interval (int): Integer number of beacon Time Units in [10, 65534] |
---|
| 737 | (http://en.wikipedia.org/wiki/TU_(Time_Unit); a TU is 1024 microseconds); |
---|
| 738 | A value of None will disable beacons; A value of False will not |
---|
| 739 | update the current beacon interval |
---|
| 740 | dtim_period (int): Number of beacon intervals between DTIMs |
---|
| 741 | ht_capable (bool): Does the node support HTMF Tx/Rx. A value of None |
---|
| 742 | will not update the current value of HT capable. |
---|
| 743 | |
---|
| 744 | |
---|
| 745 | """ |
---|
| 746 | def __init__(self, bssid=False, ssid=None, channel=None, beacon_interval=False, dtim_period=None, ht_capable=None): |
---|
| 747 | # Initialize the field definitions with BSS_CONFIG fields first |
---|
| 748 | super(BSSConfigUpdate, self).__init__(field_sets=['BSS_CONFIG_UPDATE', 'BSS_CONFIG_COMMON']) |
---|
| 749 | |
---|
| 750 | # Default values used if value not provided: |
---|
| 751 | # bssid - 00:00:00:00:00:00 |
---|
| 752 | # ssid - "" |
---|
| 753 | # channel - 0 |
---|
| 754 | # beacon_interval - 0xFFFF |
---|
| 755 | # ht_capable - 0xFF |
---|
| 756 | |
---|
| 757 | # Initialize update mask |
---|
| 758 | self['update_mask'] = 0 |
---|
| 759 | |
---|
| 760 | # Set the BSSID field |
---|
| 761 | if bssid is not False: |
---|
| 762 | if bssid is not None: |
---|
| 763 | self['bssid'] = bssid |
---|
| 764 | |
---|
| 765 | # Convert BSSID to colon delimited string for internal storage |
---|
| 766 | if type(bssid) in [int, long]: |
---|
| 767 | self['bssid'] = util.mac_addr_to_str(self['bssid']) |
---|
| 768 | |
---|
| 769 | else: |
---|
| 770 | self['bssid'] = "00:00:00:00:00:00" |
---|
| 771 | |
---|
| 772 | # Set update mask |
---|
| 773 | self['update_mask'] |= self._consts.update_mask.BSSID |
---|
| 774 | else: |
---|
| 775 | # Remove current BSS on the node |
---|
| 776 | self['bssid'] = "00:00:00:00:00:00" |
---|
| 777 | |
---|
| 778 | # Set SSID field |
---|
| 779 | if ssid is not None: |
---|
| 780 | # Check SSID |
---|
| 781 | |
---|
| 782 | if type(ssid) is not str: |
---|
| 783 | raise ValueError("The SSID type was {0}".format(type(ssid))) |
---|
| 784 | |
---|
| 785 | if len(ssid) > 32: |
---|
| 786 | print("WARNING: Requested SSID {0} too long - truncating to {1}".format(ssid, ssid[:32])) |
---|
| 787 | ssid = ssid[:32] |
---|
| 788 | |
---|
| 789 | try: |
---|
| 790 | self['ssid'] = bytes(ssid, "UTF8") |
---|
| 791 | except: |
---|
| 792 | self['ssid'] = ssid |
---|
| 793 | |
---|
| 794 | # Set update mask |
---|
| 795 | self['update_mask'] |= self._consts.update_mask.SSID |
---|
| 796 | else: |
---|
| 797 | self['ssid'] = bytes() |
---|
| 798 | |
---|
| 799 | # Set Channel field |
---|
| 800 | if channel is not None: |
---|
| 801 | # Check Channel |
---|
| 802 | # - Make sure it is a valid channel; only store channel |
---|
| 803 | if channel not in util.wlan_channels: |
---|
| 804 | msg = "The channel must be a valid channel number. See util.py wlan_channels." |
---|
| 805 | raise ValueError(msg) |
---|
| 806 | |
---|
| 807 | self['channel'] = channel |
---|
| 808 | |
---|
| 809 | # Set update mask |
---|
| 810 | self['update_mask'] |= self._consts.update_mask.CHANNEL |
---|
| 811 | else: |
---|
| 812 | self['channel'] = 0 |
---|
| 813 | |
---|
| 814 | self['channel_type'] = self._consts.channel_type.BW20 |
---|
| 815 | |
---|
| 816 | # Set the beacon interval field |
---|
| 817 | if beacon_interval is not False: |
---|
| 818 | if beacon_interval is not None: |
---|
| 819 | # Check beacon interval |
---|
| 820 | b = int(beacon_interval) |
---|
| 821 | if b != beacon_interval: |
---|
| 822 | print("WARNING: Beacon interval {0} rounded to integer {1}".format(beacon_interval, b)) |
---|
| 823 | beacon_interval = b |
---|
| 824 | |
---|
| 825 | if not ((beacon_interval > 9) and (beacon_interval < (2**16 - 1))): |
---|
| 826 | msg = "The beacon interval must be in [10, 65534] (ie 16-bit positive integer)." |
---|
| 827 | raise ValueError(msg) |
---|
| 828 | |
---|
| 829 | self['beacon_interval'] = beacon_interval |
---|
| 830 | else: |
---|
| 831 | # Disable beacons |
---|
| 832 | self['beacon_interval'] = 0 |
---|
| 833 | |
---|
| 834 | # Set update mask |
---|
| 835 | self['update_mask'] |= self._consts.update_mask.BEACON_INTERVAL |
---|
| 836 | else: |
---|
| 837 | self['beacon_interval'] = 0xFFFF |
---|
| 838 | |
---|
| 839 | |
---|
| 840 | # Set the DTIM period |
---|
| 841 | if dtim_period is not None: |
---|
| 842 | |
---|
| 843 | # Check DTIM period |
---|
| 844 | d = int(dtim_period) |
---|
| 845 | if d != dtim_period: |
---|
| 846 | print("WARNING: DTIM period {0} rounded to nearest integer {1}".format(dtim_period, d)) |
---|
| 847 | dtim_period = d |
---|
| 848 | |
---|
| 849 | if not ((dtim_period > 0) and (dtim_period < 255)): |
---|
| 850 | msg = "The DTIM period must be in [1, 255] (ie 8-bit positive integer)." |
---|
| 851 | raise ValueError(msg) |
---|
| 852 | |
---|
| 853 | self['dtim_period'] = dtim_period |
---|
| 854 | |
---|
| 855 | # Set update mask |
---|
| 856 | self['update_mask'] |= self._consts.update_mask.DTIM_PERIOD |
---|
| 857 | else: |
---|
| 858 | self['dtim_period'] = 0xFF |
---|
| 859 | |
---|
| 860 | # Set the HT capable field |
---|
| 861 | if ht_capable is not None: |
---|
| 862 | # Check HT capable |
---|
| 863 | if type(ht_capable) is not bool: |
---|
| 864 | msg = "ht_capable must be a boolean." |
---|
| 865 | raise ValueError(msg) |
---|
| 866 | |
---|
| 867 | self['ht_capable'] = ht_capable |
---|
| 868 | |
---|
| 869 | # Set update mask |
---|
| 870 | self['update_mask'] |= self._consts.update_mask.HT_CAPABLE |
---|
| 871 | else: |
---|
| 872 | self['ht_capable'] = 0xFF |
---|
| 873 | |
---|
| 874 | |
---|
| 875 | def serialize(self): |
---|
| 876 | # Convert bssid to byte string for transmit |
---|
| 877 | bssid_tmp = self['bssid'] |
---|
| 878 | self['bssid'] = util.str_to_mac_addr(self['bssid']) |
---|
| 879 | self['bssid'] = util.mac_addr_to_byte_str(self['bssid']) |
---|
| 880 | |
---|
| 881 | ret_val = super(BSSConfigUpdate, self).serialize() |
---|
| 882 | |
---|
| 883 | # Revert bssid to colon delimited string |
---|
| 884 | self['bssid'] = bssid_tmp |
---|
| 885 | |
---|
| 886 | return ret_val |
---|
| 887 | |
---|
| 888 | |
---|
| 889 | def deserialize(self, buf): |
---|
| 890 | raise NotImplementedError("") |
---|
| 891 | |
---|
| 892 | |
---|
| 893 | # End Class |
---|
| 894 | |
---|
| 895 | |
---|
| 896 | |
---|
| 897 | # ----------------------------------------------------------------------------- |
---|
| 898 | # Misc Utilities |
---|
| 899 | # ----------------------------------------------------------------------------- |
---|
| 900 | def deserialize_info_buffer(buffer, buffer_class): |
---|
| 901 | """Unpacks a buffer of data into a list of objects |
---|
| 902 | |
---|
| 903 | Args: |
---|
| 904 | buf (bytearray): Array of bytes containing 1 or more objects of the same type |
---|
| 905 | |
---|
| 906 | Returns: |
---|
| 907 | information objects (List of Info Objects): |
---|
| 908 | Each Info object in the list has been filled in with the corresponding |
---|
| 909 | data from the buffer. |
---|
| 910 | """ |
---|
| 911 | ret_val = [] |
---|
| 912 | buffer_size = len(buffer) |
---|
| 913 | index = 0 |
---|
| 914 | object_size = 1 |
---|
| 915 | |
---|
| 916 | try: |
---|
| 917 | tmp_obj = eval(buffer_class, globals(), locals()) |
---|
| 918 | object_size = tmp_obj.sizeof() |
---|
| 919 | except: |
---|
| 920 | print("ERROR: Cannot create information object of class: {0}".format(buffer_class)) |
---|
| 921 | |
---|
| 922 | while (index < buffer_size): |
---|
| 923 | # try: |
---|
| 924 | tmp_obj = eval(buffer_class, globals(), locals()) |
---|
| 925 | tmp_obj.deserialize(buffer[index:index+object_size]) |
---|
| 926 | # except: |
---|
| 927 | # tmp_obj = None |
---|
| 928 | |
---|
| 929 | ret_val.append(tmp_obj) |
---|
| 930 | |
---|
| 931 | index += object_size |
---|
| 932 | |
---|
| 933 | return ret_val |
---|
| 934 | |
---|
| 935 | # End def |
---|
| 936 | |
---|