1010module_logger = logging .getLogger ('blockly.loader' )
1111
1212
13- # Elements of WiFi Port Records (wports )
14- wpUID = 0
15- wpName = 1
16- wpIP = 2
17- wpMAC = 3
18- wpLife = 4
13+ # Elements of Port Records (portRec )
14+ prUID = 0
15+ prName = 1
16+ prIP = 2
17+ prMAC = 3
18+ prLife = 4
1919
20- # Max lifetime+1 for WiFi Port Records to remain without refresh
21- MaxLife = 4
20+ # Max lifetime+1 for wired (w) and wifi (wf) Port Records to remain without refresh
21+ wMaxLife = 2
22+ wfMaxLife = 4
23+
24+ # Wi-Fi Record Headers
25+ wfNameHdr = "Name: '"
26+ wfIPHdr = "', IP: "
27+ wfMACHdr = ", MAC: "
2228
2329
2430class PropellerLoad :
2531 loading = False
26- # COM & WiFi-UID (unique name) ports list
32+ discovering = False
33+ # "Unique identifier" ports list
2734 ports = []
28- # WiFi Port Record list
29- wports = []
35+ # Port Record list- contains wired (UID) and wireless ports (UID, Name, IP, MAC)
36+ portRec = []
37+ # Lists for manipulation
38+ wnames = []
39+ wlnames = []
3040
3141
3242 def __init__ (self ):
@@ -61,39 +71,41 @@ def __init__(self):
6171
6272
6373 def get_ports (self ):
64- # Find COM/Wi-Fi serial ports
65- self .logger .info ('Received port list request' )
66-
67- # Return last results if we're currently downloading
68- if self .loading :
74+ # Search for wired/wireless serial ports
75+ # Return previous results if we're currently downloading to a port or discovering ports
76+ if self .loading or self .discovering :
6977 return self .ports
7078
71- self .logger .info ("Generating ports list" )
79+ self .logger .info ("Generating new ports list" )
80+ # Set discovering flag to prevent interruption
81+ self .discovering = True
7282
73- # Get COM ports
74- (success , out , err ) = loader (self , ["-P" ])
75- if success :
76- self .ports = out .splitlines ()
77- self .ports .sort (None , None , False )
78- else :
79- self .logger .debug ('COM Port request returned %s' , err )
83+ try :
84+ # Find wired & wireless serial ports
85+ (success , out , err ) = loader (self , ["-P" , "-W" ])
86+ # Process wired response
87+ if success :
88+ # Update port records (in self.portRec)
89+ updatePorts (self , out .splitlines ())
90+ # Extract unique port names (UID; from port records) and sort them alphabetically
91+ wnames = [wiredport [prUID ] for wiredport in self .portRec if wiredport [prName ] == "" ]
92+ wnames .sort (None , None , False )
93+ wlnames = [wirelessport [prUID ] for wirelessport in self .portRec if wirelessport [prName ] != "" ]
94+ wlnames .sort (None , None , False )
95+ # Assign to return list (with wired group on top, wireless group below) in a single step
96+ # to avoid partial results being used by parallel calling process
97+ self .ports = wnames + wlnames
98+ self .logger .debug ('Found %s ports' , len (self .ports ))
99+ else :
100+ # Error with external loader
101+ self .logger .error ('Serial port request returned %s' , err )
102+ self .ports = []
80103
81- # Get Wi-Fi ports
82- (success , out , err ) = loader (self , ["-W" ])
83- if success :
84- # Save Wi-Fi port records (in self.wports)
85- updateWiFiPorts (self , out .splitlines ())
86- # Extract unique Wi-Fi module names (UID; from Wi-Fi records) and sort them
87- wnames = [wifiports [wpUID ] for wifiports in self .wports ]
88- wnames .sort (None , None , False )
89- else :
90- self .logger .debug ('WiFi Port request returned %s' , err )
91-
92- # Append Wi-Fi ports to COM ports list
93- self .ports .extend (wnames )
94- self .logger .debug ('Found %s ports' , len (self .ports ))
104+ return self .ports
95105
96- return self .ports
106+ finally :
107+ # Done, clear discovering flag to process other events
108+ self .discovering = False
97109
98110
99111 def download (self , action , file_to_load , com_port ):
@@ -115,25 +127,25 @@ def download(self, action, file_to_load, com_port):
115127# # launch path is blank; try extracting from argv
116128# self.appdir = os.path.dirname(os.path.realpath(sys.argv[0]))
117129
118- # Set command download to RAM or EEPROM and to run afterward download
130+ # Set command to download to RAM or EEPROM and to run afterward download
119131 command = []
120132 if self .loaderAction [action ]["compile-options" ] != "" :
121133 # if RAM/EEPROM compile-option not empty, add it to the list
122134 command .extend ([self .loaderAction [action ]["compile-options" ]])
123135 command .extend (["-r" ])
124136
125- # Add requested port
137+ # Specify requested port
126138 if com_port is not None :
127- # Find port(s) named com_port
128- if com_port in [ wifiports [ wpUID ] for wifiports in self .wports ]:
129- # Found Wi-Fi match
130- idx = [ wifiports [ wpUID ] for wifiports in self . wports ]. index ( com_port )
131- IPAddr = [wifiports [ wpIP ] for wifiports in self . wports ][ idx ]
139+ # Determine port type and insert into command
140+ wlports = [ wirelessport for wirelessport in self .portRec if wirelessport [ prName ] != "" ]
141+ if com_port in wlports :
142+ # Found wireless port match
143+ IPAddr = [wirelessport [ prIP ] for wirelessport in wlports ][ wlports . index ( com_port ) ]
132144 self .logger .debug ('Requested port %s is at %s' , com_port , IPAddr )
133145 command .extend (["-i" ])
134146 command .extend ([IPAddr .encode ('ascii' , 'ignore' )])
135147 else :
136- # Not Wi-Fi match, should be COM port
148+ # Not wireless port match, should be wired port
137149 self .logger .debug ('Requested port is %s' , com_port )
138150 command .extend (["-p" ])
139151 command .extend ([com_port .encode ('ascii' , 'ignore' )])
@@ -190,33 +202,43 @@ def loader(self, cmdOptions):
190202 return False , '' , 'Exception: OSError'
191203
192204
193- def updateWiFiPorts (self , wstrings ):
194- # Merge wstrings into WiFi Ports list
205+ def updatePorts (self , strings ):
206+ # Merge strings into Port Record list
195207# Ensures unique entries (UIDs), updates existing entries, and removes ancient entries
196- # Records "age" with each update unless refreshed by a matching port; those older than MaxLife-1 are considered ancient
197- for newPort in wstrings :
198- # Search for MAC address in known ports
199- if not getWiFiMAC (newPort ) in [port [wpMAC ] for port in self .wports ]:
200- # No MAC match, enter as unique port record
201- enterUniqueWiFiPort (self , newPort )
202- else :
203- # Found MAC match, update record as necessary
204- idx = [port [wpMAC ] for port in self .wports ].index (getWiFiMAC (newPort ))
205- if self .wports [idx ][wpName ] == getWiFiName (newPort ):
206- # Name hasn't changed; leave Name and UID, update IP and Life
207- self .wports [idx ][wpIP ] = getWiFiIP (newPort )
208- self .wports [idx ][wpLife ] = MaxLife
208+ # Records "age" with each update unless refreshed by a matching port; those older than xMaxLife-1 are considered ancient
209+ for newPort in strings :
210+ if not isWiFiStr (newPort ):
211+ # Wired port- search for existing identifier
212+ if newPort in [port [prUID ] for port in self .portRec ]:
213+ # Found existing- just refresh life
214+ self .portRec [[port [prUID ] for port in self .portRec ].index (newPort )][prLife ] = wMaxLife
209215 else :
210- # Name has changed; replace entire record with guaranteed-unique entry
211- self .wports .pop (idx )
216+ # No match- create new entry (UID, n/a, n/a, n/a, MaxLife)
217+ self .portRec .append ([newPort , '' , '' , '' , wMaxLife ])
218+ else :
219+ # Wireless port- search for its MAC address within known ports
220+ if not getWiFiMAC (newPort ) in [port [prMAC ] for port in self .portRec ]:
221+ # No MAC match- enter as unique port record
212222 enterUniqueWiFiPort (self , newPort )
223+ else :
224+ # Found MAC match- update record as necessary
225+ idx = [port [prMAC ] for port in self .portRec ].index (getWiFiMAC (newPort ))
226+ if self .portRec [idx ][prName ] == getWiFiName (newPort ):
227+ # Name hasn't changed- leave Name and UID, update IP and Life
228+ self .portRec [idx ][prIP ] = getWiFiIP (newPort )
229+ self .portRec [idx ][prLife ] = wfMaxLife
230+ else :
231+ # Name has changed- replace entire record with guaranteed-unique entry
232+ self .portRec .pop (idx )
233+ enterUniqueWiFiPort (self , newPort )
234+
213235
214236 # Age records
215- for port in self .wports :
216- port [wpLife ] = port [wpLife ] - 1
237+ for port in self .portRec :
238+ port [prLife ] = port [prLife ] - 1
217239 # Remove ancients
218- while 0 in [port [wpLife ] for port in self .wports ]:
219- self .wports .pop ([port [wpLife ] for port in self .wports ].index (0 ))
240+ while 0 in [port [prLife ] for port in self .portRec ]:
241+ self .portRec .pop ([port [prLife ] for port in self .portRec ].index (0 ))
220242
221243
222244def enterUniqueWiFiPort (self , newPort ):
@@ -230,8 +252,8 @@ def enterUniqueWiFiPort(self, newPort):
230252
231253 # Check for unique name (UID)
232254 Size = 1
233- while UID in [port [wpUID ] for port in self .wports ]:
234- # Name is duplicate; modify for unique name
255+ while UID in [port [prUID ] for port in self .portRec ]:
256+ # Name is duplicate- modify for unique name
235257 UID = Name + Modifier [- Size :]
236258 Size += 1
237259 if Size == len (Modifier ):
@@ -240,9 +262,13 @@ def enterUniqueWiFiPort(self, newPort):
240262 Size = 0
241263
242264 # UID is unique, create new entry (UID, Name, IP, MAC, MaxLife)
243- self .wports .append ([UID , getWiFiName (newPort ), getWiFiIP (newPort ), getWiFiMAC (newPort ), MaxLife ])
265+ self .portRec .append ([UID , getWiFiName (newPort ), getWiFiIP (newPort ), getWiFiMAC (newPort ), wfMaxLife ])
266+
244267
245268
269+ def isWiFiStr (string ):
270+ # Return True if string is a Wi-Fi record string, False otherwise
271+ return (string .find (wfNameHdr ) > - 1 ) and (string .find (wfIPHdr ) > - 1 ) and (string .find (wfMACHdr ) > - 1 )
246272
247273
248274def isWiFiName (string , wifiName ):
@@ -252,17 +278,17 @@ def isWiFiName(string, wifiName):
252278
253279def getWiFiName (string ):
254280# Return Wi-Fi Module Name from string, or None if not found
255- return strBetween (string , "Name: '" , "', IP: " )
281+ return strBetween (string , wfNameHdr , wfIPHdr )
256282
257283
258284def getWiFiIP (string ):
259285# Return Wi-Fi Module IP address from string, or None if not found
260- return strBetween (string , "', IP: " , ", MAC: " )
286+ return strBetween (string , wfIPHdr , wfMACHdr )
261287
262288
263289def getWiFiMAC (string ):
264290# Return Wi-Fi Module MAC address from string, or None if not found
265- return strAfter (string , ", MAC: " )
291+ return strAfter (string , wfMACHdr )
266292
267293
268294def strBetween (string , startStr , endStr ):
0 commit comments