xref: /aosp_15_r20/external/openthread/tests/scripts/thread-cert/simulator.py (revision cfb92d1480a9e65faed56933e9c12405f45898b4)
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