|
12 | 12 |
|
13 | 13 | class PropellerLoad:
|
14 | 14 | loading = False
|
| 15 | + # COM & WiFi-Name ports list |
15 | 16 | ports = []
|
| 17 | + # Full WiFi ports list |
| 18 | + wports = [] |
| 19 | + |
16 | 20 |
|
17 | 21 | def __init__(self):
|
| 22 | + |
18 | 23 | self.logger = logging.getLogger('blockly.loader')
|
19 | 24 | self.logger.info('Creating loader logger.')
|
20 | 25 |
|
21 | 26 | # Find the path from which application was launched
|
22 | 27 | # 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__)) |
24 | 29 | if self.appdir == "" or self.appdir == "/":
|
25 |
| - # launch path is blank; try extracting from argv |
| 30 | + # launch path is blank; try extracting from argv |
26 | 31 | 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) |
28 | 33 |
|
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" |
34 | 39 | }
|
35 | 40 |
|
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"} |
39 | 44 | }
|
40 | 45 |
|
41 |
| - if not platform.system() in self.propeller_load_executables: |
| 46 | + if not platform.system() in self.loaderExe: |
42 | 47 | 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") |
44 | 49 | exit(1)
|
45 | 50 |
|
| 51 | + |
46 | 52 | 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') |
48 | 55 |
|
| 56 | + # Return last results if we're currently downloading |
49 | 57 | if self.loading:
|
50 | 58 | return self.ports
|
51 | 59 |
|
| 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 |
52 | 155 | if platform.system() == "Windows":
|
53 |
| - self.logger.info("Enumerating Windows ports") |
54 | 156 | startupinfo = subprocess.STARTUPINFO()
|
55 | 157 | 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) |
56 | 161 |
|
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 |
62 | 171 | else:
|
63 |
| - self.logger.info("Enumerating host ports") |
| 172 | + success = False |
64 | 173 |
|
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 '' |
67 | 175 |
|
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' |
70 | 180 |
|
71 |
| - def load(self, action, file_to_load, com_port): |
72 |
| - self.loading = True |
73 | 181 |
|
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])) |
81 | 182 |
|
82 |
| - executable = self.appdir + self.propeller_load_executables[platform.system()] |
83 |
| - self.logger.debug('Loader executable path is: %s)', executable) |
84 | 183 |
|
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) |
88 | 184 |
|
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')) |
93 | 185 |
|
94 |
| - executing_data.append(file_to_load.name.encode('ascii', 'ignore').replace('\\', '/')) |
95 | 186 |
|
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 |
98 | 190 |
|
99 |
| - try: |
100 |
| - if platform.system() == "Windows": |
101 |
| - startupinfo = subprocess.STARTUPINFO() |
102 |
| - startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW |
103 | 191 |
|
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: ") |
107 | 195 |
|
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) |
112 | 196 |
|
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: ") |
117 | 205 |
|
118 |
| - self.loading = False |
119 |
| - return success, out or '', err or '' |
120 | 206 |
|
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] |
123 | 218 |
|
124 | 219 |
|
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] |
0 commit comments