# -*- coding: utf-8 -*- """ ------------------------------------------------------------------------------ Mango 802.11 Reference Design Experiments Framework - Transport / Network / Basic Node Commands ------------------------------------------------------------------------------ License: Copyright 2019 Mango Communications, Inc. All rights reserved. Use and distribution subject to terms in LICENSE.txt ------------------------------------------------------------------------------ This module provides class definitions for transport, network, and basic node commands. Functions (see below for more information): GetType() Identify() GetNodeInfo() SetupNetwork() ResetNetwork() Ping() TestPayloadSize() AddNodeGrpId() ClearNodeGrpId() Integer constants: GRPID_NODE, GRPID_TRANS - Command Groups GRP_NODE, GRP_TRANSPORT - Command Group Names Many other constants may be defined; please do not use these values when defining other sub-classes of CmdResp and BufferCmd. """ from . import message __all__ = ['NodeGetType', 'NodeIdentify', 'GetNodeInfo', 'NodeSetupNetwork', 'NodeResetNetwork', 'NodeGetTemperature', 'TransportPing', 'TestTransportMTU', 'TransportAddNodeGroupId', 'TransportClearNodeGroupId'] # Command Groups GROUP_NODE = 0x00 GROUP_TRANSPORT = 0x10 GROUP_USER = 0x20 # Command Group names GROUP_NAME_NODE = 'node' GROUP_NAME_TRANSPORT = 'transport' GROUP_NAME_USER = 'user' # Node Command IDs # - The C counterparts are found in *_node.h CMD_PARAM_NODE_TYPE_RSVD = 0xFFFFFFFF CMDID_NODE_TYPE = 0x000000 CMDID_NODE_INFO = 0x000001 CMDID_NODE_IDENTIFY = 0x000002 CMD_PARAM_NODE_IDENTIFY_ALL_NODES = 0xFFFFFFFF CMDID_NODE_CONFIG_SETUP = 0x000003 CMDID_NODE_CONFIG_RESET_MULTICAST = 0x000004 CMD_PARAM_NODE_NETWORK_RESET_ALL_NODES = 0xFFFFFFFF CMDID_NODE_TEMPERATURE = 0x000005 # Transport Command IDs # - The C counterparts are found in *_transport.h CMDID_TRANSPORT_PING = 0x000001 CMDID_TRANSPORT_SET_MAX_RESP_WORDS = 0x000002 CMDID_TRANSPORT_TEST_MTU = 0x000003 CMDID_TRANSPORT_NODE_GROUP_ID_ADD = 0x000100 CMDID_TRANSPORT_NODE_GROUP_ID_CLEAR = 0x000101 # Local Constants _CMD_GROUP_NODE = (GROUP_NODE << 24) _CMD_GROUP_TRANSPORT = (GROUP_TRANSPORT << 24) _CMD_GROUP_USER = (GROUP_USER << 24) # ----------------------------------------------------------------------------- # Node Commands # ----------------------------------------------------------------------------- class NodeGetType(message.Cmd): """Command to get the Type of the node""" def __init__(self): super(NodeGetType, self).__init__() self.command = _CMD_GROUP_NODE + CMDID_NODE_TYPE def process_resp(self, resp): if resp.resp_is_valid(num_args=3): args = resp.get_args() # Command returns 3 arguments: # Platform ID # High software application ID # Low software application ID # Return 3 IDs in tuple return (args[0], args[1], args[2]) else: raise Exception('ERROR: node returned invalid Node Type values: {}'.format(args)) class NodeIdentify(message.Cmd): """Command to blink the Node LEDs.""" def __init__(self, serial_number): super(NodeIdentify, self).__init__() self.command = _CMD_GROUP_NODE + CMDID_NODE_IDENTIFY if (serial_number == CMD_PARAM_NODE_IDENTIFY_ALL_NODES): (sn, sn_str) = (CMD_PARAM_NODE_IDENTIFY_ALL_NODES, "All nodes") platform_id = CMD_PARAM_NODE_IDENTIFY_ALL_NODES else: from . import util from .. import platform (sn, sn_str) = util.get_serial_number(serial_number) p = platform.lookup_platform_by_serial_num(serial_number) if p is None: raise Exception('ERROR: no registered platform matches serial number {0}') platform_id = p.platform_id self.id = sn_str self.add_args(platform_id) self.add_args(sn) def process_resp(self, resp): print("Blinking LEDs of node: {0}".format(self.id)) # End Class class GetNodeInfo(message.Cmd): """Command to get the hardware parameters from the node.""" def __init__(self): super(GetNodeInfo, self).__init__() self.command = _CMD_GROUP_NODE + CMDID_NODE_INFO def process_resp(self, resp): # NODE_INFO response format: # No arguments # payload[0:N-1]: Framework node info struct (same for all platforms) # payload[N:end]: Platform node info struct (platform-specific format) from wlan_exp.info import NodeInfo import wlan_exp.platform as platform # Parse the framework node info from the first N bytes of the response payload ni = NodeInfo() N = ni.sizeof() ni.deserialize(resp.resp_payload[0:N]) # Pass rest of payload to platform-specific InfoStruct # The current code only constructs one platform-specific node info struct # Additional info structs could be constructed here and appended to the return tuple p_id = ni['platform_id'] p_config = ni['platform_config'] p_ni = platform.get_platform_node_info_format(p_id, p_config) if p_ni is None: # node object is out-of-scope, so rely on serial number from NODE_INFO in error msg raise Exception('ERROR: no wlan_exp_platform found for platform_id {0} reported by node with s/n {1}'.format(p_id, ni['serial_number'])) p_ni.deserialize(resp.resp_payload[N:]) return (ni, p_ni) # End Class class NodeSetupNetwork(message.Cmd): """Command to perform initial network setup of a node.""" def __init__(self, node): super(NodeSetupNetwork, self).__init__() self.command = _CMD_GROUP_NODE + CMDID_NODE_CONFIG_SETUP self.add_args(node.platform_id) self.add_args(node.serial_number) self.add_args(node.node_id) self.add_args(node.transport.ip_to_int(node.transport.ip_address)) self.add_args(node.transport.unicast_port) self.add_args(node.transport.broadcast_port) def process_resp(self, resp): pass # End Class class NodeResetNetwork(message.Cmd): """Command to reset the network configuration of a node.""" def __init__(self, serial_number, output=False): super(NodeResetNetwork, self).__init__() self.command = _CMD_GROUP_NODE + CMDID_NODE_CONFIG_RESET_MULTICAST self.output = output if (serial_number == CMD_PARAM_NODE_NETWORK_RESET_ALL_NODES): (sn, sn_str) = (CMD_PARAM_NODE_NETWORK_RESET_ALL_NODES, "All nodes") platform_id = CMD_PARAM_NODE_NETWORK_RESET_ALL_NODES else: from . import util from .. import platform (sn, sn_str) = util.get_serial_number(serial_number) p = platform.lookup_platform_by_serial_num(serial_number) if p is None: raise Exception('ERROR: no registered platform matches serial number {0}') platform_id = p.platform_id self.id = sn_str self.add_args(platform_id) self.add_args(sn) def process_resp(self, resp): if (self.output): print("Reset network config of node: {0}".format(self.id)) # End Class class NodeGetTemperature(message.Cmd): """Command to get the temperature of a node. The response must be converted to Celsius with the given formula: ((double(temp)/65536.0)/0.00198421639) - 273.15 - http://www.xilinx.com/support/documentation/user_guides/ug370.pdf - 16 bit value where 10 MSBs are an ADC value """ def __init__(self): super(NodeGetTemperature, self).__init__() self.command = _CMD_GROUP_NODE + CMDID_NODE_TEMPERATURE def process_resp(self, resp): if resp.resp_is_valid(num_args=3): args = resp.get_args() curr_temp = ((float(args[0])/65536.0)/0.00198421639) - 273.15 min_temp = ((float(args[1])/65536.0)/0.00198421639) - 273.15 max_temp = ((float(args[2])/65536.0)/0.00198421639) - 273.15 return (curr_temp, min_temp, max_temp) else: return (0, 0, 0) # End Class # ----------------------------------------------------------------------------- # Transport Commands # ----------------------------------------------------------------------------- class TransportPing(message.Cmd): """Command to ping the node.""" def __init__(self): super(TransportPing, self).__init__() self.command = _CMD_GROUP_TRANSPORT + CMDID_TRANSPORT_PING def process_resp(self, resp): pass # End Class class TransportSetMaxRespWords(message.Cmd): """Sets the maximum number words the node may send in any response packet""" def __init__(self, max_words): super(TransportSetMaxRespWords, self).__init__() self.command = _CMD_GROUP_TRANSPORT + CMDID_TRANSPORT_SET_MAX_RESP_WORDS self.add_args(max_words) def process_resp(self, resp): # Response has no arguments - always successful if node responds return True class TestTransportMTU(message.Cmd): """Command to perform an uplink payload size test on a node.""" def __init__(self, req_mtu): super(TestTransportMTU, self).__init__() self.command = _CMD_GROUP_TRANSPORT + CMDID_TRANSPORT_TEST_MTU # Remember the requested MTU for comparison to the length of the # payload in the response packet self.req_mtu = req_mtu # Single argument is the MTU of the packet the node should send in # its reponse to this command. "MTU" in this context refers to the # entire Ethernet payload (raw bytes-on-wire minus 14-byte header and # 4-byte FCS). The the command argument below is computed from the # requested MTU less the size of the wlan_exp transport and response # headers that will precede the long payload in the response packet. import wlan_exp.transport.util as util # Response packet includes one u32 argument after # the cmdRespHdr, before the bogus payload (hence +4 below) self.num_hdr_bytes = util.get_total_header_len() + 4 self.add_args(int(req_mtu) - self.num_hdr_bytes) def process_resp(self, resp): args = resp.get_args() if resp.resp_is_valid(num_args=1): resp_req_len = int(args[0]) if resp_req_len == (self.req_mtu - self.num_hdr_bytes): # Response packet header arg matched requested size # Verify the response packet itself was the expected length # The 'resp' argument is the command response payload including # args and bogus payload. Response has 1 argument (4 bytes) # followed by bogus payload to fill requested MTU if (resp.length - 4) == resp_req_len: # Entire requested payload was received return True # Response packet was tuncated or malformed return False else: raise Exception('ERROR: TestTransportMTU response arguments invalid; args={}'.format(args)) # End Class class TransportAddNodeGroupId(message.Cmd): """Command to add a Node Group ID to the node so that it can process broadcast commands that are received from that node group.""" def __init__(self, group): super(TransportAddNodeGroupId, self).__init__() self.command = _CMD_GROUP_TRANSPORT + CMDID_TRANSPORT_NODE_GROUP_ID_ADD self.add_args(group) def process_resp(self, resp): pass # End Class class TransportClearNodeGroupId(message.Cmd): """Command to clear a Node Group ID to the node so that it can ignore broadcast commands that are received from that node group.""" def __init__(self, group): super(TransportClearNodeGroupId, self).__init__() self.command = _CMD_GROUP_TRANSPORT + CMDID_TRANSPORT_NODE_GROUP_ID_CLEAR self.add_args(group) def process_resp(self, resp): pass # End Class