Skip to content

Commit 77d2dd6

Browse files
author
Developer
committed
Major refactoring to combine wired and wireless port history list into one. This allows Wi-Fi module names to be made unique among both wi-fi modules and wired ports as well. Also, simplified port discovery to a single call to PropLoader (instead of two separate calls) and created flag, try/finally block, and updated self.ports in just one place all to prevent the occasional parallel called server process from starting a port discovery process during another port discovery process and thus returning partial results to the browser. No more. Runs nice and clean!
1 parent f16d2bf commit 77d2dd6

File tree

2 files changed

+102
-76
lines changed

2 files changed

+102
-76
lines changed

BlocklyServer.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,15 @@ def index(self):
6262
def ports(self):
6363
cherrypy.response.headers['Access-Control-Allow-Origin'] = '*'
6464
self.queue.put((3, 'DEBUG', 'Port list retrieved'))
65-
self.logger.debug('Port list retreived')
65+
self.logger.debug('Port list request received')
6666

6767
ports = self.propellerLoad.get_ports()
6868
if len(ports) > 0:
6969
filtered_ports = []
7070
for port in ports:
71-
self.logger.debug('Port %s discovered.', port)
7271
# Filter out Bluetooth ports; they are risky to open and scan
7372
if ' bt ' not in port.lower() and 'bluetooth' not in port.lower():
73+
self.logger.debug('Port %s discovered.', port)
7474
filtered_ports.append(port)
7575
else:
7676
self.logger.debug("Port %s filtered from the list.", port)

PropellerLoad.py

Lines changed: 100 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,33 @@
1010
module_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

2430
class 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

222244
def 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

248274
def isWiFiName(string, wifiName):
@@ -252,17 +278,17 @@ def isWiFiName(string, wifiName):
252278

253279
def 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

258284
def 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

263289
def 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

268294
def strBetween(string, startStr, endStr):

0 commit comments

Comments
 (0)