xref: /aosp_15_r20/external/openthread/tests/toranj/ncp/wpan.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 sys
31*cfb92d14SAndroid Build Coastguard Workerimport os
32*cfb92d14SAndroid Build Coastguard Workerimport time
33*cfb92d14SAndroid Build Coastguard Workerimport re
34*cfb92d14SAndroid Build Coastguard Workerimport random
35*cfb92d14SAndroid Build Coastguard Workerimport weakref
36*cfb92d14SAndroid Build Coastguard Workerimport subprocess
37*cfb92d14SAndroid Build Coastguard Workerimport socket
38*cfb92d14SAndroid Build Coastguard Workerimport asyncore
39*cfb92d14SAndroid Build Coastguard Workerimport inspect
40*cfb92d14SAndroid Build Coastguard Worker
41*cfb92d14SAndroid Build Coastguard Worker# ----------------------------------------------------------------------------------------------------------------------
42*cfb92d14SAndroid Build Coastguard Worker# wpantund properties
43*cfb92d14SAndroid Build Coastguard Worker
44*cfb92d14SAndroid Build Coastguard WorkerWPAN_STATE = 'NCP:State'
45*cfb92d14SAndroid Build Coastguard WorkerWPAN_NAME = 'Network:Name'
46*cfb92d14SAndroid Build Coastguard WorkerWPAN_PANID = 'Network:PANID'
47*cfb92d14SAndroid Build Coastguard WorkerWPAN_XPANID = 'Network:XPANID'
48*cfb92d14SAndroid Build Coastguard WorkerWPAN_KEY = 'Network:Key'
49*cfb92d14SAndroid Build Coastguard WorkerWPAN_KEY_INDEX = 'Network:KeyIndex'
50*cfb92d14SAndroid Build Coastguard WorkerWPAN_CHANNEL = 'NCP:Channel'
51*cfb92d14SAndroid Build Coastguard WorkerWPAN_HW_ADDRESS = 'NCP:HardwareAddress'
52*cfb92d14SAndroid Build Coastguard WorkerWPAN_EXT_ADDRESS = 'NCP:ExtendedAddress'
53*cfb92d14SAndroid Build Coastguard WorkerWPAN_POLL_INTERVAL = 'NCP:SleepyPollInterval'
54*cfb92d14SAndroid Build Coastguard WorkerWPAN_NODE_TYPE = 'Network:NodeType'
55*cfb92d14SAndroid Build Coastguard WorkerWPAN_ROLE = 'Network:Role'
56*cfb92d14SAndroid Build Coastguard WorkerWPAN_PARTITION_ID = 'Network:PartitionId'
57*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_VERSION = 'NCP:Version'
58*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_MCU_POWER_STATE = "NCP:MCUPowerState"
59*cfb92d14SAndroid Build Coastguard WorkerWPAN_NETWORK_PASSTHRU_PORT = 'com.nestlabs.internal:Network:PassthruPort'
60*cfb92d14SAndroid Build Coastguard WorkerWPAN_RCP_VERSION = "POSIXApp:RCPVersion"
61*cfb92d14SAndroid Build Coastguard Worker
62*cfb92d14SAndroid Build Coastguard WorkerWPAN_IP6_LINK_LOCAL_ADDRESS = "IPv6:LinkLocalAddress"
63*cfb92d14SAndroid Build Coastguard WorkerWPAN_IP6_MESH_LOCAL_ADDRESS = "IPv6:MeshLocalAddress"
64*cfb92d14SAndroid Build Coastguard WorkerWPAN_IP6_MESH_LOCAL_PREFIX = "IPv6:MeshLocalPrefix"
65*cfb92d14SAndroid Build Coastguard WorkerWPAN_IP6_ALL_ADDRESSES = "IPv6:AllAddresses"
66*cfb92d14SAndroid Build Coastguard WorkerWPAN_IP6_MULTICAST_ADDRESSES = "IPv6:MulticastAddresses"
67*cfb92d14SAndroid Build Coastguard WorkerWPAN_IP6_INTERFACE_ROUTES = "IPv6:Routes"
68*cfb92d14SAndroid Build Coastguard Worker
69*cfb92d14SAndroid Build Coastguard WorkerWPAN_DAEMON_OFF_MESH_ROUTE_AUTO_ADD_ON_INTERFACE = "Daemon:OffMeshRoute:AutoAddOnInterface"
70*cfb92d14SAndroid Build Coastguard WorkerWPAN_DAEMON_OFF_MESH_ROUTE_FILTER_SELF_AUTO_ADDED = "Daemon:OffMeshRoute:FilterSelfAutoAdded"
71*cfb92d14SAndroid Build Coastguard WorkerWPAN_DAEMON_ON_MESH_PREFIX_AUTO_ADD_AS_INTERFACE_ROUTE = "Daemon:OnMeshPrefix:AutoAddAsInterfaceRoute"
72*cfb92d14SAndroid Build Coastguard Worker
73*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_RLOC16 = "Thread:RLOC16"
74*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_ROUTER_ID = "Thread:RouterID"
75*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_LEADER_ADDRESS = "Thread:Leader:Address"
76*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_LEADER_ROUTER_ID = "Thread:Leader:RouterID"
77*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_LEADER_WEIGHT = "Thread:Leader:Weight"
78*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_LEADER_LOCAL_WEIGHT = "Thread:Leader:LocalWeight"
79*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_LEADER_NETWORK_DATA = "Thread:Leader:NetworkData"
80*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_STABLE_LEADER_NETWORK_DATA = "Thread:Leader:StableNetworkData"
81*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_NETWORK_DATA = "Thread:NetworkData"
82*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_CHILD_TABLE = "Thread:ChildTable"
83*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_CHILD_TABLE_ASVALMAP = "Thread:ChildTable:AsValMap"
84*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_CHILD_TABLE_ADDRESSES = "Thread:ChildTable:Addresses"
85*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_NEIGHBOR_TABLE = "Thread:NeighborTable"
86*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_NEIGHBOR_TABLE_ASVALMAP = "Thread:NeighborTable:AsValMap"
87*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_NEIGHBOR_TABLE_ERR_RATES = "Thread:NeighborTable:ErrorRates"
88*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_NEIGHBOR_TABLE_ERR_RATES_AVVALMAP = "Thread:NeighborTable:ErrorRates:AsValMap"
89*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_ROUTER_TABLE = "Thread:RouterTable"
90*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_ROUTER_TABLE_ASVALMAP = "Thread:RouterTable:AsValMap"
91*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_CHILD_TIMEOUT = "Thread:ChildTimeout"
92*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_PARENT = "Thread:Parent"
93*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_PARENT_ASVALMAP = "Thread:Parent:AsValMap"
94*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_NETWORK_DATA_VERSION = "Thread:NetworkDataVersion"
95*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_STABLE_NETWORK_DATA = "Thread:StableNetworkData"
96*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_STABLE_NETWORK_DATA_VERSION = "Thread:StableNetworkDataVersion"
97*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_PREFERRED_ROUTER_ID = "Thread:PreferredRouterID"
98*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_COMMISSIONER_ENABLED = "Thread:Commissioner:Enabled"
99*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_DEVICE_MODE = "Thread:DeviceMode"
100*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_OFF_MESH_ROUTES = "Thread:OffMeshRoutes"
101*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_ON_MESH_PREFIXES = "Thread:OnMeshPrefixes"
102*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_ROUTER_ROLE_ENABLED = "Thread:RouterRole:Enabled"
103*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_CONFIG_FILTER_RLOC_ADDRESSES = "Thread:Config:FilterRLOCAddresses"
104*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_ROUTER_UPGRADE_THRESHOLD = "Thread:RouterUpgradeThreshold"
105*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_ROUTER_DOWNGRADE_THRESHOLD = "Thread:RouterDowngradeThreshold"
106*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_ACTIVE_DATASET = "Thread:ActiveDataset"
107*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_ACTIVE_DATASET_ASVALMAP = "Thread:ActiveDataset:AsValMap"
108*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_PENDING_DATASET = "Thread:PendingDataset"
109*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_PENDING_DATASET_ASVALMAP = "Thread:PendingDataset:AsValMap"
110*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_ADDRESS_CACHE_TABLE = "Thread:AddressCacheTable"
111*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_ADDRESS_CACHE_TABLE_ASVALMAP = "Thread:AddressCacheTable:AsValMap"
112*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_JOINER_DISCERNER_VALUE = "Joiner:Discerner:Value"
113*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_JOINER_DISCERNER_BIT_LENGTH = "Joiner:Discerner:BitLength"
114*cfb92d14SAndroid Build Coastguard WorkerWPAN_THREAD_COMMISSIONER_JOINERS = "Commissioner:Joiners"
115*cfb92d14SAndroid Build Coastguard Worker
116*cfb92d14SAndroid Build Coastguard WorkerWPAN_OT_LOG_LEVEL = "OpenThread:LogLevel"
117*cfb92d14SAndroid Build Coastguard WorkerWPAN_OT_SLAAC_ENABLED = "OpenThread:SLAAC:Enabled"
118*cfb92d14SAndroid Build Coastguard WorkerWPAN_OT_STEERING_DATA_ADDRESS = "OpenThread:SteeringData:Address"
119*cfb92d14SAndroid Build Coastguard WorkerWPAN_OT_STEERING_DATA_SET_WHEN_JOINABLE = "OpenThread:SteeringData:SetWhenJoinable"
120*cfb92d14SAndroid Build Coastguard WorkerWPAN_OT_MSG_BUFFER_COUNTERS = "OpenThread:MsgBufferCounters"
121*cfb92d14SAndroid Build Coastguard WorkerWPAN_OT_MSG_BUFFER_COUNTERS_AS_STRING = "OpenThread:MsgBufferCounters:AsString"
122*cfb92d14SAndroid Build Coastguard WorkerWPAN_OT_DEBUG_TEST_ASSERT = "OpenThread:Debug:TestAssert"
123*cfb92d14SAndroid Build Coastguard WorkerWPAN_OT_DEBUG_TEST_WATCHDOG = "OpenThread:Debug:TestWatchdog"
124*cfb92d14SAndroid Build Coastguard WorkerWPAN_OT_SUPPORTED_RADIO_LINKS = "OpenThread:SupportedRadioLinks"
125*cfb92d14SAndroid Build Coastguard WorkerWPAN_OT_NEIGHBOR_TABLE_MULTI_RADIO_INFO = "OpenThread:NeighborTable::MultiRadioInfo"
126*cfb92d14SAndroid Build Coastguard WorkerWPAN_OT_TREL_TEST_MODE_ENABLE = "OpenThread:Trel:TestMode:Enable"
127*cfb92d14SAndroid Build Coastguard Worker
128*cfb92d14SAndroid Build Coastguard WorkerWPAN_MAC_ALLOWLIST_ENABLED = "MAC:Allowlist:Enabled"
129*cfb92d14SAndroid Build Coastguard WorkerWPAN_MAC_ALLOWLIST_ENTRIES = "MAC:Allowlist:Entries"
130*cfb92d14SAndroid Build Coastguard WorkerWPAN_MAC_ALLOWLIST_ENTRIES_ASVALMAP = "MAC:Allowlist:Entries:AsValMap"
131*cfb92d14SAndroid Build Coastguard WorkerWPAN_MAC_DENYLIST_ENABLED = "MAC:Denylist:Enabled"
132*cfb92d14SAndroid Build Coastguard WorkerWPAN_MAC_DENYLIST_ENTRIES = "MAC:Denylist:Entries"
133*cfb92d14SAndroid Build Coastguard WorkerWPAN_MAC_DENYLIST_ENTRIES_ASVALMAP = "MAC:Denylist:Entries:AsValMap"
134*cfb92d14SAndroid Build Coastguard Worker
135*cfb92d14SAndroid Build Coastguard WorkerWPAN_MAC_FILTER_FIXED_RSSI = "MAC:Filter:FixedRssi"
136*cfb92d14SAndroid Build Coastguard WorkerWPAN_MAC_FILTER_ENTRIES = "MAC:Filter:Entries"
137*cfb92d14SAndroid Build Coastguard WorkerWPAN_MAC_FILTER_ENTRIES_ASVALMAP = "MAC:Filter:Entries:AsValMap"
138*cfb92d14SAndroid Build Coastguard Worker
139*cfb92d14SAndroid Build Coastguard WorkerWPAN_CHILD_SUPERVISION_INTERVAL = "ChildSupervision:Interval"
140*cfb92d14SAndroid Build Coastguard WorkerWPAN_CHILD_SUPERVISION_CHECK_TIMEOUT = "ChildSupervision:CheckTimeout"
141*cfb92d14SAndroid Build Coastguard Worker
142*cfb92d14SAndroid Build Coastguard WorkerWPAN_JAM_DETECTION_STATUS = "JamDetection:Status"
143*cfb92d14SAndroid Build Coastguard WorkerWPAN_JAM_DETECTION_ENABLE = "JamDetection:Enable"
144*cfb92d14SAndroid Build Coastguard WorkerWPAN_JAM_DETECTION_RSSI_THRESHOLD = "JamDetection:RssiThreshold"
145*cfb92d14SAndroid Build Coastguard WorkerWPAN_JAM_DETECTION_WINDOW = "JamDetection:Window"
146*cfb92d14SAndroid Build Coastguard WorkerWPAN_JAM_DETECTION_BUSY_PERIOD = "JamDetection:BusyPeriod"
147*cfb92d14SAndroid Build Coastguard WorkerWPAN_JAM_DETECTION_DEBUG_HISTORY_BITMAP = "JamDetection:Debug:HistoryBitmap"
148*cfb92d14SAndroid Build Coastguard Worker
149*cfb92d14SAndroid Build Coastguard WorkerWPAN_CHANNEL_MONITOR_SAMPLE_INTERVAL = "ChannelMonitor:SampleInterval"
150*cfb92d14SAndroid Build Coastguard WorkerWPAN_CHANNEL_MONITOR_RSSI_THRESHOLD = "ChannelMonitor:RssiThreshold"
151*cfb92d14SAndroid Build Coastguard WorkerWPAN_CHANNEL_MONITOR_SAMPLE_WINDOW = "ChannelMonitor:SampleWindow"
152*cfb92d14SAndroid Build Coastguard WorkerWPAN_CHANNEL_MONITOR_SAMPLE_COUNT = "ChannelMonitor:SampleCount"
153*cfb92d14SAndroid Build Coastguard WorkerWPAN_CHANNEL_MONITOR_CHANNEL_QUALITY = "ChannelMonitor:ChannelQuality"
154*cfb92d14SAndroid Build Coastguard WorkerWPAN_CHANNEL_MONITOR_CHANNEL_QUALITY_ASVALMAP = "ChannelMonitor:ChannelQuality:AsValMap"
155*cfb92d14SAndroid Build Coastguard Worker
156*cfb92d14SAndroid Build Coastguard WorkerWPAN_CHANNEL_MANAGER_NEW_CHANNEL = "ChannelManager:NewChannel"
157*cfb92d14SAndroid Build Coastguard WorkerWPAN_CHANNEL_MANAGER_DELAY = "ChannelManager:Delay"
158*cfb92d14SAndroid Build Coastguard WorkerWPAN_CHANNEL_MANAGER_CHANNEL_SELECT = "ChannelManager:ChannelSelect"
159*cfb92d14SAndroid Build Coastguard WorkerWPAN_CHANNEL_MANAGER_AUTO_SELECT_ENABLED = "ChannelManager:AutoSelect:Enabled"
160*cfb92d14SAndroid Build Coastguard WorkerWPAN_CHANNEL_MANAGER_AUTO_SELECT_INTERVAL = "ChannelManager:AutoSelect:Interval"
161*cfb92d14SAndroid Build Coastguard WorkerWPAN_CHANNEL_MANAGER_SUPPORTED_CHANNEL_MASK = "ChannelManager:SupportedChannelMask"
162*cfb92d14SAndroid Build Coastguard WorkerWPAN_CHANNEL_MANAGER_FAVORED_CHANNEL_MASK = "ChannelManager:FavoredChannelMask"
163*cfb92d14SAndroid Build Coastguard Worker
164*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_ALL_MAC = "NCP:Counter:AllMac"
165*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_ALL_MAC_ASVALMAP = "NCP:Counter:AllMac:AsValMap"
166*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_TX_PKT_TOTAL = "NCP:Counter:TX_PKT_TOTAL"
167*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_TX_PKT_UNICAST = "NCP:Counter:TX_PKT_UNICAST"
168*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_TX_PKT_BROADCAST = "NCP:Counter:TX_PKT_BROADCAST"
169*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_TX_PKT_ACK_REQ = "NCP:Counter:TX_PKT_ACK_REQ"
170*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_TX_PKT_ACKED = "NCP:Counter:TX_PKT_ACKED"
171*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_TX_PKT_NO_ACK_REQ = "NCP:Counter:TX_PKT_NO_ACK_REQ"
172*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_TX_PKT_DATA = "NCP:Counter:TX_PKT_DATA"
173*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_TX_PKT_DATA_POLL = "NCP:Counter:TX_PKT_DATA_POLL"
174*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_TX_PKT_BEACON = "NCP:Counter:TX_PKT_BEACON"
175*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_TX_PKT_BEACON_REQ = "NCP:Counter:TX_PKT_BEACON_REQ"
176*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_TX_PKT_OTHER = "NCP:Counter:TX_PKT_OTHER"
177*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_TX_PKT_RETRY = "NCP:Counter:TX_PKT_RETRY"
178*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_TX_ERR_CCA = "NCP:Counter:TX_ERR_CCA"
179*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_TX_ERR_ABORT = "NCP:Counter:TX_ERR_ABORT"
180*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_RX_PKT_TOTAL = "NCP:Counter:RX_PKT_TOTAL"
181*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_RX_PKT_UNICAST = "NCP:Counter:RX_PKT_UNICAST"
182*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_RX_PKT_BROADCAST = "NCP:Counter:RX_PKT_BROADCAST"
183*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_RX_PKT_DATA = "NCP:Counter:RX_PKT_DATA"
184*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_RX_PKT_DATA_POLL = "NCP:Counter:RX_PKT_DATA_POLL"
185*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_RX_PKT_BEACON = "NCP:Counter:RX_PKT_BEACON"
186*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_RX_PKT_BEACON_REQ = "NCP:Counter:RX_PKT_BEACON_REQ"
187*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_RX_PKT_OTHER = "NCP:Counter:RX_PKT_OTHER"
188*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_RX_PKT_FILT_WL = "NCP:Counter:RX_PKT_FILT_WL"
189*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_RX_PKT_FILT_DA = "NCP:Counter:RX_PKT_FILT_DA"
190*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_RX_ERR_EMPTY = "NCP:Counter:RX_ERR_EMPTY"
191*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_RX_ERR_UKWN_NBR = "NCP:Counter:RX_ERR_UKWN_NBR"
192*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_RX_ERR_NVLD_SADDR = "NCP:Counter:RX_ERR_NVLD_SADDR"
193*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_RX_ERR_SECURITY = "NCP:Counter:RX_ERR_SECURITY"
194*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_RX_ERR_BAD_FCS = "NCP:Counter:RX_ERR_BAD_FCS"
195*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_RX_ERR_OTHER = "NCP:Counter:RX_ERR_OTHER"
196*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_TX_IP_SEC_TOTAL = "NCP:Counter:TX_IP_SEC_TOTAL"
197*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_TX_IP_INSEC_TOTAL = "NCP:Counter:TX_IP_INSEC_TOTAL"
198*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_TX_IP_DROPPED = "NCP:Counter:TX_IP_DROPPED"
199*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_RX_IP_SEC_TOTAL = "NCP:Counter:RX_IP_SEC_TOTAL"
200*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_RX_IP_INSEC_TOTAL = "NCP:Counter:RX_IP_INSEC_TOTAL"
201*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_RX_IP_DROPPED = "NCP:Counter:RX_IP_DROPPED"
202*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_TX_SPINEL_TOTAL = "NCP:Counter:TX_SPINEL_TOTAL"
203*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_RX_SPINEL_TOTAL = "NCP:Counter:RX_SPINEL_TOTAL"
204*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_RX_SPINEL_ERR = "NCP:Counter:RX_SPINEL_ERR"
205*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_IP_TX_SUCCESS = "NCP:Counter:IP_TX_SUCCESS"
206*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_IP_RX_SUCCESS = "NCP:Counter:IP_RX_SUCCESS"
207*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_IP_TX_FAILURE = "NCP:Counter:IP_TX_FAILURE"
208*cfb92d14SAndroid Build Coastguard WorkerWPAN_NCP_COUNTER_IP_RX_FAILURE = "NCP:Counter:IP_RX_FAILURE"
209*cfb92d14SAndroid Build Coastguard Worker
210*cfb92d14SAndroid Build Coastguard Worker# ----------------------------------------------------------------------------------------------------------------------
211*cfb92d14SAndroid Build Coastguard Worker# Valid state values
212*cfb92d14SAndroid Build Coastguard Worker
213*cfb92d14SAndroid Build Coastguard WorkerSTATE_UNINITIALIZED = '"uninitialized"'
214*cfb92d14SAndroid Build Coastguard WorkerSTATE_FAULT = '"uninitialized:fault"'
215*cfb92d14SAndroid Build Coastguard WorkerSTATE_UPGRADING = '"uninitialized:upgrading"'
216*cfb92d14SAndroid Build Coastguard WorkerSTATE_DEEP_SLEEP = '"offline:deep-sleep"'
217*cfb92d14SAndroid Build Coastguard WorkerSTATE_OFFLINE = '"offline"'
218*cfb92d14SAndroid Build Coastguard WorkerSTATE_COMMISSIONED = '"offline:commissioned"'
219*cfb92d14SAndroid Build Coastguard WorkerSTATE_ASSOCIATING = '"associating"'
220*cfb92d14SAndroid Build Coastguard WorkerSTATE_CREDENTIALS_NEEDED = '"associating:credentials-needed"'
221*cfb92d14SAndroid Build Coastguard WorkerSTATE_ASSOCIATED = '"associated"'
222*cfb92d14SAndroid Build Coastguard WorkerSTATE_ISOLATED = '"associated:no-parent"'
223*cfb92d14SAndroid Build Coastguard WorkerSTATE_NETWAKE_ASLEEP = '"associated:netwake-asleep"'
224*cfb92d14SAndroid Build Coastguard WorkerSTATE_NETWAKE_WAKING = '"associated:netwake-waking"'
225*cfb92d14SAndroid Build Coastguard Worker
226*cfb92d14SAndroid Build Coastguard Worker# -----------------------------------------------------------------------------------------------------------------------
227*cfb92d14SAndroid Build Coastguard Worker# MCU Power state from `WPAN_NCP_MCU_POWER_STATE`
228*cfb92d14SAndroid Build Coastguard Worker
229*cfb92d14SAndroid Build Coastguard WorkerMCU_POWER_STATE_ON = '"on"'
230*cfb92d14SAndroid Build Coastguard WorkerMCU_POWER_STATE_LOW_POWER = '"low-power"'
231*cfb92d14SAndroid Build Coastguard WorkerMCU_POWER_STATE_OFF = '"off"'
232*cfb92d14SAndroid Build Coastguard Worker
233*cfb92d14SAndroid Build Coastguard Worker# -----------------------------------------------------------------------------------------------------------------------
234*cfb92d14SAndroid Build Coastguard Worker# Node Radio Link Types (Use as input to `Node()` initializer)
235*cfb92d14SAndroid Build Coastguard Worker
236*cfb92d14SAndroid Build Coastguard WorkerNODE_15_4 = "-15.4"
237*cfb92d14SAndroid Build Coastguard WorkerNODE_TREL = "-trel"
238*cfb92d14SAndroid Build Coastguard WorkerNODE_15_4_TREL = "-15.4-trel"
239*cfb92d14SAndroid Build Coastguard Worker
240*cfb92d14SAndroid Build Coastguard Worker# -----------------------------------------------------------------------------------------------------------------------
241*cfb92d14SAndroid Build Coastguard Worker# Node types (from `WPAN_NODE_TYPE` property)
242*cfb92d14SAndroid Build Coastguard Worker
243*cfb92d14SAndroid Build Coastguard WorkerNODE_TYPE_UNKNOWN = '"unknown"'
244*cfb92d14SAndroid Build Coastguard WorkerNODE_TYPE_LEADER = '"leader"'
245*cfb92d14SAndroid Build Coastguard WorkerNODE_TYPE_ROUTER = '"router"'
246*cfb92d14SAndroid Build Coastguard WorkerNODE_TYPE_END_DEVICE = '"end-device"'
247*cfb92d14SAndroid Build Coastguard WorkerNODE_TYPE_SLEEPY_END_DEVICE = '"sleepy-end-device"'
248*cfb92d14SAndroid Build Coastguard WorkerNODE_TYPE_COMMISSIONER = '"commissioner"'
249*cfb92d14SAndroid Build Coastguard WorkerNODE_TYPE_NEST_LURKER = '"nl-lurker"'
250*cfb92d14SAndroid Build Coastguard Worker
251*cfb92d14SAndroid Build Coastguard Worker# -----------------------------------------------------------------------------------------------------------------------
252*cfb92d14SAndroid Build Coastguard Worker# Node types used by `Node.join()`
253*cfb92d14SAndroid Build Coastguard Worker
254*cfb92d14SAndroid Build Coastguard WorkerJOIN_TYPE_ROUTER = 'r'
255*cfb92d14SAndroid Build Coastguard WorkerJOIN_TYPE_END_DEVICE = 'e'
256*cfb92d14SAndroid Build Coastguard WorkerJOIN_TYPE_SLEEPY_END_DEVICE = 's'
257*cfb92d14SAndroid Build Coastguard Worker
258*cfb92d14SAndroid Build Coastguard Worker# -----------------------------------------------------------------------------------------------------------------------
259*cfb92d14SAndroid Build Coastguard Worker# Address Cache Table Entry States
260*cfb92d14SAndroid Build Coastguard Worker
261*cfb92d14SAndroid Build Coastguard WorkerADDRESS_CACHE_ENTRY_STATE_CACHED = "cached"
262*cfb92d14SAndroid Build Coastguard WorkerADDRESS_CACHE_ENTRY_STATE_SNOOPED = "snooped"
263*cfb92d14SAndroid Build Coastguard WorkerADDRESS_CACHE_ENTRY_STATE_QUERY = "query"
264*cfb92d14SAndroid Build Coastguard WorkerADDRESS_CACHE_ENTRY_STATE_RETRY_QUERY = "retry-query"
265*cfb92d14SAndroid Build Coastguard Worker
266*cfb92d14SAndroid Build Coastguard Worker# -----------------------------------------------------------------------------------------------------------------------
267*cfb92d14SAndroid Build Coastguard Worker# Bit Flags for Thread Device Mode `WPAN_THREAD_DEVICE_MODE`
268*cfb92d14SAndroid Build Coastguard Worker
269*cfb92d14SAndroid Build Coastguard WorkerTHREAD_MODE_FLAG_FULL_NETWORK_DATA = (1 << 0)
270*cfb92d14SAndroid Build Coastguard WorkerTHREAD_MODE_FLAG_FULL_THREAD_DEV = (1 << 1)
271*cfb92d14SAndroid Build Coastguard WorkerTHREAD_MODE_FLAG_RX_ON_WHEN_IDLE = (1 << 3)
272*cfb92d14SAndroid Build Coastguard Worker
273*cfb92d14SAndroid Build Coastguard Worker# -----------------------------------------------------------------------------------------------------------------------
274*cfb92d14SAndroid Build Coastguard Worker# Radio Link type
275*cfb92d14SAndroid Build Coastguard Worker
276*cfb92d14SAndroid Build Coastguard WorkerRADIO_LINK_IEEE_802_15_4 = "IEEE_802_15_4"
277*cfb92d14SAndroid Build Coastguard WorkerRADIO_LINK_TREL_UDP6 = "TREL_UDP6"
278*cfb92d14SAndroid Build Coastguard WorkerRADIO_LINK_TOBLE = "TOBLE"
279*cfb92d14SAndroid Build Coastguard Worker
280*cfb92d14SAndroid Build Coastguard Worker_OT_BUILDDIR = os.getenv('top_builddir', '../../..')
281*cfb92d14SAndroid Build Coastguard Worker_WPANTUND_PREFIX = os.getenv('WPANTUND_PREFIX', '/usr/local')
282*cfb92d14SAndroid Build Coastguard Worker
283*cfb92d14SAndroid Build Coastguard Worker# -----------------------------------------------------------------------------------------------------------------------
284*cfb92d14SAndroid Build Coastguard Worker
285*cfb92d14SAndroid Build Coastguard Worker
286*cfb92d14SAndroid Build Coastguard Workerdef _log(text, new_line=True, flush=True):
287*cfb92d14SAndroid Build Coastguard Worker    sys.stdout.write(text)
288*cfb92d14SAndroid Build Coastguard Worker    if new_line:
289*cfb92d14SAndroid Build Coastguard Worker        sys.stdout.write('\n')
290*cfb92d14SAndroid Build Coastguard Worker    if flush:
291*cfb92d14SAndroid Build Coastguard Worker        sys.stdout.flush()
292*cfb92d14SAndroid Build Coastguard Worker
293*cfb92d14SAndroid Build Coastguard Worker
294*cfb92d14SAndroid Build Coastguard Worker# -----------------------------------------------------------------------------------------------------------------------
295*cfb92d14SAndroid Build Coastguard Worker# Node class
296*cfb92d14SAndroid Build Coastguard Worker
297*cfb92d14SAndroid Build Coastguard Worker
298*cfb92d14SAndroid Build Coastguard Workerclass Node(object):
299*cfb92d14SAndroid Build Coastguard Worker    """ A wpantund OT NCP instance """
300*cfb92d14SAndroid Build Coastguard Worker    # defines the default verbosity setting (can be changed per `Node`)
301*cfb92d14SAndroid Build Coastguard Worker    _VERBOSE = os.getenv('TORANJ_VERBOSE', 'no').lower() in ['true', '1', 't', 'y', 'yes', 'on']
302*cfb92d14SAndroid Build Coastguard Worker    _SPEED_UP_FACTOR = 1  # defines the default time speed up factor
303*cfb92d14SAndroid Build Coastguard Worker
304*cfb92d14SAndroid Build Coastguard Worker    # path to `wpantund`, `wpanctl` and `ot-ncp-ftd`
305*cfb92d14SAndroid Build Coastguard Worker    _WPANTUND = '%s/sbin/wpantund' % _WPANTUND_PREFIX
306*cfb92d14SAndroid Build Coastguard Worker    _WPANCTL = '%s/bin/wpanctl' % _WPANTUND_PREFIX
307*cfb92d14SAndroid Build Coastguard Worker
308*cfb92d14SAndroid Build Coastguard Worker    _OT_NCP_FTD = '%s/examples/apps/ncp/ot-ncp-ftd' % _OT_BUILDDIR
309*cfb92d14SAndroid Build Coastguard Worker
310*cfb92d14SAndroid Build Coastguard Worker    # determines if the wpantund logs are saved in file or sent to stdout
311*cfb92d14SAndroid Build Coastguard Worker    _TUND_LOG_TO_FILE = True
312*cfb92d14SAndroid Build Coastguard Worker    # name of wpantund log file (if # name of wpantund _TUND_LOG_TO_FILE is
313*cfb92d14SAndroid Build Coastguard Worker    # True)
314*cfb92d14SAndroid Build Coastguard Worker    _TUND_LOG_FNAME = 'wpantund-logs'
315*cfb92d14SAndroid Build Coastguard Worker
316*cfb92d14SAndroid Build Coastguard Worker    # interface name
317*cfb92d14SAndroid Build Coastguard Worker    _INTFC_NAME_PREFIX = 'utun' if sys.platform == 'darwin' else 'wpan'
318*cfb92d14SAndroid Build Coastguard Worker    _START_INDEX = 4 if sys.platform == 'darwin' else 1
319*cfb92d14SAndroid Build Coastguard Worker
320*cfb92d14SAndroid Build Coastguard Worker    _cur_index = _START_INDEX
321*cfb92d14SAndroid Build Coastguard Worker    _all_nodes = weakref.WeakSet()
322*cfb92d14SAndroid Build Coastguard Worker
323*cfb92d14SAndroid Build Coastguard Worker    def __init__(self, radios=None, verbose=_VERBOSE):
324*cfb92d14SAndroid Build Coastguard Worker        """Creates a new `Node` instance"""
325*cfb92d14SAndroid Build Coastguard Worker
326*cfb92d14SAndroid Build Coastguard Worker        index = Node._cur_index
327*cfb92d14SAndroid Build Coastguard Worker        Node._cur_index += 1
328*cfb92d14SAndroid Build Coastguard Worker
329*cfb92d14SAndroid Build Coastguard Worker        self._index = index
330*cfb92d14SAndroid Build Coastguard Worker        self._interface_name = self._INTFC_NAME_PREFIX + str(index)
331*cfb92d14SAndroid Build Coastguard Worker        self._verbose = verbose
332*cfb92d14SAndroid Build Coastguard Worker
333*cfb92d14SAndroid Build Coastguard Worker        ncp_socket_path = 'system:{}{} {} --time-speed={}'.format(self._OT_NCP_FTD, '' if radios is None else radios,
334*cfb92d14SAndroid Build Coastguard Worker                                                                  index, self._SPEED_UP_FACTOR)
335*cfb92d14SAndroid Build Coastguard Worker
336*cfb92d14SAndroid Build Coastguard Worker        cmd = self._WPANTUND + \
337*cfb92d14SAndroid Build Coastguard Worker            ' -o Config:NCP:SocketPath \"{}\"'.format(ncp_socket_path) + \
338*cfb92d14SAndroid Build Coastguard Worker            ' -o Config:TUN:InterfaceName {}'.format(self._interface_name) + \
339*cfb92d14SAndroid Build Coastguard Worker            ' -o Config:NCP:DriverName spinel' + \
340*cfb92d14SAndroid Build Coastguard Worker            ' -o Daemon:SyslogMask \"all -debug\"'
341*cfb92d14SAndroid Build Coastguard Worker
342*cfb92d14SAndroid Build Coastguard Worker        if Node._TUND_LOG_TO_FILE:
343*cfb92d14SAndroid Build Coastguard Worker            self._tund_log_file = open(self._TUND_LOG_FNAME + str(index) + '.log', 'wb')
344*cfb92d14SAndroid Build Coastguard Worker        else:
345*cfb92d14SAndroid Build Coastguard Worker            self._tund_log_file = None
346*cfb92d14SAndroid Build Coastguard Worker
347*cfb92d14SAndroid Build Coastguard Worker        if self._verbose:
348*cfb92d14SAndroid Build Coastguard Worker            _log('$ Node{}.__init__() cmd: {}'.format(index, cmd))
349*cfb92d14SAndroid Build Coastguard Worker
350*cfb92d14SAndroid Build Coastguard Worker        self._wpantund_process = subprocess.Popen(cmd, shell=True, stderr=self._tund_log_file)
351*cfb92d14SAndroid Build Coastguard Worker
352*cfb92d14SAndroid Build Coastguard Worker        self._wpanctl_cmd = self._WPANCTL + ' -I ' + self._interface_name + ' '
353*cfb92d14SAndroid Build Coastguard Worker
354*cfb92d14SAndroid Build Coastguard Worker        # map from local_port to `AsyncReceiver` object
355*cfb92d14SAndroid Build Coastguard Worker        self._recvers = weakref.WeakValueDictionary()
356*cfb92d14SAndroid Build Coastguard Worker        Node._all_nodes.add(self)
357*cfb92d14SAndroid Build Coastguard Worker
358*cfb92d14SAndroid Build Coastguard Worker    def __del__(self):
359*cfb92d14SAndroid Build Coastguard Worker        self._wpantund_process.poll()
360*cfb92d14SAndroid Build Coastguard Worker        if self._wpantund_process.returncode is None:
361*cfb92d14SAndroid Build Coastguard Worker            self._wpantund_process.terminate()
362*cfb92d14SAndroid Build Coastguard Worker            self._wpantund_process.wait()
363*cfb92d14SAndroid Build Coastguard Worker
364*cfb92d14SAndroid Build Coastguard Worker    def __repr__(self):
365*cfb92d14SAndroid Build Coastguard Worker        return 'Node (index={}, interface_name={})'.format(self._index, self._interface_name)
366*cfb92d14SAndroid Build Coastguard Worker
367*cfb92d14SAndroid Build Coastguard Worker    @property
368*cfb92d14SAndroid Build Coastguard Worker    def index(self):
369*cfb92d14SAndroid Build Coastguard Worker        return self._index
370*cfb92d14SAndroid Build Coastguard Worker
371*cfb92d14SAndroid Build Coastguard Worker    @property
372*cfb92d14SAndroid Build Coastguard Worker    def interface_name(self):
373*cfb92d14SAndroid Build Coastguard Worker        return self._interface_name
374*cfb92d14SAndroid Build Coastguard Worker
375*cfb92d14SAndroid Build Coastguard Worker    @property
376*cfb92d14SAndroid Build Coastguard Worker    def tund_log_file(self):
377*cfb92d14SAndroid Build Coastguard Worker        return self._tund_log_file
378*cfb92d14SAndroid Build Coastguard Worker
379*cfb92d14SAndroid Build Coastguard Worker    # ------------------------------------------------------------------------------------------------------------------
380*cfb92d14SAndroid Build Coastguard Worker    # Executing a `wpanctl` command
381*cfb92d14SAndroid Build Coastguard Worker
382*cfb92d14SAndroid Build Coastguard Worker    def wpanctl(self, cmd):
383*cfb92d14SAndroid Build Coastguard Worker        """ Runs a wpanctl command on the given wpantund/OT-NCP instance and returns the output """
384*cfb92d14SAndroid Build Coastguard Worker
385*cfb92d14SAndroid Build Coastguard Worker        if self._verbose:
386*cfb92d14SAndroid Build Coastguard Worker            _log('$ Node{}.wpanctl(\'{}\')'.format(self._index, cmd), new_line=False)
387*cfb92d14SAndroid Build Coastguard Worker
388*cfb92d14SAndroid Build Coastguard Worker        result = subprocess.check_output(self._wpanctl_cmd + cmd, shell=True, stderr=subprocess.STDOUT)
389*cfb92d14SAndroid Build Coastguard Worker
390*cfb92d14SAndroid Build Coastguard Worker        if len(result) >= 1 and result[-1] == '\n':  # remove the last char if it is '\n',
391*cfb92d14SAndroid Build Coastguard Worker            result = result[:-1]
392*cfb92d14SAndroid Build Coastguard Worker
393*cfb92d14SAndroid Build Coastguard Worker        if self._verbose:
394*cfb92d14SAndroid Build Coastguard Worker            if '\n' in result:
395*cfb92d14SAndroid Build Coastguard Worker                _log(':')
396*cfb92d14SAndroid Build Coastguard Worker                for line in result.splitlines():
397*cfb92d14SAndroid Build Coastguard Worker                    _log('     ' + line)
398*cfb92d14SAndroid Build Coastguard Worker            else:
399*cfb92d14SAndroid Build Coastguard Worker                _log(' -> \'{}\''.format(result))
400*cfb92d14SAndroid Build Coastguard Worker
401*cfb92d14SAndroid Build Coastguard Worker        return result
402*cfb92d14SAndroid Build Coastguard Worker
403*cfb92d14SAndroid Build Coastguard Worker    # ------------------------------------------------------------------------------------------------------------------
404*cfb92d14SAndroid Build Coastguard Worker    # APIs matching `wpanctl` commands.
405*cfb92d14SAndroid Build Coastguard Worker
406*cfb92d14SAndroid Build Coastguard Worker    def get(self, prop_name, value_only=True):
407*cfb92d14SAndroid Build Coastguard Worker        return self.wpanctl('get ' + ('-v ' if value_only else '') + prop_name)
408*cfb92d14SAndroid Build Coastguard Worker
409*cfb92d14SAndroid Build Coastguard Worker    def set(self, prop_name, value, binary_data=False):
410*cfb92d14SAndroid Build Coastguard Worker        return self._update_prop('set', prop_name, value, binary_data)
411*cfb92d14SAndroid Build Coastguard Worker
412*cfb92d14SAndroid Build Coastguard Worker    def add(self, prop_name, value, binary_data=False):
413*cfb92d14SAndroid Build Coastguard Worker        return self._update_prop('add', prop_name, value, binary_data)
414*cfb92d14SAndroid Build Coastguard Worker
415*cfb92d14SAndroid Build Coastguard Worker    def remove(self, prop_name, value, binary_data=False):
416*cfb92d14SAndroid Build Coastguard Worker        return self._update_prop('remove', prop_name, value, binary_data)
417*cfb92d14SAndroid Build Coastguard Worker
418*cfb92d14SAndroid Build Coastguard Worker    def _update_prop(self, action, prop_name, value, binary_data):
419*cfb92d14SAndroid Build Coastguard Worker        return self.wpanctl(action + ' ' + prop_name + ' ' + ('-d ' if binary_data else '') + '-v ' +
420*cfb92d14SAndroid Build Coastguard Worker                            value)  # use -v to handle values starting with `-`.
421*cfb92d14SAndroid Build Coastguard Worker
422*cfb92d14SAndroid Build Coastguard Worker    def reset(self):
423*cfb92d14SAndroid Build Coastguard Worker        return self.wpanctl('reset')
424*cfb92d14SAndroid Build Coastguard Worker
425*cfb92d14SAndroid Build Coastguard Worker    def status(self):
426*cfb92d14SAndroid Build Coastguard Worker        return self.wpanctl('status')
427*cfb92d14SAndroid Build Coastguard Worker
428*cfb92d14SAndroid Build Coastguard Worker    def leave(self):
429*cfb92d14SAndroid Build Coastguard Worker        return self.wpanctl('leave')
430*cfb92d14SAndroid Build Coastguard Worker
431*cfb92d14SAndroid Build Coastguard Worker    def form(self,
432*cfb92d14SAndroid Build Coastguard Worker             name,
433*cfb92d14SAndroid Build Coastguard Worker             channel=None,
434*cfb92d14SAndroid Build Coastguard Worker             channel_mask=None,
435*cfb92d14SAndroid Build Coastguard Worker             panid=None,
436*cfb92d14SAndroid Build Coastguard Worker             xpanid=None,
437*cfb92d14SAndroid Build Coastguard Worker             key=None,
438*cfb92d14SAndroid Build Coastguard Worker             key_index=None,
439*cfb92d14SAndroid Build Coastguard Worker             node_type=None,
440*cfb92d14SAndroid Build Coastguard Worker             mesh_local_prefix=None,
441*cfb92d14SAndroid Build Coastguard Worker             legacy_prefix=None):
442*cfb92d14SAndroid Build Coastguard Worker        return self.wpanctl('form \"' + name + '\"' + (' -c {}'.format(channel) if channel is not None else '') +
443*cfb92d14SAndroid Build Coastguard Worker                            (' -m {}'.format(channel_mask) if channel_mask is not None else '') +
444*cfb92d14SAndroid Build Coastguard Worker                            (' -p {}'.format(panid) if panid is not None else '') +
445*cfb92d14SAndroid Build Coastguard Worker                            (' -x {}'.format(xpanid) if xpanid is not None else '') +
446*cfb92d14SAndroid Build Coastguard Worker                            (' -k {}'.format(key) if key is not None else '') +
447*cfb92d14SAndroid Build Coastguard Worker                            (' -i {}'.format(key_index) if key_index is not None else '') +
448*cfb92d14SAndroid Build Coastguard Worker                            (' -T {}'.format(node_type) if node_type is not None else '') +
449*cfb92d14SAndroid Build Coastguard Worker                            (' -M {}'.format(mesh_local_prefix) if mesh_local_prefix is not None else '') +
450*cfb92d14SAndroid Build Coastguard Worker                            (' -L {}'.format(legacy_prefix) if legacy_prefix is not None else ''))
451*cfb92d14SAndroid Build Coastguard Worker
452*cfb92d14SAndroid Build Coastguard Worker    def join(self, name, channel=None, node_type=None, panid=None, xpanid=None, key=None):
453*cfb92d14SAndroid Build Coastguard Worker        return self.wpanctl('join \"' + name + '\"' + (' -c {}'.format(channel) if channel is not None else '') +
454*cfb92d14SAndroid Build Coastguard Worker                            (' -T {}'.format(node_type) if node_type is not None else '') +
455*cfb92d14SAndroid Build Coastguard Worker                            (' -p {}'.format(panid) if panid is not None else '') +
456*cfb92d14SAndroid Build Coastguard Worker                            (' -x {}'.format(xpanid) if xpanid is not None else '') +
457*cfb92d14SAndroid Build Coastguard Worker                            (' -k {}'.format(key) if key is not None else '') + (' -n'))
458*cfb92d14SAndroid Build Coastguard Worker
459*cfb92d14SAndroid Build Coastguard Worker    def active_scan(self, channel=None):
460*cfb92d14SAndroid Build Coastguard Worker        return self.wpanctl('scan' + (' -c {}'.format(channel) if channel is not None else ''))
461*cfb92d14SAndroid Build Coastguard Worker
462*cfb92d14SAndroid Build Coastguard Worker    def energy_scan(self, channel=None):
463*cfb92d14SAndroid Build Coastguard Worker        return self.wpanctl('scan -e' + (' -c {}'.format(channel) if channel is not None else ''))
464*cfb92d14SAndroid Build Coastguard Worker
465*cfb92d14SAndroid Build Coastguard Worker    def discover_scan(self, channel=None, joiner_only=False, enable_filtering=False, panid_filter=None):
466*cfb92d14SAndroid Build Coastguard Worker        return self.wpanctl('scan -d' + (' -c {}'.format(channel) if channel is not None else '') +
467*cfb92d14SAndroid Build Coastguard Worker                            (' -j' if joiner_only else '') + (' -f' if enable_filtering else '') +
468*cfb92d14SAndroid Build Coastguard Worker                            (' -p {}'.format(panid_filter) if panid_filter is not None else ''))
469*cfb92d14SAndroid Build Coastguard Worker
470*cfb92d14SAndroid Build Coastguard Worker    def permit_join(self, duration_sec=None, port=None, udp=True, tcp=True):
471*cfb92d14SAndroid Build Coastguard Worker        if not udp and not tcp:  # incorrect use!
472*cfb92d14SAndroid Build Coastguard Worker            return ''
473*cfb92d14SAndroid Build Coastguard Worker        traffic_type = ''
474*cfb92d14SAndroid Build Coastguard Worker        if udp and not tcp:
475*cfb92d14SAndroid Build Coastguard Worker            traffic_type = ' --udp'
476*cfb92d14SAndroid Build Coastguard Worker        if tcp and not udp:
477*cfb92d14SAndroid Build Coastguard Worker            traffic_type = ' --tcp'
478*cfb92d14SAndroid Build Coastguard Worker        if port is not None and duration_sec is None:
479*cfb92d14SAndroid Build Coastguard Worker            duration_sec = '240'
480*cfb92d14SAndroid Build Coastguard Worker
481*cfb92d14SAndroid Build Coastguard Worker        return self.wpanctl('permit-join' + (' {}'.format(duration_sec) if duration_sec is not None else '') +
482*cfb92d14SAndroid Build Coastguard Worker                            (' {}'.format(port) if port is not None else '') + traffic_type)
483*cfb92d14SAndroid Build Coastguard Worker
484*cfb92d14SAndroid Build Coastguard Worker    def config_gateway(self, prefix, default_route=False, priority=None):
485*cfb92d14SAndroid Build Coastguard Worker        return self.wpanctl('config-gateway ' + prefix + (' -d' if default_route else '') +
486*cfb92d14SAndroid Build Coastguard Worker                            (' -P {}'.format(priority) if priority is not None else ''))
487*cfb92d14SAndroid Build Coastguard Worker
488*cfb92d14SAndroid Build Coastguard Worker    def add_prefix(self,
489*cfb92d14SAndroid Build Coastguard Worker                   prefix,
490*cfb92d14SAndroid Build Coastguard Worker                   prefix_len=None,
491*cfb92d14SAndroid Build Coastguard Worker                   priority=None,
492*cfb92d14SAndroid Build Coastguard Worker                   stable=True,
493*cfb92d14SAndroid Build Coastguard Worker                   on_mesh=False,
494*cfb92d14SAndroid Build Coastguard Worker                   slaac=False,
495*cfb92d14SAndroid Build Coastguard Worker                   dhcp=False,
496*cfb92d14SAndroid Build Coastguard Worker                   configure=False,
497*cfb92d14SAndroid Build Coastguard Worker                   default_route=False,
498*cfb92d14SAndroid Build Coastguard Worker                   preferred=False):
499*cfb92d14SAndroid Build Coastguard Worker        return self.wpanctl('add-prefix ' + prefix + (' -l {}'.format(prefix_len) if prefix_len is not None else '') +
500*cfb92d14SAndroid Build Coastguard Worker                            (' -P {}'.format(priority) if priority is not None else '') + (' -s' if stable else '') +
501*cfb92d14SAndroid Build Coastguard Worker                            (' -f' if preferred else '') + (' -a' if slaac else '') + (' -d' if dhcp else '') +
502*cfb92d14SAndroid Build Coastguard Worker                            (' -c' if configure else '') + (' -r' if default_route else '') +
503*cfb92d14SAndroid Build Coastguard Worker                            (' -o' if on_mesh else ''))
504*cfb92d14SAndroid Build Coastguard Worker
505*cfb92d14SAndroid Build Coastguard Worker    def remove_prefix(self, prefix, prefix_len=None):
506*cfb92d14SAndroid Build Coastguard Worker        return self.wpanctl('remove-prefix ' + prefix +
507*cfb92d14SAndroid Build Coastguard Worker                            (' -l {}'.format(prefix_len) if prefix_len is not None else ''))
508*cfb92d14SAndroid Build Coastguard Worker
509*cfb92d14SAndroid Build Coastguard Worker    def add_route(self, route_prefix, prefix_len=None, priority=None, stable=True):
510*cfb92d14SAndroid Build Coastguard Worker        """route priority [(>0 for high, 0 for medium, <0 for low)]"""
511*cfb92d14SAndroid Build Coastguard Worker        return self.wpanctl('add-route ' + route_prefix +
512*cfb92d14SAndroid Build Coastguard Worker                            (' -l {}'.format(prefix_len) if prefix_len is not None else '') +
513*cfb92d14SAndroid Build Coastguard Worker                            (' -p {}'.format(priority) if priority is not None else '') + ('' if stable else ' -n'))
514*cfb92d14SAndroid Build Coastguard Worker
515*cfb92d14SAndroid Build Coastguard Worker    def remove_route(self, route_prefix, prefix_len=None, priority=None, stable=True):
516*cfb92d14SAndroid Build Coastguard Worker        """route priority [(>0 for high, 0 for medium, <0 for low)]"""
517*cfb92d14SAndroid Build Coastguard Worker        return self.wpanctl('remove-route ' + route_prefix +
518*cfb92d14SAndroid Build Coastguard Worker                            (' -l {}'.format(prefix_len) if prefix_len is not None else '') +
519*cfb92d14SAndroid Build Coastguard Worker                            (' -p {}'.format(priority) if priority is not None else ''))
520*cfb92d14SAndroid Build Coastguard Worker
521*cfb92d14SAndroid Build Coastguard Worker    def commissioner_start(self):
522*cfb92d14SAndroid Build Coastguard Worker        return self.wpanctl('commissioner start')
523*cfb92d14SAndroid Build Coastguard Worker
524*cfb92d14SAndroid Build Coastguard Worker    def commissioner_add_joiner(self, eui64, pskd, timeout='100'):
525*cfb92d14SAndroid Build Coastguard Worker        return self.wpanctl('commissioner joiner-add {} {} {}'.format(eui64, timeout, pskd))
526*cfb92d14SAndroid Build Coastguard Worker
527*cfb92d14SAndroid Build Coastguard Worker    def commissioner_add_joiner_with_discerner(self, discerner_value, discerner_bit_len, pskd, timeout='100'):
528*cfb92d14SAndroid Build Coastguard Worker        return self.wpanctl('commissioner joiner-add-discerner {} {} {} {}'.format(discerner_value, discerner_bit_len,
529*cfb92d14SAndroid Build Coastguard Worker                                                                                   timeout, pskd))
530*cfb92d14SAndroid Build Coastguard Worker
531*cfb92d14SAndroid Build Coastguard Worker    def joiner_join(self, pskd):
532*cfb92d14SAndroid Build Coastguard Worker        return self.wpanctl('joiner --join {}'.format(pskd))
533*cfb92d14SAndroid Build Coastguard Worker
534*cfb92d14SAndroid Build Coastguard Worker    def joiner_attach(self):
535*cfb92d14SAndroid Build Coastguard Worker        return self.wpanctl('joiner --attach')
536*cfb92d14SAndroid Build Coastguard Worker
537*cfb92d14SAndroid Build Coastguard Worker    # ------------------------------------------------------------------------------------------------------------------
538*cfb92d14SAndroid Build Coastguard Worker    # Helper methods
539*cfb92d14SAndroid Build Coastguard Worker
540*cfb92d14SAndroid Build Coastguard Worker    def is_associated(self):
541*cfb92d14SAndroid Build Coastguard Worker        return self.get(WPAN_STATE) == STATE_ASSOCIATED
542*cfb92d14SAndroid Build Coastguard Worker
543*cfb92d14SAndroid Build Coastguard Worker    def join_node(self, node, node_type=JOIN_TYPE_ROUTER, should_set_key=True):
544*cfb92d14SAndroid Build Coastguard Worker        """Join a network specified by another node, `node` should be a Node"""
545*cfb92d14SAndroid Build Coastguard Worker
546*cfb92d14SAndroid Build Coastguard Worker        if not node.is_associated():
547*cfb92d14SAndroid Build Coastguard Worker            return "{} is not associated".format(node)
548*cfb92d14SAndroid Build Coastguard Worker
549*cfb92d14SAndroid Build Coastguard Worker        return self.join(node.get(WPAN_NAME)[1:-1],
550*cfb92d14SAndroid Build Coastguard Worker                         channel=node.get(WPAN_CHANNEL),
551*cfb92d14SAndroid Build Coastguard Worker                         node_type=node_type,
552*cfb92d14SAndroid Build Coastguard Worker                         panid=node.get(WPAN_PANID),
553*cfb92d14SAndroid Build Coastguard Worker                         xpanid=node.get(WPAN_XPANID),
554*cfb92d14SAndroid Build Coastguard Worker                         key=node.get(WPAN_KEY)[1:-1] if should_set_key else None)
555*cfb92d14SAndroid Build Coastguard Worker
556*cfb92d14SAndroid Build Coastguard Worker    def allowlist_node(self, node):
557*cfb92d14SAndroid Build Coastguard Worker        """Adds a given node (of type `Node`) to the allowlist of `self` and enables allowlisting on `self`"""
558*cfb92d14SAndroid Build Coastguard Worker
559*cfb92d14SAndroid Build Coastguard Worker        self.add(WPAN_MAC_ALLOWLIST_ENTRIES, node.get(WPAN_EXT_ADDRESS)[1:-1])
560*cfb92d14SAndroid Build Coastguard Worker        self.set(WPAN_MAC_ALLOWLIST_ENABLED, '1')
561*cfb92d14SAndroid Build Coastguard Worker
562*cfb92d14SAndroid Build Coastguard Worker    def un_allowlist_node(self, node):
563*cfb92d14SAndroid Build Coastguard Worker        """Removes a given node (of node `Node) from the allowlist"""
564*cfb92d14SAndroid Build Coastguard Worker        self.remove(WPAN_MAC_ALLOWLIST_ENTRIES, node.get(WPAN_EXT_ADDRESS)[1:-1])
565*cfb92d14SAndroid Build Coastguard Worker
566*cfb92d14SAndroid Build Coastguard Worker    def is_in_scan_result(self, scan_result):
567*cfb92d14SAndroid Build Coastguard Worker        """Checks if node is in the scan results
568*cfb92d14SAndroid Build Coastguard Worker           `scan_result` must be an array of `ScanResult` object (see `parse_scan_result`).
569*cfb92d14SAndroid Build Coastguard Worker        """
570*cfb92d14SAndroid Build Coastguard Worker        panid = self.get(WPAN_PANID)
571*cfb92d14SAndroid Build Coastguard Worker        xpanid = self.get(WPAN_XPANID)[2:]
572*cfb92d14SAndroid Build Coastguard Worker        name = self.get(WPAN_NAME)[1:-1]
573*cfb92d14SAndroid Build Coastguard Worker        channel = self.get(WPAN_CHANNEL)
574*cfb92d14SAndroid Build Coastguard Worker        ext_address = self.get(WPAN_EXT_ADDRESS)[1:-1]
575*cfb92d14SAndroid Build Coastguard Worker
576*cfb92d14SAndroid Build Coastguard Worker        for item in scan_result:
577*cfb92d14SAndroid Build Coastguard Worker            if all([item.panid == panid, item.channel == channel, item.ext_address == ext_address]):
578*cfb92d14SAndroid Build Coastguard Worker                if (item.type == ScanResult.TYPE_DISCOVERY_SCAN):
579*cfb92d14SAndroid Build Coastguard Worker                    if all([item.network_name == name, item.xpanid == xpanid]):
580*cfb92d14SAndroid Build Coastguard Worker                        return True
581*cfb92d14SAndroid Build Coastguard Worker                    else:
582*cfb92d14SAndroid Build Coastguard Worker                        continue
583*cfb92d14SAndroid Build Coastguard Worker                return True
584*cfb92d14SAndroid Build Coastguard Worker
585*cfb92d14SAndroid Build Coastguard Worker        return False
586*cfb92d14SAndroid Build Coastguard Worker
587*cfb92d14SAndroid Build Coastguard Worker    def find_ip6_address_with_prefix(self, prefix):
588*cfb92d14SAndroid Build Coastguard Worker        """Find an IPv6 address on node matching a given prefix.
589*cfb92d14SAndroid Build Coastguard Worker           `prefix` should be an string containing the prefix.
590*cfb92d14SAndroid Build Coastguard Worker           Returns a string containing the IPv6 address matching the prefix or empty string if no address found.
591*cfb92d14SAndroid Build Coastguard Worker        """
592*cfb92d14SAndroid Build Coastguard Worker        if len(prefix) > 2 and prefix[-1] == ':' and prefix[-2] == ':':
593*cfb92d14SAndroid Build Coastguard Worker            prefix = prefix[:-1]
594*cfb92d14SAndroid Build Coastguard Worker        all_addrs = parse_list(self.get(WPAN_IP6_ALL_ADDRESSES))
595*cfb92d14SAndroid Build Coastguard Worker        matched_addr = [addr for addr in all_addrs if addr.startswith(prefix)]
596*cfb92d14SAndroid Build Coastguard Worker        return matched_addr[0] if len(matched_addr) >= 1 else ''
597*cfb92d14SAndroid Build Coastguard Worker
598*cfb92d14SAndroid Build Coastguard Worker    def add_ip6_address_on_interface(self, address, prefix_len=64):
599*cfb92d14SAndroid Build Coastguard Worker        """Adds an IPv6 interface on the network interface.
600*cfb92d14SAndroid Build Coastguard Worker           `address` should be string containing the IPv6 address.
601*cfb92d14SAndroid Build Coastguard Worker           `prefix_len` is an `int` specifying the prefix length.
602*cfb92d14SAndroid Build Coastguard Worker           NOTE: this method uses linux `ip` command.
603*cfb92d14SAndroid Build Coastguard Worker        """
604*cfb92d14SAndroid Build Coastguard Worker        cmd = 'ip -6 addr add ' + address + \
605*cfb92d14SAndroid Build Coastguard Worker            '/{} dev '.format(prefix_len) + self.interface_name
606*cfb92d14SAndroid Build Coastguard Worker        if self._verbose:
607*cfb92d14SAndroid Build Coastguard Worker            _log('$ Node{} \'{}\')'.format(self._index, cmd))
608*cfb92d14SAndroid Build Coastguard Worker
609*cfb92d14SAndroid Build Coastguard Worker        result = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
610*cfb92d14SAndroid Build Coastguard Worker        return result
611*cfb92d14SAndroid Build Coastguard Worker
612*cfb92d14SAndroid Build Coastguard Worker    def remove_ip6_address_on_interface(self, address, prefix_len=64):
613*cfb92d14SAndroid Build Coastguard Worker        """Removes an IPv6 interface on the network interface.
614*cfb92d14SAndroid Build Coastguard Worker           `address` should be string containing the IPv6 address.
615*cfb92d14SAndroid Build Coastguard Worker           `prefix_len` is an `int` specifying the prefix length.
616*cfb92d14SAndroid Build Coastguard Worker           NOTE: this method uses linux `ip` command.
617*cfb92d14SAndroid Build Coastguard Worker        """
618*cfb92d14SAndroid Build Coastguard Worker        cmd = 'ip -6 addr del ' + address + \
619*cfb92d14SAndroid Build Coastguard Worker            '/{} dev '.format(prefix_len) + self.interface_name
620*cfb92d14SAndroid Build Coastguard Worker        if self._verbose:
621*cfb92d14SAndroid Build Coastguard Worker            _log('$ Node{} \'{}\')'.format(self._index, cmd))
622*cfb92d14SAndroid Build Coastguard Worker
623*cfb92d14SAndroid Build Coastguard Worker        result = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
624*cfb92d14SAndroid Build Coastguard Worker        return result
625*cfb92d14SAndroid Build Coastguard Worker
626*cfb92d14SAndroid Build Coastguard Worker    # ------------------------------------------------------------------------------------------------------------------
627*cfb92d14SAndroid Build Coastguard Worker    # class methods
628*cfb92d14SAndroid Build Coastguard Worker
629*cfb92d14SAndroid Build Coastguard Worker    @classmethod
630*cfb92d14SAndroid Build Coastguard Worker    def init_all_nodes(cls, disable_logs=not _VERBOSE, wait_time=15):
631*cfb92d14SAndroid Build Coastguard Worker        """Issues a `wpanctl.leave` on all `Node` objects and waits for them to be ready"""
632*cfb92d14SAndroid Build Coastguard Worker        random.seed(123456)
633*cfb92d14SAndroid Build Coastguard Worker        time.sleep(0.5)
634*cfb92d14SAndroid Build Coastguard Worker        for node in Node._all_nodes:
635*cfb92d14SAndroid Build Coastguard Worker            start_time = time.time()
636*cfb92d14SAndroid Build Coastguard Worker            while True:
637*cfb92d14SAndroid Build Coastguard Worker                try:
638*cfb92d14SAndroid Build Coastguard Worker                    node._wpantund_process.poll()
639*cfb92d14SAndroid Build Coastguard Worker                    if node._wpantund_process.returncode is not None:
640*cfb92d14SAndroid Build Coastguard Worker                        print('Node {} wpantund instance has terminated unexpectedly'.format(node))
641*cfb92d14SAndroid Build Coastguard Worker                    if disable_logs:
642*cfb92d14SAndroid Build Coastguard Worker                        node.set(WPAN_OT_LOG_LEVEL, '0')
643*cfb92d14SAndroid Build Coastguard Worker                    node.leave()
644*cfb92d14SAndroid Build Coastguard Worker                except subprocess.CalledProcessError as e:
645*cfb92d14SAndroid Build Coastguard Worker                    if (node._verbose):
646*cfb92d14SAndroid Build Coastguard Worker                        _log(' -> \'{}\' exit code: {}'.format(e.output, e.returncode))
647*cfb92d14SAndroid Build Coastguard Worker                    interval = time.time() - start_time
648*cfb92d14SAndroid Build Coastguard Worker                    if interval > wait_time:
649*cfb92d14SAndroid Build Coastguard Worker                        print('Took too long to init node {} ({}>{} sec)'.format(node, interval, wait_time))
650*cfb92d14SAndroid Build Coastguard Worker                        raise
651*cfb92d14SAndroid Build Coastguard Worker                except BaseException:
652*cfb92d14SAndroid Build Coastguard Worker                    raise
653*cfb92d14SAndroid Build Coastguard Worker                else:
654*cfb92d14SAndroid Build Coastguard Worker                    break
655*cfb92d14SAndroid Build Coastguard Worker                time.sleep(0.4)
656*cfb92d14SAndroid Build Coastguard Worker
657*cfb92d14SAndroid Build Coastguard Worker    @classmethod
658*cfb92d14SAndroid Build Coastguard Worker    def finalize_all_nodes(cls):
659*cfb92d14SAndroid Build Coastguard Worker        """Finalizes all previously created `Node` instances (stops the wpantund process)"""
660*cfb92d14SAndroid Build Coastguard Worker        for node in Node._all_nodes:
661*cfb92d14SAndroid Build Coastguard Worker            node._wpantund_process.terminate()
662*cfb92d14SAndroid Build Coastguard Worker            node._wpantund_process.wait()
663*cfb92d14SAndroid Build Coastguard Worker
664*cfb92d14SAndroid Build Coastguard Worker    @classmethod
665*cfb92d14SAndroid Build Coastguard Worker    def set_time_speedup_factor(cls, factor):
666*cfb92d14SAndroid Build Coastguard Worker        """Sets up the time speed up factor - should be set before creating any `Node` objects"""
667*cfb92d14SAndroid Build Coastguard Worker        if len(Node._all_nodes) != 0:
668*cfb92d14SAndroid Build Coastguard Worker            raise Node._NodeError('set_time_speedup_factor() cannot be called after creating a `Node`')
669*cfb92d14SAndroid Build Coastguard Worker        Node._SPEED_UP_FACTOR = factor
670*cfb92d14SAndroid Build Coastguard Worker
671*cfb92d14SAndroid Build Coastguard Worker    # ------------------------------------------------------------------------------------------------------------------
672*cfb92d14SAndroid Build Coastguard Worker    # IPv6 message Sender and Receiver class
673*cfb92d14SAndroid Build Coastguard Worker
674*cfb92d14SAndroid Build Coastguard Worker    class _NodeError(Exception):
675*cfb92d14SAndroid Build Coastguard Worker        pass
676*cfb92d14SAndroid Build Coastguard Worker
677*cfb92d14SAndroid Build Coastguard Worker    def prepare_tx(self, src, dst, data=40, count=1, mcast_hops=None):
678*cfb92d14SAndroid Build Coastguard Worker        """Prepares an IPv6 msg transmission.
679*cfb92d14SAndroid Build Coastguard Worker
680*cfb92d14SAndroid Build Coastguard Worker        - `src` and `dst` can be either a string containing IPv6 address, or a tuple (ipv6 address as string, port),
681*cfb92d14SAndroid Build Coastguard Worker           if no port is given, a random port number is used.
682*cfb92d14SAndroid Build Coastguard Worker        - `data` can be either a string containing the message to be sent, or an int indicating size of the message (a
683*cfb92d14SAndroid Build Coastguard Worker           random message with the given length will be used).
684*cfb92d14SAndroid Build Coastguard Worker        - `count` gives number of times the message will be sent (default is 1).
685*cfb92d14SAndroid Build Coastguard Worker        - `mcast_hops` specifies multicast hop limit (only applicable for multicast tx).
686*cfb92d14SAndroid Build Coastguard Worker
687*cfb92d14SAndroid Build Coastguard Worker        Returns an `AsyncSender` object.
688*cfb92d14SAndroid Build Coastguard Worker
689*cfb92d14SAndroid Build Coastguard Worker        """
690*cfb92d14SAndroid Build Coastguard Worker        if isinstance(src, tuple):
691*cfb92d14SAndroid Build Coastguard Worker            src_addr = src[0]
692*cfb92d14SAndroid Build Coastguard Worker            src_port = src[1]
693*cfb92d14SAndroid Build Coastguard Worker        else:
694*cfb92d14SAndroid Build Coastguard Worker            src_addr = src
695*cfb92d14SAndroid Build Coastguard Worker            src_port = random.randint(49152, 65535)
696*cfb92d14SAndroid Build Coastguard Worker
697*cfb92d14SAndroid Build Coastguard Worker        if isinstance(dst, tuple):
698*cfb92d14SAndroid Build Coastguard Worker            dst_addr = dst[0]
699*cfb92d14SAndroid Build Coastguard Worker            dst_port = dst[1]
700*cfb92d14SAndroid Build Coastguard Worker        else:
701*cfb92d14SAndroid Build Coastguard Worker            dst_addr = dst
702*cfb92d14SAndroid Build Coastguard Worker            dst_port = random.randint(49152, 65535)
703*cfb92d14SAndroid Build Coastguard Worker
704*cfb92d14SAndroid Build Coastguard Worker        if isinstance(data, int):
705*cfb92d14SAndroid Build Coastguard Worker            # create a random message with the given length.
706*cfb92d14SAndroid Build Coastguard Worker            all_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.,><?;:[]=-+)(*&^%$#@'
707*cfb92d14SAndroid Build Coastguard Worker            msg = ''.join(random.choice(all_chars) for _ in range(data))
708*cfb92d14SAndroid Build Coastguard Worker        else:
709*cfb92d14SAndroid Build Coastguard Worker            msg = data
710*cfb92d14SAndroid Build Coastguard Worker
711*cfb92d14SAndroid Build Coastguard Worker        return AsyncSender(self, src_addr, src_port, dst_addr, dst_port, msg, count, mcast_hops)
712*cfb92d14SAndroid Build Coastguard Worker
713*cfb92d14SAndroid Build Coastguard Worker    def _get_receiver(self, local_port):
714*cfb92d14SAndroid Build Coastguard Worker        # Gets or creates a receiver (an `AsyncReceiver`) tied to given port
715*cfb92d14SAndroid Build Coastguard Worker        # number
716*cfb92d14SAndroid Build Coastguard Worker        if local_port in self._recvers:
717*cfb92d14SAndroid Build Coastguard Worker            receiver = self._recvers[local_port]
718*cfb92d14SAndroid Build Coastguard Worker        else:
719*cfb92d14SAndroid Build Coastguard Worker            receiver = AsyncReceiver(self, local_port)
720*cfb92d14SAndroid Build Coastguard Worker            self._recvers[local_port] = receiver
721*cfb92d14SAndroid Build Coastguard Worker        return receiver
722*cfb92d14SAndroid Build Coastguard Worker
723*cfb92d14SAndroid Build Coastguard Worker    def _remove_recver(self, recvr):
724*cfb92d14SAndroid Build Coastguard Worker        # Removes a receiver from weak dictionary - called when the receiver is
725*cfb92d14SAndroid Build Coastguard Worker        # done and its socket is closed
726*cfb92d14SAndroid Build Coastguard Worker        local_port = recvr.local_port
727*cfb92d14SAndroid Build Coastguard Worker        if local_port in self._recvers:
728*cfb92d14SAndroid Build Coastguard Worker            del self._recvers[local_port]
729*cfb92d14SAndroid Build Coastguard Worker
730*cfb92d14SAndroid Build Coastguard Worker    def prepare_rx(self, sender):
731*cfb92d14SAndroid Build Coastguard Worker        """Prepare to receive messages from a sender (an `AsyncSender`)"""
732*cfb92d14SAndroid Build Coastguard Worker        receiver = self._get_receiver(sender.dst_port)
733*cfb92d14SAndroid Build Coastguard Worker        receiver._add_sender(sender.src_addr, sender.src_port, sender.msg, sender.count)
734*cfb92d14SAndroid Build Coastguard Worker        return receiver
735*cfb92d14SAndroid Build Coastguard Worker
736*cfb92d14SAndroid Build Coastguard Worker    def prepare_listener(self, local_port, timeout=1):
737*cfb92d14SAndroid Build Coastguard Worker        """Prepares a listener (an `AsyncReceiver`) listening on the given `local_port` for given `timeout` (sec)"""
738*cfb92d14SAndroid Build Coastguard Worker        receiver = self._get_receiver(local_port)
739*cfb92d14SAndroid Build Coastguard Worker        receiver._set_listen_timeout(timeout)
740*cfb92d14SAndroid Build Coastguard Worker        return receiver
741*cfb92d14SAndroid Build Coastguard Worker
742*cfb92d14SAndroid Build Coastguard Worker    @staticmethod
743*cfb92d14SAndroid Build Coastguard Worker    def perform_async_tx_rx(timeout=20):
744*cfb92d14SAndroid Build Coastguard Worker        """Called to perform all previously prepared async rx/listen and tx operations"""
745*cfb92d14SAndroid Build Coastguard Worker        try:
746*cfb92d14SAndroid Build Coastguard Worker            start_time = time.time()
747*cfb92d14SAndroid Build Coastguard Worker            while asyncore.socket_map:
748*cfb92d14SAndroid Build Coastguard Worker                elapsed_time = time.time() - start_time
749*cfb92d14SAndroid Build Coastguard Worker                if elapsed_time > timeout:
750*cfb92d14SAndroid Build Coastguard Worker                    print('Performing async tx/tx took too long ({}>{} sec)'.format(elapsed_time, timeout))
751*cfb92d14SAndroid Build Coastguard Worker                    raise Node._NodeError('perform_tx_rx timed out ({}>{} sec)'.format(elapsed_time, timeout))
752*cfb92d14SAndroid Build Coastguard Worker                # perform a single asyncore loop
753*cfb92d14SAndroid Build Coastguard Worker                asyncore.loop(timeout=0.5, count=1)
754*cfb92d14SAndroid Build Coastguard Worker        except BaseException:
755*cfb92d14SAndroid Build Coastguard Worker            print('Failed to perform async rx/tx')
756*cfb92d14SAndroid Build Coastguard Worker            raise
757*cfb92d14SAndroid Build Coastguard Worker
758*cfb92d14SAndroid Build Coastguard Worker
759*cfb92d14SAndroid Build Coastguard Worker# -----------------------------------------------------------------------------------------------------------------------
760*cfb92d14SAndroid Build Coastguard Worker# `AsyncSender` and `AsyncReceiver classes
761*cfb92d14SAndroid Build Coastguard Worker
762*cfb92d14SAndroid Build Coastguard Worker_SO_BINDTODEVICE = 25
763*cfb92d14SAndroid Build Coastguard Worker
764*cfb92d14SAndroid Build Coastguard Worker
765*cfb92d14SAndroid Build Coastguard Workerdef _is_ipv6_addr_link_local(ip_addr):
766*cfb92d14SAndroid Build Coastguard Worker    """Indicates if a given IPv6 address is link-local"""
767*cfb92d14SAndroid Build Coastguard Worker    return ip_addr.lower().startswith('fe80::')
768*cfb92d14SAndroid Build Coastguard Worker
769*cfb92d14SAndroid Build Coastguard Worker
770*cfb92d14SAndroid Build Coastguard Workerdef _create_socket_address(ip_address, port):
771*cfb92d14SAndroid Build Coastguard Worker    """Convert a given IPv6 address (string) and port number into a socket address"""
772*cfb92d14SAndroid Build Coastguard Worker    # `socket.getaddrinfo()` returns a list of `(family, socktype, proto, canonname, sockaddr)` where `sockaddr`
773*cfb92d14SAndroid Build Coastguard Worker    # (at index 4) can be used as input in socket methods (like `sendto()`, `bind()`, etc.).
774*cfb92d14SAndroid Build Coastguard Worker    return socket.getaddrinfo(ip_address, port)[0][4]
775*cfb92d14SAndroid Build Coastguard Worker
776*cfb92d14SAndroid Build Coastguard Worker
777*cfb92d14SAndroid Build Coastguard Workerclass AsyncSender(asyncore.dispatcher):
778*cfb92d14SAndroid Build Coastguard Worker    """ An IPv6 async message sender - use `Node.prepare_tx()` to create one"""
779*cfb92d14SAndroid Build Coastguard Worker
780*cfb92d14SAndroid Build Coastguard Worker    def __init__(self, node, src_addr, src_port, dst_addr, dst_port, msg, count, mcast_hops=None):
781*cfb92d14SAndroid Build Coastguard Worker        self._node = node
782*cfb92d14SAndroid Build Coastguard Worker        self._src_addr = src_addr
783*cfb92d14SAndroid Build Coastguard Worker        self._src_port = src_port
784*cfb92d14SAndroid Build Coastguard Worker        self._dst_addr = dst_addr
785*cfb92d14SAndroid Build Coastguard Worker        self._dst_port = dst_port
786*cfb92d14SAndroid Build Coastguard Worker        self._msg = msg
787*cfb92d14SAndroid Build Coastguard Worker        self._count = count
788*cfb92d14SAndroid Build Coastguard Worker        self._dst_sock_addr = _create_socket_address(dst_addr, dst_port)
789*cfb92d14SAndroid Build Coastguard Worker        self._tx_buffer = self._msg
790*cfb92d14SAndroid Build Coastguard Worker        self._tx_counter = 0
791*cfb92d14SAndroid Build Coastguard Worker
792*cfb92d14SAndroid Build Coastguard Worker        # Create a socket, bind it to the node's interface
793*cfb92d14SAndroid Build Coastguard Worker        sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
794*cfb92d14SAndroid Build Coastguard Worker        sock.setsockopt(socket.SOL_SOCKET, _SO_BINDTODEVICE, node.interface_name + '\0')
795*cfb92d14SAndroid Build Coastguard Worker        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
796*cfb92d14SAndroid Build Coastguard Worker
797*cfb92d14SAndroid Build Coastguard Worker        # Set the IPV6_MULTICAST_HOPS
798*cfb92d14SAndroid Build Coastguard Worker        if mcast_hops is not None:
799*cfb92d14SAndroid Build Coastguard Worker            sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, mcast_hops)
800*cfb92d14SAndroid Build Coastguard Worker
801*cfb92d14SAndroid Build Coastguard Worker        # Bind the socket to the given src address
802*cfb92d14SAndroid Build Coastguard Worker        if _is_ipv6_addr_link_local(src_addr):
803*cfb92d14SAndroid Build Coastguard Worker            # If src is a link local address it requires the interface name to
804*cfb92d14SAndroid Build Coastguard Worker            # be specified.
805*cfb92d14SAndroid Build Coastguard Worker            src_sock_addr = _create_socket_address(src_addr + '%' + node.interface_name, src_port)
806*cfb92d14SAndroid Build Coastguard Worker        else:
807*cfb92d14SAndroid Build Coastguard Worker            src_sock_addr = _create_socket_address(src_addr, src_port)
808*cfb92d14SAndroid Build Coastguard Worker        sock.bind(src_sock_addr)
809*cfb92d14SAndroid Build Coastguard Worker
810*cfb92d14SAndroid Build Coastguard Worker        asyncore.dispatcher.__init__(self, sock)
811*cfb92d14SAndroid Build Coastguard Worker
812*cfb92d14SAndroid Build Coastguard Worker    # Property getters
813*cfb92d14SAndroid Build Coastguard Worker
814*cfb92d14SAndroid Build Coastguard Worker    @property
815*cfb92d14SAndroid Build Coastguard Worker    def node(self):
816*cfb92d14SAndroid Build Coastguard Worker        return self._node
817*cfb92d14SAndroid Build Coastguard Worker
818*cfb92d14SAndroid Build Coastguard Worker    @property
819*cfb92d14SAndroid Build Coastguard Worker    def src_addr(self):
820*cfb92d14SAndroid Build Coastguard Worker        return self._src_addr
821*cfb92d14SAndroid Build Coastguard Worker
822*cfb92d14SAndroid Build Coastguard Worker    @property
823*cfb92d14SAndroid Build Coastguard Worker    def src_port(self):
824*cfb92d14SAndroid Build Coastguard Worker        return self._src_port
825*cfb92d14SAndroid Build Coastguard Worker
826*cfb92d14SAndroid Build Coastguard Worker    @property
827*cfb92d14SAndroid Build Coastguard Worker    def dst_addr(self):
828*cfb92d14SAndroid Build Coastguard Worker        return self._dst_addr
829*cfb92d14SAndroid Build Coastguard Worker
830*cfb92d14SAndroid Build Coastguard Worker    @property
831*cfb92d14SAndroid Build Coastguard Worker    def dst_port(self):
832*cfb92d14SAndroid Build Coastguard Worker        return self._dst_port
833*cfb92d14SAndroid Build Coastguard Worker
834*cfb92d14SAndroid Build Coastguard Worker    @property
835*cfb92d14SAndroid Build Coastguard Worker    def msg(self):
836*cfb92d14SAndroid Build Coastguard Worker        return self._msg
837*cfb92d14SAndroid Build Coastguard Worker
838*cfb92d14SAndroid Build Coastguard Worker    @property
839*cfb92d14SAndroid Build Coastguard Worker    def count(self):
840*cfb92d14SAndroid Build Coastguard Worker        return self._count
841*cfb92d14SAndroid Build Coastguard Worker
842*cfb92d14SAndroid Build Coastguard Worker    @property
843*cfb92d14SAndroid Build Coastguard Worker    def was_successful(self):
844*cfb92d14SAndroid Build Coastguard Worker        """Indicates if the transmission of IPv6 messages finished successfully"""
845*cfb92d14SAndroid Build Coastguard Worker        return self._tx_counter == self._count
846*cfb92d14SAndroid Build Coastguard Worker
847*cfb92d14SAndroid Build Coastguard Worker    # asyncore.dispatcher callbacks
848*cfb92d14SAndroid Build Coastguard Worker
849*cfb92d14SAndroid Build Coastguard Worker    def readable(self):
850*cfb92d14SAndroid Build Coastguard Worker        return False
851*cfb92d14SAndroid Build Coastguard Worker
852*cfb92d14SAndroid Build Coastguard Worker    def writable(self):
853*cfb92d14SAndroid Build Coastguard Worker        return True
854*cfb92d14SAndroid Build Coastguard Worker
855*cfb92d14SAndroid Build Coastguard Worker    def handle_write(self):
856*cfb92d14SAndroid Build Coastguard Worker        sent_len = self.sendto(self._tx_buffer, self._dst_sock_addr)
857*cfb92d14SAndroid Build Coastguard Worker
858*cfb92d14SAndroid Build Coastguard Worker        if self._node._verbose:
859*cfb92d14SAndroid Build Coastguard Worker            if sent_len < 30:
860*cfb92d14SAndroid Build Coastguard Worker                info_text = '{} bytes ("{}")'.format(sent_len, self._tx_buffer[:sent_len])
861*cfb92d14SAndroid Build Coastguard Worker            else:
862*cfb92d14SAndroid Build Coastguard Worker                info_text = '{} bytes'.format(sent_len)
863*cfb92d14SAndroid Build Coastguard Worker            _log('- Node{} sent {} to [{}]:{} from [{}]:{}'.format(self._node._index, info_text, self._dst_addr,
864*cfb92d14SAndroid Build Coastguard Worker                                                                   self._dst_port, self._src_addr, self._src_port))
865*cfb92d14SAndroid Build Coastguard Worker
866*cfb92d14SAndroid Build Coastguard Worker        self._tx_buffer = self._tx_buffer[sent_len:]
867*cfb92d14SAndroid Build Coastguard Worker
868*cfb92d14SAndroid Build Coastguard Worker        if len(self._tx_buffer) == 0:
869*cfb92d14SAndroid Build Coastguard Worker            self._tx_counter += 1
870*cfb92d14SAndroid Build Coastguard Worker            if self._tx_counter < self._count:
871*cfb92d14SAndroid Build Coastguard Worker                self._tx_buffer = self._msg
872*cfb92d14SAndroid Build Coastguard Worker            else:
873*cfb92d14SAndroid Build Coastguard Worker                self.handle_close()
874*cfb92d14SAndroid Build Coastguard Worker
875*cfb92d14SAndroid Build Coastguard Worker    def handle_close(self):
876*cfb92d14SAndroid Build Coastguard Worker        self.close()
877*cfb92d14SAndroid Build Coastguard Worker
878*cfb92d14SAndroid Build Coastguard Worker
879*cfb92d14SAndroid Build Coastguard Worker# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
880*cfb92d14SAndroid Build Coastguard Worker
881*cfb92d14SAndroid Build Coastguard Worker
882*cfb92d14SAndroid Build Coastguard Workerclass AsyncReceiver(asyncore.dispatcher):
883*cfb92d14SAndroid Build Coastguard Worker    """ An IPv6 async message receiver - use `prepare_rx()` to create one"""
884*cfb92d14SAndroid Build Coastguard Worker
885*cfb92d14SAndroid Build Coastguard Worker    _MAX_RECV_SIZE = 2048
886*cfb92d14SAndroid Build Coastguard Worker
887*cfb92d14SAndroid Build Coastguard Worker    class _SenderInfo(object):
888*cfb92d14SAndroid Build Coastguard Worker
889*cfb92d14SAndroid Build Coastguard Worker        def __init__(self, sender_addr, sender_port, msg, count):
890*cfb92d14SAndroid Build Coastguard Worker            self._sender_addr = sender_addr
891*cfb92d14SAndroid Build Coastguard Worker            self._sender_port = sender_port
892*cfb92d14SAndroid Build Coastguard Worker            self._msg = msg
893*cfb92d14SAndroid Build Coastguard Worker            self._count = count
894*cfb92d14SAndroid Build Coastguard Worker            self._rx_counter = 0
895*cfb92d14SAndroid Build Coastguard Worker
896*cfb92d14SAndroid Build Coastguard Worker        def _check_received(self, msg, sender_addr, sender_port):
897*cfb92d14SAndroid Build Coastguard Worker            if self._msg == msg and self._sender_addr == sender_addr and self._sender_port == sender_port:
898*cfb92d14SAndroid Build Coastguard Worker                self._rx_counter += 1
899*cfb92d14SAndroid Build Coastguard Worker            return self._did_recv_all()
900*cfb92d14SAndroid Build Coastguard Worker
901*cfb92d14SAndroid Build Coastguard Worker        def _did_recv_all(self):
902*cfb92d14SAndroid Build Coastguard Worker            return self._rx_counter >= self._count
903*cfb92d14SAndroid Build Coastguard Worker
904*cfb92d14SAndroid Build Coastguard Worker    def __init__(self, node, local_port):
905*cfb92d14SAndroid Build Coastguard Worker        self._node = node
906*cfb92d14SAndroid Build Coastguard Worker        self._local_port = local_port
907*cfb92d14SAndroid Build Coastguard Worker        self._senders = []  # list of `_SenderInfo` objects
908*cfb92d14SAndroid Build Coastguard Worker        # contains all received messages as a list of (pkt, (src_addr,
909*cfb92d14SAndroid Build Coastguard Worker        # src_port))
910*cfb92d14SAndroid Build Coastguard Worker        self._all_rx = []
911*cfb92d14SAndroid Build Coastguard Worker        self._timeout = 0  # listen timeout (zero means forever)
912*cfb92d14SAndroid Build Coastguard Worker        self._started = False
913*cfb92d14SAndroid Build Coastguard Worker        self._start_time = 0
914*cfb92d14SAndroid Build Coastguard Worker
915*cfb92d14SAndroid Build Coastguard Worker        # Create a socket, bind it to the node's interface
916*cfb92d14SAndroid Build Coastguard Worker        sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
917*cfb92d14SAndroid Build Coastguard Worker        sock.setsockopt(socket.SOL_SOCKET, _SO_BINDTODEVICE, node.interface_name + '\0')
918*cfb92d14SAndroid Build Coastguard Worker        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
919*cfb92d14SAndroid Build Coastguard Worker
920*cfb92d14SAndroid Build Coastguard Worker        # Bind the socket to any IPv6 address with the given local port
921*cfb92d14SAndroid Build Coastguard Worker        local_sock_addr = _create_socket_address('::', local_port)
922*cfb92d14SAndroid Build Coastguard Worker        sock.bind(local_sock_addr)
923*cfb92d14SAndroid Build Coastguard Worker
924*cfb92d14SAndroid Build Coastguard Worker        asyncore.dispatcher.__init__(self, sock)
925*cfb92d14SAndroid Build Coastguard Worker
926*cfb92d14SAndroid Build Coastguard Worker    def _add_sender(self, sender_addr, sender_port, msg, count):
927*cfb92d14SAndroid Build Coastguard Worker        self._senders.append(AsyncReceiver._SenderInfo(sender_addr, sender_port, msg, count))
928*cfb92d14SAndroid Build Coastguard Worker
929*cfb92d14SAndroid Build Coastguard Worker    def _set_listen_timeout(self, timeout):
930*cfb92d14SAndroid Build Coastguard Worker        self._timeout = timeout
931*cfb92d14SAndroid Build Coastguard Worker
932*cfb92d14SAndroid Build Coastguard Worker    # Property getters
933*cfb92d14SAndroid Build Coastguard Worker
934*cfb92d14SAndroid Build Coastguard Worker    @property
935*cfb92d14SAndroid Build Coastguard Worker    def node(self):
936*cfb92d14SAndroid Build Coastguard Worker        return self._node
937*cfb92d14SAndroid Build Coastguard Worker
938*cfb92d14SAndroid Build Coastguard Worker    @property
939*cfb92d14SAndroid Build Coastguard Worker    def local_port(self):
940*cfb92d14SAndroid Build Coastguard Worker        return self._local_port
941*cfb92d14SAndroid Build Coastguard Worker
942*cfb92d14SAndroid Build Coastguard Worker    @property
943*cfb92d14SAndroid Build Coastguard Worker    def all_rx_msg(self):
944*cfb92d14SAndroid Build Coastguard Worker        """returns all received messages as a list of (msg, (src_addr, src_port))"""
945*cfb92d14SAndroid Build Coastguard Worker        return self._all_rx
946*cfb92d14SAndroid Build Coastguard Worker
947*cfb92d14SAndroid Build Coastguard Worker    @property
948*cfb92d14SAndroid Build Coastguard Worker    def was_successful(self):
949*cfb92d14SAndroid Build Coastguard Worker        """Indicates if all expected IPv6 messages were received successfully"""
950*cfb92d14SAndroid Build Coastguard Worker        return len(self._senders) == 0 or all([sender._did_recv_all() for sender in self._senders])
951*cfb92d14SAndroid Build Coastguard Worker
952*cfb92d14SAndroid Build Coastguard Worker    # asyncore.dispatcher callbacks
953*cfb92d14SAndroid Build Coastguard Worker
954*cfb92d14SAndroid Build Coastguard Worker    def readable(self):
955*cfb92d14SAndroid Build Coastguard Worker        if not self._started:
956*cfb92d14SAndroid Build Coastguard Worker            self._start_time = time.time()
957*cfb92d14SAndroid Build Coastguard Worker            self._started = True
958*cfb92d14SAndroid Build Coastguard Worker        if self._timeout != 0 and time.time() - self._start_time >= self._timeout:
959*cfb92d14SAndroid Build Coastguard Worker            self.handle_close()
960*cfb92d14SAndroid Build Coastguard Worker            if self._node._verbose:
961*cfb92d14SAndroid Build Coastguard Worker                _log('- Node{} finished listening on port {} for {} sec, received {} msg(s)'.format(
962*cfb92d14SAndroid Build Coastguard Worker                    self._node._index, self._local_port, self._timeout, len(self._all_rx)))
963*cfb92d14SAndroid Build Coastguard Worker            return False
964*cfb92d14SAndroid Build Coastguard Worker        return True
965*cfb92d14SAndroid Build Coastguard Worker
966*cfb92d14SAndroid Build Coastguard Worker    def writable(self):
967*cfb92d14SAndroid Build Coastguard Worker        return False
968*cfb92d14SAndroid Build Coastguard Worker
969*cfb92d14SAndroid Build Coastguard Worker    def handle_read(self):
970*cfb92d14SAndroid Build Coastguard Worker        (msg, src_sock_addr) = self.recvfrom(AsyncReceiver._MAX_RECV_SIZE)
971*cfb92d14SAndroid Build Coastguard Worker        src_addr = src_sock_addr[0]
972*cfb92d14SAndroid Build Coastguard Worker        src_port = src_sock_addr[1]
973*cfb92d14SAndroid Build Coastguard Worker
974*cfb92d14SAndroid Build Coastguard Worker        if (_is_ipv6_addr_link_local(src_addr)):
975*cfb92d14SAndroid Build Coastguard Worker            if '%' in src_addr:
976*cfb92d14SAndroid Build Coastguard Worker                # remove the interface name from address
977*cfb92d14SAndroid Build Coastguard Worker                src_addr = src_addr.split('%')[0]
978*cfb92d14SAndroid Build Coastguard Worker
979*cfb92d14SAndroid Build Coastguard Worker        if self._node._verbose:
980*cfb92d14SAndroid Build Coastguard Worker            if len(msg) < 30:
981*cfb92d14SAndroid Build Coastguard Worker                info_text = '{} bytes ("{}")'.format(len(msg), msg)
982*cfb92d14SAndroid Build Coastguard Worker            else:
983*cfb92d14SAndroid Build Coastguard Worker                info_text = '{} bytes'.format(len(msg))
984*cfb92d14SAndroid Build Coastguard Worker            _log('- Node{} received {} on port {} from [{}]:{}'.format(self._node._index, info_text, self._local_port,
985*cfb92d14SAndroid Build Coastguard Worker                                                                       src_addr, src_port))
986*cfb92d14SAndroid Build Coastguard Worker
987*cfb92d14SAndroid Build Coastguard Worker        self._all_rx.append((msg, (src_addr, src_port)))
988*cfb92d14SAndroid Build Coastguard Worker
989*cfb92d14SAndroid Build Coastguard Worker        if all([sender._check_received(msg, src_addr, src_port) for sender in self._senders]):
990*cfb92d14SAndroid Build Coastguard Worker            self.handle_close()
991*cfb92d14SAndroid Build Coastguard Worker
992*cfb92d14SAndroid Build Coastguard Worker    def handle_close(self):
993*cfb92d14SAndroid Build Coastguard Worker        self.close()
994*cfb92d14SAndroid Build Coastguard Worker        # remove the receiver from the node once the socket is closed
995*cfb92d14SAndroid Build Coastguard Worker        self._node._remove_recver(self)
996*cfb92d14SAndroid Build Coastguard Worker
997*cfb92d14SAndroid Build Coastguard Worker
998*cfb92d14SAndroid Build Coastguard Worker# -----------------------------------------------------------------------------------------------------------------------
999*cfb92d14SAndroid Build Coastguard Worker
1000*cfb92d14SAndroid Build Coastguard Worker
1001*cfb92d14SAndroid Build Coastguard Workerclass VerifyError(Exception):
1002*cfb92d14SAndroid Build Coastguard Worker    pass
1003*cfb92d14SAndroid Build Coastguard Worker
1004*cfb92d14SAndroid Build Coastguard Worker
1005*cfb92d14SAndroid Build Coastguard Worker_is_in_verify_within = False
1006*cfb92d14SAndroid Build Coastguard Worker
1007*cfb92d14SAndroid Build Coastguard Worker
1008*cfb92d14SAndroid Build Coastguard Workerdef verify(condition):
1009*cfb92d14SAndroid Build Coastguard Worker    """Verifies that a `condition` is true, otherwise raises a VerifyError"""
1010*cfb92d14SAndroid Build Coastguard Worker    global _is_in_verify_within
1011*cfb92d14SAndroid Build Coastguard Worker    if not condition:
1012*cfb92d14SAndroid Build Coastguard Worker        calling_frame = inspect.currentframe().f_back
1013*cfb92d14SAndroid Build Coastguard Worker        error_message = 'verify() failed at line {} in "{}"'.format(calling_frame.f_lineno,
1014*cfb92d14SAndroid Build Coastguard Worker                                                                    calling_frame.f_code.co_filename)
1015*cfb92d14SAndroid Build Coastguard Worker        if not _is_in_verify_within:
1016*cfb92d14SAndroid Build Coastguard Worker            print(error_message)
1017*cfb92d14SAndroid Build Coastguard Worker        raise VerifyError(error_message)
1018*cfb92d14SAndroid Build Coastguard Worker
1019*cfb92d14SAndroid Build Coastguard Worker
1020*cfb92d14SAndroid Build Coastguard Workerdef verify_within(condition_checker_func, wait_time, delay_time=0.1):
1021*cfb92d14SAndroid Build Coastguard Worker    """Verifies that a given function `condition_checker_func` passes successfully within a given wait timeout.
1022*cfb92d14SAndroid Build Coastguard Worker       `wait_time` is maximum time waiting for condition_checker to pass (in seconds).
1023*cfb92d14SAndroid Build Coastguard Worker       `delay_time` specifies a delay interval added between failed attempts (in seconds).
1024*cfb92d14SAndroid Build Coastguard Worker    """
1025*cfb92d14SAndroid Build Coastguard Worker    global _is_in_verify_within
1026*cfb92d14SAndroid Build Coastguard Worker    start_time = time.time()
1027*cfb92d14SAndroid Build Coastguard Worker    old_is_in_verify_within = _is_in_verify_within
1028*cfb92d14SAndroid Build Coastguard Worker    _is_in_verify_within = True
1029*cfb92d14SAndroid Build Coastguard Worker    while True:
1030*cfb92d14SAndroid Build Coastguard Worker        try:
1031*cfb92d14SAndroid Build Coastguard Worker            condition_checker_func()
1032*cfb92d14SAndroid Build Coastguard Worker        except VerifyError as e:
1033*cfb92d14SAndroid Build Coastguard Worker            if time.time() - start_time > wait_time:
1034*cfb92d14SAndroid Build Coastguard Worker                print('Took too long to pass the condition ({}>{} sec)'.format(time.time() - start_time, wait_time))
1035*cfb92d14SAndroid Build Coastguard Worker                print(e.message)
1036*cfb92d14SAndroid Build Coastguard Worker                raise e
1037*cfb92d14SAndroid Build Coastguard Worker        except BaseException:
1038*cfb92d14SAndroid Build Coastguard Worker            raise
1039*cfb92d14SAndroid Build Coastguard Worker        else:
1040*cfb92d14SAndroid Build Coastguard Worker            break
1041*cfb92d14SAndroid Build Coastguard Worker        if delay_time != 0:
1042*cfb92d14SAndroid Build Coastguard Worker            time.sleep(delay_time)
1043*cfb92d14SAndroid Build Coastguard Worker    _is_in_verify_within = old_is_in_verify_within
1044*cfb92d14SAndroid Build Coastguard Worker
1045*cfb92d14SAndroid Build Coastguard Worker
1046*cfb92d14SAndroid Build Coastguard Worker# -----------------------------------------------------------------------------------------------------------------------
1047*cfb92d14SAndroid Build Coastguard Worker# Parsing `wpanctl` output
1048*cfb92d14SAndroid Build Coastguard Worker
1049*cfb92d14SAndroid Build Coastguard Worker# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1050*cfb92d14SAndroid Build Coastguard Worker
1051*cfb92d14SAndroid Build Coastguard Worker
1052*cfb92d14SAndroid Build Coastguard Workerclass ScanResult(object):
1053*cfb92d14SAndroid Build Coastguard Worker    """ This object encapsulates a scan result (active/discover/energy scan)"""
1054*cfb92d14SAndroid Build Coastguard Worker
1055*cfb92d14SAndroid Build Coastguard Worker    TYPE_ACTIVE_SCAN = 'active-scan'
1056*cfb92d14SAndroid Build Coastguard Worker    TYPE_DISCOVERY_SCAN = 'discover-scan'
1057*cfb92d14SAndroid Build Coastguard Worker    TYPE_ENERGY_SCAN = 'energy-scan'
1058*cfb92d14SAndroid Build Coastguard Worker
1059*cfb92d14SAndroid Build Coastguard Worker    def __init__(self, result_text):
1060*cfb92d14SAndroid Build Coastguard Worker
1061*cfb92d14SAndroid Build Coastguard Worker        items = [item.strip() for item in result_text.split('|')]
1062*cfb92d14SAndroid Build Coastguard Worker
1063*cfb92d14SAndroid Build Coastguard Worker        if len(items) == 2:
1064*cfb92d14SAndroid Build Coastguard Worker            self._type = ScanResult.TYPE_ENERGY_SCAN
1065*cfb92d14SAndroid Build Coastguard Worker            self._channel = items[0]
1066*cfb92d14SAndroid Build Coastguard Worker            self._rssi = items[1]
1067*cfb92d14SAndroid Build Coastguard Worker        if len(items) == 7 and '------ NONE ------' in items[1]:
1068*cfb92d14SAndroid Build Coastguard Worker            self._type = ScanResult.TYPE_ACTIVE_SCAN
1069*cfb92d14SAndroid Build Coastguard Worker            self._index = items[0]
1070*cfb92d14SAndroid Build Coastguard Worker            self._panid = items[2]
1071*cfb92d14SAndroid Build Coastguard Worker            self._channel = items[3]
1072*cfb92d14SAndroid Build Coastguard Worker            self._ext_address = items[5]
1073*cfb92d14SAndroid Build Coastguard Worker            self._rssi = items[6]
1074*cfb92d14SAndroid Build Coastguard Worker        elif len(items) == 7:
1075*cfb92d14SAndroid Build Coastguard Worker            self._type = ScanResult.TYPE_DISCOVERY_SCAN
1076*cfb92d14SAndroid Build Coastguard Worker            self._index = items[0]
1077*cfb92d14SAndroid Build Coastguard Worker            self._network_name = items[1][1:-1]
1078*cfb92d14SAndroid Build Coastguard Worker            self._panid = items[2]
1079*cfb92d14SAndroid Build Coastguard Worker            self._channel = items[3]
1080*cfb92d14SAndroid Build Coastguard Worker            self._xpanid = items[4]
1081*cfb92d14SAndroid Build Coastguard Worker            self._ext_address = items[5]
1082*cfb92d14SAndroid Build Coastguard Worker            self._rssi = items[6]
1083*cfb92d14SAndroid Build Coastguard Worker        else:
1084*cfb92d14SAndroid Build Coastguard Worker            raise ValueError('"%s" does not seem to be a valid scan result string' % result_text)
1085*cfb92d14SAndroid Build Coastguard Worker
1086*cfb92d14SAndroid Build Coastguard Worker    @property
1087*cfb92d14SAndroid Build Coastguard Worker    def type(self):
1088*cfb92d14SAndroid Build Coastguard Worker        return self._type
1089*cfb92d14SAndroid Build Coastguard Worker
1090*cfb92d14SAndroid Build Coastguard Worker    @property
1091*cfb92d14SAndroid Build Coastguard Worker    def joinable(self):
1092*cfb92d14SAndroid Build Coastguard Worker        return self._joinable
1093*cfb92d14SAndroid Build Coastguard Worker
1094*cfb92d14SAndroid Build Coastguard Worker    @property
1095*cfb92d14SAndroid Build Coastguard Worker    def network_name(self):
1096*cfb92d14SAndroid Build Coastguard Worker        return self._network_name
1097*cfb92d14SAndroid Build Coastguard Worker
1098*cfb92d14SAndroid Build Coastguard Worker    @property
1099*cfb92d14SAndroid Build Coastguard Worker    def panid(self):
1100*cfb92d14SAndroid Build Coastguard Worker        return self._panid
1101*cfb92d14SAndroid Build Coastguard Worker
1102*cfb92d14SAndroid Build Coastguard Worker    @property
1103*cfb92d14SAndroid Build Coastguard Worker    def channel(self):
1104*cfb92d14SAndroid Build Coastguard Worker        return self._channel
1105*cfb92d14SAndroid Build Coastguard Worker
1106*cfb92d14SAndroid Build Coastguard Worker    @property
1107*cfb92d14SAndroid Build Coastguard Worker    def xpanid(self):
1108*cfb92d14SAndroid Build Coastguard Worker        return self._xpanid
1109*cfb92d14SAndroid Build Coastguard Worker
1110*cfb92d14SAndroid Build Coastguard Worker    @property
1111*cfb92d14SAndroid Build Coastguard Worker    def ext_address(self):
1112*cfb92d14SAndroid Build Coastguard Worker        return self._ext_address
1113*cfb92d14SAndroid Build Coastguard Worker
1114*cfb92d14SAndroid Build Coastguard Worker    @property
1115*cfb92d14SAndroid Build Coastguard Worker    def rssi(self):
1116*cfb92d14SAndroid Build Coastguard Worker        return self._rssi
1117*cfb92d14SAndroid Build Coastguard Worker
1118*cfb92d14SAndroid Build Coastguard Worker    def __repr__(self):
1119*cfb92d14SAndroid Build Coastguard Worker        return 'ScanResult({})'.format(self.__dict__)
1120*cfb92d14SAndroid Build Coastguard Worker
1121*cfb92d14SAndroid Build Coastguard Worker
1122*cfb92d14SAndroid Build Coastguard Workerdef parse_scan_result(scan_result):
1123*cfb92d14SAndroid Build Coastguard Worker    """ Parses scan result string and returns an array of `ScanResult` objects"""
1124*cfb92d14SAndroid Build Coastguard Worker    return [ScanResult(item) for item in scan_result.split('\n')[2:]]  # skip first two lines which are table headers
1125*cfb92d14SAndroid Build Coastguard Worker
1126*cfb92d14SAndroid Build Coastguard Worker
1127*cfb92d14SAndroid Build Coastguard Workerdef parse_list(list_string):
1128*cfb92d14SAndroid Build Coastguard Worker    """
1129*cfb92d14SAndroid Build Coastguard Worker    Parses IPv6/prefix/route list string (output of wpanctl get for properties WPAN_IP6_ALL_ADDRESSES,
1130*cfb92d14SAndroid Build Coastguard Worker    IP6_MULTICAST_ADDRESSES, WPAN_THREAD_ON_MESH_PREFIXES, ...)
1131*cfb92d14SAndroid Build Coastguard Worker    Returns an array of strings each containing an IPv6/prefix/route entry.
1132*cfb92d14SAndroid Build Coastguard Worker    """
1133*cfb92d14SAndroid Build Coastguard Worker    # List string example (get(WPAN_IP6_ALL_ADDRESSES) output):
1134*cfb92d14SAndroid Build Coastguard Worker    #
1135*cfb92d14SAndroid Build Coastguard Worker    # '[\n
1136*cfb92d14SAndroid Build Coastguard Worker    # \t"fdf4:5632:4940:0:8798:8701:85d4:e2be     prefix_len:64   origin:ncp      valid:forever   preferred:forever"\n
1137*cfb92d14SAndroid Build Coastguard Worker    # \t"fe80::2092:9358:97ea:71c6                prefix_len:64   origin:ncp      valid:forever   preferred:forever"\n
1138*cfb92d14SAndroid Build Coastguard Worker    # ]'
1139*cfb92d14SAndroid Build Coastguard Worker    #
1140*cfb92d14SAndroid Build Coastguard Worker    # We split the lines ('\n' as separator) and skip the first and last lines which are '['  and ']'.
1141*cfb92d14SAndroid Build Coastguard Worker    # For each line, skip the first two characters (which are '\t"') and last character ('"'), then split the string
1142*cfb92d14SAndroid Build Coastguard Worker    # using whitespace as separator. The first entry is the IPv6 address.
1143*cfb92d14SAndroid Build Coastguard Worker    #
1144*cfb92d14SAndroid Build Coastguard Worker    return [line[2:-1].split()[0] for line in list_string.split('\n')[1:-1]]
1145*cfb92d14SAndroid Build Coastguard Worker
1146*cfb92d14SAndroid Build Coastguard Worker
1147*cfb92d14SAndroid Build Coastguard Worker# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1148*cfb92d14SAndroid Build Coastguard Worker
1149*cfb92d14SAndroid Build Coastguard Worker
1150*cfb92d14SAndroid Build Coastguard Workerclass OnMeshPrefix(object):
1151*cfb92d14SAndroid Build Coastguard Worker    """ This object encapsulates an on-mesh prefix"""
1152*cfb92d14SAndroid Build Coastguard Worker
1153*cfb92d14SAndroid Build Coastguard Worker    def __init__(self, text):
1154*cfb92d14SAndroid Build Coastguard Worker
1155*cfb92d14SAndroid Build Coastguard Worker        # Example of expected text:
1156*cfb92d14SAndroid Build Coastguard Worker        #
1157*cfb92d14SAndroid Build Coastguard Worker        # '\t"fd00:abba:cafe::       prefix_len:64   origin:user     stable:yes flags:0x31'
1158*cfb92d14SAndroid Build Coastguard Worker        # ' [on-mesh:1 def-route:0 config:0 dhcp:0 slaac:1 pref:1 prio:med] rloc:0x0000"'
1159*cfb92d14SAndroid Build Coastguard Worker
1160*cfb92d14SAndroid Build Coastguard Worker        m = re.match(
1161*cfb92d14SAndroid Build Coastguard Worker            r'\t"([0-9a-fA-F:]+)\s*prefix_len:(\d+)\s+origin:(\w*)\s+stable:(\w*).* \[' +
1162*cfb92d14SAndroid Build Coastguard Worker            r'on-mesh:(\d)\s+def-route:(\d)\s+config:(\d)\s+dhcp:(\d)\s+slaac:(\d)\s+pref:(\d)\s+.*prio:(\w*)\]' +
1163*cfb92d14SAndroid Build Coastguard Worker            r'\s+rloc:(0x[0-9a-fA-F]+)', text)
1164*cfb92d14SAndroid Build Coastguard Worker        verify(m is not None)
1165*cfb92d14SAndroid Build Coastguard Worker        data = m.groups()
1166*cfb92d14SAndroid Build Coastguard Worker
1167*cfb92d14SAndroid Build Coastguard Worker        self._prefix = data[0]
1168*cfb92d14SAndroid Build Coastguard Worker        self._prefix_len = data[1]
1169*cfb92d14SAndroid Build Coastguard Worker        self._origin = data[2]
1170*cfb92d14SAndroid Build Coastguard Worker        self._stable = (data[3] == 'yes')
1171*cfb92d14SAndroid Build Coastguard Worker        self._on_mesh = (data[4] == '1')
1172*cfb92d14SAndroid Build Coastguard Worker        self._def_route = (data[5] == '1')
1173*cfb92d14SAndroid Build Coastguard Worker        self._config = (data[6] == '1')
1174*cfb92d14SAndroid Build Coastguard Worker        self._dhcp = (data[7] == '1')
1175*cfb92d14SAndroid Build Coastguard Worker        self._slaac = (data[8] == '1')
1176*cfb92d14SAndroid Build Coastguard Worker        self._preferred = (data[9] == '1')
1177*cfb92d14SAndroid Build Coastguard Worker        self._priority = (data[10])
1178*cfb92d14SAndroid Build Coastguard Worker        self._rloc16 = (data[11])
1179*cfb92d14SAndroid Build Coastguard Worker
1180*cfb92d14SAndroid Build Coastguard Worker    @property
1181*cfb92d14SAndroid Build Coastguard Worker    def prefix(self):
1182*cfb92d14SAndroid Build Coastguard Worker        return self._prefix
1183*cfb92d14SAndroid Build Coastguard Worker
1184*cfb92d14SAndroid Build Coastguard Worker    @property
1185*cfb92d14SAndroid Build Coastguard Worker    def prefix_len(self):
1186*cfb92d14SAndroid Build Coastguard Worker        return self._prefix_len
1187*cfb92d14SAndroid Build Coastguard Worker
1188*cfb92d14SAndroid Build Coastguard Worker    @property
1189*cfb92d14SAndroid Build Coastguard Worker    def origin(self):
1190*cfb92d14SAndroid Build Coastguard Worker        return self._origin
1191*cfb92d14SAndroid Build Coastguard Worker
1192*cfb92d14SAndroid Build Coastguard Worker    @property
1193*cfb92d14SAndroid Build Coastguard Worker    def priority(self):
1194*cfb92d14SAndroid Build Coastguard Worker        return self._priority
1195*cfb92d14SAndroid Build Coastguard Worker
1196*cfb92d14SAndroid Build Coastguard Worker    def is_stable(self):
1197*cfb92d14SAndroid Build Coastguard Worker        return self._stable
1198*cfb92d14SAndroid Build Coastguard Worker
1199*cfb92d14SAndroid Build Coastguard Worker    def is_on_mesh(self):
1200*cfb92d14SAndroid Build Coastguard Worker        return self._on_mesh
1201*cfb92d14SAndroid Build Coastguard Worker
1202*cfb92d14SAndroid Build Coastguard Worker    def is_def_route(self):
1203*cfb92d14SAndroid Build Coastguard Worker        return self._def_route
1204*cfb92d14SAndroid Build Coastguard Worker
1205*cfb92d14SAndroid Build Coastguard Worker    def is_config(self):
1206*cfb92d14SAndroid Build Coastguard Worker        return self._config
1207*cfb92d14SAndroid Build Coastguard Worker
1208*cfb92d14SAndroid Build Coastguard Worker    def is_dhcp(self):
1209*cfb92d14SAndroid Build Coastguard Worker        return self._dhcp
1210*cfb92d14SAndroid Build Coastguard Worker
1211*cfb92d14SAndroid Build Coastguard Worker    def is_slaac(self):
1212*cfb92d14SAndroid Build Coastguard Worker        return self._slaac
1213*cfb92d14SAndroid Build Coastguard Worker
1214*cfb92d14SAndroid Build Coastguard Worker    def is_preferred(self):
1215*cfb92d14SAndroid Build Coastguard Worker        return self._preferred
1216*cfb92d14SAndroid Build Coastguard Worker
1217*cfb92d14SAndroid Build Coastguard Worker    def rloc16(self):
1218*cfb92d14SAndroid Build Coastguard Worker        return self._rloc16
1219*cfb92d14SAndroid Build Coastguard Worker
1220*cfb92d14SAndroid Build Coastguard Worker    def __repr__(self):
1221*cfb92d14SAndroid Build Coastguard Worker        return 'OnMeshPrefix({})'.format(self.__dict__)
1222*cfb92d14SAndroid Build Coastguard Worker
1223*cfb92d14SAndroid Build Coastguard Worker
1224*cfb92d14SAndroid Build Coastguard Workerdef parse_on_mesh_prefix_result(on_mesh_prefix_list):
1225*cfb92d14SAndroid Build Coastguard Worker    """ Parses on-mesh prefix list string and returns an array of `OnMeshPrefix` objects"""
1226*cfb92d14SAndroid Build Coastguard Worker    return [OnMeshPrefix(item) for item in on_mesh_prefix_list.split('\n')[1:-1]]
1227*cfb92d14SAndroid Build Coastguard Worker
1228*cfb92d14SAndroid Build Coastguard Worker
1229*cfb92d14SAndroid Build Coastguard Worker# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1230*cfb92d14SAndroid Build Coastguard Worker
1231*cfb92d14SAndroid Build Coastguard Worker
1232*cfb92d14SAndroid Build Coastguard Workerclass ChildEntry(object):
1233*cfb92d14SAndroid Build Coastguard Worker    """ This object encapsulates a child entry"""
1234*cfb92d14SAndroid Build Coastguard Worker
1235*cfb92d14SAndroid Build Coastguard Worker    def __init__(self, text):
1236*cfb92d14SAndroid Build Coastguard Worker
1237*cfb92d14SAndroid Build Coastguard Worker        # Example of expected text:
1238*cfb92d14SAndroid Build Coastguard Worker        #
1239*cfb92d14SAndroid Build Coastguard Worker        # `\t"E24C5F67F4B8CBB9, RLOC16:d402, NetDataVer:175, LQIn:3, AveRssi:-20, LastRssi:-20, Timeout:120, Age:0, `
1240*cfb92d14SAndroid Build Coastguard Worker        # `RxOnIdle:no, FTD:no, SecDataReq:yes, FullNetData:yes"`
1241*cfb92d14SAndroid Build Coastguard Worker        #
1242*cfb92d14SAndroid Build Coastguard Worker
1243*cfb92d14SAndroid Build Coastguard Worker        # We get rid of the first two chars `\t"' and last char '"', split the rest using whitespace as separator.
1244*cfb92d14SAndroid Build Coastguard Worker        # Then remove any ',' at end of items in the list.
1245*cfb92d14SAndroid Build Coastguard Worker        items = [item[:-1] if item[-1] == ',' else item for item in text[2:-1].split()]
1246*cfb92d14SAndroid Build Coastguard Worker
1247*cfb92d14SAndroid Build Coastguard Worker        # First item in the extended address
1248*cfb92d14SAndroid Build Coastguard Worker        self._ext_address = items[0]
1249*cfb92d14SAndroid Build Coastguard Worker
1250*cfb92d14SAndroid Build Coastguard Worker        # Convert the rest into a dictionary by splitting using ':' as
1251*cfb92d14SAndroid Build Coastguard Worker        # separator
1252*cfb92d14SAndroid Build Coastguard Worker        dict = {item.split(':')[0]: item.split(':')[1] for item in items[1:]}
1253*cfb92d14SAndroid Build Coastguard Worker
1254*cfb92d14SAndroid Build Coastguard Worker        self._rloc16 = dict['RLOC16']
1255*cfb92d14SAndroid Build Coastguard Worker        self._timeout = dict['Timeout']
1256*cfb92d14SAndroid Build Coastguard Worker        self._rx_on_idle = (dict['RxOnIdle'] == 'yes')
1257*cfb92d14SAndroid Build Coastguard Worker        self._ftd = (dict['FTD'] == 'yes')
1258*cfb92d14SAndroid Build Coastguard Worker        self._sec_data_req = (dict['SecDataReq'] == 'yes')
1259*cfb92d14SAndroid Build Coastguard Worker        self._full_net_data = (dict['FullNetData'] == 'yes')
1260*cfb92d14SAndroid Build Coastguard Worker
1261*cfb92d14SAndroid Build Coastguard Worker    @property
1262*cfb92d14SAndroid Build Coastguard Worker    def ext_address(self):
1263*cfb92d14SAndroid Build Coastguard Worker        return self._ext_address
1264*cfb92d14SAndroid Build Coastguard Worker
1265*cfb92d14SAndroid Build Coastguard Worker    @property
1266*cfb92d14SAndroid Build Coastguard Worker    def rloc16(self):
1267*cfb92d14SAndroid Build Coastguard Worker        return self._rloc16
1268*cfb92d14SAndroid Build Coastguard Worker
1269*cfb92d14SAndroid Build Coastguard Worker    @property
1270*cfb92d14SAndroid Build Coastguard Worker    def timeout(self):
1271*cfb92d14SAndroid Build Coastguard Worker        return self._timeout
1272*cfb92d14SAndroid Build Coastguard Worker
1273*cfb92d14SAndroid Build Coastguard Worker    def is_rx_on_when_idle(self):
1274*cfb92d14SAndroid Build Coastguard Worker        return self._rx_on_idle
1275*cfb92d14SAndroid Build Coastguard Worker
1276*cfb92d14SAndroid Build Coastguard Worker    def is_ftd(self):
1277*cfb92d14SAndroid Build Coastguard Worker        return self._ftd
1278*cfb92d14SAndroid Build Coastguard Worker
1279*cfb92d14SAndroid Build Coastguard Worker    def is_sec_data_req(self):
1280*cfb92d14SAndroid Build Coastguard Worker        return self._sec_data_req
1281*cfb92d14SAndroid Build Coastguard Worker
1282*cfb92d14SAndroid Build Coastguard Worker    def is_full_net_data(self):
1283*cfb92d14SAndroid Build Coastguard Worker        return self._full_net_data
1284*cfb92d14SAndroid Build Coastguard Worker
1285*cfb92d14SAndroid Build Coastguard Worker    def __repr__(self):
1286*cfb92d14SAndroid Build Coastguard Worker        return 'ChildEntry({})'.format(self.__dict__)
1287*cfb92d14SAndroid Build Coastguard Worker
1288*cfb92d14SAndroid Build Coastguard Worker
1289*cfb92d14SAndroid Build Coastguard Workerdef parse_child_table_result(child_table_list):
1290*cfb92d14SAndroid Build Coastguard Worker    """ Parses child table list string and returns an array of `ChildEntry` objects"""
1291*cfb92d14SAndroid Build Coastguard Worker    return [ChildEntry(item) for item in child_table_list.split('\n')[1:-1]]
1292*cfb92d14SAndroid Build Coastguard Worker
1293*cfb92d14SAndroid Build Coastguard Worker
1294*cfb92d14SAndroid Build Coastguard Worker# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1295*cfb92d14SAndroid Build Coastguard Worker
1296*cfb92d14SAndroid Build Coastguard Worker
1297*cfb92d14SAndroid Build Coastguard Workerclass NeighborEntry(object):
1298*cfb92d14SAndroid Build Coastguard Worker    """ This object encapsulates a neighbor entry"""
1299*cfb92d14SAndroid Build Coastguard Worker
1300*cfb92d14SAndroid Build Coastguard Worker    def __init__(self, text):
1301*cfb92d14SAndroid Build Coastguard Worker
1302*cfb92d14SAndroid Build Coastguard Worker        # Example of expected text:
1303*cfb92d14SAndroid Build Coastguard Worker        #
1304*cfb92d14SAndroid Build Coastguard Worker        # `\t"5AC95ED4646D6565, RLOC16:9403, LQIn:3, AveRssi:-20, LastRssi:-20, Age:0, LinkFC:8, MleFC:0, IsChild:yes,'
1305*cfb92d14SAndroid Build Coastguard Worker        # 'RxOnIdle:no, FTD:no, SecDataReq:yes, FullNetData:yes"'
1306*cfb92d14SAndroid Build Coastguard Worker        #
1307*cfb92d14SAndroid Build Coastguard Worker
1308*cfb92d14SAndroid Build Coastguard Worker        # We get rid of the first two chars `\t"' and last char '"', split the rest using whitespace as separator.
1309*cfb92d14SAndroid Build Coastguard Worker        # Then remove any ',' at end of items in the list.
1310*cfb92d14SAndroid Build Coastguard Worker        items = [item[:-1] if item[-1] == ',' else item for item in text[2:-1].split()]
1311*cfb92d14SAndroid Build Coastguard Worker
1312*cfb92d14SAndroid Build Coastguard Worker        # First item in the extended address
1313*cfb92d14SAndroid Build Coastguard Worker        self._ext_address = items[0]
1314*cfb92d14SAndroid Build Coastguard Worker
1315*cfb92d14SAndroid Build Coastguard Worker        # Convert the rest into a dictionary by splitting the text using ':' as
1316*cfb92d14SAndroid Build Coastguard Worker        # separator
1317*cfb92d14SAndroid Build Coastguard Worker        dict = {item.split(':')[0]: item.split(':')[1] for item in items[1:]}
1318*cfb92d14SAndroid Build Coastguard Worker
1319*cfb92d14SAndroid Build Coastguard Worker        self._rloc16 = dict['RLOC16']
1320*cfb92d14SAndroid Build Coastguard Worker        self._is_child = (dict['IsChild'] == 'yes')
1321*cfb92d14SAndroid Build Coastguard Worker        self._rx_on_idle = (dict['RxOnIdle'] == 'yes')
1322*cfb92d14SAndroid Build Coastguard Worker        self._ftd = (dict['FTD'] == 'yes')
1323*cfb92d14SAndroid Build Coastguard Worker
1324*cfb92d14SAndroid Build Coastguard Worker    @property
1325*cfb92d14SAndroid Build Coastguard Worker    def ext_address(self):
1326*cfb92d14SAndroid Build Coastguard Worker        return self._ext_address
1327*cfb92d14SAndroid Build Coastguard Worker
1328*cfb92d14SAndroid Build Coastguard Worker    @property
1329*cfb92d14SAndroid Build Coastguard Worker    def rloc16(self):
1330*cfb92d14SAndroid Build Coastguard Worker        return self._rloc16
1331*cfb92d14SAndroid Build Coastguard Worker
1332*cfb92d14SAndroid Build Coastguard Worker    def is_rx_on_when_idle(self):
1333*cfb92d14SAndroid Build Coastguard Worker        return self._rx_on_idle
1334*cfb92d14SAndroid Build Coastguard Worker
1335*cfb92d14SAndroid Build Coastguard Worker    def is_ftd(self):
1336*cfb92d14SAndroid Build Coastguard Worker        return self._ftd
1337*cfb92d14SAndroid Build Coastguard Worker
1338*cfb92d14SAndroid Build Coastguard Worker    def is_child(self):
1339*cfb92d14SAndroid Build Coastguard Worker        return self._is_child
1340*cfb92d14SAndroid Build Coastguard Worker
1341*cfb92d14SAndroid Build Coastguard Worker    def __repr__(self):
1342*cfb92d14SAndroid Build Coastguard Worker        return 'NeighborEntry({})'.format(self.__dict__)
1343*cfb92d14SAndroid Build Coastguard Worker
1344*cfb92d14SAndroid Build Coastguard Worker
1345*cfb92d14SAndroid Build Coastguard Workerdef parse_neighbor_table_result(neighbor_table_list):
1346*cfb92d14SAndroid Build Coastguard Worker    """ Parses neighbor table list string and returns an array of `NeighborEntry` objects"""
1347*cfb92d14SAndroid Build Coastguard Worker    return [NeighborEntry(item) for item in neighbor_table_list.split('\n')[1:-1]]
1348*cfb92d14SAndroid Build Coastguard Worker
1349*cfb92d14SAndroid Build Coastguard Worker
1350*cfb92d14SAndroid Build Coastguard Worker# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1351*cfb92d14SAndroid Build Coastguard Worker
1352*cfb92d14SAndroid Build Coastguard Worker
1353*cfb92d14SAndroid Build Coastguard Workerclass RouterTableEntry(object):
1354*cfb92d14SAndroid Build Coastguard Worker    """ This object encapsulates a router table entry"""
1355*cfb92d14SAndroid Build Coastguard Worker
1356*cfb92d14SAndroid Build Coastguard Worker    def __init__(self, text):
1357*cfb92d14SAndroid Build Coastguard Worker
1358*cfb92d14SAndroid Build Coastguard Worker        # Example of expected text:
1359*cfb92d14SAndroid Build Coastguard Worker        #
1360*cfb92d14SAndroid Build Coastguard Worker        # `\t"8A970B3251810826, RLOC16:4000, RouterId:16, NextHop:43, PathCost:1, LQIn:3, LQOut:3, Age:3, LinkEst:yes"`
1361*cfb92d14SAndroid Build Coastguard Worker        #
1362*cfb92d14SAndroid Build Coastguard Worker
1363*cfb92d14SAndroid Build Coastguard Worker        # We get rid of the first two chars `\t"' and last char '"', split the rest using whitespace as separator.
1364*cfb92d14SAndroid Build Coastguard Worker        # Then remove any ',' at end of items in the list.
1365*cfb92d14SAndroid Build Coastguard Worker        items = [item[:-1] if item[-1] == ',' else item for item in text[2:-1].split()]
1366*cfb92d14SAndroid Build Coastguard Worker
1367*cfb92d14SAndroid Build Coastguard Worker        # First item in the extended address
1368*cfb92d14SAndroid Build Coastguard Worker        self._ext_address = items[0]
1369*cfb92d14SAndroid Build Coastguard Worker
1370*cfb92d14SAndroid Build Coastguard Worker        # Convert the rest into a dictionary by splitting the text using ':' as
1371*cfb92d14SAndroid Build Coastguard Worker        # separator
1372*cfb92d14SAndroid Build Coastguard Worker        dict = {item.split(':')[0]: item.split(':')[1] for item in items[1:]}
1373*cfb92d14SAndroid Build Coastguard Worker
1374*cfb92d14SAndroid Build Coastguard Worker        self._rloc16 = int(dict['RLOC16'], 16)
1375*cfb92d14SAndroid Build Coastguard Worker        self._router_id = int(dict['RouterId'], 0)
1376*cfb92d14SAndroid Build Coastguard Worker        self._next_hop = int(dict['NextHop'], 0)
1377*cfb92d14SAndroid Build Coastguard Worker        self._path_cost = int(dict['PathCost'], 0)
1378*cfb92d14SAndroid Build Coastguard Worker        self._age = int(dict['Age'], 0)
1379*cfb92d14SAndroid Build Coastguard Worker        self._le = (dict['LinkEst'] == 'yes')
1380*cfb92d14SAndroid Build Coastguard Worker
1381*cfb92d14SAndroid Build Coastguard Worker    @property
1382*cfb92d14SAndroid Build Coastguard Worker    def ext_address(self):
1383*cfb92d14SAndroid Build Coastguard Worker        return self._ext_address
1384*cfb92d14SAndroid Build Coastguard Worker
1385*cfb92d14SAndroid Build Coastguard Worker    @property
1386*cfb92d14SAndroid Build Coastguard Worker    def rloc16(self):
1387*cfb92d14SAndroid Build Coastguard Worker        return self._rloc16
1388*cfb92d14SAndroid Build Coastguard Worker
1389*cfb92d14SAndroid Build Coastguard Worker    @property
1390*cfb92d14SAndroid Build Coastguard Worker    def router_id(self):
1391*cfb92d14SAndroid Build Coastguard Worker        return self._router_id
1392*cfb92d14SAndroid Build Coastguard Worker
1393*cfb92d14SAndroid Build Coastguard Worker    @property
1394*cfb92d14SAndroid Build Coastguard Worker    def next_hop(self):
1395*cfb92d14SAndroid Build Coastguard Worker        return self._next_hop
1396*cfb92d14SAndroid Build Coastguard Worker
1397*cfb92d14SAndroid Build Coastguard Worker    @property
1398*cfb92d14SAndroid Build Coastguard Worker    def path_cost(self):
1399*cfb92d14SAndroid Build Coastguard Worker        return self._path_cost
1400*cfb92d14SAndroid Build Coastguard Worker
1401*cfb92d14SAndroid Build Coastguard Worker    def is_link_established(self):
1402*cfb92d14SAndroid Build Coastguard Worker        return self._le
1403*cfb92d14SAndroid Build Coastguard Worker
1404*cfb92d14SAndroid Build Coastguard Worker    def __repr__(self):
1405*cfb92d14SAndroid Build Coastguard Worker        return 'RouterTableEntry({})'.format(self.__dict__)
1406*cfb92d14SAndroid Build Coastguard Worker
1407*cfb92d14SAndroid Build Coastguard Worker
1408*cfb92d14SAndroid Build Coastguard Workerdef parse_router_table_result(router_table_list):
1409*cfb92d14SAndroid Build Coastguard Worker    """ Parses router table list string and returns an array of `RouterTableEntry` objects"""
1410*cfb92d14SAndroid Build Coastguard Worker    return [RouterTableEntry(item) for item in router_table_list.split('\n')[1:-1]]
1411*cfb92d14SAndroid Build Coastguard Worker
1412*cfb92d14SAndroid Build Coastguard Worker
1413*cfb92d14SAndroid Build Coastguard Worker# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1414*cfb92d14SAndroid Build Coastguard Worker
1415*cfb92d14SAndroid Build Coastguard Worker
1416*cfb92d14SAndroid Build Coastguard Workerclass AddressCacheEntry(object):
1417*cfb92d14SAndroid Build Coastguard Worker    """ This object encapsulates an address cache entry"""
1418*cfb92d14SAndroid Build Coastguard Worker
1419*cfb92d14SAndroid Build Coastguard Worker    def __init__(self, text):
1420*cfb92d14SAndroid Build Coastguard Worker
1421*cfb92d14SAndroid Build Coastguard Worker        # Example of expected text:
1422*cfb92d14SAndroid Build Coastguard Worker        #
1423*cfb92d14SAndroid Build Coastguard Worker        # '\t"fd00:1234::100:8 -> 0xfffe, Age:1, State:query, CanEvict:no, Timeout:3, RetryDelay:15"`
1424*cfb92d14SAndroid Build Coastguard Worker        # '\t"fd00:1234::3:2 -> 0x2000, Age:0, State:cached, LastTrans:0, ML-EID:fd40:ea58:a88c:0:b7ab:4919:aa7b:11a3"`
1425*cfb92d14SAndroid Build Coastguard Worker
1426*cfb92d14SAndroid Build Coastguard Worker        # We get rid of the first two chars `\t"' and last char '"', split the rest using whitespace as separator.
1427*cfb92d14SAndroid Build Coastguard Worker        # Then remove any ',' at end of items in the list.
1428*cfb92d14SAndroid Build Coastguard Worker        items = [item[:-1] if item[-1] == ',' else item for item in text[2:-1].split()]
1429*cfb92d14SAndroid Build Coastguard Worker
1430*cfb92d14SAndroid Build Coastguard Worker        # First item in the extended address
1431*cfb92d14SAndroid Build Coastguard Worker        self._address = items[0]
1432*cfb92d14SAndroid Build Coastguard Worker        self._rloc16 = int(items[2], 16)
1433*cfb92d14SAndroid Build Coastguard Worker
1434*cfb92d14SAndroid Build Coastguard Worker        # Convert the rest into a dictionary by splitting the text using ':' as
1435*cfb92d14SAndroid Build Coastguard Worker        # separator
1436*cfb92d14SAndroid Build Coastguard Worker        dict = {item.split(':')[0]: item.split(':')[1] for item in items[3:]}
1437*cfb92d14SAndroid Build Coastguard Worker
1438*cfb92d14SAndroid Build Coastguard Worker        self._age = int(dict['Age'], 0)
1439*cfb92d14SAndroid Build Coastguard Worker
1440*cfb92d14SAndroid Build Coastguard Worker        self._state = dict['State']
1441*cfb92d14SAndroid Build Coastguard Worker
1442*cfb92d14SAndroid Build Coastguard Worker        if self._state == ADDRESS_CACHE_ENTRY_STATE_CACHED:
1443*cfb92d14SAndroid Build Coastguard Worker            self._last_trans = int(dict.get("LastTrans", "-1"), 0)
1444*cfb92d14SAndroid Build Coastguard Worker        else:
1445*cfb92d14SAndroid Build Coastguard Worker            self._can_evict = (dict['CanEvict'] == 'yes')
1446*cfb92d14SAndroid Build Coastguard Worker            self._timeout = int(dict['Timeout'])
1447*cfb92d14SAndroid Build Coastguard Worker            self._retry_delay = int(dict['RetryDelay'])
1448*cfb92d14SAndroid Build Coastguard Worker
1449*cfb92d14SAndroid Build Coastguard Worker    @property
1450*cfb92d14SAndroid Build Coastguard Worker    def address(self):
1451*cfb92d14SAndroid Build Coastguard Worker        return self._address
1452*cfb92d14SAndroid Build Coastguard Worker
1453*cfb92d14SAndroid Build Coastguard Worker    @property
1454*cfb92d14SAndroid Build Coastguard Worker    def rloc16(self):
1455*cfb92d14SAndroid Build Coastguard Worker        return self._rloc16
1456*cfb92d14SAndroid Build Coastguard Worker
1457*cfb92d14SAndroid Build Coastguard Worker    @property
1458*cfb92d14SAndroid Build Coastguard Worker    def age(self):
1459*cfb92d14SAndroid Build Coastguard Worker        return self._age
1460*cfb92d14SAndroid Build Coastguard Worker
1461*cfb92d14SAndroid Build Coastguard Worker    @property
1462*cfb92d14SAndroid Build Coastguard Worker    def state(self):
1463*cfb92d14SAndroid Build Coastguard Worker        return self._state
1464*cfb92d14SAndroid Build Coastguard Worker
1465*cfb92d14SAndroid Build Coastguard Worker    def can_evict(self):
1466*cfb92d14SAndroid Build Coastguard Worker        return self._can_evict
1467*cfb92d14SAndroid Build Coastguard Worker
1468*cfb92d14SAndroid Build Coastguard Worker    @property
1469*cfb92d14SAndroid Build Coastguard Worker    def timeout(self):
1470*cfb92d14SAndroid Build Coastguard Worker        return self._timeout
1471*cfb92d14SAndroid Build Coastguard Worker
1472*cfb92d14SAndroid Build Coastguard Worker    @property
1473*cfb92d14SAndroid Build Coastguard Worker    def retry_delay(self):
1474*cfb92d14SAndroid Build Coastguard Worker        return self._retry_delay
1475*cfb92d14SAndroid Build Coastguard Worker
1476*cfb92d14SAndroid Build Coastguard Worker    @property
1477*cfb92d14SAndroid Build Coastguard Worker    def last_trans(self):
1478*cfb92d14SAndroid Build Coastguard Worker        return self._last_trans
1479*cfb92d14SAndroid Build Coastguard Worker
1480*cfb92d14SAndroid Build Coastguard Worker    def __repr__(self):
1481*cfb92d14SAndroid Build Coastguard Worker        return 'AddressCacheEntry({})'.format(self.__dict__)
1482*cfb92d14SAndroid Build Coastguard Worker
1483*cfb92d14SAndroid Build Coastguard Worker
1484*cfb92d14SAndroid Build Coastguard Workerdef parse_address_cache_table_result(addr_cache_table_list):
1485*cfb92d14SAndroid Build Coastguard Worker    """ Parses address cache table list string and returns an array of `AddressCacheEntry` objects"""
1486*cfb92d14SAndroid Build Coastguard Worker    return [AddressCacheEntry(item) for item in addr_cache_table_list.split('\n')[1:-1]]
1487*cfb92d14SAndroid Build Coastguard Worker
1488*cfb92d14SAndroid Build Coastguard Worker
1489*cfb92d14SAndroid Build Coastguard Worker# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1490*cfb92d14SAndroid Build Coastguard Worker
1491*cfb92d14SAndroid Build Coastguard Worker
1492*cfb92d14SAndroid Build Coastguard Workerclass InterfaceRoute(object):
1493*cfb92d14SAndroid Build Coastguard Worker    """ This object encapsulates an interface route entry"""
1494*cfb92d14SAndroid Build Coastguard Worker
1495*cfb92d14SAndroid Build Coastguard Worker    def __init__(self, text):
1496*cfb92d14SAndroid Build Coastguard Worker
1497*cfb92d14SAndroid Build Coastguard Worker        # Example of expected text:
1498*cfb92d14SAndroid Build Coastguard Worker        #
1499*cfb92d14SAndroid Build Coastguard Worker        # '\t"fd00:abba::/64             metric:256   "'
1500*cfb92d14SAndroid Build Coastguard Worker        #
1501*cfb92d14SAndroid Build Coastguard Worker
1502*cfb92d14SAndroid Build Coastguard Worker        # We get rid of the first two chars `\t"' and last char '"', split the rest using whitespace as separator.
1503*cfb92d14SAndroid Build Coastguard Worker        # Then remove any ',' at end of items in the list.
1504*cfb92d14SAndroid Build Coastguard Worker        items = [item[:-1] if item[-1] == ',' else item for item in text[2:-1].split()]
1505*cfb92d14SAndroid Build Coastguard Worker
1506*cfb92d14SAndroid Build Coastguard Worker        # First item in the extended address
1507*cfb92d14SAndroid Build Coastguard Worker        self._route_prefix = items[0].split('/')[0]
1508*cfb92d14SAndroid Build Coastguard Worker        self._prefix_len = int(items[0].split('/')[1], 0)
1509*cfb92d14SAndroid Build Coastguard Worker        self._metric = int(items[1].split(':')[1], 0)
1510*cfb92d14SAndroid Build Coastguard Worker
1511*cfb92d14SAndroid Build Coastguard Worker    @property
1512*cfb92d14SAndroid Build Coastguard Worker    def route_prefix(self):
1513*cfb92d14SAndroid Build Coastguard Worker        return self._route_prefix
1514*cfb92d14SAndroid Build Coastguard Worker
1515*cfb92d14SAndroid Build Coastguard Worker    @property
1516*cfb92d14SAndroid Build Coastguard Worker    def prefix_len(self):
1517*cfb92d14SAndroid Build Coastguard Worker        return self._prefix_len
1518*cfb92d14SAndroid Build Coastguard Worker
1519*cfb92d14SAndroid Build Coastguard Worker    @property
1520*cfb92d14SAndroid Build Coastguard Worker    def metric(self):
1521*cfb92d14SAndroid Build Coastguard Worker        return self._metric
1522*cfb92d14SAndroid Build Coastguard Worker
1523*cfb92d14SAndroid Build Coastguard Worker    def __repr__(self):
1524*cfb92d14SAndroid Build Coastguard Worker        return 'InterfaceRoute({})'.format(self.__dict__)
1525*cfb92d14SAndroid Build Coastguard Worker
1526*cfb92d14SAndroid Build Coastguard Worker
1527*cfb92d14SAndroid Build Coastguard Workerdef parse_interface_routes_result(interface_routes_list):
1528*cfb92d14SAndroid Build Coastguard Worker    """ Parses interface routes list string and returns an array of `InterfaceRoute` objects"""
1529*cfb92d14SAndroid Build Coastguard Worker    return [InterfaceRoute(item) for item in interface_routes_list.split('\n')[1:-1]]
1530*cfb92d14SAndroid Build Coastguard Worker
1531*cfb92d14SAndroid Build Coastguard Worker
1532*cfb92d14SAndroid Build Coastguard Worker# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1533*cfb92d14SAndroid Build Coastguard Worker
1534*cfb92d14SAndroid Build Coastguard Worker
1535*cfb92d14SAndroid Build Coastguard Workerclass MultiRadioEntry(object):
1536*cfb92d14SAndroid Build Coastguard Worker    """ This object encapsulates a multi radio info entry"""
1537*cfb92d14SAndroid Build Coastguard Worker
1538*cfb92d14SAndroid Build Coastguard Worker    def __init__(self, text):
1539*cfb92d14SAndroid Build Coastguard Worker
1540*cfb92d14SAndroid Build Coastguard Worker        # Example of expected text:
1541*cfb92d14SAndroid Build Coastguard Worker        #
1542*cfb92d14SAndroid Build Coastguard Worker        # `\t"0EB758375B4976E7, RLOC16:f403, Radios:[IEEE_802_15_4(200), TREL_UDP6(255)]"`
1543*cfb92d14SAndroid Build Coastguard Worker        #
1544*cfb92d14SAndroid Build Coastguard Worker
1545*cfb92d14SAndroid Build Coastguard Worker        # We get rid of the first two chars `\t"' and last char '"', split the rest using whitespace as separator.
1546*cfb92d14SAndroid Build Coastguard Worker        # Then remove any ',' at end of items in the list.
1547*cfb92d14SAndroid Build Coastguard Worker        items = [item[:-1] if item[-1] == ',' else item for item in text[2:-1].split()]
1548*cfb92d14SAndroid Build Coastguard Worker
1549*cfb92d14SAndroid Build Coastguard Worker        # First item is the extended address
1550*cfb92d14SAndroid Build Coastguard Worker        self._ext_address = items[0]
1551*cfb92d14SAndroid Build Coastguard Worker
1552*cfb92d14SAndroid Build Coastguard Worker        # Second items is 'RLCO16:{rloc}'
1553*cfb92d14SAndroid Build Coastguard Worker        self._rloc16 = items[1].split(':')[1]
1554*cfb92d14SAndroid Build Coastguard Worker
1555*cfb92d14SAndroid Build Coastguard Worker        # Join back rest of items, split using ":" to get list of radios of form "[IEEE_802_15_4(200) TREL_UDP6(255)]"
1556*cfb92d14SAndroid Build Coastguard Worker        radios = " ".join(items[2:]).split(":")[1]
1557*cfb92d14SAndroid Build Coastguard Worker
1558*cfb92d14SAndroid Build Coastguard Worker        if radios != "[]":
1559*cfb92d14SAndroid Build Coastguard Worker            # Get rid of `[ and `]`, then split using " ",  then convert to dictionary mapping radio type
1560*cfb92d14SAndroid Build Coastguard Worker            # to its preference value.
1561*cfb92d14SAndroid Build Coastguard Worker            self._radios = {radio.split("(")[0]: radio.split("(")[1][:-1] for radio in radios[1:-1].split(' ')}
1562*cfb92d14SAndroid Build Coastguard Worker        else:
1563*cfb92d14SAndroid Build Coastguard Worker            self._radios = {}
1564*cfb92d14SAndroid Build Coastguard Worker
1565*cfb92d14SAndroid Build Coastguard Worker    @property
1566*cfb92d14SAndroid Build Coastguard Worker    def ext_address(self):
1567*cfb92d14SAndroid Build Coastguard Worker        return self._ext_address
1568*cfb92d14SAndroid Build Coastguard Worker
1569*cfb92d14SAndroid Build Coastguard Worker    @property
1570*cfb92d14SAndroid Build Coastguard Worker    def rloc16(self):
1571*cfb92d14SAndroid Build Coastguard Worker        return self._rloc16
1572*cfb92d14SAndroid Build Coastguard Worker
1573*cfb92d14SAndroid Build Coastguard Worker    @property
1574*cfb92d14SAndroid Build Coastguard Worker    def radios(self):
1575*cfb92d14SAndroid Build Coastguard Worker        return self._radios
1576*cfb92d14SAndroid Build Coastguard Worker
1577*cfb92d14SAndroid Build Coastguard Worker    def supports(self, radio_type):
1578*cfb92d14SAndroid Build Coastguard Worker        return radio_type in self._radios
1579*cfb92d14SAndroid Build Coastguard Worker
1580*cfb92d14SAndroid Build Coastguard Worker    def preference(self, radio_type):
1581*cfb92d14SAndroid Build Coastguard Worker        return int(self._radios[radio_type], 0) if self.supports(radio_type) else None
1582*cfb92d14SAndroid Build Coastguard Worker
1583*cfb92d14SAndroid Build Coastguard Worker    def __repr__(self):
1584*cfb92d14SAndroid Build Coastguard Worker        return 'MultiRadioEntry({})'.format(self.__dict__)
1585*cfb92d14SAndroid Build Coastguard Worker
1586*cfb92d14SAndroid Build Coastguard Worker
1587*cfb92d14SAndroid Build Coastguard Workerdef parse_multi_radio_result(multi_radio_list):
1588*cfb92d14SAndroid Build Coastguard Worker    """ Parses multi radio neighbor list string and returns an array of `MultiRadioEntry` objects"""
1589*cfb92d14SAndroid Build Coastguard Worker    return [MultiRadioEntry(item) for item in multi_radio_list.split('\n')[1:-1]]
1590