1*cfb92d14SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*cfb92d14SAndroid Build Coastguard Worker# 3*cfb92d14SAndroid Build Coastguard Worker# Copyright (c) 2018, The OpenThread Authors. 4*cfb92d14SAndroid Build Coastguard Worker# All rights reserved. 5*cfb92d14SAndroid Build Coastguard Worker# 6*cfb92d14SAndroid Build Coastguard Worker# Redistribution and use in source and binary forms, with or without 7*cfb92d14SAndroid Build Coastguard Worker# modification, are permitted provided that the following conditions are met: 8*cfb92d14SAndroid Build Coastguard Worker# 1. Redistributions of source code must retain the above copyright 9*cfb92d14SAndroid Build Coastguard Worker# notice, this list of conditions and the following disclaimer. 10*cfb92d14SAndroid Build Coastguard Worker# 2. Redistributions in binary form must reproduce the above copyright 11*cfb92d14SAndroid Build Coastguard Worker# notice, this list of conditions and the following disclaimer in the 12*cfb92d14SAndroid Build Coastguard Worker# documentation and/or other materials provided with the distribution. 13*cfb92d14SAndroid Build Coastguard Worker# 3. Neither the name of the copyright holder nor the 14*cfb92d14SAndroid Build Coastguard Worker# names of its contributors may be used to endorse or promote products 15*cfb92d14SAndroid Build Coastguard Worker# derived from this software without specific prior written permission. 16*cfb92d14SAndroid Build Coastguard Worker# 17*cfb92d14SAndroid Build Coastguard Worker# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18*cfb92d14SAndroid Build Coastguard Worker# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19*cfb92d14SAndroid Build Coastguard Worker# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20*cfb92d14SAndroid Build Coastguard Worker# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21*cfb92d14SAndroid Build Coastguard Worker# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22*cfb92d14SAndroid Build Coastguard Worker# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23*cfb92d14SAndroid Build Coastguard Worker# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24*cfb92d14SAndroid Build Coastguard Worker# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25*cfb92d14SAndroid Build Coastguard Worker# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26*cfb92d14SAndroid Build Coastguard Worker# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27*cfb92d14SAndroid Build Coastguard Worker# POSSIBILITY OF SUCH DAMAGE. 28*cfb92d14SAndroid Build Coastguard Worker# 29*cfb92d14SAndroid Build Coastguard Worker 30*cfb92d14SAndroid Build Coastguard Workerimport binascii 31*cfb92d14SAndroid Build Coastguard Workerimport bisect 32*cfb92d14SAndroid Build Coastguard Workerimport os 33*cfb92d14SAndroid Build Coastguard Workerimport socket 34*cfb92d14SAndroid Build Coastguard Workerimport struct 35*cfb92d14SAndroid Build Coastguard Workerimport traceback 36*cfb92d14SAndroid Build Coastguard Workerimport time 37*cfb92d14SAndroid Build Coastguard Worker 38*cfb92d14SAndroid Build Coastguard Workerimport io 39*cfb92d14SAndroid Build Coastguard Workerimport config 40*cfb92d14SAndroid Build Coastguard Workerimport mesh_cop 41*cfb92d14SAndroid Build Coastguard Workerimport message 42*cfb92d14SAndroid Build Coastguard Workerimport pcap 43*cfb92d14SAndroid Build Coastguard Workerimport wpan 44*cfb92d14SAndroid Build Coastguard Worker 45*cfb92d14SAndroid Build Coastguard Worker 46*cfb92d14SAndroid Build Coastguard Workerdef dbg_print(*args): 47*cfb92d14SAndroid Build Coastguard Worker if False: 48*cfb92d14SAndroid Build Coastguard Worker print(args) 49*cfb92d14SAndroid Build Coastguard Worker 50*cfb92d14SAndroid Build Coastguard Worker 51*cfb92d14SAndroid Build Coastguard Workerclass BaseSimulator(object): 52*cfb92d14SAndroid Build Coastguard Worker 53*cfb92d14SAndroid Build Coastguard Worker def __init__(self): 54*cfb92d14SAndroid Build Coastguard Worker self._nodes = {} 55*cfb92d14SAndroid Build Coastguard Worker self.commissioning_messages = {} 56*cfb92d14SAndroid Build Coastguard Worker self._payload_parse_factory = mesh_cop.MeshCopCommandFactory(mesh_cop.create_default_mesh_cop_tlv_factories()) 57*cfb92d14SAndroid Build Coastguard Worker self._mesh_cop_msg_set = mesh_cop.create_mesh_cop_message_type_set() 58*cfb92d14SAndroid Build Coastguard Worker 59*cfb92d14SAndroid Build Coastguard Worker def __del__(self): 60*cfb92d14SAndroid Build Coastguard Worker self._nodes = None 61*cfb92d14SAndroid Build Coastguard Worker 62*cfb92d14SAndroid Build Coastguard Worker def add_node(self, node): 63*cfb92d14SAndroid Build Coastguard Worker self._nodes[node.nodeid] = node 64*cfb92d14SAndroid Build Coastguard Worker self.commissioning_messages[node.nodeid] = [] 65*cfb92d14SAndroid Build Coastguard Worker 66*cfb92d14SAndroid Build Coastguard Worker def set_lowpan_context(self, cid, prefix): 67*cfb92d14SAndroid Build Coastguard Worker raise NotImplementedError 68*cfb92d14SAndroid Build Coastguard Worker 69*cfb92d14SAndroid Build Coastguard Worker def get_messages_sent_by(self, nodeid): 70*cfb92d14SAndroid Build Coastguard Worker raise NotImplementedError 71*cfb92d14SAndroid Build Coastguard Worker 72*cfb92d14SAndroid Build Coastguard Worker def go(self, duration, nodeid=None): 73*cfb92d14SAndroid Build Coastguard Worker raise NotImplementedError 74*cfb92d14SAndroid Build Coastguard Worker 75*cfb92d14SAndroid Build Coastguard Worker def stop(self): 76*cfb92d14SAndroid Build Coastguard Worker raise NotImplementedError 77*cfb92d14SAndroid Build Coastguard Worker 78*cfb92d14SAndroid Build Coastguard Worker def read_cert_messages_in_commissioning_log(self, nodeids): 79*cfb92d14SAndroid Build Coastguard Worker for nodeid in nodeids: 80*cfb92d14SAndroid Build Coastguard Worker node = self._nodes[nodeid] 81*cfb92d14SAndroid Build Coastguard Worker if not node: 82*cfb92d14SAndroid Build Coastguard Worker continue 83*cfb92d14SAndroid Build Coastguard Worker for ( 84*cfb92d14SAndroid Build Coastguard Worker direction, 85*cfb92d14SAndroid Build Coastguard Worker type, 86*cfb92d14SAndroid Build Coastguard Worker payload, 87*cfb92d14SAndroid Build Coastguard Worker ) in node.read_cert_messages_in_commissioning_log(): 88*cfb92d14SAndroid Build Coastguard Worker msg = self._payload_parse_factory.parse(type.decode("utf-8"), io.BytesIO(payload)) 89*cfb92d14SAndroid Build Coastguard Worker self.commissioning_messages[nodeid].append(msg) 90*cfb92d14SAndroid Build Coastguard Worker 91*cfb92d14SAndroid Build Coastguard Worker 92*cfb92d14SAndroid Build Coastguard Workerclass RealTime(BaseSimulator): 93*cfb92d14SAndroid Build Coastguard Worker 94*cfb92d14SAndroid Build Coastguard Worker def __init__(self, use_message_factory=True): 95*cfb92d14SAndroid Build Coastguard Worker super(RealTime, self).__init__() 96*cfb92d14SAndroid Build Coastguard Worker self._sniffer = config.create_default_thread_sniffer(use_message_factory=use_message_factory) 97*cfb92d14SAndroid Build Coastguard Worker self._sniffer.start() 98*cfb92d14SAndroid Build Coastguard Worker 99*cfb92d14SAndroid Build Coastguard Worker def set_lowpan_context(self, cid, prefix): 100*cfb92d14SAndroid Build Coastguard Worker self._sniffer.set_lowpan_context(cid, prefix) 101*cfb92d14SAndroid Build Coastguard Worker 102*cfb92d14SAndroid Build Coastguard Worker def get_messages_sent_by(self, nodeid): 103*cfb92d14SAndroid Build Coastguard Worker messages = self._sniffer.get_messages_sent_by(nodeid).messages 104*cfb92d14SAndroid Build Coastguard Worker ret = message.MessagesSet(messages, self.commissioning_messages[nodeid]) 105*cfb92d14SAndroid Build Coastguard Worker self.commissioning_messages[nodeid] = [] 106*cfb92d14SAndroid Build Coastguard Worker return ret 107*cfb92d14SAndroid Build Coastguard Worker 108*cfb92d14SAndroid Build Coastguard Worker def now(self): 109*cfb92d14SAndroid Build Coastguard Worker return time.time() 110*cfb92d14SAndroid Build Coastguard Worker 111*cfb92d14SAndroid Build Coastguard Worker def go(self, duration, nodeid=None): 112*cfb92d14SAndroid Build Coastguard Worker time.sleep(duration) 113*cfb92d14SAndroid Build Coastguard Worker 114*cfb92d14SAndroid Build Coastguard Worker def stop(self): 115*cfb92d14SAndroid Build Coastguard Worker if self.is_running: 116*cfb92d14SAndroid Build Coastguard Worker # self._sniffer.stop() # FIXME: seems it blocks forever 117*cfb92d14SAndroid Build Coastguard Worker self._sniffer = None 118*cfb92d14SAndroid Build Coastguard Worker 119*cfb92d14SAndroid Build Coastguard Worker @property 120*cfb92d14SAndroid Build Coastguard Worker def is_running(self): 121*cfb92d14SAndroid Build Coastguard Worker return self._sniffer is not None 122*cfb92d14SAndroid Build Coastguard Worker 123*cfb92d14SAndroid Build Coastguard Worker 124*cfb92d14SAndroid Build Coastguard Workerclass VirtualTime(BaseSimulator): 125*cfb92d14SAndroid Build Coastguard Worker 126*cfb92d14SAndroid Build Coastguard Worker OT_SIM_EVENT_ALARM_FIRED = 0 127*cfb92d14SAndroid Build Coastguard Worker OT_SIM_EVENT_RADIO_RECEIVED = 1 128*cfb92d14SAndroid Build Coastguard Worker OT_SIM_EVENT_UART_WRITE = 2 129*cfb92d14SAndroid Build Coastguard Worker OT_SIM_EVENT_RADIO_SPINEL_WRITE = 3 130*cfb92d14SAndroid Build Coastguard Worker OT_SIM_EVENT_POSTCMD = 4 131*cfb92d14SAndroid Build Coastguard Worker 132*cfb92d14SAndroid Build Coastguard Worker EVENT_TIME = 0 133*cfb92d14SAndroid Build Coastguard Worker EVENT_SEQUENCE = 1 134*cfb92d14SAndroid Build Coastguard Worker EVENT_ADDR = 2 135*cfb92d14SAndroid Build Coastguard Worker EVENT_TYPE = 3 136*cfb92d14SAndroid Build Coastguard Worker EVENT_DATA_LENGTH = 4 137*cfb92d14SAndroid Build Coastguard Worker EVENT_DATA = 5 138*cfb92d14SAndroid Build Coastguard Worker 139*cfb92d14SAndroid Build Coastguard Worker BASE_PORT = 9000 140*cfb92d14SAndroid Build Coastguard Worker MAX_NODES = 33 141*cfb92d14SAndroid Build Coastguard Worker MAX_MESSAGE = 1024 142*cfb92d14SAndroid Build Coastguard Worker END_OF_TIME = float('inf') 143*cfb92d14SAndroid Build Coastguard Worker PORT_OFFSET = int(os.getenv('PORT_OFFSET', '0')) 144*cfb92d14SAndroid Build Coastguard Worker 145*cfb92d14SAndroid Build Coastguard Worker BLOCK_TIMEOUT = 10 146*cfb92d14SAndroid Build Coastguard Worker 147*cfb92d14SAndroid Build Coastguard Worker NCP_SIM = os.getenv('NODE_TYPE', 'sim') == 'ncp-sim' 148*cfb92d14SAndroid Build Coastguard Worker 149*cfb92d14SAndroid Build Coastguard Worker _message_factory = None 150*cfb92d14SAndroid Build Coastguard Worker 151*cfb92d14SAndroid Build Coastguard Worker def __init__(self, use_message_factory=True): 152*cfb92d14SAndroid Build Coastguard Worker super(VirtualTime, self).__init__() 153*cfb92d14SAndroid Build Coastguard Worker self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 154*cfb92d14SAndroid Build Coastguard Worker self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 2 * 1024 * 1024) 155*cfb92d14SAndroid Build Coastguard Worker self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 2 * 1024 * 1024) 156*cfb92d14SAndroid Build Coastguard Worker 157*cfb92d14SAndroid Build Coastguard Worker ip = '127.0.0.1' 158*cfb92d14SAndroid Build Coastguard Worker self.port = self.BASE_PORT + (self.PORT_OFFSET * (self.MAX_NODES + 1)) 159*cfb92d14SAndroid Build Coastguard Worker self.sock.bind((ip, self.port)) 160*cfb92d14SAndroid Build Coastguard Worker 161*cfb92d14SAndroid Build Coastguard Worker self.devices = {} 162*cfb92d14SAndroid Build Coastguard Worker self.event_queue = [] 163*cfb92d14SAndroid Build Coastguard Worker # there could be events scheduled at exactly the same time 164*cfb92d14SAndroid Build Coastguard Worker self.event_sequence = 0 165*cfb92d14SAndroid Build Coastguard Worker self.current_time = 0 166*cfb92d14SAndroid Build Coastguard Worker self.current_event = None 167*cfb92d14SAndroid Build Coastguard Worker self.awake_devices = set() 168*cfb92d14SAndroid Build Coastguard Worker self._nodes_by_ack_seq = {} 169*cfb92d14SAndroid Build Coastguard Worker self._node_ack_seq = {} 170*cfb92d14SAndroid Build Coastguard Worker 171*cfb92d14SAndroid Build Coastguard Worker self._pcap = pcap.PcapCodec(os.getenv('TEST_NAME', 'current')) 172*cfb92d14SAndroid Build Coastguard Worker # the addr for spinel-cli sending OT_SIM_EVENT_POSTCMD 173*cfb92d14SAndroid Build Coastguard Worker self._spinel_cli_addr = (ip, self.BASE_PORT + self.port) 174*cfb92d14SAndroid Build Coastguard Worker self.current_nodeid = None 175*cfb92d14SAndroid Build Coastguard Worker self._pause_time = 0 176*cfb92d14SAndroid Build Coastguard Worker 177*cfb92d14SAndroid Build Coastguard Worker if use_message_factory: 178*cfb92d14SAndroid Build Coastguard Worker self._message_factory = config.create_default_thread_message_factory() 179*cfb92d14SAndroid Build Coastguard Worker else: 180*cfb92d14SAndroid Build Coastguard Worker self._message_factory = None 181*cfb92d14SAndroid Build Coastguard Worker 182*cfb92d14SAndroid Build Coastguard Worker def __del__(self): 183*cfb92d14SAndroid Build Coastguard Worker if self.sock: 184*cfb92d14SAndroid Build Coastguard Worker self.stop() 185*cfb92d14SAndroid Build Coastguard Worker 186*cfb92d14SAndroid Build Coastguard Worker def stop(self): 187*cfb92d14SAndroid Build Coastguard Worker if self.sock: 188*cfb92d14SAndroid Build Coastguard Worker self.sock.close() 189*cfb92d14SAndroid Build Coastguard Worker self.sock = None 190*cfb92d14SAndroid Build Coastguard Worker 191*cfb92d14SAndroid Build Coastguard Worker @property 192*cfb92d14SAndroid Build Coastguard Worker def is_running(self): 193*cfb92d14SAndroid Build Coastguard Worker return self.sock is not None 194*cfb92d14SAndroid Build Coastguard Worker 195*cfb92d14SAndroid Build Coastguard Worker def _add_message(self, nodeid, message_obj): 196*cfb92d14SAndroid Build Coastguard Worker addr = ('127.0.0.1', self.port + nodeid) 197*cfb92d14SAndroid Build Coastguard Worker 198*cfb92d14SAndroid Build Coastguard Worker # Ignore any exceptions 199*cfb92d14SAndroid Build Coastguard Worker try: 200*cfb92d14SAndroid Build Coastguard Worker if self._message_factory is not None: 201*cfb92d14SAndroid Build Coastguard Worker messages = self._message_factory.create(io.BytesIO(message_obj)) 202*cfb92d14SAndroid Build Coastguard Worker self.devices[addr]['msgs'] += messages 203*cfb92d14SAndroid Build Coastguard Worker 204*cfb92d14SAndroid Build Coastguard Worker except message.DropPacketException: 205*cfb92d14SAndroid Build Coastguard Worker print('Drop current packet because it cannot be handled in test scripts') 206*cfb92d14SAndroid Build Coastguard Worker except Exception as e: 207*cfb92d14SAndroid Build Coastguard Worker # Just print the exception to the console 208*cfb92d14SAndroid Build Coastguard Worker print("EXCEPTION: %s" % e) 209*cfb92d14SAndroid Build Coastguard Worker traceback.print_exc() 210*cfb92d14SAndroid Build Coastguard Worker 211*cfb92d14SAndroid Build Coastguard Worker def set_lowpan_context(self, cid, prefix): 212*cfb92d14SAndroid Build Coastguard Worker if self._message_factory is not None: 213*cfb92d14SAndroid Build Coastguard Worker self._message_factory.set_lowpan_context(cid, prefix) 214*cfb92d14SAndroid Build Coastguard Worker 215*cfb92d14SAndroid Build Coastguard Worker def get_messages_sent_by(self, nodeid): 216*cfb92d14SAndroid Build Coastguard Worker """ Get sniffed messages. 217*cfb92d14SAndroid Build Coastguard Worker 218*cfb92d14SAndroid Build Coastguard Worker Note! This method flushes the message queue so calling this 219*cfb92d14SAndroid Build Coastguard Worker method again will return only the newly logged messages. 220*cfb92d14SAndroid Build Coastguard Worker 221*cfb92d14SAndroid Build Coastguard Worker Args: 222*cfb92d14SAndroid Build Coastguard Worker nodeid (int): node id 223*cfb92d14SAndroid Build Coastguard Worker 224*cfb92d14SAndroid Build Coastguard Worker Returns: 225*cfb92d14SAndroid Build Coastguard Worker MessagesSet: a set with received messages. 226*cfb92d14SAndroid Build Coastguard Worker """ 227*cfb92d14SAndroid Build Coastguard Worker addr = ('127.0.0.1', self.port + nodeid) 228*cfb92d14SAndroid Build Coastguard Worker 229*cfb92d14SAndroid Build Coastguard Worker messages = self.devices[addr]['msgs'] 230*cfb92d14SAndroid Build Coastguard Worker self.devices[addr]['msgs'] = [] 231*cfb92d14SAndroid Build Coastguard Worker 232*cfb92d14SAndroid Build Coastguard Worker ret = message.MessagesSet(messages, self.commissioning_messages[nodeid]) 233*cfb92d14SAndroid Build Coastguard Worker self.commissioning_messages[nodeid] = [] 234*cfb92d14SAndroid Build Coastguard Worker return ret 235*cfb92d14SAndroid Build Coastguard Worker 236*cfb92d14SAndroid Build Coastguard Worker def _is_radio(self, addr): 237*cfb92d14SAndroid Build Coastguard Worker return addr[1] < self.BASE_PORT * 2 238*cfb92d14SAndroid Build Coastguard Worker 239*cfb92d14SAndroid Build Coastguard Worker def _to_core_addr(self, addr): 240*cfb92d14SAndroid Build Coastguard Worker assert self._is_radio(addr) 241*cfb92d14SAndroid Build Coastguard Worker return (addr[0], addr[1] + self.BASE_PORT) 242*cfb92d14SAndroid Build Coastguard Worker 243*cfb92d14SAndroid Build Coastguard Worker def _to_radio_addr(self, addr): 244*cfb92d14SAndroid Build Coastguard Worker assert not self._is_radio(addr) 245*cfb92d14SAndroid Build Coastguard Worker return (addr[0], addr[1] - self.BASE_PORT) 246*cfb92d14SAndroid Build Coastguard Worker 247*cfb92d14SAndroid Build Coastguard Worker def _core_addr_from(self, nodeid): 248*cfb92d14SAndroid Build Coastguard Worker if self._nodes[nodeid].is_posix: 249*cfb92d14SAndroid Build Coastguard Worker return ('127.0.0.1', self.BASE_PORT + self.port + nodeid) 250*cfb92d14SAndroid Build Coastguard Worker else: 251*cfb92d14SAndroid Build Coastguard Worker return ('127.0.0.1', self.port + nodeid) 252*cfb92d14SAndroid Build Coastguard Worker 253*cfb92d14SAndroid Build Coastguard Worker def _next_event_time(self): 254*cfb92d14SAndroid Build Coastguard Worker if len(self.event_queue) == 0: 255*cfb92d14SAndroid Build Coastguard Worker return self.END_OF_TIME 256*cfb92d14SAndroid Build Coastguard Worker else: 257*cfb92d14SAndroid Build Coastguard Worker return self.event_queue[0][0] 258*cfb92d14SAndroid Build Coastguard Worker 259*cfb92d14SAndroid Build Coastguard Worker def receive_events(self): 260*cfb92d14SAndroid Build Coastguard Worker """ Receive events until all devices are asleep. """ 261*cfb92d14SAndroid Build Coastguard Worker while True: 262*cfb92d14SAndroid Build Coastguard Worker if (self.current_event or len(self.awake_devices) or 263*cfb92d14SAndroid Build Coastguard Worker (self._next_event_time() > self._pause_time and self.current_nodeid)): 264*cfb92d14SAndroid Build Coastguard Worker self.sock.settimeout(self.BLOCK_TIMEOUT) 265*cfb92d14SAndroid Build Coastguard Worker try: 266*cfb92d14SAndroid Build Coastguard Worker msg, addr = self.sock.recvfrom(self.MAX_MESSAGE) 267*cfb92d14SAndroid Build Coastguard Worker except socket.error: 268*cfb92d14SAndroid Build Coastguard Worker # print debug information on failure 269*cfb92d14SAndroid Build Coastguard Worker print('Current nodeid:') 270*cfb92d14SAndroid Build Coastguard Worker print(self.current_nodeid) 271*cfb92d14SAndroid Build Coastguard Worker print('Current awake:') 272*cfb92d14SAndroid Build Coastguard Worker print(self.awake_devices) 273*cfb92d14SAndroid Build Coastguard Worker print('Current time:') 274*cfb92d14SAndroid Build Coastguard Worker print(self.current_time) 275*cfb92d14SAndroid Build Coastguard Worker print('Current event:') 276*cfb92d14SAndroid Build Coastguard Worker print(self.current_event) 277*cfb92d14SAndroid Build Coastguard Worker print('Events:') 278*cfb92d14SAndroid Build Coastguard Worker for event in self.event_queue: 279*cfb92d14SAndroid Build Coastguard Worker print(event) 280*cfb92d14SAndroid Build Coastguard Worker raise 281*cfb92d14SAndroid Build Coastguard Worker else: 282*cfb92d14SAndroid Build Coastguard Worker self.sock.settimeout(0) 283*cfb92d14SAndroid Build Coastguard Worker try: 284*cfb92d14SAndroid Build Coastguard Worker msg, addr = self.sock.recvfrom(self.MAX_MESSAGE) 285*cfb92d14SAndroid Build Coastguard Worker except socket.error: 286*cfb92d14SAndroid Build Coastguard Worker break 287*cfb92d14SAndroid Build Coastguard Worker 288*cfb92d14SAndroid Build Coastguard Worker if addr != self._spinel_cli_addr and addr not in self.devices: 289*cfb92d14SAndroid Build Coastguard Worker self.devices[addr] = {} 290*cfb92d14SAndroid Build Coastguard Worker self.devices[addr]['alarm'] = None 291*cfb92d14SAndroid Build Coastguard Worker self.devices[addr]['msgs'] = [] 292*cfb92d14SAndroid Build Coastguard Worker self.devices[addr]['time'] = self.current_time 293*cfb92d14SAndroid Build Coastguard Worker self.awake_devices.discard(addr) 294*cfb92d14SAndroid Build Coastguard Worker # print "New device:", addr, self.devices 295*cfb92d14SAndroid Build Coastguard Worker 296*cfb92d14SAndroid Build Coastguard Worker delay, type, datalen = struct.unpack('=QBH', msg[:11]) 297*cfb92d14SAndroid Build Coastguard Worker data = msg[11:] 298*cfb92d14SAndroid Build Coastguard Worker 299*cfb92d14SAndroid Build Coastguard Worker event_time = self.current_time + delay 300*cfb92d14SAndroid Build Coastguard Worker 301*cfb92d14SAndroid Build Coastguard Worker if data: 302*cfb92d14SAndroid Build Coastguard Worker dbg_print( 303*cfb92d14SAndroid Build Coastguard Worker "New event: ", 304*cfb92d14SAndroid Build Coastguard Worker event_time, 305*cfb92d14SAndroid Build Coastguard Worker addr, 306*cfb92d14SAndroid Build Coastguard Worker type, 307*cfb92d14SAndroid Build Coastguard Worker datalen, 308*cfb92d14SAndroid Build Coastguard Worker binascii.hexlify(data), 309*cfb92d14SAndroid Build Coastguard Worker ) 310*cfb92d14SAndroid Build Coastguard Worker else: 311*cfb92d14SAndroid Build Coastguard Worker dbg_print("New event: ", event_time, addr, type, datalen) 312*cfb92d14SAndroid Build Coastguard Worker 313*cfb92d14SAndroid Build Coastguard Worker if type == self.OT_SIM_EVENT_ALARM_FIRED: 314*cfb92d14SAndroid Build Coastguard Worker # remove any existing alarm event for device 315*cfb92d14SAndroid Build Coastguard Worker if self.devices[addr]['alarm']: 316*cfb92d14SAndroid Build Coastguard Worker self.event_queue.remove(self.devices[addr]['alarm']) 317*cfb92d14SAndroid Build Coastguard Worker # print "-- Remove\t", self.devices[addr]['alarm'] 318*cfb92d14SAndroid Build Coastguard Worker 319*cfb92d14SAndroid Build Coastguard Worker # add alarm event to event queue 320*cfb92d14SAndroid Build Coastguard Worker event = (event_time, self.event_sequence, addr, type, datalen) 321*cfb92d14SAndroid Build Coastguard Worker self.event_sequence += 1 322*cfb92d14SAndroid Build Coastguard Worker # print "-- Enqueue\t", event, delay, self.current_time 323*cfb92d14SAndroid Build Coastguard Worker bisect.insort(self.event_queue, event) 324*cfb92d14SAndroid Build Coastguard Worker self.devices[addr]['alarm'] = event 325*cfb92d14SAndroid Build Coastguard Worker 326*cfb92d14SAndroid Build Coastguard Worker self.awake_devices.discard(addr) 327*cfb92d14SAndroid Build Coastguard Worker 328*cfb92d14SAndroid Build Coastguard Worker if (self.current_event and self.current_event[self.EVENT_ADDR] == addr): 329*cfb92d14SAndroid Build Coastguard Worker # print "Done\t", self.current_event 330*cfb92d14SAndroid Build Coastguard Worker self.current_event = None 331*cfb92d14SAndroid Build Coastguard Worker 332*cfb92d14SAndroid Build Coastguard Worker elif type == self.OT_SIM_EVENT_RADIO_RECEIVED: 333*cfb92d14SAndroid Build Coastguard Worker assert self._is_radio(addr) 334*cfb92d14SAndroid Build Coastguard Worker # add radio receive events event queue 335*cfb92d14SAndroid Build Coastguard Worker frame_info = wpan.dissect(data) 336*cfb92d14SAndroid Build Coastguard Worker 337*cfb92d14SAndroid Build Coastguard Worker recv_devices = None 338*cfb92d14SAndroid Build Coastguard Worker if frame_info.frame_type == wpan.FrameType.ACK: 339*cfb92d14SAndroid Build Coastguard Worker recv_devices = self._nodes_by_ack_seq.get(frame_info.seq_no) 340*cfb92d14SAndroid Build Coastguard Worker 341*cfb92d14SAndroid Build Coastguard Worker recv_devices = recv_devices or self.devices.keys() 342*cfb92d14SAndroid Build Coastguard Worker 343*cfb92d14SAndroid Build Coastguard Worker for device in recv_devices: 344*cfb92d14SAndroid Build Coastguard Worker if device != addr and self._is_radio(device): 345*cfb92d14SAndroid Build Coastguard Worker event = ( 346*cfb92d14SAndroid Build Coastguard Worker event_time, 347*cfb92d14SAndroid Build Coastguard Worker self.event_sequence, 348*cfb92d14SAndroid Build Coastguard Worker device, 349*cfb92d14SAndroid Build Coastguard Worker type, 350*cfb92d14SAndroid Build Coastguard Worker datalen, 351*cfb92d14SAndroid Build Coastguard Worker data, 352*cfb92d14SAndroid Build Coastguard Worker ) 353*cfb92d14SAndroid Build Coastguard Worker self.event_sequence += 1 354*cfb92d14SAndroid Build Coastguard Worker # print "-- Enqueue\t", event 355*cfb92d14SAndroid Build Coastguard Worker bisect.insort(self.event_queue, event) 356*cfb92d14SAndroid Build Coastguard Worker 357*cfb92d14SAndroid Build Coastguard Worker self._pcap.append(data, (event_time // 1000000, event_time % 1000000)) 358*cfb92d14SAndroid Build Coastguard Worker self._add_message(addr[1] - self.port, data) 359*cfb92d14SAndroid Build Coastguard Worker 360*cfb92d14SAndroid Build Coastguard Worker # add radio transmit done events to event queue 361*cfb92d14SAndroid Build Coastguard Worker event = ( 362*cfb92d14SAndroid Build Coastguard Worker event_time, 363*cfb92d14SAndroid Build Coastguard Worker self.event_sequence, 364*cfb92d14SAndroid Build Coastguard Worker addr, 365*cfb92d14SAndroid Build Coastguard Worker type, 366*cfb92d14SAndroid Build Coastguard Worker datalen, 367*cfb92d14SAndroid Build Coastguard Worker data, 368*cfb92d14SAndroid Build Coastguard Worker ) 369*cfb92d14SAndroid Build Coastguard Worker self.event_sequence += 1 370*cfb92d14SAndroid Build Coastguard Worker bisect.insort(self.event_queue, event) 371*cfb92d14SAndroid Build Coastguard Worker 372*cfb92d14SAndroid Build Coastguard Worker if frame_info.frame_type != wpan.FrameType.ACK and not frame_info.is_broadcast: 373*cfb92d14SAndroid Build Coastguard Worker self._on_ack_seq_change(addr, frame_info.seq_no) 374*cfb92d14SAndroid Build Coastguard Worker 375*cfb92d14SAndroid Build Coastguard Worker self.awake_devices.add(addr) 376*cfb92d14SAndroid Build Coastguard Worker 377*cfb92d14SAndroid Build Coastguard Worker elif type == self.OT_SIM_EVENT_RADIO_SPINEL_WRITE: 378*cfb92d14SAndroid Build Coastguard Worker assert not self._is_radio(addr) 379*cfb92d14SAndroid Build Coastguard Worker radio_addr = self._to_radio_addr(addr) 380*cfb92d14SAndroid Build Coastguard Worker if radio_addr not in self.devices: 381*cfb92d14SAndroid Build Coastguard Worker self.awake_devices.add(radio_addr) 382*cfb92d14SAndroid Build Coastguard Worker 383*cfb92d14SAndroid Build Coastguard Worker event = ( 384*cfb92d14SAndroid Build Coastguard Worker event_time, 385*cfb92d14SAndroid Build Coastguard Worker self.event_sequence, 386*cfb92d14SAndroid Build Coastguard Worker radio_addr, 387*cfb92d14SAndroid Build Coastguard Worker self.OT_SIM_EVENT_UART_WRITE, 388*cfb92d14SAndroid Build Coastguard Worker datalen, 389*cfb92d14SAndroid Build Coastguard Worker data, 390*cfb92d14SAndroid Build Coastguard Worker ) 391*cfb92d14SAndroid Build Coastguard Worker self.event_sequence += 1 392*cfb92d14SAndroid Build Coastguard Worker bisect.insort(self.event_queue, event) 393*cfb92d14SAndroid Build Coastguard Worker 394*cfb92d14SAndroid Build Coastguard Worker self.awake_devices.add(addr) 395*cfb92d14SAndroid Build Coastguard Worker 396*cfb92d14SAndroid Build Coastguard Worker elif type == self.OT_SIM_EVENT_UART_WRITE: 397*cfb92d14SAndroid Build Coastguard Worker assert self._is_radio(addr) 398*cfb92d14SAndroid Build Coastguard Worker core_addr = self._to_core_addr(addr) 399*cfb92d14SAndroid Build Coastguard Worker if core_addr not in self.devices: 400*cfb92d14SAndroid Build Coastguard Worker self.awake_devices.add(core_addr) 401*cfb92d14SAndroid Build Coastguard Worker 402*cfb92d14SAndroid Build Coastguard Worker event = ( 403*cfb92d14SAndroid Build Coastguard Worker event_time, 404*cfb92d14SAndroid Build Coastguard Worker self.event_sequence, 405*cfb92d14SAndroid Build Coastguard Worker core_addr, 406*cfb92d14SAndroid Build Coastguard Worker self.OT_SIM_EVENT_RADIO_SPINEL_WRITE, 407*cfb92d14SAndroid Build Coastguard Worker datalen, 408*cfb92d14SAndroid Build Coastguard Worker data, 409*cfb92d14SAndroid Build Coastguard Worker ) 410*cfb92d14SAndroid Build Coastguard Worker self.event_sequence += 1 411*cfb92d14SAndroid Build Coastguard Worker bisect.insort(self.event_queue, event) 412*cfb92d14SAndroid Build Coastguard Worker 413*cfb92d14SAndroid Build Coastguard Worker self.awake_devices.add(addr) 414*cfb92d14SAndroid Build Coastguard Worker 415*cfb92d14SAndroid Build Coastguard Worker elif type == self.OT_SIM_EVENT_POSTCMD: 416*cfb92d14SAndroid Build Coastguard Worker assert self.current_time == self._pause_time 417*cfb92d14SAndroid Build Coastguard Worker nodeid = struct.unpack('=B', data)[0] 418*cfb92d14SAndroid Build Coastguard Worker if self.current_nodeid == nodeid: 419*cfb92d14SAndroid Build Coastguard Worker self.current_nodeid = None 420*cfb92d14SAndroid Build Coastguard Worker 421*cfb92d14SAndroid Build Coastguard Worker def _on_ack_seq_change(self, device: tuple, seq_no: int): 422*cfb92d14SAndroid Build Coastguard Worker old_seq = self._node_ack_seq.pop(device, None) 423*cfb92d14SAndroid Build Coastguard Worker if old_seq is not None: 424*cfb92d14SAndroid Build Coastguard Worker self._nodes_by_ack_seq[old_seq].remove(device) 425*cfb92d14SAndroid Build Coastguard Worker 426*cfb92d14SAndroid Build Coastguard Worker self._node_ack_seq[device] = seq_no 427*cfb92d14SAndroid Build Coastguard Worker self._nodes_by_ack_seq.setdefault(seq_no, set()).add(device) 428*cfb92d14SAndroid Build Coastguard Worker 429*cfb92d14SAndroid Build Coastguard Worker def _send_message(self, message, addr): 430*cfb92d14SAndroid Build Coastguard Worker while True: 431*cfb92d14SAndroid Build Coastguard Worker try: 432*cfb92d14SAndroid Build Coastguard Worker sent = self.sock.sendto(message, addr) 433*cfb92d14SAndroid Build Coastguard Worker except socket.error: 434*cfb92d14SAndroid Build Coastguard Worker traceback.print_exc() 435*cfb92d14SAndroid Build Coastguard Worker time.sleep(0) 436*cfb92d14SAndroid Build Coastguard Worker else: 437*cfb92d14SAndroid Build Coastguard Worker break 438*cfb92d14SAndroid Build Coastguard Worker assert sent == len(message) 439*cfb92d14SAndroid Build Coastguard Worker 440*cfb92d14SAndroid Build Coastguard Worker def process_next_event(self): 441*cfb92d14SAndroid Build Coastguard Worker assert self.current_event is None 442*cfb92d14SAndroid Build Coastguard Worker assert self._next_event_time() < self.END_OF_TIME 443*cfb92d14SAndroid Build Coastguard Worker 444*cfb92d14SAndroid Build Coastguard Worker # process next event 445*cfb92d14SAndroid Build Coastguard Worker event = self.event_queue.pop(0) 446*cfb92d14SAndroid Build Coastguard Worker 447*cfb92d14SAndroid Build Coastguard Worker if len(event) == 5: 448*cfb92d14SAndroid Build Coastguard Worker event_time, sequence, addr, type, datalen = event 449*cfb92d14SAndroid Build Coastguard Worker dbg_print("Pop event: ", event_time, addr, type, datalen) 450*cfb92d14SAndroid Build Coastguard Worker else: 451*cfb92d14SAndroid Build Coastguard Worker event_time, sequence, addr, type, datalen, data = event 452*cfb92d14SAndroid Build Coastguard Worker dbg_print( 453*cfb92d14SAndroid Build Coastguard Worker "Pop event: ", 454*cfb92d14SAndroid Build Coastguard Worker event_time, 455*cfb92d14SAndroid Build Coastguard Worker addr, 456*cfb92d14SAndroid Build Coastguard Worker type, 457*cfb92d14SAndroid Build Coastguard Worker datalen, 458*cfb92d14SAndroid Build Coastguard Worker binascii.hexlify(data), 459*cfb92d14SAndroid Build Coastguard Worker ) 460*cfb92d14SAndroid Build Coastguard Worker 461*cfb92d14SAndroid Build Coastguard Worker self.current_event = event 462*cfb92d14SAndroid Build Coastguard Worker 463*cfb92d14SAndroid Build Coastguard Worker assert event_time >= self.current_time 464*cfb92d14SAndroid Build Coastguard Worker self.current_time = event_time 465*cfb92d14SAndroid Build Coastguard Worker 466*cfb92d14SAndroid Build Coastguard Worker elapsed = event_time - self.devices[addr]['time'] 467*cfb92d14SAndroid Build Coastguard Worker self.devices[addr]['time'] = event_time 468*cfb92d14SAndroid Build Coastguard Worker 469*cfb92d14SAndroid Build Coastguard Worker message = struct.pack('=QBH', elapsed, type, datalen) 470*cfb92d14SAndroid Build Coastguard Worker 471*cfb92d14SAndroid Build Coastguard Worker if type == self.OT_SIM_EVENT_ALARM_FIRED: 472*cfb92d14SAndroid Build Coastguard Worker self.devices[addr]['alarm'] = None 473*cfb92d14SAndroid Build Coastguard Worker self._send_message(message, addr) 474*cfb92d14SAndroid Build Coastguard Worker elif type == self.OT_SIM_EVENT_RADIO_RECEIVED: 475*cfb92d14SAndroid Build Coastguard Worker message += data 476*cfb92d14SAndroid Build Coastguard Worker self._send_message(message, addr) 477*cfb92d14SAndroid Build Coastguard Worker elif type == self.OT_SIM_EVENT_RADIO_SPINEL_WRITE: 478*cfb92d14SAndroid Build Coastguard Worker message += data 479*cfb92d14SAndroid Build Coastguard Worker self._send_message(message, addr) 480*cfb92d14SAndroid Build Coastguard Worker elif type == self.OT_SIM_EVENT_UART_WRITE: 481*cfb92d14SAndroid Build Coastguard Worker message += data 482*cfb92d14SAndroid Build Coastguard Worker self._send_message(message, addr) 483*cfb92d14SAndroid Build Coastguard Worker 484*cfb92d14SAndroid Build Coastguard Worker def sync_devices(self): 485*cfb92d14SAndroid Build Coastguard Worker self.current_time = self._pause_time 486*cfb92d14SAndroid Build Coastguard Worker for addr in self.devices: 487*cfb92d14SAndroid Build Coastguard Worker elapsed = self.current_time - self.devices[addr]['time'] 488*cfb92d14SAndroid Build Coastguard Worker if elapsed == 0: 489*cfb92d14SAndroid Build Coastguard Worker continue 490*cfb92d14SAndroid Build Coastguard Worker dbg_print('syncing', addr, elapsed) 491*cfb92d14SAndroid Build Coastguard Worker self.devices[addr]['time'] = self.current_time 492*cfb92d14SAndroid Build Coastguard Worker message = struct.pack('=QBH', elapsed, self.OT_SIM_EVENT_ALARM_FIRED, 0) 493*cfb92d14SAndroid Build Coastguard Worker self._send_message(message, addr) 494*cfb92d14SAndroid Build Coastguard Worker self.awake_devices.add(addr) 495*cfb92d14SAndroid Build Coastguard Worker self.receive_events() 496*cfb92d14SAndroid Build Coastguard Worker self.awake_devices.clear() 497*cfb92d14SAndroid Build Coastguard Worker 498*cfb92d14SAndroid Build Coastguard Worker def now(self): 499*cfb92d14SAndroid Build Coastguard Worker return self.current_time / 1000000 500*cfb92d14SAndroid Build Coastguard Worker 501*cfb92d14SAndroid Build Coastguard Worker def go(self, duration, nodeid=None): 502*cfb92d14SAndroid Build Coastguard Worker assert self.current_time == self._pause_time 503*cfb92d14SAndroid Build Coastguard Worker duration = int(duration * 1000000) 504*cfb92d14SAndroid Build Coastguard Worker dbg_print('running for %d us' % duration) 505*cfb92d14SAndroid Build Coastguard Worker self._pause_time += duration 506*cfb92d14SAndroid Build Coastguard Worker if nodeid: 507*cfb92d14SAndroid Build Coastguard Worker if self.NCP_SIM: 508*cfb92d14SAndroid Build Coastguard Worker self.current_nodeid = nodeid 509*cfb92d14SAndroid Build Coastguard Worker self.awake_devices.add(self._core_addr_from(nodeid)) 510*cfb92d14SAndroid Build Coastguard Worker self.receive_events() 511*cfb92d14SAndroid Build Coastguard Worker while self._next_event_time() <= self._pause_time: 512*cfb92d14SAndroid Build Coastguard Worker self.process_next_event() 513*cfb92d14SAndroid Build Coastguard Worker self.receive_events() 514*cfb92d14SAndroid Build Coastguard Worker if duration > 0: 515*cfb92d14SAndroid Build Coastguard Worker self.sync_devices() 516*cfb92d14SAndroid Build Coastguard Worker dbg_print('current time %d us' % self.current_time) 517*cfb92d14SAndroid Build Coastguard Worker 518*cfb92d14SAndroid Build Coastguard Worker 519*cfb92d14SAndroid Build Coastguard Workerif __name__ == '__main__': 520*cfb92d14SAndroid Build Coastguard Worker simulator = VirtualTime() 521*cfb92d14SAndroid Build Coastguard Worker while True: 522*cfb92d14SAndroid Build Coastguard Worker simulator.go(0) 523