Skip to content

Commit 15b3fe0

Browse files
authored
Merge pull request #87 from PropGit/WiFi
Adding PropLoader (instead of Propeller-Load)
2 parents c0d9c6a + 58eb2fc commit 15b3fe0

11 files changed

+172
-77
lines changed

BlocklyPropClient.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
# Please verify that the version number in the local about.txt and the
3737
# ./package/win-resources/blocklypropclient-installer.iss matches this.
3838
# -----------------------------------------------------------------------
39-
VERSION = "0.5.4"
39+
VERSION = "0.6.2"
4040

4141

4242
# Enable logging for functions outside of the class definition

BlocklyServer.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ def load(self, action, binary, extension, comport=None):
105105

106106
self.logger.debug('Loading program to device.')
107107

108-
(success, out, err) = self.propellerLoad.load(action, binary_file, comport)
108+
(success, out, err) = self.propellerLoad.download(action, binary_file, comport)
109109
self.queue.put((10, 'INFO', 'Application loaded (%s)' % action))
110110

111111
self.logger.info('Application load complete.')

PropellerLoad.py

+168-73
Original file line numberDiff line numberDiff line change
@@ -12,121 +12,216 @@
1212

1313
class PropellerLoad:
1414
loading = False
15+
# COM & WiFi-Name ports list
1516
ports = []
17+
# Full WiFi ports list
18+
wports = []
19+
1620

1721
def __init__(self):
22+
1823
self.logger = logging.getLogger('blockly.loader')
1924
self.logger.info('Creating loader logger.')
2025

2126
# Find the path from which application was launched
2227
# realpath expands to full path if __file__ or sys.argv[0] contains just a filename
23-
self.appdir = os.path.dirname(os.path.realpath(__file__))
28+
self.appdir = os.path.dirname(os.path.realpath(__file__))
2429
if self.appdir == "" or self.appdir == "/":
25-
# launch path is blank; try extracting from argv
30+
# launch path is blank; try extracting from argv
2631
self.appdir = os.path.dirname(os.path.realpath(sys.argv[0]))
27-
self.logger.debug("PropellerLoad.py: Application running from: %s", self.appdir)
32+
self.logger.debug("Application running from: %s", self.appdir)
2833

29-
self.propeller_load_executables = {
30-
"Windows": "/propeller-tools/windows/propeller-load.exe",
31-
"Linux": "/propeller-tools/linux/propeller-load",
32-
"MacOS": "/propeller-tools/mac/propeller-load",
33-
"Darwin": "/propeller-tools/mac/propeller-load"
34+
self.loaderExe = {
35+
"Windows": "/propeller-tools/windows/proploader.exe",
36+
"Linux": "/propeller-tools/linux/proploader",
37+
"MacOS": "/propeller-tools/mac/proploader",
38+
"Darwin": "/propeller-tools/mac/proploader"
3439
}
3540

36-
self.load_actions = {
37-
"RAM": {"compile-options": []},
38-
"EEPROM": {"compile-options": ["-e"]}
41+
self.loaderAction = {
42+
"RAM": {"compile-options": ""},
43+
"EEPROM": {"compile-options": "-e"}
3944
}
4045

41-
if not platform.system() in self.propeller_load_executables:
46+
if not platform.system() in self.loaderExe:
4247
self.logger.error('The %s platform is not supported at this time.', platform.system())
43-
print("Unsupported", platform.system() + " is currently unsupported")
48+
print(platform.system() + " is currently unsupported")
4449
exit(1)
4550

51+
4652
def get_ports(self):
47-
self.logger.info('Getting ports')
53+
# Find COM/Wi-Fi serial ports
54+
self.logger.info('Received port list request')
4855

56+
# Return last results if we're currently downloading
4957
if self.loading:
5058
return self.ports
5159

60+
self.logger.info("Generating ports list")
61+
62+
# Get COM ports
63+
(success, out, err) = loader(self, ["-P"])
64+
if success:
65+
self.ports = out.splitlines()
66+
else:
67+
self.logger.debug('COM Port request returned %s', err)
68+
69+
# Get Wi-Fi ports
70+
(success, out, err) = loader(self, ["-W"])
71+
if success:
72+
# Save Wi-Fi port record(s)
73+
self.wports = out.splitlines()
74+
# Extract Wi-Fi module names (from Wi-Fi records) and sort them
75+
wnames = []
76+
for i in range(len(self.wports)):
77+
wnames.extend([getWiFiName(self.wports[i])])
78+
wnames.sort(None, None, False)
79+
else:
80+
self.logger.debug('WiFi Port request returned %s', err)
81+
82+
# Append Wi-Fi ports to COM ports list
83+
self.ports.extend(wnames)
84+
self.logger.debug('Found %s ports', len(self.ports))
85+
86+
return self.ports
87+
88+
89+
def download(self, action, file_to_load, com_port):
90+
# Download application to Propeller
91+
# Set loading flag to prevent interruption
92+
self.loading = True
93+
94+
try:
95+
# Patch - see if __init__ is back in full operation
96+
if not self.appdir or self.appdir == "" or self.appdir == "/":
97+
self.logger.info('ERROR: LOADER FOLDER NOT FOUND!')
98+
return False, ' ', ' '
99+
# Patch below removed temporarily during platform testing
100+
# # Patch until we figure out why the __init__ is not getting called
101+
# if not self.appdir or self.appdir == "" or self.appdir == "/":
102+
# # realpath expands to full path if __file__ or sys.argv[0] contains just a filename
103+
# self.appdir = os.path.dirname(os.path.realpath(__file__))
104+
# if self.appdir == "" or self.appdir == "/":
105+
# # launch path is blank; try extracting from argv
106+
# self.appdir = os.path.dirname(os.path.realpath(sys.argv[0]))
107+
108+
# Set command download to RAM or EEPROM and to run afterward download
109+
command = []
110+
if self.loaderAction[action]["compile-options"] != "":
111+
# if RAM/EEPROM compile-option not empty, add it to the list
112+
command.extend([self.loaderAction[action]["compile-options"]])
113+
command.extend(["-r"])
114+
115+
# Add requested port
116+
if com_port is not None:
117+
# Find port(s) named com_port
118+
targetWiFi = [l for l in self.wports if isWiFiName(l, com_port)]
119+
if len(targetWiFi) > 0:
120+
# Found Wi-Fi match
121+
self.logger.debug('Requested port %s is at %s', com_port, getWiFiIP(targetWiFi[0]))
122+
command.extend(["-i"])
123+
command.extend([getWiFiIP(targetWiFi[0]).encode('ascii', 'ignore')])
124+
else:
125+
# Not Wi-Fi match, should be COM port
126+
self.logger.debug('Requested port is %s', com_port)
127+
command.extend(["-p"])
128+
command.extend([com_port.encode('ascii', 'ignore')])
129+
130+
# Add target file
131+
command.extend([file_to_load.name.encode('ascii', 'ignore').replace('\\', '/')])
132+
133+
# Download
134+
(success, out, err) = loader(self, command)
135+
136+
# Return results
137+
return success, out or '', err or ''
138+
139+
finally:
140+
# Done, clear loading flag to process other events
141+
self.loading = False
142+
143+
144+
def loader(self, cmdOptions):
145+
# Launch Propeller Loader with cmdOptions and return True/False, output and error string
146+
# cmdOptions must be a list
147+
try:
148+
# Form complete command line as a list: [path+exe, option {,more_options...} {, filename}]
149+
cmdLine = [self.appdir + self.loaderExe[platform.system()]]
150+
cmdLine.extend(cmdOptions)
151+
152+
self.logger.debug('Running loader command: %s', cmdLine)
153+
154+
# Run command
52155
if platform.system() == "Windows":
53-
self.logger.info("Enumerating Windows ports")
54156
startupinfo = subprocess.STARTUPINFO()
55157
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
158+
process = subprocess.Popen(cmdLine, stdout=subprocess.PIPE, stderr=subprocess.PIPE, startupinfo=startupinfo)
159+
else:
160+
process = subprocess.Popen(cmdLine, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
56161

57-
process = subprocess.Popen([self.appdir + self.propeller_load_executables[platform.system()], "-P"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, startupinfo=startupinfo)
58-
out, err = process.communicate()
59-
self.logger.debug('Loader complete: Error code %s returned.', err)
60-
self.ports = out.splitlines()
61-
return self.ports
162+
out, err = process.communicate()
163+
164+
# If error, log extra error detail
165+
if process.returncode:
166+
self.logger.error("Error result: %s - %s", process.returncode, err)
167+
self.logger.debug("Loader response: %s", out)
168+
169+
if process.returncode == 0:
170+
success = True
62171
else:
63-
self.logger.info("Enumerating host ports")
172+
success = False
64173

65-
ports = [port for (port, driver, usb) in list_ports.comports()]
66-
self.logger.debug('Port count: %s', len(ports))
174+
return success, out or '', err or ''
67175

68-
self.ports = ports
69-
return ports
176+
except OSError as ex:
177+
# Exception; log error and return fail status
178+
self.logger.error("%s", ex.message)
179+
return False, '', 'Exception: OSError'
70180

71-
def load(self, action, file_to_load, com_port):
72-
self.loading = True
73181

74-
# Patch until we figure out why the __init__ is not getting called
75-
if not self.appdir or self.appdir == '':
76-
# realpath expands to full path if __file__ or sys.argv[0] contains just a filename
77-
self.appdir = os.path.dirname(os.path.realpath(__file__))
78-
if self.appdir == "" or self.appdir == "/":
79-
# launch path is blank; try extracting from argv
80-
self.appdir = os.path.dirname(os.path.realpath(sys.argv[0]))
81182

82-
executable = self.appdir + self.propeller_load_executables[platform.system()]
83-
self.logger.debug('Loader executable path is: %s)', executable)
84183

85-
executing_data = [executable, "-r"]
86-
executing_data.extend(self.load_actions[action]["compile-options"])
87-
self.logger.debug('Loader commandline is: %s', executing_data)
88184

89-
if com_port is not None:
90-
self.logger.info("Talking to com port.")
91-
executing_data.append("-p")
92-
executing_data.append(com_port.encode('ascii', 'ignore'))
93185

94-
executing_data.append(file_to_load.name.encode('ascii', 'ignore').replace('\\', '/'))
95186

96-
print(executing_data)
97-
self.logger.info("Executing process %s", executing_data)
187+
def isWiFiName(string, wifiName):
188+
# Return True if string contains Wi-Fi Module record named wifiName
189+
return getWiFiName(string) == wifiName
98190

99-
try:
100-
if platform.system() == "Windows":
101-
startupinfo = subprocess.STARTUPINFO()
102-
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
103191

104-
process = subprocess.Popen(executing_data, stdout=subprocess.PIPE, stderr=subprocess.PIPE, startupinfo=startupinfo)
105-
else:
106-
process = subprocess.Popen(executing_data, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
192+
def getWiFiName(string):
193+
# Return Wi-Fi Module Name from string, or None if not found
194+
return strBetween(string, "Name: '", "', IP: ")
107195

108-
out, err = process.communicate()
109-
self.logger.info("Load result is: %s", process.returncode)
110-
self.logger.debug("Load error string: %s", err)
111-
self.logger.debug("Load output string: %s", out)
112196

113-
if process.returncode == 0:
114-
success = True
115-
else:
116-
success = False
197+
def getWiFiIP(string):
198+
# Return Wi-Fi Module IP address from string, or None if not found
199+
return strBetween(string, "', IP: ", ", MAC: ")
200+
201+
202+
def getWiFiMAC(string):
203+
# Return Wi-Fi Module MAC address from string, or None if not found
204+
return strAfter(string, ", MAC: ")
117205

118-
self.loading = False
119-
return success, out or '', err or ''
120206

121-
except OSError as ex:
122-
self.logger.error("%s", ex.message)
207+
def strBetween(string, startStr, endStr):
208+
# Return substring from string in between startStr and endStr, or None if no match
209+
# Find startStr
210+
sPos = string.find(startStr)
211+
if sPos == -1: return None
212+
sPos += len(startStr)
213+
# Find endStr
214+
ePos = string.find(endStr, sPos)
215+
if ePos == -1: return None
216+
# Return middle
217+
return string[sPos:ePos]
123218

124219

125-
def resource_path(relative):
126-
return os.path.join(
127-
os.environ.get(
128-
"_MEIPASS2",
129-
os.path.abspath(".")
130-
),
131-
relative
132-
)
220+
def strAfter(string, startStr):
221+
# Return substring from string after startStr, or None if no match
222+
# Find startStr
223+
sPos = string.find(startStr)
224+
if sPos == -1: return None
225+
sPos += len(startStr)
226+
# Return string after
227+
return string[sPos:-1]

about.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Version: v0.5.4
1+
Version: v0.6.2
22

33
Contributors:
44
- Michel Lampo

package/blocklypropclient-installer.iss

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
33

44
#define MyAppName "BlocklyPropClient"
5-
#define MyAppVersion "0.5.4"
5+
#define MyAppVersion "0.6.2"
66
#define MyAppPublisher "Parallax Inc."
77
#define MyAppURL "http://blockly.parallax.com/"
88
#define MyAppExeName "BlocklyPropClient.exe"

propeller-tools/linux/propeller-load

-86.3 KB
Binary file not shown.

propeller-tools/linux/proploader

134 KB
Binary file not shown.

propeller-tools/mac/propeller-load

-116 KB
Binary file not shown.

propeller-tools/mac/proploader

125 KB
Binary file not shown.
-131 KB
Binary file not shown.
1.78 MB
Binary file not shown.

0 commit comments

Comments
 (0)