aos-cube
Advanced tools
+130
| import os, sys | ||
| import subprocess | ||
| import shutil | ||
| import traceback | ||
| from aos.util import log, error, is_domestic | ||
| ## Download externals | ||
| def check_download_require(require, config_file): | ||
| """ Check download require """ | ||
| if not os.path.isfile(config_file): | ||
| return False | ||
| with open(config_file, "r") as f: | ||
| for line in f.readlines(): | ||
| line = line.strip() | ||
| if require == line: | ||
| return True | ||
| return False | ||
| def download_externals(externals, source_root, config_file): | ||
| """ Download external repos """ | ||
| tmpdir = 'tmp_{0:02x}'.format(ord(os.urandom(1))) | ||
| while os.path.isdir(tmpdir): | ||
| tmpdir = 'tmp_{0:02x}'.format(ord(os.urandom(1))) | ||
| try: | ||
| os.mkdir(tmpdir) | ||
| except: | ||
| error("Can not create temp folder %s" % tmpdir) | ||
| try: | ||
| os.chdir(tmpdir) | ||
| for key in externals: | ||
| result = 0 | ||
| t = externals[key] | ||
| name = t["name"] | ||
| gittag = t["gittag"] | ||
| destdir = t["destdir"] | ||
| postscript = t["postscript"] | ||
| require = t["require"] | ||
| destdir = os.path.join(source_root, destdir) | ||
| if os.path.exists(destdir): | ||
| return | ||
| giturl = t["giturl"]["github"] | ||
| if is_domestic(): | ||
| if "gitee" in t["giturl"] and t["giturl"]["gitee"] != "": | ||
| giturl = t["giturl"]["gitee"] | ||
| # Don't download if require is not meet | ||
| if require != "": | ||
| if not check_download_require(require, config_file): | ||
| continue | ||
| print('Downloading %s: %s -> %s ...' % (name, giturl, destdir)) | ||
| cmd = "git clone %s %s" % (giturl, name) | ||
| if gittag != "": | ||
| cmd = "git clone -b %s %s %s" % (gittag, giturl, name) | ||
| result += subprocess.call(cmd, shell=True) | ||
| if result > 0: | ||
| print('git clone %s failed' % name) | ||
| print('You can mannually try fix this problem by running:') | ||
| print(' %s' % cmd) | ||
| print(' mv %s %s && rm -rf %s' % (name, destdir, name)) | ||
| srcdir = name | ||
| if os.path.exists(srcdir) == False: | ||
| print('The folder %s is not exist' % (srcdir)) | ||
| result += 1 | ||
| if result == 0: | ||
| if os.path.isfile(destdir): | ||
| os.remove(destdir) | ||
| if os.path.isdir(destdir): | ||
| shutil.rmtree(destdir) | ||
| if not os.path.isdir(os.path.dirname(destdir)): | ||
| os.makedirs(os.path.dirname(destdir)) | ||
| shutil.move(srcdir, destdir) | ||
| print('Download %s succeed' % name) | ||
| else: | ||
| print('Download %s failed' % name) | ||
| # Run post scripts | ||
| ret = 0 | ||
| if postscript != "": | ||
| cmd = "python %s/%s" % (source_root, postscript) | ||
| ret += subprocess.call(cmd, shell=True) | ||
| if ret > 0: | ||
| print("Run post script failed: %s" % cmd) | ||
| except: | ||
| traceback.print_exc() | ||
| finally: | ||
| os.chdir('../') | ||
| try: | ||
| shutil.rmtree(tmpdir) | ||
| except: | ||
| print("Can not remove temp folder %s, please remove it manually" % tmpdir) | ||
| def install_externals(source_root, app_root=None): | ||
| """ Download externals according to build/external_config.py """ | ||
| auto_dl = False | ||
| externals = {} | ||
| external_config = os.path.join(source_root, "build/external_config.py") | ||
| if os.path.isfile(external_config): | ||
| sys.path.append(os.path.dirname(external_config)) | ||
| try: | ||
| import external_config as ec | ||
| except: | ||
| error("Import external configs failed") | ||
| if ec.auto_download == "yes": | ||
| auto_dl = True | ||
| externals = ec.externals | ||
| if not auto_dl: | ||
| return | ||
| build_config = os.path.join(source_root, ".config") | ||
| if app_root: | ||
| build_config = os.path.join(app_root, ".config") | ||
| download_externals(externals, source_root, build_config) |
| [console_scripts] | ||
| aos = aos.__main__:main | ||
| aos-cube = aos.__main__:main | ||
| udc = udc.udc:main | ||
| Metadata-Version: 1.2 | ||
| Name: aos-cube | ||
| Version: 0.3.7 | ||
| Version: 0.3.8 | ||
| Summary: aos commmand line tool for AliOS-Things development. | ||
@@ -191,2 +191,2 @@ Home-page: UNKNOWN | ||
| Platform: UNKNOWN | ||
| Requires-Python: >=2.7, <3 | ||
| Requires-Python: >=2.7 |
@@ -10,5 +10,4 @@ LICENSE | ||
| aos/constant.py | ||
| aos/download.py | ||
| aos/ota_upload.py | ||
| aos/program.py | ||
| aos/repo.py | ||
| aos/util.py | ||
@@ -34,13 +33,2 @@ aos/commands/__init__.py | ||
| aos_cube.egg-info/requires.txt | ||
| aos_cube.egg-info/top_level.txt | ||
| udc/__init__.py | ||
| udc/client.py | ||
| udc/controller_certificate.pem | ||
| udc/packet.py | ||
| udc/terminal.py | ||
| udc/udc.py | ||
| udc/board/eml3047/eml3047.py | ||
| udc/board/esp32/esp32.py | ||
| udc/board/mk3060/mk3060.py | ||
| udc/board/mk3060/mk3060_firmware_update.py | ||
| udc/board/stm32_nucleo/stm32_nucleo.py | ||
| aos_cube.egg-info/top_level.txt |
| aos | ||
| udc |
+4
-4
| import sys | ||
| __version__ = "0.3.7" | ||
| __version__ = "0.3.8" | ||
| __title__ = "aos-cube" | ||
@@ -15,5 +15,5 @@ __description__ = ( | ||
| if sys.version_info < (2, 7, 0) or sys.version_info >= (3, 0, 0): | ||
| msg = ("aos cube is compatible with Python version >= 2.7 and < 3.0\n") | ||
| sys.stderr.write(msg % (__version__, sys.version)) | ||
| if sys.version_info < (2, 7, 0): | ||
| msg = ("aos cube is compatible with Python version >= 2.7\n") | ||
| sys.stderr.write(msg) | ||
| sys.exit(1) |
+11
-2
@@ -6,2 +6,3 @@ import os, sys | ||
| from imp import reload | ||
| from aos import __version__, __email__ | ||
@@ -79,5 +80,13 @@ from aos.util import error, ProcessException | ||
| def main(): | ||
| reload(sys) | ||
| sys.setdefaultencoding('UTF8') | ||
| try: | ||
| reload(sys) | ||
| sys.setdefaultencoding('UTF8') | ||
| except: | ||
| pass | ||
| # Convert the "\\" to "/" on Windows for AOS_SDK_PATH | ||
| aos_sdk_path = os.environ.get("AOS_SDK_PATH") | ||
| if aos_sdk_path: | ||
| os.environ["AOS_SDK_PATH"] = aos_sdk_path.replace("\\", "/") | ||
| ret = 0 | ||
@@ -84,0 +93,0 @@ try: |
+1
-107
@@ -21,5 +21,4 @@ #!/usr/bin/env python | ||
| from aos.util import * | ||
| from aos.repo import * | ||
| from aos.managers.comp import CompManager | ||
| import ConfigParser | ||
| import configparser | ||
@@ -31,106 +30,2 @@ @click.group(short_help="Component Manager") | ||
| # | ||
| # Functions | ||
| # | ||
| def print_component_status(json_format): | ||
| cwd_type = Repo().pathtype() | ||
| os_name = OS_NAME | ||
| if cwd_type == 'program': | ||
| pd = Program(os.getcwd()) | ||
| aos_path = pd.get_cfg(OS_PATH) | ||
| aos_remote_components = get_aos_components(pd.get_cfg(REMOTE_PATH)) | ||
| os_name = 'aos' | ||
| else: | ||
| if os.path.isdir("kernel/rhino"): | ||
| aos_path = os.getcwd() | ||
| else: | ||
| aos_path = Global().get_cfg(AOS_SDK_PATH) | ||
| aos_local_components = get_aos_components(aos_path) | ||
| if json_format: | ||
| json_components_dict = {} | ||
| if cwd_type == 'program': | ||
| pd = Program(os.getcwd()) | ||
| with cd(pd.get_cfg(PROGRAM_PATH)): | ||
| cube_add_components = None | ||
| cube_remove_components = None | ||
| with open(CUBE_MAKEFILE, 'r') as fin: | ||
| for line in fin: | ||
| if line.startswith('CUBE_ADD_COMPONENTS :='): | ||
| cube_add_components = re.split('\s+', line[23:].strip()) | ||
| if line.startswith('CUBE_REMOVE_COMPONENTS :='): | ||
| cube_remove_components = re.split('\s+', line[26:].strip()) | ||
| for path, name in aos_remote_components.items(): | ||
| cube_add = False | ||
| cube_remove = False | ||
| rel_path = os.path.dirname(relpath(pd.get_cfg(PROGRAM_PATH), path)) | ||
| if cube_add_components and os.path.dirname(rel_path) in cube_add_components: | ||
| cube_add = True | ||
| if cube_remove_components and os.path.dirname(rel_path) in cube_remove_components: | ||
| cube_remove = True | ||
| json_components_dict[rel_path] = {'name': name, 'cube_add': cube_add, 'cube_remove': cube_remove} | ||
| for path, name in aos_local_components.items(): | ||
| cube_add = False | ||
| cube_remove = False | ||
| rel_aos_path = os.path.dirname(relpath(aos_path, path)) | ||
| rel_path = os.path.join(os_name, rel_aos_path).replace(os.path.sep, '/') | ||
| if cube_add_components and rel_aos_path in cube_add_components: | ||
| cube_add = True | ||
| if cube_remove_components and rel_aos_path in cube_remove_components: | ||
| cube_remove = True | ||
| json_components_dict[rel_path] = {'name': name, 'cube_add': cube_add, 'cube_remove': cube_remove} | ||
| else: | ||
| cube_add = False | ||
| cube_remove = False | ||
| for path, name in aos_local_components.items(): | ||
| json_components_dict[os.path.dirname(relpath(os.path.dirname(aos_path), path))] = {'name': name, 'cube_add': cube_add, 'cube_remove': cube_remove} | ||
| print(json.dumps(json_components_dict, indent=4, sort_keys=True)) | ||
| else: | ||
| if cwd_type != 'program': | ||
| print('\nCurrent directory isn\'t AliOS-Things program, list AOS_SDK_PATH components.') | ||
| print("\n AliOS-Things COMPONENTS ") | ||
| print('|===================================================================================================================|') | ||
| print('| %-30s | %-80s |' % ('NAME', 'LOCATION')) | ||
| if cwd_type == 'program': | ||
| program_path = Program(os.getcwd()).get_cfg(PROGRAM_PATH) | ||
| for path, name in aos_remote_components.items(): | ||
| print('| %-30s | %-80s |' % (name, os.path.dirname(relpath(program_path, path)))) | ||
| for path, name in aos_local_components.items(): | ||
| print('| %-30s | %-80s |' % (name, os.path.dirname(relpath(os.path.dirname(aos_path), path)))) | ||
| print('|===================================================================================================================|') | ||
| def get_aos_components(aos_path): | ||
| makefile_dict = {} | ||
| if os.path.isdir(aos_path): | ||
| for (dir_path, dir_names, file_names) in os.walk(aos_path): | ||
| for f in file_names: | ||
| if ('out' not in dir_path) and ('build' not in dir_path) and ('tools/codesync' not in dir_path) and f.endswith('.mk'): | ||
| makefile_dict[os.path.join(dir_path, f)] = f[:-3] | ||
| else: | ||
| error('Find components dir is empty!') | ||
| aos_components_dict = {} | ||
| for path, name in makefile_dict.items(): | ||
| with open(path, 'r') as f: | ||
| s = f.read() | ||
| component_name = re.findall('^\s*NAME\s*:?=\s*(\S+)\s*\n', s) | ||
| if len(component_name) == 1: | ||
| aos_components_dict[path.replace(os.path.sep, '/')] = component_name[0] | ||
| return aos_components_dict | ||
| # List command | ||
@@ -144,3 +39,2 @@ @cli.command("list", short_help="List installed components") | ||
| def list_component(cm, components, json_output, remote, showduplicates): | ||
| #print_component_status(json_output) | ||
| args = [] | ||
@@ -147,0 +41,0 @@ if showduplicates: |
@@ -86,3 +86,3 @@ import os, sys | ||
| from board_debug import aos_debug | ||
| except Exception, err: | ||
| except Exception as err: | ||
| info(err) | ||
@@ -95,3 +95,3 @@ info(Exception) | ||
| from scons_debug import aos_debug | ||
| except Exception, err: | ||
| except Exception as err: | ||
| info(err) | ||
@@ -98,0 +98,0 @@ info(Exception) |
@@ -8,3 +8,3 @@ import os | ||
| from aos.util import update_config_in | ||
| import ConfigParser | ||
| import configparser | ||
@@ -78,3 +78,3 @@ def validate_boards(ctx, param, value): | ||
| cf = ConfigParser.ConfigParser() | ||
| cf = configparser.ConfigParser() | ||
| cf.add_section("global") | ||
@@ -81,0 +81,0 @@ cf.set("global", "project_dir", project_dir) |
+21
-5
@@ -1,2 +0,4 @@ | ||
| import os, sys | ||
| import os | ||
| import sys | ||
| import re | ||
| import subprocess | ||
@@ -7,5 +9,5 @@ import shutil | ||
| import click | ||
| from aos.util import log, error, cd_aos_root, is_domestic, popen, get_board_dir, Config | ||
| from aos.program import * | ||
| from aos.util import * | ||
| from aos.constant import APP_CONFIG, APP_INCLUDES, APP_UPDATE_MKFILE, APP_GEN_INCLUDES | ||
| from aos.download import install_externals | ||
@@ -247,4 +249,9 @@ # Make command | ||
| if os.path.isfile(config_file): | ||
| ac = Config(config_file) | ||
| aos_sdk_path = ac.get("AOS_SDK_PATH") | ||
| aos_sdk_path = os.environ.get("AOS_SDK_PATH") | ||
| if not aos_sdk_path: | ||
| error("No AliOS SDK installed") | ||
| if not os.path.isdir(aos_sdk_path): | ||
| error("Can't access AOS_SDK_PATH, no such directory: %s" % aos_sdk_path) | ||
| source_root = "SOURCE_ROOT=%s" % aos_sdk_path | ||
@@ -292,2 +299,4 @@ | ||
| app_root = "" | ||
| download_externals = True | ||
| download_toolchain = True | ||
@@ -303,4 +312,11 @@ target_no_toolchain = [".config", ".menuconfig", "clean", "distclean", "help", "export-keil", "export-iar", "_defconfig"] | ||
| download_toolchain = False | ||
| download_externals = False | ||
| break | ||
| if download_externals: | ||
| if app_root: | ||
| install_externals(source_root, app_root) | ||
| else: | ||
| install_externals(source_root) | ||
| if download_toolchain: | ||
@@ -307,0 +323,0 @@ if app_root: |
@@ -8,3 +8,3 @@ import os, sys | ||
| from aos.managers.comp import CompManager | ||
| import ConfigParser | ||
| import configparser | ||
@@ -11,0 +11,0 @@ @click.group(short_help="OTA firmware management tool") |
@@ -83,3 +83,3 @@ import os, sys | ||
| from board_upload import aos_upload | ||
| except Exception, err: | ||
| except Exception as err: | ||
| info(err) | ||
@@ -92,3 +92,3 @@ info(Exception) | ||
| from scons_upload import aos_upload | ||
| except Exception, err: | ||
| except Exception as err: | ||
| info(err) | ||
@@ -95,0 +95,0 @@ info(Exception) |
+3
-3
| import re | ||
| from util import * | ||
| import click | ||
| import ConfigParser | ||
| import configparser | ||
| from aos.util import * | ||
@@ -106,3 +106,3 @@ # Cfg classed used for handling the config backend | ||
| self.conf_file = conf_file | ||
| self.conf = ConfigParser.ConfigParser() | ||
| self.conf = configparser.ConfigParser() | ||
| self.conf.read(self.conf_file) | ||
@@ -109,0 +109,0 @@ |
+0
-26
@@ -143,26 +143,2 @@ import os | ||
| # reference to local (unpublished) repo - dir#rev | ||
| regex_local_ref = r'^([\w.+-][\w./+-]*?)/?(?:#(.*))?$' | ||
| # reference to repo - url#rev | ||
| regex_url_ref = r'^(.*/([\w.+-]+)(?:\.\w+)?)/?(?:#(.*?)?)?$' | ||
| # git url (no #rev) | ||
| regex_git_url = r'^(git\://|ssh\://|https?\://|)(([^/:@]+)(\:([^/:@]+))?@)?([^/:]+)[:/](.+?)(\.git|\/?)$' | ||
| # hg url (no #rev) | ||
| regex_hg_url = r'^(file|ssh|https?)://([^/:]+)/([^/]+)/?([^/]+?)?$' | ||
| # aos url is subset of hg. aos doesn't support ssh transport | ||
| regex_aos_url = r'^(https?)://([\w\-\.]*aos\.(co\.uk|org|com))/(users|teams)/([\w\-]{1,32})/(repos|code)/([\w\-]+)/?$' | ||
| # aos sdk builds url | ||
| regex_build_url = r'^(https?://([\w\-\.]*aos\.(co\.uk|org|com))/(users|teams)/([\w\-]{1,32})/(repos|code)/([\w\-]+))/builds/?([\w\-]{6,40}|tip)?/?$' | ||
| # default aos url | ||
| aos_os_url = 'https://github.com/alibaba/AliOS-Things.git' | ||
| # default aos component url | ||
| aos_lib_url = 'https://aos.org/users/aos_official/code/aos/builds/' | ||
| # aos SDK tools needed for programs based on aos SDK component | ||
| aos_sdk_tools_url = 'https://aos.org/users/aos_official/code/aos-sdk-tools' | ||
| # open_ocd_zip | ||
| open_ocd_url = 'https://files.alicdn.com/tpsservice/27ba2d597a43abfca94de351dae65dff.zip' | ||
| # verbose logging | ||
@@ -177,4 +153,2 @@ verbose = False | ||
| eclispe_project_dir = 'aos/makefiles/eclipse_project' | ||
| APP_PATH = 'app_path' | ||
@@ -181,0 +155,0 @@ PROGRAM_PATH = 'program_path' |
@@ -9,3 +9,3 @@ import os, sys | ||
| from aos.constant import * | ||
| import ConfigParser | ||
| import configparser | ||
@@ -55,3 +55,3 @@ class Rpm(): | ||
| cf = ConfigParser.ConfigParser() | ||
| cf = configparser.ConfigParser() | ||
| cf.add_section("main") | ||
@@ -58,0 +58,0 @@ cf.set("main", "cachedir", "/var/cache/yum/aos") |
@@ -15,3 +15,2 @@ import os, sys | ||
| from aos.util import log, error, cd_aos_root, is_domestic, popen | ||
| from aos.program import * | ||
@@ -187,3 +186,4 @@ first_progress = 0 | ||
| # The callback when connect to mqtt broker | ||
| def on_connect(client, (dn, pk, payload), flags, rc): | ||
| def on_connect(client, userdata, flags, rc): | ||
| (dn, pk, payload) = userdata | ||
| global bar | ||
@@ -200,3 +200,4 @@ # print userdata | ||
| # The callback for when a PUBLISH message is received from the mqtt broker. | ||
| def on_message(client, (dn, pk, payload), msg): | ||
| def on_message(client, userdata, msg): | ||
| (dn, pk, payload) = userdata | ||
| global first_progress, bar, timer | ||
@@ -203,0 +204,0 @@ |
+4
-3
@@ -31,3 +31,3 @@ import contextlib | ||
| def staticclass(cls): | ||
| for k, v in cls.__dict__.items(): | ||
| for k, v in list(cls.__dict__.items()): | ||
| if hasattr(v, '__call__') and not k.startswith('__'): | ||
@@ -83,3 +83,3 @@ setattr(cls, k, staticmethod(v)) | ||
| def progress(): | ||
| sys.stdout.write(progress_spinner.next()) | ||
| sys.stdout.write(next(progress_spinner)) | ||
| sys.stdout.flush() | ||
@@ -288,3 +288,3 @@ sys.stdout.write('\b') | ||
| return match | ||
| lines = ret.split('\n') | ||
| lines = ret.decode().split('\n') | ||
| for line in lines: | ||
@@ -360,2 +360,3 @@ if version in line: | ||
| def get(self, keyword): | ||
| value = None | ||
| with open(self.conf, "r") as f: | ||
@@ -362,0 +363,0 @@ for line in f.readlines(): |
+2
-2
| Metadata-Version: 1.2 | ||
| Name: aos-cube | ||
| Version: 0.3.7 | ||
| Version: 0.3.8 | ||
| Summary: aos commmand line tool for AliOS-Things development. | ||
@@ -191,2 +191,2 @@ Home-page: UNKNOWN | ||
| Platform: UNKNOWN | ||
| Requires-Python: >=2.7, <3 | ||
| Requires-Python: >=2.7 |
+3
-5
@@ -28,7 +28,6 @@ import os, sys | ||
| license=LICENSE, | ||
| python_requires='>=2.7, <3', | ||
| # packages=["aos", "aos.commands", "aos.managers", "udc"], | ||
| python_requires='>=2.7', | ||
| packages=find_packages(), | ||
| package_dir={'aos':'aos', 'udc':'udc'}, | ||
| package_data={'aos': ['.vscode/*'], 'udc': ['controller_certificate.pem', 'board/*/*']}, | ||
| package_dir={'aos':'aos'}, | ||
| package_data={'aos': ['.vscode/*']}, | ||
| install_requires=install_requires, | ||
@@ -38,3 +37,2 @@ entry_points={ | ||
| 'aos=aos.__main__:main', | ||
| 'udc=udc.udc:main', | ||
| 'aos-cube=aos.__main__:main', | ||
@@ -41,0 +39,0 @@ ] |
-282
| import ctypes | ||
| from aos.config import * | ||
| from aos.util import * | ||
| from aos.repo import * | ||
| # Program class, acts code base root | ||
| class Program(object): | ||
| path = None | ||
| name = None | ||
| is_cwd = False | ||
| is_repo = False | ||
| is_classic = False | ||
| build_dir = "BUILD" | ||
| def __init__(self, path=None, print_warning=False): | ||
| path = os.path.abspath(path or os.getcwd()) | ||
| self.path = path | ||
| self.is_cwd = True | ||
| while cd(path): | ||
| tpath = path | ||
| if os.path.isfile(os.path.join(path, Cfg.file)): | ||
| self.path = path | ||
| self.is_cwd = False | ||
| break | ||
| path = os.path.split(path)[0] | ||
| if tpath == path: # Reached root. | ||
| break | ||
| self.name = os.path.basename(self.path) | ||
| self.is_classic = os.path.isfile(os.path.join(self.path, 'aos.bld')) | ||
| # is_cwd flag indicates that current dir is assumed to be root, not root repo | ||
| if self.is_cwd and print_warning: | ||
| warning( | ||
| "Could not find aos program in current path \"%s\".\n" | ||
| "You can fix this by calling \"aos new .\" in the root of your program." % self.path) | ||
| def get_cfg(self, *args, **kwargs): | ||
| return Cfg(self.path).get(*args, **kwargs) or Global().get_cfg(*args, **kwargs) | ||
| def set_cfg(self, *args, **kwargs): | ||
| return Cfg(self.path).set(*args, **kwargs) | ||
| def list_cfg(self, *args, **kwargs): | ||
| return Cfg(self.path).list(*args, **kwargs) | ||
| def set_root(self): | ||
| return self.set_cfg('ROOT', '.') | ||
| def unset_root(self, path=None): | ||
| fl = os.path.join(path or self.path, Cfg.file) | ||
| if os.path.isfile(fl): | ||
| os.remove(fl) | ||
| # Gets aos dir (unified) | ||
| def get_os_dir(self): | ||
| if os.path.isdir(os.path.join(self.path, 'aos')): | ||
| return os.path.join(self.path, 'aos') | ||
| elif self.name == 'aos': | ||
| return self.path | ||
| else: | ||
| return None | ||
| def get_aoslib_dir(self): | ||
| if os.path.isdir(os.path.join(self.path, 'aos')): | ||
| return os.path.join(self.path, 'aos') | ||
| else: | ||
| return None | ||
| # Gets aos tools dir (unified) | ||
| def get_tools_dir(self): | ||
| paths = [] | ||
| # aos dir identified and tools is a sub dir | ||
| aos_os_path = self.get_os_dir() | ||
| if aos_os_path: | ||
| paths.append([aos_os_path, 'tools']) | ||
| paths.append([aos_os_path, 'core', 'tools']) | ||
| # aos not identified but tools found under cwd/tools | ||
| paths.append([self.path, 'tools']) | ||
| paths.append([self.path, 'core', 'tools']) | ||
| # aos Classic deployed tools | ||
| paths.append([self.path, '.temp', 'tools']) | ||
| return self._find_file_paths(paths, 'make.py') | ||
| def get_requirements(self): | ||
| paths = [] | ||
| aos_os_path = self.get_os_dir() | ||
| if aos_os_path: | ||
| paths.append([aos_os_path, 'tools']) | ||
| paths.append([aos_os_path]) | ||
| # aos not identified but tools found under cwd/tools | ||
| paths.append([self.path, 'tools']) | ||
| # aos Classic deployed tools | ||
| paths.append([self.path, '.temp', 'tools']) | ||
| # current dir | ||
| paths.append([self.path]) | ||
| return self._find_file_paths(paths, 'requirements.txt') | ||
| def _find_file_paths(self, paths, fl): | ||
| for p in paths: | ||
| path = os.path.join(*p) | ||
| if os.path.isdir(path) and os.path.isfile(os.path.join(path, fl)): | ||
| return os.path.join(path) | ||
| return None | ||
| def check_requirements(self, show_warning=False): | ||
| req_path = self.get_requirements() or self.path | ||
| req_file = 'requirements.txt' | ||
| missing = [] | ||
| try: | ||
| with open(os.path.join(req_path, req_file), 'r') as f: | ||
| import pip | ||
| installed_packages = [re.sub(r'-', '_', package.project_name.lower()) for package in pip.get_installed_distributions(local_only=True)] | ||
| for line in f.read().splitlines(): | ||
| pkg = re.sub(r'-', '_', re.sub(r'^([\w-]+).*$', r'\1', line).lower()) | ||
| if not pkg in installed_packages: | ||
| missing.append(pkg) | ||
| if missing and install_requirements: | ||
| try: | ||
| action("Auto-installing missing Python modules...") | ||
| pquery(['pip', 'install', '-q', '-r', os.path.join(req_path, req_file)]) | ||
| missing = [] | ||
| except ProcessException: | ||
| warning("Unable to auto-install required Python modules.") | ||
| except (IOError, ImportError, OSError): | ||
| pass | ||
| if missing: | ||
| err = ( | ||
| "-----------------------------------------------------------------\n" | ||
| "The aos OS tools in this program require the following Python modules: %s\n" | ||
| "You can install all missing modules by running \"pip install -r %s\" in \"%s\"" % (', '.join(missing), req_file, req_path)) | ||
| if os.name == 'posix': | ||
| err += "\nOn Posix systems (Linux, Mac, etc) you might have to switch to superuser account or use \"sudo\"" | ||
| if show_warning: | ||
| warning(err) | ||
| else: | ||
| error(err, 1) | ||
| # Routines after cloning aos | ||
| def post_action(self): | ||
| aos_tools_path = self.get_tools_dir() | ||
| if not aos_tools_path and (self.is_classic or os.path.exists(os.path.join(self.path, Cfg.file))): | ||
| self.add_tools(os.path.join(self.path, '.temp')) | ||
| aos_tools_path = self.get_tools_dir() | ||
| if not aos_tools_path: | ||
| warning("Cannot find the aos tools directory in \"%s\"" % self.path) | ||
| return False | ||
| if (not os.path.isfile(os.path.join(self.path, 'aos_settings.py')) and | ||
| os.path.isfile(os.path.join(aos_tools_path, 'default_settings.py'))): | ||
| shutil.copy(os.path.join(aos_tools_path, 'default_settings.py'), os.path.join(self.path, 'aos_settings.py')) | ||
| self.check_requirements(True) | ||
| def add_tools(self, path): | ||
| if not os.path.exists(path): | ||
| os.mkdir(path) | ||
| with cd(path): | ||
| tools_dir = 'tools' | ||
| if not os.path.exists(tools_dir): | ||
| try: | ||
| action("Couldn't find build tools in your program. Downloading the aos 2.0 SDK tools...") | ||
| repo = Repo.fromurl(aos_sdk_tools_url) | ||
| repo.clone(aos_sdk_tools_url, tools_dir) | ||
| except Exception: | ||
| if os.path.exists(tools_dir): | ||
| rmtree_readonly(tools_dir) | ||
| error("An error occurred while cloning the aos SDK tools from \"%s\"" % aos_sdk_tools_url) | ||
| def update_tools(self, path): | ||
| tools_dir = 'tools' | ||
| if os.path.exists(os.path.join(path, tools_dir)): | ||
| with cd(os.path.join(path, tools_dir)): | ||
| try: | ||
| action("Updating the aos 2.0 SDK tools...") | ||
| repo = Repo.fromrepo() | ||
| repo.update() | ||
| except Exception: | ||
| error("An error occurred while update the aos SDK tools from \"%s\"" % aos_sdk_tools_url) | ||
| def get_tools(self): | ||
| aos_tools_path = self.get_tools_dir() | ||
| if not aos_tools_path: | ||
| error('The aos tools were not found in "%s". \nRun `aos deploy` to install dependencies and tools. ' % self.path, -1) | ||
| return aos_tools_path | ||
| def get_env(self): | ||
| env = os.environ.copy() | ||
| env['PYTHONPATH'] = os.path.abspath(self.path) | ||
| compilers = ['ARM', 'GCC_ARM', 'IAR'] | ||
| for c in compilers: | ||
| if self.get_cfg(c+'_PATH'): | ||
| env['aos_'+c+'_PATH'] = self.get_cfg(c+'_PATH') | ||
| return env | ||
| def get_target(self, target=None): | ||
| target_cfg = self.get_cfg('TARGET') | ||
| target = target if target else target_cfg | ||
| if target and (target.lower() == 'detect' or target.lower() == 'auto'): | ||
| targets = self.get_detected_targets() | ||
| if targets == False: | ||
| error("The target detection requires that the 'aos-ls' python module is installed.\nYou can install aos-ls by running 'pip install aos-ls'.") | ||
| elif len(targets) > 1: | ||
| error("Multiple targets were detected.\nOnly 1 target board should be connected to your system when you use the '-m auto' switch.") | ||
| elif len(targets) == 0: | ||
| error("No targets were detected.\nPlease make sure a target board is connected to this system.") | ||
| else: | ||
| action("Detected \"%s\" connected to \"%s\" and using com port \"%s\"" % (targets[0]['name'], targets[0]['mount'], targets[0]['serial'])) | ||
| target = targets[0]['name'] | ||
| if target is None: | ||
| error("Please specify target using the -m switch or set default target using command 'aos target'", 1) | ||
| return target | ||
| def get_toolchain(self, toolchain=None): | ||
| toolchain_cfg = self.get_cfg('TOOLCHAIN') | ||
| tchain = toolchain if toolchain else toolchain_cfg | ||
| if tchain is None: | ||
| error("Please specify toolchain using the -t switch or set default toolchain using command 'aos toolchain'", 1) | ||
| return tchain | ||
| def set_defaults(self, target=None, toolchain=None): | ||
| if target and not self.get_cfg('TARGET'): | ||
| self.set_cfg('TARGET', target) | ||
| if toolchain and not self.get_cfg('TOOLCHAIN'): | ||
| self.set_cfg('TOOLCHAIN', toolchain) | ||
| def get_macros(self): | ||
| macros = [] | ||
| if os.path.isfile('MACROS.txt'): | ||
| with open('MACROS.txt') as f: | ||
| macros = f.read().splitlines() | ||
| return macros | ||
| def ignore_build_dir(self): | ||
| build_path = os.path.join(self.path, self.build_dir) | ||
| if not os.path.exists(build_path): | ||
| os.mkdir(build_path) | ||
| if not os.path.exists(os.path.join(build_path, '.aosignore')): | ||
| try: | ||
| with open(os.path.join(build_path, '.aosignore'), 'w') as f: | ||
| f.write('*\n') | ||
| except IOError: | ||
| error("Unable to write build ignore file in \"%s\"" % os.path.join(build_path, '.aosignore'), 1) | ||
| def get_detected_targets(self): | ||
| targets = [] | ||
| try: | ||
| import aos_lstools | ||
| oldError = None | ||
| if os.name == 'nt': | ||
| oldError = ctypes.windll.kernel32.SetErrorMode(1) # Disable Windows error box temporarily. note that SEM_FAILCRITICALERRORS = 1 | ||
| aoss = aos_lstools.create() | ||
| detect_muts_list = aoss.list_aoss() | ||
| if os.name == 'nt': | ||
| ctypes.windll.kernel32.SetErrorMode(oldError) | ||
| for mut in detect_muts_list: | ||
| targets.append({ | ||
| 'id': mut['target_id'], 'name': mut['platform_name'], | ||
| 'mount': mut['mount_point'], 'serial': mut['serial_port'] | ||
| }) | ||
| except (IOError, ImportError, OSError): | ||
| return False | ||
| return targets |
-761
| import subprocess | ||
| import tempfile | ||
| import errno | ||
| from urlparse import urlparse | ||
| from aos.program import * | ||
| def formaturl(url, format="default"): | ||
| url = "%s" % url | ||
| m = re.match(regex_aos_url, url) | ||
| if m: | ||
| if format == "http": | ||
| url = 'http://%s/%s/%s/%s/%s' % (m.group(2), m.group(4), m.group(5), m.group(6), m.group(7)) | ||
| else: | ||
| url = 'https://%s/%s/%s/%s/%s' % (m.group(2), m.group(4), m.group(5), m.group(6), m.group(7)) | ||
| else: | ||
| m = re.match(regex_git_url, url) | ||
| if m: | ||
| if format == "ssh": | ||
| url = 'ssh://%s%s/%s.git' % (m.group(2) or 'git@', m.group(6), m.group(7)) | ||
| elif format == "http": | ||
| url = 'http://%s%s/%s' % ( | ||
| m.group(2) if (m.group(2) and (m.group(5) or m.group(3) != 'git')) else '', m.group(6), m.group(7)) | ||
| elif format == "https": | ||
| url = 'https://%s%s/%s%s' % ( | ||
| m.group(2) if (m.group(2) and (m.group(5) or m.group(3) != 'git')) else '', m.group(6), m.group(7), | ||
| m.group(8)) | ||
| else: | ||
| m = re.match(regex_hg_url, url) | ||
| if m: | ||
| if format == "ssh": | ||
| url = 'ssh://%s/%s' % (m.group(2), m.group(3)) | ||
| elif format == "http": | ||
| url = 'http://%s/%s' % (m.group(2), m.group(3)) | ||
| elif format == "https": | ||
| url = 'https://%s/%s' % (m.group(2), m.group(3)) | ||
| return url | ||
| # Handling for multiple version controls | ||
| scms = {} | ||
| def scm(name): | ||
| def _scm(cls): | ||
| scms[name] = cls() | ||
| return cls | ||
| return _scm | ||
| # pylint: disable=no-self-argument, no-method-argument, no-member, no-self-use, unused-argument | ||
| @scm('git') | ||
| @staticclass | ||
| class Git(object): | ||
| name = 'git' | ||
| ignore_file = os.path.join('.git', 'info', 'exclude') | ||
| def isurl(url): | ||
| m_url = re.match(regex_url_ref, url.strip().replace('\\', '/')) | ||
| if m_url and not re.match(regex_build_url, m_url.group(1)) and not re.match(regex_aos_url, m_url.group(1)): | ||
| return re.match(regex_git_url, m_url.group(1)) | ||
| else: | ||
| return False | ||
| def init(path=None): | ||
| popen([git_cmd, 'init'] + ([path] if path else []) + ([] if very_verbose else ['-q'])) | ||
| def cleanup(): | ||
| info("Cleaning up Git index") | ||
| if os.path.exists(os.path.join('.git', 'logs')): | ||
| rmtree_readonly(os.path.join('.git', 'logs')) | ||
| def clone(url, name=None, depth=None, protocol=None): | ||
| popen([git_cmd, 'clone', formaturl(url, protocol), name] + (['--depth', depth] if depth else []) + ['-v']) | ||
| def add(dest): | ||
| info("Adding reference " + dest) | ||
| try: | ||
| popen([git_cmd, 'add', dest] + (['-v'] if very_verbose else [])) | ||
| except ProcessException: | ||
| pass | ||
| def remove(dest): | ||
| info("Removing reference " + dest) | ||
| try: | ||
| popen([git_cmd, 'rm', '-f', dest] + ([] if very_verbose else ['-q'])) | ||
| except ProcessException: | ||
| pass | ||
| def commit(msg=None): | ||
| popen([git_cmd, 'commit', '-a'] + (['-m', msg] if msg else []) + ( | ||
| ['-v'] if very_verbose else ([] if verbose else ['-q']))) | ||
| def publish(all_refs=None): | ||
| if all_refs: | ||
| popen([git_cmd, 'push', '--all'] + (['-v'] if very_verbose else ([] if verbose else ['-q']))) | ||
| else: | ||
| remote = Git.getremote() | ||
| branch = Git.getbranch() | ||
| if remote and branch: | ||
| popen([git_cmd, 'push', remote, branch] + (['-v'] if very_verbose else ([] if verbose else ['-q']))) | ||
| else: | ||
| err = "Unable to publish outgoing changes for \"%s\" in \"%s\".\n" % ( | ||
| os.path.basename(os.getcwd()), os.getcwd()) | ||
| if not remote: | ||
| error(err + "The local repository is not associated with a remote one.", 1) | ||
| if not branch: | ||
| error(err + "Working set is not on a branch.", 1) | ||
| def fetch(): | ||
| info("Fetching revisions from remote repository to \"%s\"" % os.path.basename(os.getcwd())) | ||
| popen([git_cmd, 'fetch', '--all', '--tags'] + (['-v'] if very_verbose else ([] if verbose else ['-q']))) | ||
| def discard(clean_files=False): | ||
| info("Discarding local changes in \"%s\"" % os.path.basename(os.getcwd())) | ||
| pquery([git_cmd, 'reset', 'HEAD'] + ([] if very_verbose else ['-q'])) # unmarks files for commit | ||
| pquery([git_cmd, 'checkout', '.'] + ([] if very_verbose else ['-q'])) # undo modified files | ||
| pquery([git_cmd, 'clean', '-fd'] + (['-x'] if clean_files else []) + ( | ||
| ['-q'] if very_verbose else ['-q'])) # cleans up untracked files and folders | ||
| def merge(dest): | ||
| info("Merging \"%s\" with \"%s\"" % (os.path.basename(os.getcwd()), dest)) | ||
| popen([git_cmd, 'merge', dest] + (['-v'] if very_verbose else ([] if verbose else ['-q']))) | ||
| def checkout(rev, clean=False): | ||
| if not rev: | ||
| return | ||
| info("Checkout \"%s\" in %s" % (rev, os.path.basename(os.getcwd()))) | ||
| branch = None | ||
| refs = Git.getrefs(rev) | ||
| for ref in refs: # re-associate with a local or remote branch (rev is the same) | ||
| m = re.match(r'^(.*?)\/(.*?)$', ref) | ||
| if m and m.group(2) != "HEAD": # matches origin/<branch> and isn't HEAD ref | ||
| if not os.path.exists(os.path.join('.git', 'refs', 'heads', m.group( | ||
| 2))): # okay only if local branch with that name doesn't exist (git will checkout the origin/<branch> in that case) | ||
| branch = m.group(2) | ||
| elif ref != "HEAD": | ||
| branch = ref # matches local branch and isn't HEAD ref | ||
| if branch: | ||
| info("Revision \"%s\" matches a branch \"%s\" reference. Re-associating with branch" % (rev, branch)) | ||
| popen([git_cmd, 'checkout', branch] + ([] if very_verbose else ['-q'])) | ||
| break | ||
| if not branch: | ||
| popen([git_cmd, 'checkout', rev] + (['-f'] if clean else []) + ([] if very_verbose else ['-q'])) | ||
| def update(rev=None, clean=False, clean_files=False, is_local=False): | ||
| if not is_local: | ||
| Git.fetch() | ||
| if clean: | ||
| Git.discard(clean_files) | ||
| if rev: | ||
| Git.checkout(rev, clean) | ||
| else: | ||
| remote = Git.getremote() | ||
| branch = Git.getbranch() | ||
| if remote and branch: | ||
| try: | ||
| Git.merge('%s/%s' % (remote, branch)) | ||
| except ProcessException: | ||
| pass | ||
| else: | ||
| err = "Unable to update \"%s\" in \"%s\"." % (os.path.basename(os.getcwd()), os.getcwd()) | ||
| if not remote: | ||
| info(err + " The local repository is not associated with a remote one.") | ||
| if not branch: | ||
| info(err + " Working set is not on a branch.") | ||
| def status(): | ||
| return pquery([git_cmd, 'status', '-s'] + (['-v'] if very_verbose else [])) | ||
| def dirty(): | ||
| return pquery([git_cmd, 'status', '-uno', '--porcelain']) | ||
| def untracked(): | ||
| return pquery([git_cmd, 'ls-files', '--others', '--exclude-standard']).splitlines() | ||
| def outgoing(): | ||
| # Get default remote | ||
| remote = Git.getremote() | ||
| if not remote: | ||
| return -1 | ||
| # Get current branch | ||
| branch = Git.getbranch() | ||
| if not branch: | ||
| # Default to "master" in detached mode | ||
| branch = "master" | ||
| try: | ||
| # Check if remote branch exists | ||
| if not pquery([git_cmd, 'rev-parse', '%s/%s' % (remote, branch)]): | ||
| return 1 | ||
| except ProcessException: | ||
| return 1 | ||
| # Check for outgoing commits for the same remote branch | ||
| return 1 if pquery([git_cmd, 'log', '%s/%s..%s' % (remote, branch, branch)]) else 0 | ||
| # Checks whether current working tree is detached | ||
| def isdetached(): | ||
| return True if Git.getbranch() == "" else False | ||
| # Finds default remote | ||
| def getremote(): | ||
| remote = None | ||
| remotes = Git.getremotes('push') | ||
| for r in remotes: | ||
| remote = r[0] | ||
| # Prefer origin which is Git's default remote when cloning | ||
| if r[0] == "origin": | ||
| break | ||
| return remote | ||
| # Finds all associated remotes for the specified remote type | ||
| def getremotes(rtype='fetch'): | ||
| result = [] | ||
| remotes = pquery([git_cmd, 'remote', '-v']).strip().splitlines() | ||
| for remote in remotes: | ||
| remote = re.split(r'\s', remote) | ||
| t = re.sub('[()]', '', remote[2]) | ||
| if not rtype or rtype == t: | ||
| result.append([remote[0], remote[1], t]) | ||
| return result | ||
| def seturl(url): | ||
| info("Setting url to \"%s\" in %s" % (url, os.getcwd())) | ||
| return pquery([git_cmd, 'remote', 'set-url', 'origin', url]).strip() | ||
| def geturl(): | ||
| url = "" | ||
| remotes = Git.getremotes() | ||
| for remote in remotes: | ||
| url = remote[1] | ||
| if remote[0] == "origin": # Prefer origin URL | ||
| break | ||
| return formaturl(url) | ||
| def getrev(): | ||
| return pquery([git_cmd, 'rev-parse', 'HEAD']).strip() | ||
| # Gets current branch or returns empty string if detached | ||
| def getbranch(rev='HEAD'): | ||
| try: | ||
| branch = pquery([git_cmd, 'rev-parse', '--symbolic-full-name', '--abbrev-ref', rev]).strip() | ||
| except ProcessException: | ||
| branch = "master" | ||
| return branch if branch != "HEAD" else "" | ||
| # Finds refs (local or remote branches). Will match rev if specified | ||
| def getrefs(rev=None, ret_rev=False): | ||
| result = [] | ||
| lines = pquery([git_cmd, 'show-ref']).strip().splitlines() | ||
| for line in lines: | ||
| m = re.match(r'^(.+)\s+(.+)$', line) | ||
| if m and (not rev or m.group(1).startswith(rev)): | ||
| if re.match(r'refs\/(heads|remotes)\/', m.group(2)): # exclude tags | ||
| result.append(m.group(1) if ret_rev else re.sub(r'refs\/(heads|remotes)\/', '', m.group(2))) | ||
| return result | ||
| # Finds branches a rev belongs to | ||
| def revbranches(rev): | ||
| branches = [] | ||
| lines = pquery([git_cmd, 'branch', '-a', '--contains'] + ([rev] if rev else [])).strip().splitlines() | ||
| for line in lines: | ||
| if re.match(r'^\*?\s+\((.+)\)$', line): | ||
| continue | ||
| line = re.sub(r'\s+', '', line) | ||
| branches.append(line) | ||
| return branches | ||
| def ignores(): | ||
| try: | ||
| ignore_file_parent_directory = os.path.dirname(Git.ignore_file) | ||
| if not os.path.exists(ignore_file_parent_directory): | ||
| os.mkdir(ignore_file_parent_directory) | ||
| with open(Git.ignore_file, 'w') as f: | ||
| f.write('\n'.join(ignores) + '\n') | ||
| except IOError: | ||
| error("Unable to write ignore file in \"%s\"" % os.path.join(os.getcwd(), Git.ignore_file), 1) | ||
| def ignore(dest): | ||
| try: | ||
| with open(Git.ignore_file) as f: | ||
| exists = dest in f.read().splitlines() | ||
| except IOError: | ||
| exists = False | ||
| if not exists: | ||
| try: | ||
| ignore_file_parent_directory = os.path.dirname(Git.ignore_file) | ||
| if not os.path.exists(ignore_file_parent_directory): | ||
| os.mkdir(ignore_file_parent_directory) | ||
| with open(Git.ignore_file, 'a') as f: | ||
| f.write(dest.replace("\\", "/") + '\n') | ||
| except IOError: | ||
| error("Unable to write ignore file in \"%s\"" % os.path.join(os.getcwd(), Git.ignore_file), 1) | ||
| def unignore(dest): | ||
| try: | ||
| with open(Git.ignore_file) as f: | ||
| lines = f.read().splitlines() | ||
| except IOError: | ||
| lines = [] | ||
| if dest in lines: | ||
| lines.remove(dest) | ||
| try: | ||
| ignore_file_parent_directory = os.path.dirname(Git.ignore_file) | ||
| if not os.path.exists(ignore_file_parent_directory): | ||
| os.mkdir(ignore_file_parent_directory) | ||
| with open(Git.ignore_file, 'w') as f: | ||
| f.write('\n'.join(lines) + '\n') | ||
| except IOError: | ||
| error("Unable to write ignore file in \"%s\"" % os.path.join(os.getcwd(), Git.ignore_file), 1) | ||
| # Repository object | ||
| class Repo(object): | ||
| is_local = False | ||
| is_build = False | ||
| name = None | ||
| path = None | ||
| url = None | ||
| rev = None | ||
| scm = None | ||
| libs = [] | ||
| codes = [] | ||
| cache = None | ||
| @classmethod | ||
| def fromurl(cls, url, path=None): | ||
| repo = cls() | ||
| m_local = re.match(regex_local_ref, url.strip().replace('\\', '/')) | ||
| m_repo_url = re.match(regex_url_ref, url.strip().replace('\\', '/')) | ||
| m_bld_url = re.match(regex_build_url, url.strip().replace('\\', '/')) | ||
| if m_local: | ||
| repo.name = os.path.basename(path or m_local.group(1)) | ||
| repo.path = os.path.abspath(path or os.path.join(os.getcwd(), m_local.group(1))) | ||
| repo.url = m_local.group(1) | ||
| repo.rev = m_local.group(2) | ||
| repo.is_local = True | ||
| elif m_bld_url: | ||
| repo.name = os.path.basename(path or m_bld_url.group(7)) | ||
| repo.path = os.path.abspath(path or os.path.join(os.getcwd(), repo.name)) | ||
| repo.url = m_bld_url.group(1) + '/builds' | ||
| repo.rev = m_bld_url.group(8) | ||
| repo.is_build = True | ||
| elif m_repo_url: | ||
| repo.name = os.path.basename(path or m_repo_url.group(2)[:-4]) | ||
| if repo.name == OS_NAME: | ||
| if path: | ||
| repo.path = os.path.abspath(path) | ||
| else: | ||
| repo.path = os.path.abspath(os.path.join(Global().get_path(), repo.name)) | ||
| Global().set_cfg(AOS_SDK_PATH, repo.path.replace(os.path.sep, '/')) | ||
| else: | ||
| pd = Program(os.getcwd()) | ||
| repo.path = os.path.abspath(path or os.path.join(pd.get_cfg(REMOTE_PATH), repo.name)) | ||
| repo.url = formaturl(m_repo_url.group(1)) | ||
| repo.rev = m_repo_url.group(3) | ||
| if repo.rev and repo.rev != 'master' and not re.match(r'^([a-fA-F0-9]{6,40})$', repo.rev) and not re.match(r'^rel_\d+\.\d+\.\d+$', repo.rev): | ||
| error('Invalid revision (%s)' % repo.rev, -1) | ||
| else: | ||
| error('Invalid repository (%s)' % url.strip(), -1) | ||
| cache_cfg = Global().get_cfg('CACHE', '') | ||
| if cache_repositories and cache_cfg and cache_cfg != 'none' and cache_cfg != 'off' and cache_cfg != 'disabled': | ||
| loc = cache_cfg if (cache_cfg and cache_cfg != 'on' and cache_cfg != 'enabled') else None | ||
| repo.cache = loc or os.path.join(tempfile.gettempdir(), 'aos-repo-cache') | ||
| return repo | ||
| @classmethod | ||
| def fromlib(cls, lib=None): | ||
| with open(lib) as f: | ||
| ref = f.read(200) | ||
| m_local = re.match(regex_local_ref, ref.strip().replace('\\', '/')) | ||
| m_repo_url = re.match(regex_url_ref, ref.strip().replace('\\', '/')) | ||
| m_bld_url = re.match(regex_build_url, ref.strip().replace('\\', '/')) | ||
| if not (m_local or m_bld_url or m_repo_url): | ||
| warning( | ||
| "File \"%s\" in \"%s\" uses a non-standard .comp or .codes file extension, which is not compatible with the aos build tools.\n" % ( | ||
| os.path.basename(lib), os.path.split(lib)[0])) | ||
| return False | ||
| else: | ||
| return cls.fromurl(ref, lib[:lib.rfind('.')]) | ||
| @classmethod | ||
| def fromcode(cls, code=None): | ||
| with open(code) as f: | ||
| ref = f.read(200) | ||
| m_local = re.match(regex_local_ref, ref.strip().replace('\\', '/')) | ||
| m_repo_url = re.match(regex_url_ref, ref.strip().replace('\\', '/')) | ||
| m_bld_url = re.match(regex_build_url, ref.strip().replace('\\', '/')) | ||
| if not (m_local or m_bld_url or m_repo_url): | ||
| warning( | ||
| "File \"%s\" in \"%s\" uses a non-standard .lib file extension, which is not compatible with the aos build tools.\n" % ( | ||
| os.path.basename(code), os.path.split(code)[0])) | ||
| return False | ||
| else: | ||
| return cls.fromurl(ref, code[:code.rfind('.')]) | ||
| @classmethod | ||
| def fromrepo(cls, path=None): | ||
| repo = cls() | ||
| if path is None: | ||
| path = Repo.findparent(os.getcwd()) | ||
| if path is None: | ||
| error( | ||
| "Could not find aos program in current path \"%s\".\n" | ||
| "You can fix this by calling \"aos new .\" or \"aos config root .\" in the root of your program." % os.getcwd()) | ||
| repo.path = os.path.abspath(path) | ||
| repo.name = os.path.basename(repo.path) | ||
| cache_cfg = Global().get_cfg('CACHE', '') | ||
| if cache_repositories and cache_cfg and cache_cfg != 'none' and cache_cfg != 'off' and cache_cfg != 'disabled': | ||
| loc = cache_cfg if (cache_cfg and cache_cfg != 'on' and cache_cfg != 'enabled') else None | ||
| repo.cache = loc or os.path.join(tempfile.gettempdir(), 'aos-repo-cache') | ||
| repo.sync() | ||
| if repo.scm is None: | ||
| info( | ||
| "Program \"%s\" in \"%s\" does not use source control management.\n" | ||
| "To fix this you should use \"aos new .\" in the root of your program." % (repo.name, repo.path)) | ||
| return repo | ||
| @classmethod | ||
| def isrepo(cls, path=None): | ||
| for name, _ in scms.items(): | ||
| if os.path.isdir(os.path.join(path, '.' + name)): | ||
| return True | ||
| return False | ||
| @classmethod | ||
| def findparent(cls, path=None): | ||
| path = os.path.abspath(path or os.getcwd()) | ||
| while cd(path): | ||
| if os.path.isfile(os.path.join(path, Cfg.file)) or Repo.isrepo(path): | ||
| return path | ||
| tpath = path | ||
| path = os.path.split(path)[0] | ||
| if tpath == path: | ||
| break | ||
| return None | ||
| @classmethod | ||
| def pathtype(cls, path=None): | ||
| path = os.path.abspath(path or os.getcwd()) | ||
| pd = Program(path) | ||
| # depth = 0 | ||
| # while cd(path): | ||
| # tpath = path | ||
| # path = Repo.findparent(path) | ||
| # if path: | ||
| # depth += 1 | ||
| # path = os.path.split(path)[0] | ||
| # if tpath == path: # Reached root. | ||
| # break | ||
| # else: | ||
| # break | ||
| return pd.get_cfg(PATH_TYPE) if pd.get_cfg(PATH_TYPE) else "directory" | ||
| @classmethod | ||
| def revtype(cls, rev, ret_rev=False): | ||
| if rev is None or len(rev) == 0: | ||
| return 'latest' + (' revision in the current branch' if ret_rev else '') | ||
| elif re.match(r'^([a-fA-F0-9]{6,40})$', rev) or re.match(r'^([0-9]+)$', rev): | ||
| return 'rev' + (' #' + rev[0:12] if ret_rev else '') | ||
| else: | ||
| return 'branch' + (' ' + rev if ret_rev else '') | ||
| @classmethod | ||
| def isurl(cls, url): | ||
| m = re.match(regex_url_ref, url.strip().replace('\\', '/')) | ||
| return True if m else False | ||
| @property | ||
| def lib(self): | ||
| return self.path + '.' + ('bld' if self.is_build else 'component') | ||
| @property | ||
| def code(self): | ||
| return self.path + '.' + ('bld' if self.is_build else 'codes') | ||
| @property | ||
| def fullurl(self): | ||
| if self.url: | ||
| return (self.url.rstrip('/') + '/' + | ||
| (('' if self.is_build else '#') + | ||
| self.rev if self.rev else '')) | ||
| def sync(self): | ||
| self.url = None | ||
| self.rev = None | ||
| if os.path.isdir(self.path): | ||
| try: | ||
| self.scm = self.getscm() | ||
| if self.scm and self.scm.name == 'bld': | ||
| self.is_build = True | ||
| except ProcessException: | ||
| pass | ||
| try: | ||
| self.url = self.geturl() | ||
| if not self.url: | ||
| self.is_local = True | ||
| ppath = self.findparent(os.path.split(self.path)[0]) | ||
| self.url = relpath(ppath, self.path).replace("\\", "/") if ppath else os.path.basename(self.path) | ||
| except ProcessException: | ||
| pass | ||
| try: | ||
| self.rev = self.getrev() | ||
| except ProcessException: | ||
| pass | ||
| try: | ||
| self.libs = list(self.getlibs()) | ||
| except ProcessException: | ||
| pass | ||
| try: | ||
| self.codes = list(self.getcodes()) | ||
| except ProcessException: | ||
| pass | ||
| def getscm(self): | ||
| for name, scm in scms.items(): | ||
| if os.path.isdir(os.path.join(self.path, '.' + name)): | ||
| return scm | ||
| # Pass backend SCM commands and parameters if SCM exists | ||
| def __wrap_scm(self, method): | ||
| def __scm_call(*args, **kwargs): | ||
| if self.scm and hasattr(self.scm, method) and callable(getattr(self.scm, method)): | ||
| with cd(self.path): | ||
| return getattr(self.scm, method)(*args, **kwargs) | ||
| return __scm_call | ||
| def __getattr__(self, attr): | ||
| if attr in ['geturl', 'getrev', 'add', 'remove', 'ignores', 'ignore', 'unignore', | ||
| 'status', 'dirty', 'commit', 'outgoing', 'publish', 'checkout', 'update', | ||
| 'isdetached']: | ||
| wrapper = self.__wrap_scm(attr) | ||
| self.__dict__[attr] = wrapper | ||
| return wrapper | ||
| else: | ||
| raise AttributeError("Repo instance doesn't have attribute '%s'" % attr) | ||
| def remove(self, dest, *args, **kwargs): | ||
| if os.path.isfile(dest): | ||
| try: | ||
| os.remove(dest) | ||
| except OSError: | ||
| pass | ||
| return self.scm.remove(dest, *args, **kwargs) | ||
| def clone(self, url, path, rev=None, depth=None, protocol=None, **kwargs): | ||
| # Sorted so repositories that match urls are attempted first | ||
| sorted_scms = [(scm.isurl(url), scm) for scm in scms.values()] | ||
| sorted_scms = filter(lambda (m, _): m, sorted_scms) | ||
| for _, scm in sorted_scms: | ||
| main = True | ||
| cache = self.get_cache(url) | ||
| # Try to clone with cache ref first | ||
| if cache and not os.path.isdir(path): | ||
| info("Found matching cached repository in \"%s\"" % cache) | ||
| try: | ||
| if os.path.split(path)[0] and not os.path.isdir(os.path.split(path)[0]): | ||
| os.makedirs(os.path.split(path)[0]) | ||
| info("Carbon copy from \"%s\" to \"%s\"" % (cache, path)) | ||
| shutil.copytree(cache, path) | ||
| with cd(path): | ||
| scm.seturl(formaturl(url, protocol)) | ||
| scm.cleanup() | ||
| info("Update cached copy from remote repository") | ||
| scm.update(rev, True) | ||
| main = False | ||
| except (ProcessException, IOError): | ||
| info("Discarding cached repository") | ||
| if os.path.isdir(path): | ||
| rmtree_readonly(path) | ||
| # Main clone routine if the clone with cache ref failed (might occur if cache ref is dirty) | ||
| if main: | ||
| try: | ||
| scm.clone(url, path, depth=depth, protocol=protocol, **kwargs) | ||
| except ProcessException: | ||
| if os.path.isdir(path): | ||
| rmtree_readonly(path) | ||
| continue | ||
| self.scm = scm | ||
| self.url = url | ||
| self.path = os.path.abspath(path) | ||
| self.ignores() | ||
| self.set_cache(url) | ||
| return True | ||
| return False | ||
| def getlibs(self): | ||
| for root, dirs, files in os.walk(self.path): | ||
| dirs[:] = [d for d in dirs if not d.startswith('.')] | ||
| files[:] = [f for f in files if not f.startswith('.')] | ||
| for f in files: | ||
| if f.endswith('.component'): | ||
| repo = Repo.fromlib(os.path.join(root, f)) | ||
| if repo: | ||
| yield repo | ||
| if f[:f.rfind('.')] in dirs: | ||
| dirs.remove(f[:f.rfind('.')]) | ||
| def getcodes(self): | ||
| for root, dirs, files in os.walk(self.path): | ||
| dirs[:] = [d for d in dirs if not d.startswith('.')] | ||
| files[:] = [f for f in files if not f.startswith('.')] | ||
| for f in files: | ||
| if f.endswith('.codes'): | ||
| repo = Repo.fromcode(os.path.join(root, f)) | ||
| if repo: | ||
| yield repo | ||
| if f[:f.rfind('.')] in dirs: | ||
| dirs.remove(f[:f.rfind('.')]) | ||
| def write(self): | ||
| if os.path.isfile(self.lib): | ||
| with open(self.lib) as f: | ||
| lib_repo = Repo.fromurl(f.read().strip()) | ||
| if (formaturl(lib_repo.url, 'https') == formaturl(self.url, | ||
| 'https') # match URLs in common format (https) | ||
| and ( | ||
| lib_repo.rev == self.rev # match revs, even if rev is None (valid for repos with no revisions) | ||
| or (lib_repo.rev and self.rev | ||
| and lib_repo.rev == self.rev[ | ||
| 0:len(lib_repo.rev)]))): # match long and short rev formats | ||
| # print self.name, 'unmodified' | ||
| return | ||
| ref = (formaturl(self.url, 'https').rstrip('/') + '/' + | ||
| (('' if self.is_build else '#') + | ||
| self.rev if self.rev else '')) | ||
| action("Update reference \"%s\" -> \"%s\"" % ( | ||
| relpath(cwd_root, self.path) if cwd_root != self.path else self.name, ref)) | ||
| with open(self.lib, 'wb') as f: | ||
| f.write(ref + '\n') | ||
| def write_codes(self): | ||
| if os.path.isfile(self.code): | ||
| with open(self.code) as f: | ||
| lib_repo = Repo.fromurl(f.read().strip()) | ||
| if (formaturl(lib_repo.url, 'ssh') == formaturl(self.url, 'ssh') # match URLs in common format (ssh) | ||
| and ( | ||
| lib_repo.rev == self.rev # match revs, even if rev is None (valid for repos with no revisions) | ||
| or (lib_repo.rev and self.rev | ||
| and lib_repo.rev == self.rev[ | ||
| 0:len(lib_repo.rev)]))): # match long and short rev formats | ||
| # print self.name, 'unmodified' | ||
| return | ||
| ref = (formaturl(self.url, 'ssh').rstrip('/') + '/' + | ||
| (('' if self.is_build else '#') + | ||
| self.rev if self.rev else '')) | ||
| action("Update reference \"%s\" -> \"%s\"" % ( | ||
| relpath(cwd_root, self.path) if cwd_root != self.path else self.name, ref)) | ||
| with open(self.code, 'wb') as f: | ||
| f.write(ref + '\n') | ||
| def rm_untracked(self): | ||
| untracked = self.scm.untracked() | ||
| for f in untracked: | ||
| if re.match(r'(.+)\.(lib|bld)$', f) and os.path.isfile(f): | ||
| action("Remove untracked library reference \"%s\"" % f) | ||
| os.remove(f) | ||
| def get_cache(self, url): | ||
| up = urlparse(formaturl(url, 'https')) | ||
| if self.cache and up and up.netloc and os.path.isdir( | ||
| os.path.join(self.cache, up.netloc, re.sub(r'^/', '', up.path))): | ||
| return os.path.join(self.cache, up.netloc, re.sub(r'^/', '', up.path)) | ||
| def set_cache(self, url): | ||
| up = urlparse(formaturl(url, 'https')) | ||
| if self.cache and up and up.netloc and os.path.isdir(self.path): | ||
| try: | ||
| cpath = os.path.join(self.cache, up.netloc, re.sub(r'^/', '', up.path)) | ||
| if not os.path.isdir(cpath): | ||
| os.makedirs(cpath) | ||
| scm_dir = '.' + self.scm.name | ||
| if os.path.isdir(os.path.join(cpath, scm_dir)): | ||
| rmtree_readonly(os.path.join(cpath, scm_dir)) | ||
| shutil.copytree(os.path.join(self.path, scm_dir), os.path.join(cpath, scm_dir)) | ||
| except Exception: | ||
| warning("Unable to cache \"%s\" to \"%s\"" % (self.path, cpath)) | ||
| return False | ||
| def can_update(self, clean, clean_deps): | ||
| err = None | ||
| if (self.is_local or self.url is None) and not clean_deps: | ||
| err = ( | ||
| "Preserving local library \"%s\" in \"%s\".\nPlease publish this library to a remote URL to be able to restore it at any time." | ||
| "You can use --ignore switch to ignore all local libraries and update only the published ones.\n" | ||
| "You can also use --clean-deps switch to remove all local libraries. WARNING: This action cannot be undone." % ( | ||
| self.name, self.path)) | ||
| elif not clean and self.dirty(): | ||
| err = ( | ||
| "Uncommitted changes in \"%s\" in \"%s\".\nPlease discard or stash them first and then retry update.\n" | ||
| "You can also use --clean switch to discard all uncommitted changes. WARNING: This action cannot be undone." % ( | ||
| self.name, self.path)) | ||
| elif not clean_deps and self.outgoing(): | ||
| err = ( | ||
| "Unpublished changes in \"%s\" in \"%s\".\nPlease publish them first using the \"publish\" command.\n" | ||
| "You can also use --clean-deps to discard all local commits and replace the library with the one included in this revision. WARNING: This action cannot be undone." % ( | ||
| self.name, self.path)) | ||
| return (False, err) if err else (True, "OK") | ||
| def check_repo(self, show_warning=None): | ||
| err = None | ||
| if not os.path.isdir(self.path): | ||
| err = ( | ||
| "Library reference \"%s\" points to non-existing library in \"%s\"\n" | ||
| "You can use \"aos deploy\" to import the missing libraries.\n" | ||
| "You can also use \"aos sync\" to synchronize and remove all invalid library references." % ( | ||
| os.path.basename(self.lib), self.path)) | ||
| elif not self.isrepo(self.path): | ||
| err = ( | ||
| "Library reference \"%s\" points to a folder \"%s\", which is not a valid repository.\n" | ||
| "You can remove the conflicting folder manually and use \"aos deploy\" to import the missing libraries\n" | ||
| "You can also remove library reference \"%s\" and use \"aos sync\" again." % ( | ||
| os.path.basename(self.lib), self.path, self.lib)) | ||
| if err: | ||
| if show_warning: | ||
| warning(err) | ||
| else: | ||
| error(err, 1) | ||
| return False | ||
| return True |
| import os, sys, time, serial, subprocess, traceback, glob | ||
| eml3047_stlink_serials = { | ||
| '/dev/lora-001':'001_serial_id', | ||
| '/dev/lora-002':'002_serial_id' | ||
| } | ||
| def list_devices(os): | ||
| return glob.glob('/dev/lora-*') | ||
| def exist(device): | ||
| return os.path.exists(device) | ||
| def new_device(device): | ||
| if device not in eml3047_stlink_serials: | ||
| print('eml3047: unknow board {0}'.format(device)) | ||
| return None | ||
| try: | ||
| ser = serial.Serial(device, 115200, timeout = 0.02) | ||
| subprocess.call(['st-flash', '--serial', eml3047_stlink_serials[device], 'reset']) | ||
| except: | ||
| ser = None | ||
| print('eml3047: open {0} error'.format(device)) | ||
| return ser | ||
| def erase(device): | ||
| retry = 3 | ||
| error = 'fail' | ||
| if device not in eml3407_stlink_serials: | ||
| return error | ||
| while retry > 0: | ||
| script = ['st-flash', '--serial', eml3407_stlink_serials[device], 'erase'] | ||
| ret = subprocess.call(script) | ||
| if ret == 0: | ||
| error = 'success' | ||
| break | ||
| retry -= 1 | ||
| time.sleep(4) | ||
| return error | ||
| def program(device, address, file): | ||
| retry = 3 | ||
| error = 'fail' | ||
| if device not in eml3047_stlink_serials: | ||
| return error | ||
| while retry > 0: | ||
| script = ['st-flash', '--serial', eml3047_stlink_serials[device]] | ||
| script += ['write', file, address] | ||
| ret = subprocess.call(script) | ||
| if ret == 0: | ||
| error = 'success' | ||
| break | ||
| retry -= 1 | ||
| time.sleep(4) | ||
| return error | ||
| def control(device, operation): | ||
| ret = 'fail' | ||
| if device not in eml3047_stlink_serials: | ||
| return ret | ||
| try: | ||
| if operation == 'reset': | ||
| subprocess.call(['st-flash', '--serial', eml3047_stlink_serials[device], 'reset']) | ||
| ret = 'success' | ||
| elif operation == 'stop': | ||
| ret = 'fail' | ||
| elif operation == 'start': | ||
| ret = 'fail' | ||
| except: | ||
| pass | ||
| return ret |
| import os, sys, time, serial, subprocess, traceback, glob | ||
| def list_devices(host_os): | ||
| return glob.glob('/dev/esp32-*') | ||
| def exist(device): | ||
| return os.path.exists(device) | ||
| def new_device(device): | ||
| try: | ||
| ser = serial.Serial(device, 115200, timeout = 0.02) | ||
| ser.setRTS(True) | ||
| ser.setDTR(False) | ||
| time.sleep(0.1) | ||
| ser.setDTR(True) | ||
| except: | ||
| ser = None | ||
| print 'esp32: open {0} error'.format(device) | ||
| return ser | ||
| def erase(device): | ||
| retry = 3 | ||
| baudrate = 230400 | ||
| error = 'fail' | ||
| while retry > 0: | ||
| script = ['esptool.py'] | ||
| script += ['--chip'] | ||
| script += ['esp32'] | ||
| script += ['--port'] | ||
| script += [device] | ||
| script += ['--baud'] | ||
| script += [str(baudrate)] | ||
| script += ['erase_flash'] | ||
| ret = subprocess.call(script) | ||
| if ret == 0: | ||
| error = 'success' | ||
| break | ||
| retry -= 1 | ||
| baudrate = baudrate / 2 | ||
| return error | ||
| def program(device, address, file): | ||
| retry = 3 | ||
| baudrate = 230400 | ||
| error = 'fail' | ||
| flash_tool_path = os.path.dirname(os.path.realpath(__file__)) + '/esptool.py' | ||
| while retry > 0: | ||
| script = ['esptool.py'] | ||
| script += ['--chip'] | ||
| script += ['esp32'] | ||
| script += ['--port'] | ||
| script += [device] | ||
| script += ['--baud'] | ||
| script += [str(baudrate)] | ||
| script += ['--before'] | ||
| script += ['default_reset'] | ||
| script += ['write_flash'] | ||
| script += ['-z'] | ||
| script += ['--flash_mode'] | ||
| script += ['dio'] | ||
| script += ['--flash_freq'] | ||
| script += ['40m'] | ||
| script += ['--flash_size'] | ||
| script += ['4MB'] | ||
| script += [address] | ||
| script += [file] | ||
| ret = subprocess.call(script) | ||
| if ret == 0: | ||
| error = 'success' | ||
| break | ||
| retry -= 1 | ||
| baudrate = baudrate / 2 | ||
| control(device, 'reset') | ||
| return error | ||
| def control(device, operation): | ||
| try: | ||
| ser = serial.Serial(device, 115200) | ||
| except: | ||
| traceback.print_exc() | ||
| print 'esp32 control error: unable to open {0}'.format(device) | ||
| return 'fail' | ||
| ret = 'fail' | ||
| try: | ||
| if operation == 'reset': | ||
| ser.setDTR(False) | ||
| time.sleep(0.1) | ||
| ser.setDTR(True) | ||
| ret = 'success' | ||
| elif operation == 'stop': | ||
| ser.setDTR(False) | ||
| ret = 'success' | ||
| elif operation == 'start': | ||
| ser.setDTR(True) | ||
| ret = 'success' | ||
| except: | ||
| pass | ||
| ser.close() | ||
| return ret |
| #!/usr/bin/env python | ||
| import sys, os, serial, time, platform, logging | ||
| from functools import partial | ||
| # MODEM Protocol bytes | ||
| NUL = b'\x00' | ||
| SOH = b'\x01' | ||
| STX = b'\x02' | ||
| EOT = b'\x04' | ||
| ACK = b'\x06' | ||
| DLE = b'\x10' | ||
| NAK = b'\x15' | ||
| CAN = b'\x18' | ||
| CRC = b'C' | ||
| LOG_LEVEL_ERROR=0 | ||
| LOG_LEVEL_WARN =1 | ||
| LOG_LEVEL_INFO =2 | ||
| LOG_LEVEL_DEBUG=3 | ||
| class XMODEM(object): | ||
| ''' | ||
| XMODEM Protocol handler, expects an object to read from and an object to | ||
| write to. | ||
| >>> def getc(size, timeout=1): | ||
| ... return data or None | ||
| ... | ||
| >>> def putc(data, timeout=1): | ||
| ... return size or None | ||
| ... | ||
| >>> modem = XMODEM(getc, putc) | ||
| :param getc: Function to retrieve bytes from a stream | ||
| :type getc: callable | ||
| :param putc: Function to transmit bytes to a stream | ||
| :type putc: callable | ||
| :param mode: XMODEM protocol mode | ||
| :type mode: string | ||
| :param pad: Padding character to make the packets match the packet size | ||
| :type pad: char | ||
| ''' | ||
| # crctab calculated by Mark G. Mendel, Network Systems Corporation | ||
| crctable = [ | ||
| 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, | ||
| 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, | ||
| 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, | ||
| 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, | ||
| 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, | ||
| 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, | ||
| 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, | ||
| 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, | ||
| 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, | ||
| 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, | ||
| 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, | ||
| 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, | ||
| 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, | ||
| 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, | ||
| 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, | ||
| 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, | ||
| 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, | ||
| 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, | ||
| 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, | ||
| 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, | ||
| 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, | ||
| 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, | ||
| 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, | ||
| 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, | ||
| 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, | ||
| 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, | ||
| 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, | ||
| 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, | ||
| 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, | ||
| 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, | ||
| 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, | ||
| 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0, | ||
| ] | ||
| def __init__(self, getc, putc, mode='xmodem', pad=b'\x1a'): | ||
| self.getc = getc | ||
| self.putc = putc | ||
| self.mode = mode | ||
| self.pad = pad | ||
| self.log_level = LOG_LEVEL_WARN | ||
| def log(self, log_level, logstr): | ||
| if log_level <= self.log_level: | ||
| print logstr | ||
| def abort(self, count=2, timeout=60): | ||
| ''' | ||
| Send an abort sequence using CAN bytes. | ||
| ''' | ||
| for _ in range(count): | ||
| self.putc(CAN, timeout) | ||
| def send(self, stream, retry=16, timeout=60, quiet=False, callback=None): | ||
| ''' | ||
| Send a stream via the XMODEM protocol. | ||
| >>> stream = open('/etc/issue', 'rb') | ||
| >>> print(modem.send(stream)) | ||
| True | ||
| Returns ``True`` upon successful transmission or ``False`` in case of | ||
| failure. | ||
| :param stream: The stream object to send data from. | ||
| :type stream: stream (file, etc.) | ||
| :param retry: The maximum number of times to try to resend a failed | ||
| packet before failing. | ||
| :type retry: int | ||
| :param timeout: The number of seconds to wait for a response before | ||
| timing out. | ||
| :type timeout: int | ||
| :param quiet: If True, write transfer information to stderr. | ||
| :type quiet: bool | ||
| :param callback: Reference to a callback function that has the | ||
| following signature. This is useful for | ||
| getting status updates while a xmodem | ||
| transfer is underway. | ||
| Expected callback signature: | ||
| def callback(total_packets, success_count, error_count) | ||
| :type callback: callable | ||
| ''' | ||
| # initialize protocol | ||
| try: | ||
| packet_size = dict( | ||
| xmodem = 128, | ||
| xmodem1k = 1024, | ||
| ymodem = 1024, | ||
| )[self.mode] | ||
| except KeyError: | ||
| raise ValueError("Invalid mode specified: {self.mode!r}" | ||
| .format(self=self)) | ||
| self.log(LOG_LEVEL_DEBUG, 'Begin start sequence, packet_size={0:d}'.format(packet_size)) | ||
| error_count = 0 | ||
| crc_mode = 0 | ||
| cancel = 0 | ||
| while True: | ||
| char = self.getc(1) | ||
| if char: | ||
| if char == NAK: | ||
| self.log(LOG_LEVEL_DEBUG, 'standard checksum requested (NAK).') | ||
| crc_mode = 0 | ||
| break | ||
| elif char == CRC: | ||
| self.log(LOG_LEVEL_DEBUG, '16-bit CRC requested (CRC).') | ||
| crc_mode = 1 | ||
| break | ||
| elif char == CAN: | ||
| if not quiet: | ||
| sys.stderr.write('received CAN\n') | ||
| if cancel: | ||
| self.log(LOG_LEVEL_INFO, 'Transmission canceled: received 2xCAN at start-sequence') | ||
| return False | ||
| else: | ||
| self.log(LOG_LEVEL_DEBUG, 'cancellation at start sequence.') | ||
| cancel = 1 | ||
| else: | ||
| self.log(LOG_LEVEL_ERROR, 'send error: expected NAK, CRC, or CAN; got {0}'.format(char)) | ||
| error_count += 1 | ||
| if error_count > retry: | ||
| self.log(LOG_LEVEL_INFO, 'send error: error_count reached {0}, aborting.'.format(retry)) | ||
| self.abort(timeout=timeout) | ||
| return False | ||
| # send data | ||
| error_count = 0 | ||
| success_count = 0 | ||
| total_packets = 0 | ||
| if self.mode == 'ymodem': | ||
| sequence = 0 | ||
| filenames = stream | ||
| else: | ||
| sequence = 1 | ||
| while True: | ||
| # build packet | ||
| if self.mode == 'ymodem' and success_count == 0: | ||
| # send packet sequence 0 containing: | ||
| # Filename Length [Modification-Date [Mode [Serial-Number]]] | ||
| # 'stream' is actually the filename | ||
| import os | ||
| if len(filenames): | ||
| filename = filenames.pop() | ||
| stream = open(filename, 'rb') | ||
| stat = os.stat(filename) | ||
| data = os.path.basename(filename) + NUL + str(stat.st_size) | ||
| self.log(LOG_LEVEL_DEBUG, 'ymodem sending : {0} len:{1}'.format(filename, stat.st_size)) | ||
| else: | ||
| # empty file name packet terminates transmission | ||
| filename = '' | ||
| data = '' | ||
| stream = None | ||
| self.log(LOG_LEVEL_DEBUG, 'ymodem done, sending empty header.') | ||
| if len(data) <= 128: | ||
| header_size = 128 | ||
| else: | ||
| header_size = 1024 | ||
| header = self._make_send_header(header_size, sequence) | ||
| data = data.ljust(header_size, NUL) | ||
| checksum = self._make_send_checksum(crc_mode, data) | ||
| else: | ||
| # happens after sending ymodem empty filename | ||
| if not stream: | ||
| return True | ||
| # normal data packet | ||
| data = stream.read(packet_size) | ||
| if not data: | ||
| # end of stream | ||
| self.log(LOG_LEVEL_DEBUG, 'send: at EOF') | ||
| break | ||
| total_packets += 1 | ||
| header = self._make_send_header(packet_size, sequence) | ||
| data = data.ljust(packet_size, self.pad) | ||
| checksum = self._make_send_checksum(crc_mode, data) | ||
| # emit packet & get ACK | ||
| while True: | ||
| self.log(LOG_LEVEL_DEBUG, 'send: block {0}'.format(sequence)) | ||
| self.putc(header + data + checksum) | ||
| char = self.getc(1, timeout) | ||
| if char == ACK: | ||
| success_count += 1 | ||
| if callable(callback): | ||
| callback(total_packets, success_count, error_count) | ||
| error_count = 0 | ||
| if self.mode == 'ymodem' and success_count == 1 and len(filename): | ||
| char = self.getc(1, timeout) | ||
| if char == DLE: # dunno why | ||
| char = self.getc(1, timeout) | ||
| if char == CRC: | ||
| break | ||
| self.log(LOG_LEVEL_ERROR, 'send error: ymodem expected CRC; got {0} for block {1}'.format(char, sequence)) | ||
| else: | ||
| break | ||
| self.log(LOG_LEVEL_ERROR, 'send error: expected ACK; got {0} for block {1}'.format(char, sequence)) | ||
| error_count += 1 | ||
| if callable(callback): | ||
| callback(total_packets, success_count, error_count) | ||
| if error_count > retry: | ||
| # excessive amounts of retransmissions requested, | ||
| # abort transfer | ||
| self.log(LOG_LEVEL_ERROR, 'send error: NAK received {0} times, aborting.'.format(error_count)) | ||
| self.abort(timeout=timeout) | ||
| return False | ||
| # keep track of sequence | ||
| sequence = (sequence + 1) % 0x100 | ||
| # emit EOT and get corresponding ACK | ||
| while True: | ||
| self.log(LOG_LEVEL_DEBUG, 'sending EOT, awaiting ACK') | ||
| # end of transmission | ||
| self.putc(EOT) | ||
| # An ACK should be returned | ||
| char = self.getc(1, timeout) | ||
| if char == ACK: | ||
| break | ||
| else: | ||
| self.log(LOG_LEVEL_ERROR, 'send error: expected ACK; got {0}'.format(char)) | ||
| error_count += 1 | ||
| if error_count > retry: | ||
| self.log(LOG_LEVEL_WARN, 'EOT was not ACKd, aborting transfer') | ||
| self.abort(timeout=timeout) | ||
| return False | ||
| self.log(LOG_LEVEL_INFO, 'Transmission successful (ACK received).') | ||
| if self.mode == 'ymodem': | ||
| # YMODEM - recursively send next file | ||
| # or empty filename header to end the xfer batch. | ||
| stream.close() | ||
| return self.send(filenames, retry, timeout, quiet, callback) | ||
| return True | ||
| def _make_send_header(self, packet_size, sequence): | ||
| assert packet_size in (128, 1024), packet_size | ||
| _bytes = [] | ||
| if packet_size == 128: | ||
| _bytes.append(ord(SOH)) | ||
| elif packet_size == 1024: | ||
| _bytes.append(ord(STX)) | ||
| _bytes.extend([sequence, 0xff - sequence]) | ||
| return bytearray(_bytes) | ||
| def _make_send_checksum(self, crc_mode, data): | ||
| _bytes = [] | ||
| if crc_mode: | ||
| crc = self.calc_crc(data) | ||
| _bytes.extend([crc >> 8, crc & 0xff]) | ||
| else: | ||
| crc = self.calc_checksum(data) | ||
| _bytes.append(crc) | ||
| return bytearray(_bytes) | ||
| def recv(self, stream, crc_mode=1, retry=16, timeout=60, delay=1, quiet=0): | ||
| ''' | ||
| Receive a stream via the XMODEM protocol. | ||
| >>> stream = open('/etc/issue', 'wb') | ||
| >>> print(modem.recv(stream)) | ||
| 2342 | ||
| Returns the number of bytes received on success or ``None`` in case of | ||
| failure. | ||
| ''' | ||
| # initiate protocol | ||
| error_count = 0 | ||
| char = 0 | ||
| cancel = 0 | ||
| while True: | ||
| # first try CRC mode, if this fails, | ||
| # fall back to checksum mode | ||
| if error_count >= retry: | ||
| self.log(LOG_LEVEL_INFO, 'error_count reached {0}, aborting.'.format(retry)) | ||
| self.abort(timeout=timeout) | ||
| return None | ||
| elif crc_mode and error_count < (retry // 2): | ||
| if not self.putc(CRC): | ||
| self.log(LOG_LEVEL_DEBUG, 'recv error: putc failed, sleeping for {0}'.format(delay)) | ||
| time.sleep(delay) | ||
| error_count += 1 | ||
| else: | ||
| crc_mode = 0 | ||
| if not self.putc(NAK): | ||
| self.log(LOG_LEVEL_DEBUG, 'recv error: putc failed, sleeping for {0}'.format(delay)) | ||
| time.sleep(delay) | ||
| error_count += 1 | ||
| char = self.getc(1, timeout) | ||
| if char is None: | ||
| self.log(LOG_LEVEL_WARN, 'recv error: getc timeout in start sequence') | ||
| error_count += 1 | ||
| continue | ||
| elif char == SOH: | ||
| self.log(LOG_LEVEL_DEBUG, 'recv: SOH') | ||
| break | ||
| elif char == STX: | ||
| self.log(LOG_LEVEL_DEBUG, 'recv: STX') | ||
| break | ||
| elif char == CAN: | ||
| if cancel: | ||
| self.log(LOG_LEVEL_INFO, 'Transmission canceled: received 2xCAN at start-sequence') | ||
| return None | ||
| else: | ||
| self.log(LOG_LEVEL_DEBUG, 'cancellation at start sequence.') | ||
| cancel = 1 | ||
| else: | ||
| error_count += 1 | ||
| # read data | ||
| error_count = 0 | ||
| income_size = 0 | ||
| packet_size = 128 | ||
| sequence = 1 | ||
| cancel = 0 | ||
| while True: | ||
| while True: | ||
| if char == SOH: | ||
| if packet_size != 128: | ||
| self.log(LOG_LEVEL_DEBUG, 'recv: SOH, using 128b packet_size') | ||
| packet_size = 128 | ||
| break | ||
| elif char == STX: | ||
| if packet_size != 1024: | ||
| self.log(LOG_LEVEL_DEBUG, 'recv: SOH, using 1k packet_size') | ||
| packet_size = 1024 | ||
| break | ||
| elif char == EOT: | ||
| # We received an EOT, so send an ACK and return the | ||
| # received data length. | ||
| self.putc(ACK) | ||
| self.log(LOG_LEVEL_INFO, "Transmission complete, {0} bytes".format(income_size)) | ||
| return income_size | ||
| elif char == CAN: | ||
| # cancel at two consecutive cancels | ||
| if cancel: | ||
| self.log(LOG_LEVEL_INFO, 'Transmission canceled: received 2xCAN at block {0}'.format(sequence)) | ||
| return None | ||
| else: | ||
| self.log(LOG_LEVEL_DEBUG, 'cancellation at block {0}'.format(sequence)) | ||
| cancel = 1 | ||
| else: | ||
| err_msg = ('recv error: expected SOH, EOT; got {0}'.format(char)) | ||
| if not quiet: | ||
| sys.stderr.write(err_msg+"\n") | ||
| self.log(LOG_LEVEL_WARN, err_msg) | ||
| error_count += 1 | ||
| if error_count > retry: | ||
| self.log(LOG_LEVEL_INFO, 'error_count reached {0}, aborting.'.format(retry)) | ||
| self.abort() | ||
| return None | ||
| # read sequence | ||
| error_count = 0 | ||
| cancel = 0 | ||
| self.log(LOG_LEVEL_DEBUG, 'recv: data block {0}'.format(sequence)) | ||
| seq1 = self.getc(1, timeout) | ||
| if seq1 is None: | ||
| self.log(LOG_LEVEL_WARN, 'getc failed to get first sequence byte') | ||
| seq2 = None | ||
| else: | ||
| seq1 = ord(seq1) | ||
| seq2 = self.getc(1, timeout) | ||
| if seq2 is None: | ||
| self.log(LOG_LEVEL_WARN, 'getc failed to get second sequence byte') | ||
| else: | ||
| # second byte is the same as first as 1's complement | ||
| seq2 = 0xff - ord(seq2) | ||
| if not (seq1 == seq2 == sequence): | ||
| # consume data anyway ... even though we will discard it, | ||
| # it is not the sequence we expected! | ||
| err_msg = 'expected sequence {0}, got (seq1={1}, seq2={2}), \ | ||
| receiving next block, will NAK.'.format(sequence, seq1, seq2) | ||
| self.log(LOG_LEVEL_ERROR, err_msg) | ||
| self.getc(packet_size + 1 + crc_mode) | ||
| else: | ||
| # sequence is ok, read packet | ||
| # packet_size + checksum | ||
| data = self.getc(packet_size + 1 + crc_mode, timeout) | ||
| valid, data = self._verify_recv_checksum(crc_mode, data) | ||
| # valid data, append chunk | ||
| if valid: | ||
| income_size += len(data) | ||
| stream.write(data) | ||
| self.putc(ACK) | ||
| sequence = (sequence + 1) % 0x100 | ||
| # get next start-of-header byte | ||
| char = self.getc(1, timeout) | ||
| continue | ||
| # something went wrong, request retransmission | ||
| self.log(LOG_LEVEL_WARN, 'recv error: purge, requesting retransmission (NAK)') | ||
| while True: | ||
| # When the receiver wishes to <nak>, it should call a "PURGE" | ||
| # subroutine, to wait for the line to clear. Recall the sender | ||
| # tosses any characters in its UART buffer immediately upon | ||
| # completing sending a block, to ensure no glitches were mis- | ||
| # interpreted. The most common technique is for "PURGE" to | ||
| # call the character receive subroutine, specifying a 1-second | ||
| # timeout, and looping back to PURGE until a timeout occurs. | ||
| # The <nak> is then sent, ensuring the other end will see it. | ||
| data = self.getc(1, timeout=1) | ||
| if data is None: | ||
| break | ||
| assert False, data | ||
| self.putc(NAK) | ||
| # get next start-of-header byte | ||
| char = self.getc(1, timeout) | ||
| continue | ||
| def _verify_recv_checksum(self, crc_mode, data): | ||
| if crc_mode: | ||
| _checksum = bytearray(data[-2:]) | ||
| their_sum = (_checksum[0] << 8) + _checksum[1] | ||
| data = data[:-2] | ||
| our_sum = self.calc_crc(data) | ||
| valid = bool(their_sum == our_sum) | ||
| if not valid: | ||
| self.log(LOG_LEVEL_WARN, 'recv error: checksum fail (theirs={0:04x}, ours={1:04x}), '.format(their_sum, our_sum)) | ||
| else: | ||
| _checksum = bytearray([data[-1]]) | ||
| their_sum = _checksum[0] | ||
| data = data[:-1] | ||
| our_sum = self.calc_checksum(data) | ||
| valid = their_sum == our_sum | ||
| if not valid: | ||
| self.log(LOG_LEVEL_WARN, 'recv error: checksum fail (theirs={0:02x}, ours={1:02x})'.forma(their_sum, our_sum)) | ||
| return valid, data | ||
| def calc_checksum(self, data, checksum=0): | ||
| ''' | ||
| Calculate the checksum for a given block of data, can also be used to | ||
| update a checksum. | ||
| >>> csum = modem.calc_checksum('hello') | ||
| >>> csum = modem.calc_checksum('world', csum) | ||
| >>> hex(csum) | ||
| '0x3c' | ||
| ''' | ||
| if platform.python_version_tuple() >= ('3', '0', '0'): | ||
| return (sum(data) + checksum) % 256 | ||
| else: | ||
| return (sum(map(ord, data)) + checksum) % 256 | ||
| def calc_crc(self, data, crc=0): | ||
| ''' | ||
| Calculate the Cyclic Redundancy Check for a given block of data, can | ||
| also be used to update a CRC. | ||
| >>> crc = modem.calc_crc('hello') | ||
| >>> crc = modem.calc_crc('world', crc) | ||
| >>> hex(crc) | ||
| '0xd5e3' | ||
| ''' | ||
| for char in bytearray(data): | ||
| crctbl_idx = ((crc >> 8) ^ char) & 0xff | ||
| crc = ((crc << 8) ^ self.crctable[crctbl_idx]) & 0xffff | ||
| return crc & 0xffff | ||
| port = 0 | ||
| prcss_debug = False | ||
| modem_debug = False | ||
| def getc(size, timeout=1): | ||
| port.timeout = timeout | ||
| ret = port.read(size) | ||
| if modem_debug: | ||
| print "read:", | ||
| print ret, | ||
| print ", {0} bytes".format(len(ret)) | ||
| return ret | ||
| def putc(data, timeout=1): | ||
| port.timeout = timeout | ||
| len = port.write(data) | ||
| if modem_debug: | ||
| print "{0} bytes writen".format(len) | ||
| return len | ||
| def send_file(filename): | ||
| modem = XMODEM(getc, putc, mode="ymodem") | ||
| return modem.send([filename]) | ||
| def assert_response(patterns, timeout): | ||
| port.timeout = 0.01 | ||
| timeout_tick = time.time() + timeout | ||
| while True: | ||
| line = port.readline() | ||
| if prcss_debug and len(line): | ||
| print line | ||
| for pattern in patterns: | ||
| if pattern in line: | ||
| return True | ||
| if time.time() > timeout_tick: | ||
| return False | ||
| def print_usage(): | ||
| print "Usage: {0} port [-a app.bin] [-b bootloader.bin] [-d driver.bin] [--bootloader-baudrate 921600] [--application-baudrate 115200] [--noboot]\n".format(sys.argv[0]) | ||
| print " examples: python {0} /dev/ttyUSB0 -a app.bin, to update app only".format(sys.argv[0]) | ||
| print " : python {0} /dev/ttyUSB1 -b bootloader.bin -a app.bin, to update bootloader and app".format(sys.argv[0]) | ||
| print " : python {0} /dev/ttyUSB0 -a app.bin -d driver.bin, to update app and driver".format(sys.argv[0]) | ||
| if len(sys.argv) < 4: | ||
| print_usage() | ||
| sys.exit(1) | ||
| device = sys.argv[1] | ||
| updates=[] | ||
| bootloader_baudrate=921600 | ||
| application_baudrate=921600 | ||
| bootapp = True | ||
| hardreboot = False | ||
| i = 2 | ||
| update = 0 | ||
| while i < len(sys.argv): | ||
| if sys.argv[i].startswith("0x") and (i + 1) < len(sys.argv): | ||
| if os.path.isfile(sys.argv[i+1]) == False: | ||
| sys.stderr.write("error: file {0} does not exist\n".format(sys.argv[i+1])) | ||
| sys.exit(1) | ||
| updates.append([sys.argv[i], sys.argv[i+1]]) | ||
| update += 1 | ||
| i += 1 | ||
| elif sys.argv[i] == "--bootloader-baudrate" and (i + 1) < len(sys.argv): | ||
| try: | ||
| bootloader_baudrate = int(sys.argv[i+1]) | ||
| except: | ||
| sys.stderr.write("error: invalid bootload baudrate value {0}\n".format(sys.argv[i+1])) | ||
| sys.exit(1) | ||
| i += 1 | ||
| elif sys.argv[i] == "--application-baudrate" and (i + 1) < len(sys.argv): | ||
| try: | ||
| application_baudrate = int(sys.argv[i+1]) | ||
| except: | ||
| sys.stderr.write("error: invalid bootload baudrate value {0}\n".format(sys.argv[i+1])) | ||
| sys.exit(1) | ||
| i += 1 | ||
| elif sys.argv[i] == "--hardreset": | ||
| hardreboot = True | ||
| elif sys.argv[i] == "--noappboot": | ||
| bootapp = False | ||
| i += 1 | ||
| if update <= 0: | ||
| sys.exit(0) | ||
| try: | ||
| port = serial.Serial(device, bootloader_baudrate, timeout = 0.05) | ||
| port.setRTS(False) | ||
| except: | ||
| sys.stderr.write("error: unable to open {0}\n".format(device)) | ||
| sys.exit(1) | ||
| if hardreboot == False: | ||
| port.write("a\r\n") #abort potential ongoing YMODEM transfer | ||
| port.flushInput() | ||
| port.write("help\r\n") | ||
| if assert_response(["bootloader", "read [address] [size]"], 1) == False: | ||
| if application_baudrate != bootloader_baudrate: | ||
| port.baudrate = application_baudrate | ||
| port.flushInput() | ||
| port.write("dummycmd_for_flushing_purpose\r\n") | ||
| time.sleep(0.1) | ||
| port.write("reboot\r\n") | ||
| if assert_response(["reboot"] , 1) == False: | ||
| sys.stderr.write("error: failed to reboot the board, it did not respond to \"reboot\" command\n") | ||
| sys.exit(1) | ||
| if application_baudrate != bootloader_baudrate: | ||
| port.baudrate = bootloader_baudrate | ||
| time.sleep(0.11); port.write(" ") #0.11s | ||
| time.sleep(0.03); port.write(" ") #0.14s | ||
| time.sleep(0.03); port.write(" ") #0.17s | ||
| time.sleep(0.03); port.write(" ") #0.20s | ||
| time.sleep(0.03); port.write(" ") #0.23s | ||
| time.sleep(0.03); port.write(" \r\n") #0.26s | ||
| if assert_response(["ootloader", "BOOTLODER"], 1) == False: | ||
| sys.stderr.write("error: failed to enter bootloader\n") | ||
| sys.exit(1) | ||
| else: | ||
| port.setRTS(True) | ||
| time.sleep(0.1) | ||
| port.setRTS(False) | ||
| time.sleep(0.11); port.write(" ") #0.11s | ||
| time.sleep(0.03); port.write(" ") #0.14s | ||
| time.sleep(0.03); port.write(" ") #0.17s | ||
| time.sleep(0.03); port.write(" ") #0.20s | ||
| time.sleep(0.03); port.write(" ") #0.23s | ||
| time.sleep(0.03); port.write(" \r\n") #0.26s | ||
| if assert_response(["ootloader"], 1) == False: | ||
| sys.stderr.write("error: failed to enter bootloader\n") | ||
| sys.exit(1) | ||
| port.flushInput() | ||
| failed_num = 0 | ||
| for [addr, image] in updates: | ||
| status_str = "updating {0} with {1} @ address {2} ...".format(device, image, addr) | ||
| print status_str | ||
| port.write("write {0}\r\n".format(addr)) | ||
| if assert_response(["Waiting for the file to be sent"], 1) == False: | ||
| sys.stderr.write("error: waiting for target to enter into YMODEM recived mode failed\n") | ||
| sys.exit(1) | ||
| result = send_file(image) | ||
| if result == True: | ||
| status_str = status_str + " succeed" | ||
| else: | ||
| status_str = status_str + " failed" | ||
| failed_num += 1 | ||
| print status_str | ||
| if bootapp and failed_num == 0: | ||
| port.write("boot\r\n") | ||
| assert_response(["Booting......"], 1) | ||
| port.close() | ||
| sys.exit(failed_num) | ||
| import os, sys, time, serial, subprocess, traceback, glob | ||
| def list_devices(host_os): | ||
| return glob.glob('/dev/mk3060-*') | ||
| def exist(device): | ||
| return os.path.exists(device) | ||
| def new_device(device): | ||
| try: | ||
| ser = serial.Serial(device, 921600, timeout = 0.02) | ||
| ser.setRTS(False) | ||
| except: | ||
| ser = None | ||
| print 'mk3060: open {0} error'.format(device) | ||
| return ser | ||
| def erase(device): | ||
| error = 'fail' | ||
| return error | ||
| def program(device, address, file): | ||
| retry = 3 | ||
| error = 'fail' | ||
| flash_tool_path = os.path.dirname(os.path.realpath(__file__)) + '/mk3060_firmware_update.py' | ||
| while retry > 0: | ||
| script = ['python', flash_tool_path] | ||
| script += [device] | ||
| script += [address] | ||
| script += [file] | ||
| script += ['--hardreset'] | ||
| ret = subprocess.call(script) | ||
| if ret == 0: | ||
| error = 'success' | ||
| break | ||
| retry -= 1 | ||
| time.sleep(4) | ||
| return error | ||
| def control(device, operation): | ||
| try: | ||
| ser = serial.Serial(device, 921600) | ||
| except: | ||
| traceback.print_exc() | ||
| print 'mk3060 control error: unable to open {0}'.format(device) | ||
| return 'fail' | ||
| ret = 'fail' | ||
| try: | ||
| if operation == 'reset': | ||
| ser.setRTS(True) | ||
| time.sleep(0.1) | ||
| ser.setRTS(False) | ||
| ret = 'success' | ||
| elif operation == 'stop': | ||
| ser.setRTS(True) | ||
| ret = 'success' | ||
| elif operation == 'start': | ||
| ser.setRTS(False) | ||
| ret = 'success' | ||
| except: | ||
| pass | ||
| ser.close() | ||
| return ret |
| import sys, time, serial, subprocess, shlex, traceback, glob | ||
| from os import path | ||
| nucleo_stlink_serials = {} | ||
| nucleo_debug_sessions = {} | ||
| def list_devices(host_os): | ||
| return glob.glob('/dev/stm32*-*') | ||
| def exist(device): | ||
| return path.exists(device) | ||
| def new_device(device): | ||
| info = subprocess.check_output(['udevadm', 'info', '-q', 'property', '-n', device]) | ||
| vendor_id = None | ||
| model_id = None | ||
| serial_id = None | ||
| for line in info.split('\n'): | ||
| line.replace('\n', '') | ||
| if line.startswith('ID_VENDOR_ID='): | ||
| vendor_id = line.replace('ID_VENDOR_ID=', '') | ||
| if line.startswith('ID_MODEL_ID='): | ||
| model_id = line.replace('ID_MODEL_ID=', '') | ||
| if line.startswith('ID_SERIAL_SHORT='): | ||
| serial_id = line.replace('ID_SERIAL_SHORT=', '') | ||
| if vendor_id != '0483' or model_id != '374b' or serial_id == None: | ||
| print('stm32_nucleo: parse stlink serial_id for {0} failed'.format(device)) | ||
| print('stm32_nucleo: vendor_id:{0} model_id:{1} serial_id:{2}'.format(repr(vendor_id), repr(model_id), repr(serial_id))) | ||
| return None | ||
| if len(serial_id) > 15: | ||
| serial_id = serial_id[:15] | ||
| serial_hexid = '' | ||
| for c in serial_id: | ||
| serial_hexid += '{0:02x}'.format(ord(c)) | ||
| nucleo_stlink_serials[device] = serial_hexid | ||
| try: | ||
| ser = serial.Serial(device, 115200, timeout = 0.02) | ||
| subprocess.call(['st-flash', '--serial', nucleo_stlink_serials[device], 'reset']) | ||
| except: | ||
| ser = None | ||
| print('stm32_nucleo: open {0} error'.format(device)) | ||
| return ser | ||
| def erase(device): | ||
| retry = 3 | ||
| error = 'fail' | ||
| if device not in nucleo_stlink_serials: | ||
| return error | ||
| while retry > 0: | ||
| script = ['st-flash', '--serial', nucleo_stlink_serials[device], 'erase'] | ||
| ret = subprocess.call(script) | ||
| if ret == 0: | ||
| error = 'success' | ||
| break | ||
| retry -= 1 | ||
| time.sleep(4) | ||
| return error | ||
| def program(device, address, file): | ||
| retry = 3 | ||
| error = 'fail' | ||
| if device not in nucleo_stlink_serials: | ||
| return error | ||
| while retry > 0: | ||
| script = ['st-flash', '--serial', nucleo_stlink_serials[device]] | ||
| script += ['write', file, address] | ||
| ret = subprocess.call(script) | ||
| if ret == 0: | ||
| error = 'success' | ||
| break | ||
| retry -= 1 | ||
| time.sleep(4) | ||
| return error | ||
| def control(device, operation): | ||
| ret = 'fail' | ||
| if device not in nucleo_stlink_serials: | ||
| return ret | ||
| try: | ||
| if operation == 'reset': | ||
| subprocess.call(['st-flash', '--serial', nucleo_stlink_serials[device], 'reset']) | ||
| ret = 'success' | ||
| elif operation == 'stop': | ||
| ret = 'fail' | ||
| elif operation == 'start': | ||
| ret = 'fail' | ||
| except: | ||
| pass | ||
| return ret | ||
| def debug_start(device, port): | ||
| if device not in nucleo_stlink_serials: | ||
| return 'nonexist' | ||
| if device in nucleo_debug_sessions: | ||
| return 'busy' | ||
| for device in nucleo_debug_sessions: | ||
| if nucleo_debug_sessions[device]['port'] == port: | ||
| return 'port_in_use' | ||
| logfile = path.join(path.expanduser('~'), '.udclient', path.basename(device) + '-debuglog.txt') | ||
| try: | ||
| flog = open(logfile, 'a+') | ||
| except: | ||
| traceback.print_exc() | ||
| return 'open_log_fail' | ||
| command = 'st-util -m --serial {0} -p {1}'.format(nucleo_stlink_serials[device], port) | ||
| command = shlex.split(command) | ||
| p = subprocess.Popen(command, stdout=flog, stderr=flog) | ||
| time.sleep(0.2) | ||
| if p.poll() != None: | ||
| return 'fail' | ||
| nucleo_debug_sessions[device] = {'process': p, 'port': port, 'flog':flog } | ||
| return 'success' | ||
| def debug_stop(device): | ||
| if device not in nucleo_debug_sessions: | ||
| return 'fail' | ||
| nucleo_debug_sessions[device]['process'].kill() | ||
| nucleo_debug_sessions[device]['flog'].close() | ||
| nucleo_debug_sessions.pop(device) | ||
| return 'success' | ||
-1080
| import os, sys, time, platform, json, traceback, random, re, glob, uuid | ||
| import socket, ssl, thread, threading, subprocess, signal, Queue, importlib | ||
| import select | ||
| from os import path | ||
| import packet as pkt | ||
| MAX_MSG_LENGTH = 65536 | ||
| ENCRYPT = True | ||
| DEBUG = True | ||
| EN_STATUS_POLL = True | ||
| LOCALLOG = False | ||
| def signal_handler(sig, frame): | ||
| print "received SIGINT" | ||
| raise KeyboardInterrupt | ||
| def queue_safeput(queue, item): | ||
| try: | ||
| queue.put(item, False) | ||
| except: | ||
| pass | ||
| class ConnectionLost(Exception): | ||
| pass | ||
| class Client: | ||
| def __init__(self): | ||
| self.service_socket = None | ||
| self.output_queue = Queue.Queue(256) | ||
| self.debug_queue = Queue.Queue(256) | ||
| self.devices = {} | ||
| self.keep_running = True | ||
| self.connected = False | ||
| self.poll_str = '\x1b[t' | ||
| bytes = os.urandom(4) | ||
| for byte in bytes: | ||
| self.poll_str += '{0:02x}'.format(ord(byte)) | ||
| self.poll_str += 'm' | ||
| self.poll_interval = 60 | ||
| self.uuid = None | ||
| self.model_interface = {} | ||
| self.mesh_changed = [re.compile('become leader'), | ||
| re.compile('become detached'), | ||
| re.compile('allocate sid 0x[0-9a-f]{4}, become [0-9] in net [0-9a-f]{4}')] | ||
| self.neighbor_changed = [re.compile('sid [0-9a-f]{4} mac [0-9a-f]{16} is replaced'), | ||
| re.compile('[0-9a-f]{1,4} neighbor [0-9a-f]{16} become inactive')] | ||
| self.device_uuid_changed = ["ACCS: connected", | ||
| "ACCS: disconnected", | ||
| 'GATEWAY: connect to server succeed'] | ||
| def packet_send_thread(self): | ||
| heartbeat_timeout = time.time() + 10 | ||
| while self.keep_running: | ||
| try: | ||
| [type, content] = self.output_queue.get(block=True, timeout=0.1) | ||
| except Queue.Empty: | ||
| type = None | ||
| pass | ||
| if self.service_socket == None: | ||
| continue | ||
| if type == None: | ||
| if time.time() < heartbeat_timeout: | ||
| continue | ||
| heartbeat_timeout += 10 | ||
| data = pkt.construct(pkt.HEARTBEAT,'') | ||
| else: | ||
| data = pkt.construct(type, content) | ||
| try: | ||
| self.service_socket.send(data) | ||
| except: | ||
| self.connected = False | ||
| continue | ||
| def send_packet(self, type, content, timeout=0.1): | ||
| if self.service_socket == None: | ||
| return False | ||
| try: | ||
| self.output_queue.put([type, content], True, timeout) | ||
| return True | ||
| except Queue.Full: | ||
| print "error: ouput buffer full, drop packet [{0] {1}]".format(type, content) | ||
| return False | ||
| def debug_daemon_thread(self): | ||
| debug_sessions = {} | ||
| while self.keep_running: | ||
| if debug_sessions == {} or self.debug_queue.empty() == False: | ||
| [type, term, device] = self.debug_queue.get() | ||
| if device not in self.devices: | ||
| content = term + ',' + device + ':' + 'nonexist' | ||
| self.send_packet(type, content) | ||
| continue | ||
| interface = self.devices[device]['interface'] | ||
| if type == pkt.DEVICE_DEBUG_START: | ||
| if 'debug_socket' in self.devices[device]: #session exist | ||
| sock = self.devices[device]['debug_socket'] | ||
| if debug_sessions[sock]['term'] != term: | ||
| content = term + ',' + device + ':' + 'busy' | ||
| self.send_packet(type, content) | ||
| continue | ||
| interface.debug_stop(device) #stop current session | ||
| debug_sessions.pop(sock) | ||
| sock.close() | ||
| if hasattr(interface, 'debug_start') == False or hasattr(interface, 'debug_stop') == False: | ||
| content = term + ',' + device + ':' + 'debug unsupported' | ||
| self.send_packet(type, content) | ||
| continue | ||
| used_ports = [] | ||
| for session in debug_sessions: | ||
| used_ports.append(debug_sessions[session]['port']) | ||
| port = 3096 + ord(os.urandom(1)) * ord(os.urandom(1)) / 64 | ||
| while port in used_ports: | ||
| port = 3096 + ord(os.urandom(1)) * ord(os.urandom(1)) / 64 | ||
| result = interface.debug_start(device, port) | ||
| if result != 'success': | ||
| print "start st-util for {0} at port {1} failed, ret={2}".format(device, port, result) | ||
| content = term + ',' + device + ':' + result | ||
| self.send_packet(type, content) | ||
| continue | ||
| sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||
| try: | ||
| sock.connect(('127.0.0.1', port)) | ||
| sock.setblocking(0) | ||
| except: | ||
| sock = None | ||
| if sock: | ||
| debug_sessions[sock] = {'term': term, 'device': device, 'port': port} | ||
| self.devices[device]['debug_socket'] = sock | ||
| content = term + ',' + device + ':' + 'success' | ||
| print 'start debugging device {0} at port {1} succeed'.format(device, port) | ||
| else: | ||
| if DEBUG: traceback.print_exc() | ||
| interface.debug_stop(device) | ||
| content = term + ',' + device + ':' + 'fail' | ||
| print 'start debugging device {0} failed'.format(device) | ||
| self.send_packet(type, content) | ||
| continue | ||
| elif type in [pkt.DEVICE_DEBUG_REINIT, pkt.DEVICE_DEBUG_STOP]: | ||
| operations = {pkt.DEVICE_DEBUG_REINIT: 'reinit', pkt.DEVICE_DEBUG_STOP: 'stop'} | ||
| sock = None | ||
| for session in debug_sessions: | ||
| if debug_sessions[session]['term'] != term: | ||
| continue | ||
| if debug_sessions[session]['device'] != device: | ||
| continue | ||
| sock = session | ||
| break | ||
| if sock == None: | ||
| print 'error: try to {0} a nonexisting debug session'.format(operations[type]) | ||
| content = term + ',' + device + ':' + 'session nonexist' | ||
| self.send_packet(type, content) | ||
| continue | ||
| port = debug_sessions[sock]['port'] | ||
| interface.debug_stop(device) | ||
| sock.close() | ||
| self.devices[device].pop('debug_socket') | ||
| debug_sessions.pop(sock) | ||
| if type == pkt.DEVICE_DEBUG_STOP: | ||
| content = term + ',' + device + ':' + 'success' | ||
| self.send_packet(type, content) | ||
| print 'stop debugging {0} succeed'.format(device) | ||
| elif type == pkt.DEVICE_DEBUG_REINIT: | ||
| result = interface.debug_start(device, port) | ||
| if result != 'success': | ||
| content = term + ',' + device + ':' + 'fail' | ||
| self.send_packet(type, content) | ||
| continue | ||
| sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||
| try: | ||
| sock.connect(('127.0.0.1', port)) | ||
| sock.setblocking(0) | ||
| except: | ||
| if DEBUG: traceback.print_exc() | ||
| sock = None | ||
| if sock: | ||
| debug_sessions[sock] = {'term': term, 'device': device, 'port': port} | ||
| self.devices[device]['debug_socket'] = sock | ||
| content = term + ',' + device + ':' + 'success' | ||
| self.send_packet(type, content) | ||
| print 'restart debugging {0} succeed'.format(device) | ||
| else: | ||
| interface.debug_stop(device) | ||
| self.devices[device].pop('debug_socket') | ||
| content = term + ',' + device + ':' + 'fail' | ||
| self.send_packet(type, content) | ||
| print 'restart debugging {0} failed, debug session closed'.format(device) | ||
| continue | ||
| else: | ||
| print "error: wrong command type '{0}'".format(type) | ||
| continue | ||
| r, w, e = select.select(list(debug_sessions), [], [], 0.02) | ||
| for sock in r: | ||
| term = debug_sessions[sock]['term'] | ||
| device = debug_sessions[sock]['device'] | ||
| try: | ||
| data = sock.recv(MAX_MSG_LENGTH) | ||
| except: | ||
| data = None | ||
| if not data: | ||
| print 'debug session for {0}:{1} closed'.format(term, device) | ||
| interface = self.devices[device]['interface'] | ||
| interface.debug_stop(device) | ||
| debug_sessions.pop(sock) | ||
| self.devices[device].pop('debug_socket') | ||
| content = term + ',' + device + ':' + 'session closed' | ||
| self.send_packet(pkt.DEVICE_DEBUG_STOP, content) | ||
| continue | ||
| content = term + ',' + device + ':' + data | ||
| self.send_packet(pkt.DEVICE_DEBUG_DATA, content) | ||
| def send_device_list(self): | ||
| device_list = [] | ||
| for device in list(self.devices): | ||
| if self.devices[device]['valid']: | ||
| device_list.append(device) | ||
| content = ':'.join(device_list) | ||
| self.send_packet(pkt.CLIENT_DEV, content) | ||
| def send_device_status(self): | ||
| for device in list(self.devices): | ||
| if self.devices[device]['valid'] == False: | ||
| continue | ||
| content = device + ':' + json.dumps(self.devices[device]['attributes'], sort_keys=True) | ||
| ret = self.send_packet(pkt.DEVICE_STATUS, content) | ||
| if ret == False: | ||
| break | ||
| def run_poll_command(self, device, command, lines_expect, timeout): | ||
| filter = {} | ||
| response = [] | ||
| while self.devices[device]['plog_queue'].empty() == False: | ||
| self.devices[device]['plog_queue'].get() | ||
| self.devices[device]['handle'].write(self.poll_str + command + '\r') | ||
| start = time.time() | ||
| while True: | ||
| try: | ||
| log = self.devices[device]['plog_queue'].get(False) | ||
| except: | ||
| log = None | ||
| if time.time() - start >= timeout: | ||
| break | ||
| if log == None: | ||
| time.sleep(0.01) | ||
| continue | ||
| log = log.replace('\r', '') | ||
| log = log.replace('\n', '') | ||
| log = log.replace(self.poll_str, '') | ||
| if log == '': | ||
| continue | ||
| response.append(log) | ||
| if len(response) > lines_expect: | ||
| break | ||
| if len(response) > 0: | ||
| response.pop(0) | ||
| if not response: | ||
| print "device {0} run poll commad '{1}' faild".format(device, command) | ||
| return response | ||
| def device_cmd_process(self, device, exit_condition): | ||
| poll_fail_num = 0 | ||
| interface = self.devices[device]['interface'] | ||
| pcmd_queue = self.devices[device]['pcmd_queue'] | ||
| if self.devices[device]['attributes'] != {}: | ||
| content = device + ':' + json.dumps(self.devices[device]['attributes'], sort_keys=True) | ||
| self.send_packet(pkt.DEVICE_STATUS, content) | ||
| poll_timeout = time.time() + 3 + random.uniform(0, self.poll_interval/10) | ||
| while interface.exist(device) and exit_condition.is_set() == False: | ||
| try: | ||
| if EN_STATUS_POLL == True and time.time() >= poll_timeout: | ||
| poll_timeout += self.poll_interval | ||
| queue_safeput(pcmd_queue, ['devname', 1, 0.2]) | ||
| queue_safeput(pcmd_queue, ['mac', 1, 0.2]) | ||
| queue_safeput(pcmd_queue, ['version', 2, 0.2]) | ||
| queue_safeput(pcmd_queue, ['uuid', 1, 0.2]) | ||
| queue_safeput(pcmd_queue, ['umesh status', 11, 0.2]) | ||
| queue_safeput(pcmd_queue, ['umesh extnetid', 1, 0.2]) | ||
| queue_safeput(pcmd_queue, ['umesh nbrs', 35, 0.3]) | ||
| block=True | ||
| timeout=0 | ||
| try: | ||
| args = None | ||
| if self.devices[device]['ucmd_queue'].empty() == True and pcmd_queue.empty() == True: | ||
| args = self.devices[device]['ucmd_queue'].get(block=True, timeout=0.1) | ||
| elif self.devices[device]['ucmd_queue'].empty() == False: | ||
| args = self.devices[device]['ucmd_queue'].get() | ||
| except Queue.Empty: | ||
| args = None | ||
| continue | ||
| except: | ||
| if DEBUG: traceback.print_exc() | ||
| args = None | ||
| continue | ||
| if args != None: | ||
| type = args[0] | ||
| term = args[1] | ||
| if type == pkt.DEVICE_ERASE: | ||
| self.device_erase(device, term) | ||
| elif type == pkt.DEVICE_PROGRAM: | ||
| address = args[2] | ||
| filename = args[3] | ||
| self.device_program(device, address, filename, term) | ||
| elif type in [pkt.DEVICE_RESET, pkt.DEVICE_START, pkt.DEVICE_STOP]: | ||
| self.device_control(device, type, term) | ||
| elif type == pkt.DEVICE_CMD: | ||
| cmd = args[2] | ||
| self.device_run_cmd(device, cmd, term) | ||
| if re.search('umesh extnetid [0-9A-Fa-f]{12}', cmd) != None: | ||
| queue_safeput(pcmd_queue, ['umesh extnetid', 1, 0.2]) | ||
| else: | ||
| print "error: unknown operation type {0}".format(repr(type)) | ||
| args = None | ||
| time.sleep(0.05) | ||
| continue | ||
| if pcmd_queue.empty() == True: | ||
| continue | ||
| [cmd, lines, timeout] = pcmd_queue.get() | ||
| response = self.run_poll_command(device, cmd, lines, timeout) | ||
| if cmd == 'devname': #poll device model | ||
| if len(response) == lines and response[0].startswith('device name:'): | ||
| poll_fail_num = 0 | ||
| self.devices[device]['attributes']['model'] = response[0].split()[-1] | ||
| else: | ||
| poll_fail_num += 1 | ||
| elif cmd == 'mac': #poll device mac | ||
| if len(response) == 1 and response[0].startswith('MAC address:'): | ||
| poll_fail_num = 0 | ||
| macaddr = response[0].split()[-1] | ||
| macaddr = macaddr.replace('-', '') + '0000' | ||
| self.devices[device]['attributes']['macaddr'] = macaddr | ||
| else: | ||
| poll_fail_num += 1 | ||
| elif cmd == 'version': #poll device version | ||
| if len(response) == lines: | ||
| poll_fail_num = 0 | ||
| for line in response: | ||
| if 'kernel version :' in line: | ||
| self.devices[device]['attributes']['kernel_version'] = line.replace('kernel version :AOS-', '') | ||
| if 'app version :' in line: | ||
| line = line.replace('app version :', '') | ||
| line = line.replace('app-', '') | ||
| line = line.replace('APP-', '') | ||
| self.devices[device]['attributes']['app_version'] = line | ||
| else: | ||
| poll_fail_num += 1 | ||
| elif cmd == 'umesh status': #poll mesh status | ||
| if len(response) == lines: | ||
| poll_fail_num = 0 | ||
| for line in response: | ||
| if 'state\t' in line: | ||
| self.devices[device]['attributes']['state'] = line.replace('state\t', '') | ||
| elif '\tnetid\t' in line: | ||
| self.devices[device]['attributes']['netid'] = line.replace('\tnetid\t', '') | ||
| elif '\tsid\t' in line: | ||
| self.devices[device]['attributes']['sid'] = line.replace('\tsid\t', '') | ||
| elif '\tnetsize\t' in line: | ||
| self.devices[device]['attributes']['netsize'] = line.replace('\tnetsize\t', '') | ||
| elif '\trouter\t' in line: | ||
| self.devices[device]['attributes']['router'] = line.replace('\trouter\t', '') | ||
| elif '\tchannel\t' in line: | ||
| self.devices[device]['attributes']['channel'] = line.replace('\tchannel\t', '') | ||
| else: | ||
| poll_fail_num += 1 | ||
| elif cmd == 'umesh nbrs': #poll mesh nbrs | ||
| if len(response) > 0 and 'num=' in response[-1]: | ||
| poll_fail_num = 0 | ||
| nbrs = {} | ||
| for line in response: | ||
| if '\t' not in line or ',' not in line: | ||
| continue | ||
| line = line.replace('\t', '') | ||
| nbr_info = line.split(',') | ||
| if len(nbr_info) < 10: | ||
| continue | ||
| nbrs[nbr_info[0]] = {'relation':nbr_info[1], \ | ||
| 'netid':nbr_info[2], \ | ||
| 'sid':nbr_info[3], \ | ||
| 'link_cost':nbr_info[4], \ | ||
| 'child_num':nbr_info[5], \ | ||
| 'channel':nbr_info[6], \ | ||
| 'reverse_rssi':nbr_info[7], \ | ||
| 'forward_rssi':nbr_info[8], \ | ||
| 'last_heard':nbr_info[9]} | ||
| if len(nbr_info) > 10: | ||
| nbrs[nbr_info[0]]['awake'] = nbr_info[10] | ||
| self.devices[device]['attributes']['nbrs'] = nbrs | ||
| else: | ||
| poll_fail_num += 1 | ||
| elif cmd == 'umesh extnetid': #poll mesh extnetid | ||
| if len(response) == 1 and response[0].count(':') == 5: | ||
| poll_fail_num += 1 | ||
| self.devices[device]['attributes']['extnetid'] = response[0] | ||
| else: | ||
| poll_fail_num += 1 | ||
| elif cmd == 'uuid': #poll uuid | ||
| if len(response) == 1: | ||
| if 'uuid:' in response[0]: | ||
| poll_fail_num = 0 | ||
| self.devices[device]['attributes']['uuid'] = response[0].replace('uuid: ', '') | ||
| elif 'alink is not connected' in response[0]: | ||
| poll_fail_num = 0 | ||
| self.devices[device]['attributes']['uuid'] = 'N/A' | ||
| else: | ||
| poll_fail_num += 1 | ||
| else: | ||
| poll_fail_num += 1 | ||
| else: | ||
| print "error: unrecognized poll cmd '{0}'".format(cmd) | ||
| continue | ||
| if poll_fail_num >= 7: | ||
| if self.devices[device]['attributes']['status'] == 'active': | ||
| print "device {0} become inactive".format(device) | ||
| self.devices[device]['attributes']['status'] = 'inactive' | ||
| else: | ||
| if self.devices[device]['attributes']['status'] == 'inactive': | ||
| print "device {0} become active".format(device) | ||
| self.devices[device]['attributes']['status'] = 'active' | ||
| if pcmd_queue.empty() == False: | ||
| continue | ||
| content = device + ':' + json.dumps(self.devices[device]['attributes'], sort_keys=True) | ||
| self.send_packet(pkt.DEVICE_STATUS, content) | ||
| except: | ||
| if interface.exist(device) == False: | ||
| exit_condition.set() | ||
| break | ||
| if exit_condition.is_set() == True: | ||
| break | ||
| if DEBUG: traceback.print_exc() | ||
| self.devices[device]['handle'].close() | ||
| self.devices[device]['handle'].open() | ||
| print 'devie command process thread for {0} exited'.format(device) | ||
| def device_log_filter(self, device, log): | ||
| pcmd_queue = self.devices[device]['pcmd_queue'] | ||
| if EN_STATUS_POLL == False: | ||
| return | ||
| if pcmd_queue.full() == True: | ||
| return | ||
| for flog in self.mesh_changed: | ||
| if flog.search(log) == None: | ||
| continue | ||
| #print log | ||
| #print "device {0} mesh status changed".format(device) | ||
| queue_safeput(pcmd_queue, ['umesh status', 11, 0.2]) | ||
| queue_safeput(pcmd_queue, ['umesh nbrs', 33, 0.3]) | ||
| return | ||
| for flog in self.neighbor_changed: | ||
| if flog.search(log) == None: | ||
| continue | ||
| #print log | ||
| #print "device {0} neighbors changed".format(device) | ||
| queue_safeput(pcmd_queue, ['umesh nbrs', 33, 0.3]) | ||
| return | ||
| for flog in self.device_uuid_changed: | ||
| if flog not in log: | ||
| continue | ||
| #print log | ||
| #print "device {0} uuid changed".format(device) | ||
| queue_safeput(pcmd_queue, ['uuid', 1, 0.2]) | ||
| return | ||
| def device_log_poll(self, device, exit_condition): | ||
| log_time = time.time() | ||
| log = '' | ||
| if LOCALLOG: | ||
| logfile= path.join(path.expanduser('~'), '.udclient', path.basename(device) + '.log') | ||
| flog = open(logfile, 'a+') | ||
| interface = self.devices[device]['interface'] | ||
| while interface.exist(device) and exit_condition.is_set() == False: | ||
| if self.connected == False or self.devices[device]['iolock'].locked(): | ||
| time.sleep(0.01) | ||
| continue | ||
| newline = False | ||
| while self.devices[device]['iolock'].acquire(False) == True: | ||
| try: | ||
| c = self.devices[device]['handle'].read(1) | ||
| except: | ||
| c = '' | ||
| finally: | ||
| self.devices[device]['iolock'].release() | ||
| if c == '': | ||
| break | ||
| if log == '': | ||
| log_time = time.time() | ||
| log += c | ||
| if c == '\n': | ||
| newline = True | ||
| break | ||
| if newline == True and log != '': | ||
| if self.poll_str in log: | ||
| queue_safeput(self.devices[device]['plog_queue'], log) | ||
| else: | ||
| self.device_log_filter(device, log) | ||
| if LOCALLOG: | ||
| flog.write('{0:.3f}:'.format(log_time) + log) | ||
| log = device + ':{0:.3f}:'.format(log_time) + log | ||
| self.send_packet(pkt.DEVICE_LOG,log) | ||
| log = '' | ||
| if LOCALLOG: | ||
| flog.close() | ||
| print 'device {0} removed'.format(device) | ||
| self.devices[device]['valid'] = False | ||
| exit_condition.set() | ||
| self.devices[device]['handle'].close() | ||
| self.send_device_list() | ||
| print 'device log poll thread for {0} exited'.format(device) | ||
| def add_new_device(self, mi, device): | ||
| handle = mi.new_device(device) | ||
| if handle == None: | ||
| return False | ||
| self.devices[device] = { | ||
| 'valid':True, \ | ||
| 'handle':handle, \ | ||
| 'interface' : mi, \ | ||
| 'iolock':threading.Lock(), \ | ||
| 'attributes':{}, \ | ||
| 'ucmd_queue':Queue.Queue(12), \ | ||
| 'pcmd_queue':Queue.Queue(64), \ | ||
| 'plog_queue':Queue.Queue(64) | ||
| } | ||
| self.devices[device]['attributes']['status'] = 'inactive' | ||
| return True | ||
| def add_old_device(self, mi, device): | ||
| ser = mi.new_device(device) | ||
| if ser == None: | ||
| return False | ||
| self.devices[device]['handle'] = ser | ||
| if self.devices[device]['iolock'].locked(): | ||
| self.devices[device]['iolock'].release() | ||
| while self.devices[device]['ucmd_queue'].empty() == False: | ||
| self.devices[device]['ucmd_queue'].get() | ||
| while self.devices[device]['pcmd_queue'].empty() == False: | ||
| self.devices[device]['pcmd_queue'].get() | ||
| while self.devices[device]['plog_queue'].empty() == False: | ||
| self.devices[device]['plog_queue'].get() | ||
| self.devices[device]['attributes']['status'] = 'inactive' | ||
| self.devices[device]['valid'] = True | ||
| return True | ||
| def list_devices(self): | ||
| os = platform.system() | ||
| devices_new = [] | ||
| for model in self.model_interface: | ||
| mi = self.model_interface[model] | ||
| devices = mi.list_devices(os) | ||
| for device in devices: | ||
| if device in self.devices and self.devices[device]['valid'] == True: | ||
| continue | ||
| if device not in self.devices: | ||
| ret = self.add_new_device(mi, device) | ||
| else: | ||
| ret = self.add_old_device(mi, device) | ||
| if ret == True: | ||
| devices_new.append(device) | ||
| devices_new.sort() | ||
| return devices_new | ||
| def device_monitor(self): | ||
| while self.keep_running: | ||
| devices_new = self.list_devices() | ||
| for device in devices_new: | ||
| print 'device {0} added'.format(device) | ||
| exit_condition = threading.Event() | ||
| thread.start_new_thread(self.device_log_poll, (device, exit_condition,)) | ||
| thread.start_new_thread(self.device_cmd_process, (device, exit_condition,)) | ||
| if devices_new != []: | ||
| self.send_device_list() | ||
| time.sleep(0.5) | ||
| print 'device monitor thread exited' | ||
| self.keep_running = False | ||
| def load_interfaces(self): | ||
| board_dir = path.join(path.dirname(path.abspath(__file__)), 'board') | ||
| candidates = os.listdir(board_dir) | ||
| for d in candidates: | ||
| if path.isdir(path.join(board_dir, d)) == False: | ||
| continue | ||
| model = path.basename(d) | ||
| interface_file = path.join(board_dir, d, model+'.py') | ||
| if path.isfile(interface_file) == False: | ||
| continue | ||
| sys.path.append(path.join(board_dir, d)) | ||
| try: | ||
| self.model_interface[model] = importlib.import_module(model) | ||
| except: | ||
| if DEBUG: traceback.print_exc() | ||
| continue | ||
| print 'model loaded - {0}'.format(model) | ||
| def device_erase(self, device, term): | ||
| interface = self.devices[device]['interface'] | ||
| self.devices[device]['iolock'].acquire() | ||
| try: | ||
| ret = interface.erase(device) | ||
| except: | ||
| if DEBUG: traceback.print_exc() | ||
| ret = 'fail' | ||
| finally: | ||
| self.devices[device]['iolock'].release() | ||
| print 'erasing', device, '...', ret | ||
| content = term + ',' + ret | ||
| self.send_packet(pkt.DEVICE_ERASE, content) | ||
| def device_program(self, device, address, file, term): | ||
| if device not in self.devices: | ||
| print "error: progamming nonexist device {0}".format(device) | ||
| content = term + ',' + 'device nonexist' | ||
| self.send_packet(pkt.DEVICE_PROGRAM, content) | ||
| return | ||
| interface = self.devices[device]['interface'] | ||
| self.devices[device]['iolock'].acquire() | ||
| try: | ||
| ret = interface.program(device, address, file) | ||
| except: | ||
| if DEBUG: traceback.print_exc() | ||
| ret = 'fail' | ||
| finally: | ||
| self.devices[device]['iolock'].release() | ||
| print 'programming', file, 'to', device, '@', address, '...', ret | ||
| content = term + ',' + ret | ||
| self.send_packet(pkt.DEVICE_PROGRAM, content) | ||
| def device_control(self, device, type, term): | ||
| operations= {pkt.DEVICE_RESET:'reset', pkt.DEVICE_STOP:'stop', pkt.DEVICE_START:'start'} | ||
| if device not in self.devices: | ||
| print "error: controlling nonexist device {0}".format(device) | ||
| content = term + ',' + 'device nonexist' | ||
| self.send_packet(type, content) | ||
| return | ||
| interface = self.devices[device]['interface'] | ||
| try: | ||
| ret = interface.control(device, operations[type]) | ||
| except: | ||
| if DEBUG: traceback.print_exc() | ||
| ret = 'fail' | ||
| print operations[type], device, ret | ||
| content = term + ',' + ret | ||
| self.send_packet(type, content) | ||
| def device_run_cmd(self, device, cmd, term): | ||
| if device not in self.devices: | ||
| print "error: run command at nonexist device {0}".format(device) | ||
| content = term + ',' + 'device nonexist' | ||
| self.send_packet(pkt.DEVICE_CMD, content) | ||
| return | ||
| try: | ||
| self.devices[device]['handle'].write(cmd+'\r') | ||
| result='success' | ||
| print "run command '{0}' at {1} succeed".format(cmd, device) | ||
| except: | ||
| if DEBUG: traceback.print_exc() | ||
| result='fail' | ||
| print "run command '{0}' at {1} failed".format(cmd, device) | ||
| content = term + ',' + result | ||
| self.send_packet(pkt.DEVICE_CMD, content) | ||
| def login_and_get_server(self, controller_ip, controller_port): | ||
| sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||
| if ENCRYPT: | ||
| cert_file = path.join(path.dirname(path.abspath(__file__)), 'controller_certificate.pem') | ||
| sock = ssl.wrap_socket(sock, cert_reqs=ssl.CERT_REQUIRED, ca_certs = cert_file) | ||
| try: | ||
| sock.connect((controller_ip, controller_port)) | ||
| sock.settimeout(3) | ||
| msg = '' | ||
| except: | ||
| if DEBUG: traceback.print_exc() | ||
| return None | ||
| content = pkt.construct(pkt.ACCESS_LOGIN, 'client,' + self.uuid) | ||
| try: | ||
| sock.send(content) | ||
| except: | ||
| if DEBUG: traceback.print_exc() | ||
| sock.close() | ||
| return None | ||
| try: | ||
| data = sock.recv(MAX_MSG_LENGTH) | ||
| except KeyboardInterrupt: | ||
| sock.close() | ||
| raise | ||
| except: | ||
| sock.close() | ||
| return None | ||
| if data == '': | ||
| sock.close() | ||
| return None | ||
| type, length, value, data = pkt.parse(data) | ||
| #print 'controller', type, value | ||
| rets = value.split(',') | ||
| if type != pkt.ACCESS_LOGIN or rets[0] != 'success': | ||
| accesskey_file = path.join(path.expanduser('~'), '.udclient', '.accesskey') | ||
| if rets[0] == 'invalid access key' and path.exists(accesskey_file): | ||
| os.remove(accesskey_file) | ||
| print "login failed, ret={0}".format(value) | ||
| sock.close() | ||
| return None | ||
| sock.close() | ||
| return rets[1:] | ||
| def connect_to_server(self, server_ip, server_port, certificate): | ||
| sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||
| if certificate != 'None': | ||
| try: | ||
| certfile = path.join(path.expanduser('~'), '.udclient', 'certificate.pem') | ||
| f = open(certfile, 'wt') | ||
| f.write(certificate) | ||
| f.close() | ||
| sock = ssl.wrap_socket(sock, cert_reqs=ssl.CERT_REQUIRED, ca_certs=certfile) | ||
| except: | ||
| if DEBUG: traceback.print_exc() | ||
| return 'fail' | ||
| try: | ||
| sock.connect((server_ip, server_port)) | ||
| while self.output_queue.empty() == False: | ||
| self.output_queue.get() | ||
| self.service_socket = sock | ||
| self.connected = True | ||
| return "success" | ||
| except: | ||
| if DEBUG: traceback.print_exc() | ||
| return "fail" | ||
| def client_func(self, contoller_ip, controller_port): | ||
| work_dir = path.join(path.expanduser('~'), '.udclient') | ||
| if path.exists(work_dir) == False: | ||
| os.mkdir(work_dir) | ||
| signal.signal(signal.SIGINT, signal_handler) | ||
| accesskey_file = path.join(path.expanduser('~'), '.udclient', '.accesskey') | ||
| if path.exists(accesskey_file) == True: | ||
| try: | ||
| file = open(accesskey_file, 'rb') | ||
| self.uuid = json.load(file) | ||
| file.close() | ||
| except: | ||
| print "read access key failed" | ||
| while self.uuid == None: | ||
| try: | ||
| uuid = raw_input("please input your access key: ") | ||
| except: | ||
| return | ||
| is_valid_uuid = re.match('^[0-9a-f]{10,20}$', uuid) | ||
| if is_valid_uuid == None: | ||
| print "invalid access key {0}, please enter a valid access key".format(uuid) | ||
| continue | ||
| self.uuid = uuid | ||
| try: | ||
| file = open(accesskey_file, 'wb') | ||
| json.dump(self.uuid, file) | ||
| file.close() | ||
| except: | ||
| print "error: save access key to file failed" | ||
| self.load_interfaces() | ||
| thread.start_new_thread(self.packet_send_thread, ()) | ||
| thread.start_new_thread(self.debug_daemon_thread, ()) | ||
| thread.start_new_thread(self.device_monitor, ()) | ||
| file_received = {} | ||
| file_receiving = {} | ||
| self.connected = False | ||
| self.service_socket = None | ||
| msg = '' | ||
| while True: | ||
| try: | ||
| if self.connected == False: | ||
| raise ConnectionLost | ||
| new_msg = self.service_socket.recv(MAX_MSG_LENGTH) | ||
| if new_msg == '': | ||
| raise ConnectionLost | ||
| break | ||
| msg += new_msg | ||
| while msg != '': | ||
| type, length, value, msg = pkt.parse(msg) | ||
| if type == pkt.TYPE_NONE: | ||
| break | ||
| for hash in list(file_receiving): | ||
| if time.time() > file_receiving[hash]['timeout']: | ||
| file_receiving[hash]['handle'].close() | ||
| try: | ||
| os.remove(file_receiving[hash]['name']) | ||
| except: | ||
| pass | ||
| file_receiving.pop(hash) | ||
| if type == pkt.FILE_BEGIN: | ||
| try: | ||
| [term, hash, fname] = value.split(':') | ||
| except: | ||
| print "argument error: {0} {1}".format(type, value) | ||
| continue | ||
| if hash in file_received: | ||
| if path.exists(file_received[hash]) == True: | ||
| content = term + ',' + 'exist' | ||
| self.send_packet(type, content) | ||
| continue | ||
| else: | ||
| file_received.pop(hash) | ||
| if hash in file_receiving: | ||
| content = term + ',' + 'busy' | ||
| self.send_packet(type, content) | ||
| continue | ||
| filename = path.join(path.expanduser('~'), '.udclient', path.basename(fname)) | ||
| filename += '-' + term + '@' + time.strftime('%Y%m%d-%H%M%S') | ||
| filehandle = open(filename, 'wb') | ||
| timeout = time.time() + 5 | ||
| file_receiving[hash] = {'name':filename, 'seq':0, 'handle':filehandle, 'timeout': timeout} | ||
| content = term + ',' + 'ok' | ||
| self.send_packet(type, content) | ||
| if DEBUG: | ||
| print 'start receiving {0} as {1}'.format(fname, filename) | ||
| elif type == pkt.FILE_DATA: | ||
| try: | ||
| split_value = value.split(':') | ||
| term = split_value[0] | ||
| hash = split_value[1] | ||
| seq = split_value[2] | ||
| data = value[(len(term) + len(hash) + len(seq) + 3):] | ||
| seq = int(seq) | ||
| except: | ||
| print "argument error: {0}".format(type) | ||
| continue | ||
| if hash not in file_receiving: | ||
| content = term + ',' + 'noexist' | ||
| self.send_packet(type, content) | ||
| continue | ||
| if file_receiving[hash]['seq'] != seq and file_receiving[hash]['seq'] != seq + 1: | ||
| content = term + ',' + 'seqerror' | ||
| self.send_packet(type, content) | ||
| continue | ||
| if file_receiving[hash]['seq'] == seq: | ||
| file_receiving[hash]['handle'].write(data) | ||
| file_receiving[hash]['seq'] += 1 | ||
| file_receiving[hash]['timeout'] = time.time() + 5 | ||
| content = term + ',' + 'ok' | ||
| self.send_packet(type, content) | ||
| elif type == pkt.FILE_END: | ||
| try: | ||
| [term, hash, fname] = value.split(':') | ||
| except: | ||
| print "argument error: {0} {1}".format(type, value) | ||
| continue | ||
| if hash not in file_receiving: | ||
| content = term + ',' + 'noexist' | ||
| self.send_packet(type, content) | ||
| continue | ||
| file_receiving[hash]['handle'].close() | ||
| localhash = pkt.hash_of_file(file_receiving[hash]['name']) | ||
| if localhash != hash: | ||
| response = 'hasherror' | ||
| else: | ||
| response = 'ok' | ||
| file_received[hash] = file_receiving[hash]['name'] | ||
| if DEBUG: | ||
| print 'finished receiving {0}, result:{1}'.format(file_receiving[hash]['name'], response) | ||
| file_receiving.pop(hash) | ||
| content = term + ',' + response | ||
| self.send_packet(type, content) | ||
| elif type == pkt.DEVICE_ERASE: | ||
| try: | ||
| [term, device] = value.split(',') | ||
| except: | ||
| print "argument error: {0} {1}".format(type, value) | ||
| continue | ||
| if device in self.devices: | ||
| if self.devices[device]['ucmd_queue'].full() == False: | ||
| self.devices[device]['ucmd_queue'].put([type, term]) | ||
| continue | ||
| else: | ||
| result = 'busy' | ||
| print 'erase', device, 'failed, device busy' | ||
| else: | ||
| result = 'nonexist' | ||
| print 'erase', device, 'failed, device nonexist' | ||
| content = term + ',' + result | ||
| self.send_packet(type, content) | ||
| elif type == pkt.DEVICE_PROGRAM: | ||
| try: | ||
| [term, device, address, hash] = value.split(',') | ||
| except: | ||
| print "argument error: {0} {1}".format(type, value) | ||
| continue | ||
| if hash not in file_received: | ||
| content = term + ',' + 'error' | ||
| self.send_packet(type, content) | ||
| continue | ||
| filename = file_received[hash] | ||
| if device in self.devices: | ||
| if self.devices[device]['ucmd_queue'].full() == False: | ||
| self.devices[device]['ucmd_queue'].put([type, term, address, filename]) | ||
| continue | ||
| else: | ||
| result = 'busy' | ||
| print 'program {0} to {1} @ {2} failed, device busy'.format(filename, device, address) | ||
| else: | ||
| result = 'error' | ||
| print 'program {0} to {1} @ {2} failed, device nonexist'.format(filename, device, address) | ||
| content = term + ',' + result | ||
| self.send_packet(type, content) | ||
| elif type in [pkt.DEVICE_RESET, pkt.DEVICE_START, pkt.DEVICE_STOP]: | ||
| operations = {pkt.DEVICE_RESET:'reset', pkt.DEVICE_START:'start', pkt.DEVICE_STOP:'stop'} | ||
| try: | ||
| [term, device] = value.split(',') | ||
| except: | ||
| print "argument error: {0} {1}".format(type, value) | ||
| continue | ||
| if device in self.devices and self.devices[device]['valid'] == True: | ||
| if self.devices[device]['ucmd_queue'].full() == False: | ||
| self.devices[device]['ucmd_queue'].put([type, term]) | ||
| continue | ||
| else: | ||
| result = 'busy' | ||
| print operations[type], device, 'failed, device busy' | ||
| else: | ||
| result = 'nonexist' | ||
| print operations[type], device, 'failed, device nonexist' | ||
| content = term + ',' + result | ||
| self.send_packet(type, content) | ||
| elif type in [pkt.DEVICE_DEBUG_START, pkt.DEVICE_DEBUG_REINIT, pkt.DEVICE_DEBUG_STOP]: | ||
| operations = { | ||
| pkt.DEVICE_DEBUG_START:'start debuging', | ||
| pkt.DEVICE_DEBUG_START:'reinit debuging', | ||
| pkt.DEVICE_STOP:'stop debugging'} | ||
| try: | ||
| [term, device] = value.split(',') | ||
| except: | ||
| print "argument error: {0} {1}".format(type, value) | ||
| continue | ||
| debug_cmd = [type, term, device] | ||
| try: | ||
| self.debug_queue.put_nowait(debug_cmd) | ||
| except: | ||
| print "error: {0} debug session failed, debug_queue full".format(operations[type]) | ||
| content = term + ',' + device + ':' + 'daemon busy' | ||
| self.send_packet(type, content) | ||
| elif type == pkt.DEVICE_CMD: | ||
| term_dev = value.split(':')[0] | ||
| term_dev_len = len(term_dev) + 1 | ||
| try: | ||
| [term, device] = term_dev.split(',') | ||
| except: | ||
| print "argument error: {0} {1}".format(type, value) | ||
| continue | ||
| cmd = value[term_dev_len:] | ||
| if type == pkt.DEVICE_CMD: | ||
| cmd = cmd.replace('|', ' ') | ||
| if device in self.devices and self.devices[device]['valid'] == True: | ||
| if self.devices[device]['ucmd_queue'].full() == False: | ||
| self.devices[device]['ucmd_queue'].put([type, term, cmd]) | ||
| continue | ||
| else: | ||
| result = 'busy' | ||
| print "run command '{0}' at {1} failed, device busy".format(cmd, device) | ||
| else: | ||
| result = 'nonexist' | ||
| print "run command '{0}' at {1} failed, device nonexist".format(cmd, device) | ||
| content = term + ',' + result | ||
| self.send_packet(type, content) | ||
| elif type == pkt.DEVICE_DEBUG_DATA: | ||
| term_dev = value.split(':')[0] | ||
| term_dev_len = len(term_dev) + 1 | ||
| try: | ||
| [term, device] = term_dev.split(',') | ||
| except: | ||
| print "argument error: {0} {1}".format(type, value) | ||
| continue | ||
| data = value[term_dev_len:] | ||
| if device not in self.devices or 'debug_socket' not in self.devices[device]: | ||
| content = term + ',' + device + ':' + 'session nonexist' | ||
| self.send_packet(pkt.DEVICE_DEBUG_STOP, content) | ||
| return | ||
| try: | ||
| self.devices[device]['debug_socket'].send(data) | ||
| except: | ||
| if DEBUG: traceback.print_exc() | ||
| print "forward debug data to {1} failed".format(device) | ||
| elif type == pkt.CLIENT_LOGIN: | ||
| if value == 'request': | ||
| print 'server request login' | ||
| data = self.uuid + ',' + self.poll_str + ',' + token | ||
| self.send_packet(pkt.CLIENT_LOGIN, data) | ||
| elif value == 'success': | ||
| print "login to server succeed" | ||
| self.send_device_list() | ||
| self.send_device_status() | ||
| else: | ||
| print "login to server failed, retry later ..." | ||
| try: | ||
| time.sleep(10) | ||
| except KeyboardInterrupt: | ||
| break | ||
| raise ConnectionLost | ||
| except ConnectionLost: | ||
| self.connected = False | ||
| if self.service_socket != None: | ||
| self.service_socket.close() | ||
| print 'connection to server lost, try reconnecting...' | ||
| try: | ||
| result = self.login_and_get_server(contoller_ip, controller_port) | ||
| except KeyboardInterrupt: | ||
| break | ||
| if result == None: | ||
| print 'login to controller failed, retry later...' | ||
| try: | ||
| time.sleep(5) | ||
| except KeyboardInterrupt: | ||
| break | ||
| continue | ||
| try: | ||
| [server_ip, server_port, token, certificate] = result | ||
| server_port = int(server_port) | ||
| print 'login to controller succees, server_ip-{0} server_port-{1}'.format(server_ip, server_port) | ||
| except: | ||
| print 'login to controller failed, invalid return={0}'.format(result) | ||
| try: | ||
| time.sleep(5) | ||
| except KeyboardInterrupt: | ||
| break | ||
| continue | ||
| result = self.connect_to_server(server_ip, server_port, certificate) | ||
| if result == 'success': | ||
| print 'connect to server {0}:{1} succeeded'.format(server_ip, server_port) | ||
| else: | ||
| print 'connect to server {0}:{1} failed, retry later ...'.format(server_ip, server_port) | ||
| try: | ||
| time.sleep(5) | ||
| except KeyboardInterrupt: | ||
| break | ||
| continue | ||
| data = self.uuid + ',' + self.poll_str + ',' + token | ||
| self.send_packet(pkt.CLIENT_LOGIN, data) | ||
| except KeyboardInterrupt: | ||
| break | ||
| except: | ||
| if DEBUG: traceback.print_exc() | ||
| print "client exiting ..." | ||
| self.keep_running = False | ||
| time.sleep(0.3) | ||
| if self.service_socket: self.service_socket.close() |
Sorry, the diff of this file is not supported yet
-182
| import hashlib | ||
| DEBUG = False | ||
| TYPE_NONE = 'NONE' | ||
| CLIENT_DEV = 'CDEV' | ||
| ALL_DEV = 'ADEV' | ||
| DEVICE_LOG = 'DLOG' | ||
| DEVICE_STATUS = 'DSTU' | ||
| DEVICE_CMD = 'DCMD' | ||
| DEVICE_ERASE = 'DERS' | ||
| DEVICE_PROGRAM = 'DPRG' | ||
| DEVICE_RESET = 'DRST' | ||
| DEVICE_START = 'DSTR' | ||
| DEVICE_STOP = 'DSTP' | ||
| DEVICE_ALLOC = 'DALC' | ||
| DEVICE_DEBUG_START = 'DDBS' | ||
| DEVICE_DEBUG_DATA = 'DDBD' | ||
| DEVICE_DEBUG_REINIT = 'DDBR' | ||
| DEVICE_DEBUG_STOP = 'DDBE' | ||
| LOG_SUB = 'LGSB' | ||
| LOG_UNSUB = 'LGUS' | ||
| STATUS_SUB = 'STSB' | ||
| STATUS_UNSUB = 'STUS' | ||
| LOG_DOWNLOAD = 'LGDL' | ||
| FILE_BEGIN = 'FBGN' | ||
| FILE_DATA = 'FDTA' | ||
| FILE_END = 'FEND' | ||
| CMD_DONE = 'CMDD' | ||
| CMD_ERROR = 'CMDE' | ||
| HEARTBEAT = 'HTBT' | ||
| CLIENT_LOGIN = 'CLGI' | ||
| TERMINAL_LOGIN = 'TLGI' | ||
| ACCESS_LOGIN = 'ALGI' | ||
| ACCESS_REPORT_STATUS = 'ARPS' | ||
| ACCESS_ADD_CLIENT = 'AADC' | ||
| ACCESS_DEL_CLIENT = 'ADLC' | ||
| ACCESS_ADD_TERMINAL = 'AADT' | ||
| ACCESS_UPDATE_TERMINAL = 'AUPT' | ||
| ACCESS_DEL_TERMINAL = 'ADLT' | ||
| def is_valid_type(type): | ||
| #frequently used commands | ||
| if type == DEVICE_LOG: | ||
| return True | ||
| if type == DEVICE_STATUS: | ||
| return True | ||
| if type == DEVICE_DEBUG_DATA: | ||
| return True | ||
| if type == HEARTBEAT: | ||
| return True | ||
| if type == DEVICE_CMD: | ||
| return True | ||
| if type == CMD_DONE: | ||
| return True | ||
| if type == CMD_ERROR: | ||
| return True | ||
| if type == DEVICE_ERASE: | ||
| return True | ||
| if type == DEVICE_PROGRAM: | ||
| return True | ||
| if type == DEVICE_RESET: | ||
| return True | ||
| if type == DEVICE_START: | ||
| return True | ||
| if type == DEVICE_STOP: | ||
| return True | ||
| if type == DEVICE_DEBUG_START: | ||
| return True | ||
| if type == DEVICE_DEBUG_REINIT: | ||
| return True | ||
| if type == DEVICE_DEBUG_STOP: | ||
| return True | ||
| #less frequently used commands | ||
| if type == CLIENT_DEV: | ||
| return True | ||
| if type == ALL_DEV: | ||
| return True | ||
| if type == LOG_SUB: | ||
| return True | ||
| if type == LOG_UNSUB: | ||
| return True | ||
| if type == STATUS_SUB: | ||
| return True | ||
| if type == STATUS_UNSUB: | ||
| return True | ||
| if type == LOG_DOWNLOAD: | ||
| return True | ||
| if type == FILE_BEGIN: | ||
| return True | ||
| if type == FILE_DATA: | ||
| return True | ||
| if type == FILE_END: | ||
| return True | ||
| if type == DEVICE_ALLOC: | ||
| return True | ||
| if type == CLIENT_LOGIN: | ||
| return True | ||
| if type == TERMINAL_LOGIN: | ||
| return True | ||
| if type == ACCESS_LOGIN: | ||
| return True | ||
| if type == ACCESS_REPORT_STATUS: | ||
| return True | ||
| if type == ACCESS_ADD_CLIENT: | ||
| return True | ||
| if type == ACCESS_DEL_CLIENT: | ||
| return True | ||
| if type == ACCESS_ADD_TERMINAL: | ||
| return True | ||
| if type == ACCESS_UPDATE_TERMINAL: | ||
| return True | ||
| if type == ACCESS_DEL_TERMINAL: | ||
| return True | ||
| return False | ||
| def construct(type, value): | ||
| if is_valid_type(type) == False: | ||
| return '' | ||
| if len(value) > 99999: | ||
| print "warning: data size larger than permited" | ||
| return '' | ||
| frame = '{' + type + ',' + '{0:05d}'.format(len(value)) + ',' + value + '}' | ||
| return frame | ||
| def parse(msg): | ||
| sync = False | ||
| type = TYPE_NONE | ||
| length = 0 | ||
| value = '' | ||
| while msg != '': | ||
| if len(msg) < 12: | ||
| type = TYPE_NONE | ||
| length = 0 | ||
| value = '' | ||
| break; | ||
| # print(msg) | ||
| for i in range(len(msg)): | ||
| if msg[i] != '{': | ||
| continue | ||
| if (i + 13) > len(msg): | ||
| break; | ||
| if is_valid_type(msg[i+1: i+5]) == False: | ||
| continue | ||
| if msg[i + 5] != ',': | ||
| continue | ||
| if msg[i+6 : i+11].isdigit() == False: | ||
| continue | ||
| if msg[i+11] != ',': | ||
| continue | ||
| sync = True | ||
| if DEBUG and i > 0: | ||
| print("msg:{0}".format(msg)) | ||
| print("discard:{0}".format(msg[0:i])) | ||
| msg = msg[i:] | ||
| break | ||
| if sync == False: | ||
| break | ||
| type = msg[1:5] | ||
| length = int(msg[6:11]) | ||
| if len(msg) < length + 13: | ||
| type = TYPE_NONE | ||
| length = 0 | ||
| value = '' | ||
| break | ||
| if msg[length + 12] != '}': | ||
| sync = False | ||
| if DEBUG: print(msg[0:12] + " Lose sync because of FOOTER error") | ||
| msg = msg[1:] | ||
| continue | ||
| value = msg[12:length+12] | ||
| msg = msg[length+13:] | ||
| break; | ||
| return type, length, value, msg | ||
| def hash_of_file(filename): | ||
| h = hashlib.sha1() | ||
| with open(filename, 'rb', buffering=0) as f: | ||
| for b in iter(lambda : f.read(1024), b''): | ||
| h.update(b) | ||
| return h.hexdigest() |
-1376
| #!/usr/bin/python | ||
| import os, sys, time, re, random, json, Queue | ||
| import curses, socket, ssl, select | ||
| import subprocess, thread, threading | ||
| import traceback | ||
| from operator import itemgetter | ||
| from os import path | ||
| import packet as pkt | ||
| MAX_MSG_LENGTH = 65536 | ||
| CMD_WINDOW_HEIGHT = 2 | ||
| DEV_WINDOW_WIDTH = 36 | ||
| LOG_WINDOW_HEIGHT = 30 | ||
| LOG_WINDOW_WIDTH = 80 | ||
| LOG_HISTORY_LENGTH = 5000 | ||
| MOUSE_SCROLL_UP = 0x80000 | ||
| MOUSE_SCROLL_DOWN = 0x8000000 | ||
| ENCRYPT = True | ||
| DEBUG = True | ||
| class ConnectionLost(Exception): | ||
| pass | ||
| class Terminal: | ||
| def __init__(self): | ||
| self.stdscr = 0 | ||
| self.log_window = 0 | ||
| self.dev_window = 0 | ||
| self.cmd_window = 0 | ||
| self.keep_running = True | ||
| self.connected = False | ||
| self.device_list= {} | ||
| self.service_socket = None | ||
| self.cmd_excute_state = 'idle' | ||
| self.cmd_excute_return = '' | ||
| self.cmd_excute_event = threading.Event() | ||
| self.output_queue = Queue.Queue(256) | ||
| self.debug_socks = {} | ||
| self.debug_queue = Queue.Queue(256) | ||
| self.log_content = [] | ||
| self.log_curr_line = -1 | ||
| self.log_subscribed = [] | ||
| self.using_list = [] | ||
| self.max_log_width = 0 | ||
| self.cur_color_pair = 7 | ||
| self.alias_tuples = {} | ||
| self.cmd_history = [] | ||
| self.last_runcmd_dev = [] | ||
| self.uuid = None | ||
| def init(self): | ||
| work_dir = path.join(path.expanduser('~'), '.udterminal') | ||
| if path.exists(work_dir) == False: | ||
| os.mkdir(work_dir) | ||
| alias_file = path.join(path.expanduser('~'), '.udterminal', '.alias') | ||
| if path.exists(alias_file) == True: | ||
| try: | ||
| file = open(alias_file, 'rb') | ||
| self.alias_tuples = json.load(file) | ||
| file.close() | ||
| except: | ||
| print 'read alias record failed' | ||
| cmd_history_file = path.join(path.expanduser('~'), '.udterminal', '.cmd_history') | ||
| if path.exists(cmd_history_file) == True: | ||
| try: | ||
| file = open(cmd_history_file, 'rb') | ||
| self.cmd_history = json.load(file) | ||
| file.close() | ||
| except: | ||
| print 'read command history failed' | ||
| accesskey_file = path.join(path.expanduser('~'), '.udterminal', '.accesskey') | ||
| if path.exists(accesskey_file) == True: | ||
| try: | ||
| file = open(accesskey_file, 'rb') | ||
| self.uuid = json.load(file) | ||
| file.close() | ||
| except: | ||
| print 'read access key failed' | ||
| while self.uuid == None: | ||
| try: | ||
| uuid = raw_input('please input your access key: ') | ||
| except: | ||
| return 'read access key fail' | ||
| is_valid_uuid = re.match('^[0-9a-f]{10,20}$', uuid) | ||
| if is_valid_uuid == None: | ||
| print 'invalid access key {0}, please enter a valid access key'.format(uuid) | ||
| continue | ||
| self.uuid = uuid | ||
| try: | ||
| accesskey_file = path.join(path.expanduser('~'), '.udterminal', '.accesskey') | ||
| file = open(accesskey_file, 'wb') | ||
| json.dump(self.uuid, file) | ||
| file.close() | ||
| except: | ||
| print 'error: save access key to file failed' | ||
| #initialize UI | ||
| try: | ||
| self.stdscr = curses.initscr() | ||
| curses.noecho() | ||
| curses.cbreak() | ||
| curses.mousemask(-1) | ||
| curses.mouseinterval(0) | ||
| curses.start_color() | ||
| curses.use_default_colors() | ||
| curses.init_pair(1, curses.COLOR_RED, -1) | ||
| curses.init_pair(2, curses.COLOR_GREEN, -1) | ||
| curses.init_pair(3, curses.COLOR_YELLOW, -1) | ||
| curses.init_pair(4, curses.COLOR_BLUE, -1) | ||
| curses.init_pair(5, curses.COLOR_MAGENTA, -1) | ||
| curses.init_pair(6, curses.COLOR_CYAN, -1) | ||
| curses.init_pair(7, curses.COLOR_WHITE, -1) | ||
| self.curseslock = threading.Lock(); | ||
| self.window_redraw() | ||
| return 'success' | ||
| except: | ||
| if DEBUG: traceback.print_exc() | ||
| return 'UI failed' | ||
| def window_redraw(self): | ||
| global LOG_WINDOW_WIDTH | ||
| global LOG_WINDOW_HEIGHT | ||
| self.curseslock.acquire() | ||
| (max_y, max_x) = self.stdscr.getmaxyx() | ||
| for y in range(max_y): | ||
| for x in range(max_x - 1): | ||
| self.stdscr.addch(y, x, ' ') | ||
| self.stdscr.refresh() | ||
| LOG_WINDOW_HEIGHT = max_y - CMD_WINDOW_HEIGHT - 3 | ||
| LOG_WINDOW_WIDTH = max_x - DEV_WINDOW_WIDTH - 3 | ||
| width = 1 + LOG_WINDOW_WIDTH + 1 + DEV_WINDOW_WIDTH + 1 | ||
| height = 1 + LOG_WINDOW_HEIGHT + 1 + CMD_WINDOW_HEIGHT + 1 | ||
| horline = '-' * (width-2) | ||
| self.stdscr.addstr(0, 1, horline) | ||
| self.stdscr.addstr(LOG_WINDOW_HEIGHT + 1, 1, horline) | ||
| self.stdscr.addstr(LOG_WINDOW_HEIGHT + CMD_WINDOW_HEIGHT + 2, 1, horline) | ||
| for i in range(1, height-1): | ||
| self.stdscr.addch(i, 0, '|') | ||
| for i in range(1, height - CMD_WINDOW_HEIGHT - 2): | ||
| self.stdscr.addch(i, LOG_WINDOW_WIDTH + 1, '|') | ||
| for i in range(1, height-1): | ||
| self.stdscr.addch(i, LOG_WINDOW_WIDTH + DEV_WINDOW_WIDTH + 2, '|') | ||
| self.stdscr.refresh() | ||
| x = 1; y = 1; | ||
| self.log_window = curses.newwin(LOG_WINDOW_HEIGHT, LOG_WINDOW_WIDTH, y, x) | ||
| x = 1 + LOG_WINDOW_WIDTH + 1; y = 1; | ||
| self.dev_window = curses.newwin(LOG_WINDOW_HEIGHT, DEV_WINDOW_WIDTH, y, x) | ||
| x = 1; y = 1 + LOG_WINDOW_HEIGHT + 1; | ||
| self.cmd_window = curses.newwin(CMD_WINDOW_HEIGHT, LOG_WINDOW_WIDTH + 1 + DEV_WINDOW_WIDTH, y, x) | ||
| self.cmd_window.keypad(1) | ||
| self.log_window.addstr(0, 0, 'Logs', curses.A_BOLD) | ||
| self.log_window.move(1, 0) | ||
| self.log_window.refresh() | ||
| self.dev_window.addstr(0, 0, 'Devices', curses.A_BOLD) | ||
| self.dev_window.addstr(0, DEV_WINDOW_WIDTH-4, 'USE') | ||
| self.dev_window.move(1, 0) | ||
| self.dev_window.refresh() | ||
| self.cmd_window.addstr(0, 0, 'Command:', curses.A_BOLD) | ||
| self.cmd_window.refresh() | ||
| self.curseslock.release() | ||
| def process_esc_sequence(self, escape): | ||
| colors = escape[2:-1].split(';') | ||
| self.cur_color_pair = 7 | ||
| for color in colors: | ||
| if color == '30': | ||
| self.cur_color_pair = 0 | ||
| elif color == '31': | ||
| self.cur_color_pair = 1 | ||
| elif color == '32': | ||
| self.cur_color_pair = 2 | ||
| elif color == '33': | ||
| self.cur_color_pair = 3 | ||
| elif color == '34': | ||
| self.cur_color_pair = 4 | ||
| elif color == '35': | ||
| self.cur_color_pair = 5 | ||
| elif color == '36': | ||
| self.cur_color_pair = 6 | ||
| elif color == '37': | ||
| self.cur_color_pair = 7 | ||
| def log_parse_oneline(self, log): | ||
| log_buffer = [] | ||
| if ':' in log: | ||
| log_index = log.split(':')[0] + ':' | ||
| else: | ||
| log_index = '' | ||
| log = log[len(log_index):] | ||
| log = log.replace('\t', ' ' * 8) | ||
| line_length = len(log) | ||
| j = 0; log_len = 0; log_str = log_index; | ||
| while j < line_length: | ||
| c = log[j] | ||
| if c == '\x1B' and log[j:j+2] == '\x1B[': #ESC | ||
| k = j + 2 | ||
| find_esc_seq = False | ||
| while k < line_length: | ||
| if log[k] == 'm': | ||
| find_esc_seq = True | ||
| break | ||
| k += 1 | ||
| if find_esc_seq: | ||
| self.process_esc_sequence(log[j:k+1]) | ||
| j = k + 1 | ||
| continue | ||
| if ord(c) >= 0x20 and ord(c) < 0x7F: | ||
| log_str += c | ||
| log_len += 1 | ||
| if log_len + len(log_index) >= LOG_WINDOW_WIDTH - 1: | ||
| log_buffer.append([self.cur_color_pair, log_str]) | ||
| log_len = 0; log_str = log_index | ||
| j += 1 | ||
| if log_len > 0: | ||
| log_buffer.append([self.cur_color_pair, log_str]) | ||
| if len(log_buffer) == 0: | ||
| log_buffer.append([self.cur_color_pair, log_index]) | ||
| return log_buffer | ||
| def log_display(self, logtime, log): | ||
| #save log | ||
| if log != '': | ||
| self.log_content.append((logtime, log)) | ||
| self.log_content.sort(key=itemgetter(0)) | ||
| if len(self.log_content) > LOG_HISTORY_LENGTH: | ||
| self.log_content.pop(0) | ||
| if self.log_curr_line > 0: | ||
| self.log_curr_line -= 1 | ||
| if self.log_curr_line != -1 and log != '': | ||
| return | ||
| #clear log screen | ||
| self.log_window.move(1,0) | ||
| clear = (' ' * (LOG_WINDOW_WIDTH - 1) + '\n') * (LOG_WINDOW_HEIGHT - 2) | ||
| clear += ' ' * (LOG_WINDOW_WIDTH-1) | ||
| self.log_window.addstr(clear) | ||
| if self.log_curr_line >= 0: | ||
| log_dsp_line = self.log_curr_line | ||
| else: | ||
| log_dsp_line = len(self.log_content) - 1 | ||
| log_dsp_buffer = [] | ||
| while log_dsp_line >= 0: | ||
| log_line_buffer = self.log_parse_oneline(self.log_content[log_dsp_line][1]) | ||
| log_dsp_buffer = log_line_buffer + log_dsp_buffer | ||
| log_dsp_line -= 1 | ||
| if len(log_dsp_buffer) >= (LOG_WINDOW_HEIGHT - 1): | ||
| break | ||
| remove_len = len(log_dsp_buffer) - (LOG_WINDOW_HEIGHT - 1) | ||
| if remove_len > 0: | ||
| log_dsp_buffer = log_dsp_buffer[remove_len:] | ||
| self.curseslock.acquire() | ||
| for i in range(len(log_dsp_buffer)): | ||
| try: | ||
| self.log_window.addstr(i+1, 0, log_dsp_buffer[i][1], curses.color_pair(log_dsp_buffer[i][0])) | ||
| except: | ||
| self.curseslock.release() | ||
| if DEBUG: | ||
| print log_dsp_buffer[i] | ||
| print 'len:', len(log_dsp_buffer[i][1]) | ||
| raise | ||
| return | ||
| self.log_window.refresh() | ||
| self.cmd_window.refresh() | ||
| self.curseslock.release() | ||
| def device_list_display(self): | ||
| self.curseslock.acquire() | ||
| self.dev_window.move(1,0) | ||
| clean = (' ' * (DEV_WINDOW_WIDTH-1) + '\n') * (LOG_WINDOW_HEIGHT - 2) | ||
| clean += ' ' * (DEV_WINDOW_WIDTH-1) | ||
| self.dev_window.addstr(clean) | ||
| devices = list(self.device_list) | ||
| devices.sort() | ||
| if len(self.device_list) > 0: | ||
| cuuid = devices[0].split(',')[0] | ||
| linenum = 1 | ||
| for i in range(len(devices)): | ||
| devfull = devices[i] | ||
| dev = devfull.split(',') | ||
| if dev[0] != cuuid: | ||
| cuuid = dev[0] | ||
| linenum += 1 | ||
| if dev[0] in self.alias_tuples: | ||
| dev_str = str(i) + '.' + self.alias_tuples[dev[0]] + ':' | ||
| else: | ||
| dev_str = str(i) + '.' + dev[0] + ':' | ||
| mid_len = DEV_WINDOW_WIDTH - len(dev_str) - 5 | ||
| devpath = dev[1].replace('/dev/','') | ||
| if mid_len >= len(devpath): | ||
| dev_str += devpath + ' '*(mid_len - len(devpath)) | ||
| else: | ||
| dev_str += devpath[:mid_len] | ||
| dev_str += ' ' + self.device_list[devfull] | ||
| try: | ||
| self.dev_window.addstr(linenum, 0, dev_str) | ||
| except: | ||
| pass | ||
| linenum += 1 | ||
| if linenum >= LOG_WINDOW_HEIGHT: | ||
| break | ||
| self.dev_window.refresh() | ||
| self.cmd_window.refresh() | ||
| self.curseslock.release() | ||
| def cmdrun_command_display(self, cmd, postion): | ||
| self.curseslock.acquire() | ||
| self.cmd_window.move(0, len('Command:')) | ||
| self.cmd_window.addstr(' ' * (LOG_WINDOW_WIDTH + DEV_WINDOW_WIDTH - len('Command:'))) | ||
| self.cmd_window.move(0, len('Command:')) | ||
| self.cmd_window.addstr(cmd) | ||
| self.cmd_window.move(0, len('Command:') + postion) | ||
| self.cmd_window.refresh() | ||
| self.curseslock.release() | ||
| def cmdrun_status_display(self, log): | ||
| self.curseslock.acquire() | ||
| self.cmd_window.move(1,0) | ||
| self.cmd_window.addstr(' ' * (LOG_WINDOW_WIDTH + DEV_WINDOW_WIDTH)) | ||
| self.cmd_window.move(1,0) | ||
| self.cmd_window.addstr(log[:(LOG_WINDOW_WIDTH + DEV_WINDOW_WIDTH)]) | ||
| self.cmd_window.refresh() | ||
| self.curseslock.release() | ||
| def packet_send_thread(self): | ||
| heartbeat_timeout = time.time() + 10 | ||
| while self.keep_running: | ||
| try: | ||
| [type, content] = self.output_queue.get(block=True, timeout=0.1) | ||
| except Queue.Empty: | ||
| type = None | ||
| pass | ||
| if self.service_socket == None: | ||
| continue | ||
| if type == None: | ||
| if time.time() < heartbeat_timeout: | ||
| continue | ||
| heartbeat_timeout += 10 | ||
| data = pkt.construct(pkt.HEARTBEAT,'') | ||
| else: | ||
| data = pkt.construct(type, content) | ||
| try: | ||
| self.service_socket.send(data) | ||
| except: | ||
| self.connected = False | ||
| continue | ||
| def send_packet(self, type, content, timeout=0.1): | ||
| if self.service_socket == None: | ||
| return False | ||
| try: | ||
| self.output_queue.put([type, content], True, timeout) | ||
| return True | ||
| except Queue.Full: | ||
| if DEBUG: self.log_display(time.time(), 'error: ouput buffer full, drop packet [{0] {1}]'.format(type, content)) | ||
| return False | ||
| def get_devstr_by_index(self, index): | ||
| devices = list(self.device_list) | ||
| devices.sort() | ||
| return devices[index] | ||
| def get_index_by_devstr(self, devstr): | ||
| devices = list(self.device_list) | ||
| devices.sort() | ||
| return devices.index(devstr) | ||
| def login_and_get_server(self, controller_ip, controller_port): | ||
| sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||
| if ENCRYPT: | ||
| cert_file = path.join(path.dirname(path.abspath(__file__)), 'controller_certificate.pem') | ||
| sock = ssl.wrap_socket(sock, cert_reqs=ssl.CERT_REQUIRED, ca_certs = cert_file) | ||
| try: | ||
| sock.connect((controller_ip, controller_port)) | ||
| sock.settimeout(3) | ||
| msg = '' | ||
| except: | ||
| if DEBUG: traceback.print_exc() | ||
| return None | ||
| content = pkt.construct(pkt.ACCESS_LOGIN, 'terminal,' + self.uuid) | ||
| try: | ||
| sock.send(content) | ||
| except: | ||
| if DEBUG: traceback.print_exc() | ||
| sock.close() | ||
| return None | ||
| try: | ||
| data = sock.recv(MAX_MSG_LENGTH) | ||
| except KeyboardInterrupt: | ||
| sock.close() | ||
| raise | ||
| except: | ||
| sock.close() | ||
| return None | ||
| if data == '': | ||
| sock.close() | ||
| return None | ||
| type, length, value, data = pkt.parse(data) | ||
| #print 'controller', type, value | ||
| rets = value.split(',') | ||
| if type != pkt.ACCESS_LOGIN or rets[0] != 'success': | ||
| if rets[0] == 'invalid access key': | ||
| self.log_display(time.time(), 'login to controller failed, invalid access key') | ||
| accesskey_file = path.join(path.expanduser('~'), '.udterminal', '.accesskey') | ||
| if path.exists(accesskey_file): | ||
| os.remove(accesskey_file) | ||
| else: | ||
| self.log_display(time.time(), 'login to controller failed, ret={0}'.format(value)) | ||
| sock.close() | ||
| return None | ||
| sock.close() | ||
| return rets[1:] | ||
| def connect_to_server(self, server_ip, server_port, certificate): | ||
| sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||
| if certificate != 'None': | ||
| try: | ||
| certfile = path.join(path.expanduser('~'), '.udterminal', 'certificate.pem') | ||
| f = open(certfile, 'wt') | ||
| f.write(certificate) | ||
| f.close() | ||
| sock = ssl.wrap_socket(sock, cert_reqs=ssl.CERT_REQUIRED, ca_certs=certfile) | ||
| except: | ||
| return 'fail' | ||
| try: | ||
| sock.connect((server_ip, server_port)) | ||
| while self.output_queue.empty() == False: | ||
| self.output_queue.get() | ||
| self.service_socket = sock | ||
| self.connected = True | ||
| return 'success' | ||
| except: | ||
| return 'fail' | ||
| def server_interaction(self): | ||
| self.connected = False | ||
| self.service_socket = None | ||
| msg = '' | ||
| while self.keep_running: | ||
| try: | ||
| if self.connected == False: | ||
| raise ConnectionLost | ||
| new_msg = self.service_socket.recv(MAX_MSG_LENGTH) | ||
| if new_msg == '': | ||
| raise ConnectionLost | ||
| break | ||
| msg += new_msg | ||
| while msg != '': | ||
| type, length, value, msg = pkt.parse(msg) | ||
| if type == pkt.TYPE_NONE: | ||
| break | ||
| #print type, length | ||
| if type == pkt.FILE_BEGIN: | ||
| if 'file' in locals() and file.closed == False: | ||
| file.close() | ||
| filename = path.join(path.expanduser('~'), '.udterminal', value) | ||
| file = open(filename, 'w') | ||
| continue | ||
| if type == pkt.FILE_DATA: | ||
| if 'file' in locals() and file.closed == False: | ||
| file.write(value) | ||
| continue | ||
| if type == pkt.FILE_END: | ||
| if 'file' in locals(): | ||
| file.close() | ||
| continue | ||
| if type == pkt.ALL_DEV: | ||
| new_list = {} | ||
| clients = value.split(':') | ||
| for c in clients: | ||
| if c == '': | ||
| continue | ||
| devs = c.split(',') | ||
| uuid = devs[0] | ||
| devs = devs[1:] | ||
| for d in devs: | ||
| if d == '': | ||
| continue | ||
| [dev, using] = d.split('|') | ||
| new_list[uuid + ',' + dev] = using | ||
| for dev in list(new_list): | ||
| self.device_list[dev] = new_list[dev] | ||
| for dev in list(self.device_list): | ||
| if dev not in list(new_list): | ||
| self.device_list.pop(dev) | ||
| self.device_list_display() | ||
| continue | ||
| if type == pkt.DEVICE_LOG: | ||
| dev = value.split(':')[0] | ||
| logtime = value.split(':')[1] | ||
| log = value[len(dev) + 1 + len(logtime):] | ||
| try: | ||
| logtime = float(logtime) | ||
| except: | ||
| continue | ||
| if dev not in list(self.device_list): | ||
| continue | ||
| index = self.get_index_by_devstr(dev) | ||
| if dev in self.log_subscribed: | ||
| log = str(index) + log | ||
| self.log_display(logtime, log) | ||
| continue | ||
| if type == pkt.DEVICE_DEBUG_DATA: | ||
| dev = value.split(':')[0] | ||
| if dev not in list(self.device_list): | ||
| continue | ||
| data = value[len(dev)+1:] | ||
| if dev not in self.debug_socks: | ||
| continue | ||
| try: | ||
| self.debug_socks[dev].sendall(data) | ||
| except: | ||
| index = self.get_index_by_devstr(dev) | ||
| self.log_display(time.time(), 'forwording debug data for {0} failed'.format(index)) | ||
| if type in [pkt.DEVICE_DEBUG_START, pkt.DEVICE_DEBUG_REINIT, pkt.DEVICE_DEBUG_STOP]: | ||
| #self.log_display(time.time(), '{0}'.format([type, value])) | ||
| try: | ||
| [dev, result] = value.split(':') | ||
| except: | ||
| self.log_display(time.time(), 'error: wrong data {0} {1}'.format(type,value)) | ||
| continue | ||
| if dev not in list(self.device_list): | ||
| continue | ||
| message = [type, dev, result] | ||
| if self.debug_queue.full() == True: | ||
| self.log_display(time.time(), 'local debug deamon busy, discard packet {0}'.format([type, value])) | ||
| continue | ||
| self.debug_queue.put(message) | ||
| continue | ||
| if type == pkt.CMD_DONE: | ||
| self.cmd_excute_return = value | ||
| self.cmd_excute_state = 'done' | ||
| self.cmd_excute_event.set() | ||
| continue | ||
| if type == pkt.CMD_ERROR: | ||
| self.cmd_excute_return = value | ||
| self.cmd_excute_state = 'error' | ||
| self.cmd_excute_event.set() | ||
| continue | ||
| if type == pkt.TERMINAL_LOGIN: | ||
| if value == 'success': | ||
| continue | ||
| self.cmdrun_status_display('login to server failed, retry later ...') | ||
| time.sleep(10) | ||
| continue | ||
| except ConnectionLost: | ||
| self.connected = False | ||
| if self.service_socket != None: | ||
| self.service_socket.close() | ||
| self.service_socket = None | ||
| self.cmdrun_status_display('connection to server lost, try reconnecting...') | ||
| result = self.login_and_get_server(self.controller_ip, self.controller_port) | ||
| if result == None: | ||
| self.cmdrun_status_display('login to controller failed, retry later...') | ||
| time.sleep(5) | ||
| continue | ||
| try: | ||
| [server_ip, server_port, token, certificate] = result | ||
| server_port = int(server_port) | ||
| except: | ||
| self.log_display(time.time(), 'login to controller failed, invalid return={0}'.format(result)) | ||
| self.cmdrun_status_display('login to controller failed, retry later...') | ||
| time.sleep(5) | ||
| continue | ||
| result = self.connect_to_server(server_ip, server_port, certificate) | ||
| if result == 'success': | ||
| self.cmdrun_status_display('connect to server succeeded') | ||
| else: | ||
| self.cmdrun_status_display('connect to server failed, retry later ...') | ||
| time.sleep(5) | ||
| continue | ||
| data = self.uuid + ',' + token | ||
| self.send_packet(pkt.TERMINAL_LOGIN, data) | ||
| except: | ||
| if DEBUG: | ||
| raise | ||
| break | ||
| self.keep_running = False; | ||
| def debug_interaction(self): | ||
| debug_sessions = {} | ||
| while self.keep_running: | ||
| if debug_sessions == {} or self.debug_queue.empty() == False: | ||
| [type, device, result] = self.debug_queue.get() | ||
| index = self.get_index_by_devstr(device) | ||
| if type == pkt.DEVICE_DEBUG_START: | ||
| if result != 'success': | ||
| self.log_display(time.time(), 'error: start debug device {0} failed, ret={1}'.format(index, result)) | ||
| self.cmd_excute_return = result | ||
| self.cmd_excute_state = 'error' | ||
| self.cmd_excute_event.set() | ||
| continue | ||
| if device in debug_sessions: | ||
| self.cmd_excute_return = port | ||
| self.cmd_excute_state = 'done' | ||
| self.cmd_excute_event.set() | ||
| port = debug_sessions[device]['port'] | ||
| self.log_display(time.time(), 'start debugging {0} succeed, serve at port {1}'.format(index, port)) | ||
| continue | ||
| used_ports = [] | ||
| for device in debug_sessions: | ||
| used_ports.append(debug_sessions[device]['port']) | ||
| port = 4120 + ord(os.urandom(1)) * ord(os.urandom(1)) / 64 | ||
| while port in used_ports: | ||
| port = 4120 + ord(os.urandom(1)) * ord(os.urandom(1)) / 64 | ||
| sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||
| sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | ||
| sock.setblocking(False) | ||
| try: | ||
| sock.bind(('localhost', port)) | ||
| except: | ||
| self.log_display(time.time(), 'start debugging {0} failed, listen at port {1} failed'.format(index, port)) | ||
| self.cmd_excute_return = 'fail' | ||
| self.cmd_excute_state = 'error' | ||
| self.cmd_excute_event.set() | ||
| continue | ||
| sock.listen(1) | ||
| debug_sessions[device] = {'port': port, 'listen_sock': sock} | ||
| self.log_display(time.time(), 'start debugging {0} succeed, serve at port {1}'.format(index, port)) | ||
| self.cmd_excute_return = port | ||
| self.cmd_excute_state = 'done' | ||
| self.cmd_excute_event.set() | ||
| elif type == pkt.DEVICE_DEBUG_REINIT: | ||
| if result == 'success': | ||
| continue | ||
| self.log_display(time.time(), 'error: restart debugging device {0} failed, ret={1}, closing debug session'.format(index, result)) | ||
| if device not in debug_sessions: | ||
| continue | ||
| if 'serve_sock' in debug_sessions[device]: | ||
| self.debug_socks.pop(device) | ||
| debug_sessions[device]['serve_sock'].close() | ||
| debug_sessions[device]['listen_sock'].close() | ||
| debug_sessions.pop(device) | ||
| elif type == pkt.DEVICE_DEBUG_STOP: | ||
| self.cmd_excute_return = result | ||
| if result == 'success': | ||
| self.cmd_excute_state = 'done' | ||
| else: | ||
| self.cmd_excute_state = 'error' | ||
| self.cmd_excute_event.set() | ||
| if device not in debug_sessions: | ||
| continue | ||
| self.log_display(time.time(), 'stop debugging {0}: {1}'.format(index, result)) | ||
| if 'serve_sock' in debug_sessions[device]: | ||
| debug_sessions[device]['serve_sock'].close() | ||
| debug_sessions[device]['listen_sock'].close() | ||
| debug_sessions.pop(device) | ||
| else: | ||
| self.log_display(time.time(), "error: wrong debug msg type '{0}'".format(type)) | ||
| continue | ||
| select_list = {} | ||
| for device in debug_sessions: | ||
| if 'serve_sock' in debug_sessions[device]: | ||
| sock = debug_sessions[device]['serve_sock'] | ||
| operation = 'recv' | ||
| else: | ||
| sock = debug_sessions[device]['listen_sock'] | ||
| operation = 'accept' | ||
| select_list[sock] = [operation, device] | ||
| r, w, e = select.select(list(select_list), [], [], 0.02) | ||
| for sock in r: | ||
| [operation, device] = select_list[sock] | ||
| if operation == 'accept': | ||
| conn, addr = sock.accept() | ||
| conn.setblocking(0) | ||
| debug_sessions[device]['serve_sock'] = conn | ||
| self.debug_socks[device] = conn | ||
| else: | ||
| try: | ||
| data = sock.recv(MAX_MSG_LENGTH) | ||
| except: | ||
| data = None | ||
| if not data: | ||
| debug_sessions[device].pop('serve_sock') | ||
| self.debug_socks.pop(device) | ||
| sock.close() | ||
| #self.log_display(time.time(), 'debug client disconnected') | ||
| self.send_packet(pkt.DEVICE_DEBUG_REINIT, device) | ||
| continue | ||
| content = device + ':' + data | ||
| self.send_packet(pkt.DEVICE_DEBUG_DATA, content) | ||
| def parse_device_index(self, index_str): | ||
| max = len(self.device_list) - 1 | ||
| if '-' in index_str: | ||
| index_split = index_str.split('-') | ||
| if len(index_split) != 2 or index_split[0].isdigit() == False or index_split[1].isdigit() == False: | ||
| return []; | ||
| try: | ||
| start = int(index_split[0]) | ||
| end = int(index_split[1]) | ||
| if start > max and end > max: | ||
| return [] | ||
| if start > max: | ||
| start = max | ||
| if end > max: | ||
| end = max | ||
| except: | ||
| return []; | ||
| if start < end: | ||
| return range(start, end + 1, 1) | ||
| else: | ||
| return range(start, end - 1, -1) | ||
| else: | ||
| if index_str.isdigit() == False: | ||
| return [] | ||
| try: | ||
| index = int(index_str) | ||
| except: | ||
| return [] | ||
| if index >= len(self.device_list): | ||
| return [] | ||
| return [index] | ||
| def wait_cmd_excute_done(self, timeout): | ||
| self.cmd_excute_state = 'wait_response' | ||
| self.cmd_excute_return = None | ||
| self.cmd_excute_event.clear() | ||
| if self.cmd_excute_event.wait(timeout) == False: | ||
| self.cmd_excute_state = 'timeout' | ||
| def send_file_to_client(self, filename, index): | ||
| status_str = 'sending file {0} to {1}...'.format(filename, self.get_devstr_by_index(index).split(',')[0]) | ||
| self.cmdrun_status_display(status_str) | ||
| filehash = pkt.hash_of_file(filename) | ||
| devstr = self.get_devstr_by_index(index) | ||
| #send file begin | ||
| content = devstr + ':' + filehash + ':' + filename.split('/')[-1] | ||
| retry = 4 | ||
| while retry > 0: | ||
| self.send_packet(pkt.FILE_BEGIN, content) | ||
| self.wait_cmd_excute_done(0.2) | ||
| if self.cmd_excute_state == 'timeout': | ||
| retry -= 1; | ||
| continue | ||
| if self.cmd_excute_return == 'busy': | ||
| time.sleep(5) | ||
| continue | ||
| elif self.cmd_excute_return == 'ok' or self.cmd_excute_return == 'exist': | ||
| break | ||
| else: | ||
| status_str += 'error' | ||
| self.cmdrun_status_display(status_str) | ||
| return False | ||
| if retry == 0: | ||
| status_str += 'error' | ||
| self.cmdrun_status_display(status_str) | ||
| return False | ||
| if self.cmd_excute_return == 'exist': | ||
| status_str += 'done' | ||
| self.cmdrun_status_display(status_str) | ||
| return True | ||
| #send file data | ||
| seq = 0 | ||
| file = open(filename,'r') | ||
| header = devstr + ':' + filehash + ':' + str(seq) + ':' | ||
| content = file.read(8192) | ||
| while(content): | ||
| retry = 4 | ||
| while retry > 0: | ||
| self.send_packet(pkt.FILE_DATA, header + content) | ||
| self.wait_cmd_excute_done(0.2) | ||
| if self.cmd_excute_return == None: | ||
| retry -= 1; | ||
| continue | ||
| elif self.cmd_excute_return != 'ok': | ||
| status_str += 'error' | ||
| self.cmdrun_status_display(status_str) | ||
| file.close() | ||
| return False | ||
| break | ||
| if retry == 0: | ||
| status_str += 'error' | ||
| self.cmdrun_status_display(status_str) | ||
| file.close() | ||
| return False | ||
| seq += 1 | ||
| header = devstr + ':' + filehash + ':' + str(seq) + ':' | ||
| content = file.read(8192) | ||
| file.close() | ||
| #send file end | ||
| content = devstr + ':' + filehash + ':' + filename.split('/')[-1] | ||
| retry = 4 | ||
| while retry > 0: | ||
| self.send_packet(pkt.FILE_END, content) | ||
| self.wait_cmd_excute_done(0.2) | ||
| if self.cmd_excute_return == None: | ||
| retry -= 1; | ||
| continue | ||
| elif self.cmd_excute_return != 'ok': | ||
| status_str += 'error' | ||
| self.cmdrun_status_display(status_str) | ||
| return False | ||
| break | ||
| if retry == 0: | ||
| status_str += 'error' | ||
| self.cmdrun_status_display(status_str) | ||
| return False | ||
| status_str += 'done' | ||
| self.cmdrun_status_display(status_str) | ||
| return True | ||
| def erase_devices(self, args): | ||
| devs = args | ||
| if devs == []: | ||
| self.cmdrun_status_display('Usage error: please specify devices you want to programg') | ||
| return False | ||
| succeed = []; failed = [] | ||
| for dev in devs: | ||
| indexes = self.parse_device_index(dev) | ||
| if indexes == []: | ||
| self.cmdrun_status_display('invalid device index {0}'.format(dev)) | ||
| continue | ||
| for index in indexes: | ||
| status_str = 'erasing {0}.{1}...'.format(index, self.get_devstr_by_index(index)) | ||
| self.cmdrun_status_display(status_str) | ||
| dev_str = self.get_devstr_by_index(index) | ||
| self.send_packet(pkt.DEVICE_ERASE, dev_str); | ||
| self.wait_cmd_excute_done(10) | ||
| status_str += self.cmd_excute_state | ||
| self.cmdrun_status_display(status_str) | ||
| if self.cmd_excute_state == 'done': | ||
| succeed.append(index) | ||
| else: | ||
| failed.append(index) | ||
| self.cmd_excute_state = 'idle' | ||
| if dev_str not in self.using_list: | ||
| self.using_list.append(dev_str) | ||
| status_str = '' | ||
| if succeed != []: | ||
| status_str += 'succeed: {0}'.format(succeed) | ||
| if failed != []: | ||
| if status_str != '': | ||
| status_str += ', ' | ||
| status_str += 'failed: {0}'.format(failed) | ||
| self.cmdrun_status_display(status_str) | ||
| def program_devices(self, args): | ||
| if len(args) < 3: | ||
| self.cmdrun_status_display('Usage: programg addresse filename device0 [device1 device2 ... deviceN]') | ||
| return False | ||
| if args[0].startswith('0x') == False: | ||
| self.cmdrun_status_display('Usage error: wrong address input {0}, address should start with 0x'.format(args[0])) | ||
| return False | ||
| address = args[0] | ||
| filename = args[1] | ||
| try: | ||
| expandname = path.expanduser(filename) | ||
| except: | ||
| self.cmdrun_status_display('{0} does not exist'.format(filename)) | ||
| return False | ||
| if path.exists(expandname) == False: | ||
| self.cmdrun_status_display('{0} does not exist'.format(filename)) | ||
| return False | ||
| filehash = pkt.hash_of_file(expandname) | ||
| devs = args[2:] | ||
| file_exist_at = [] | ||
| if devs == []: | ||
| self.cmdrun_status_display('Usage error: please specify devices you want to programg') | ||
| return False | ||
| succeed = []; failed = [] | ||
| for dev in devs: | ||
| indexes = self.parse_device_index(dev) | ||
| if indexes == []: | ||
| self.cmdrun_status_display('invalid device index {0}'.format(dev)) | ||
| continue | ||
| for index in indexes: | ||
| dev_str = self.get_devstr_by_index(index) | ||
| [uuid, port] = dev_str.split(',') | ||
| if uuid not in file_exist_at: | ||
| if self.send_file_to_client(expandname, index) == False: | ||
| failed.append(index) | ||
| continue | ||
| file_exist_at.append(uuid) | ||
| status_str = 'programming {0} to {1}.{2}:{3}...'.format(filename, index, uuid, port.replace('/dev/', '')) | ||
| self.cmdrun_status_display(status_str) | ||
| content = dev_str + ',' + address + ',' + filehash | ||
| self.send_packet(pkt.DEVICE_PROGRAM, content); | ||
| self.wait_cmd_excute_done(270) | ||
| status_str += self.cmd_excute_state | ||
| self.cmdrun_status_display(status_str) | ||
| if self.cmd_excute_state == 'done': | ||
| succeed.append(index) | ||
| else: | ||
| failed.append(index) | ||
| self.cmd_excute_state = 'idle' | ||
| if dev_str not in self.using_list: | ||
| self.using_list.append(dev_str) | ||
| status_str = '' | ||
| if succeed != []: | ||
| status_str += 'succeed: {0}'.format(succeed) | ||
| if failed != []: | ||
| if status_str != '': | ||
| status_str += ', ' | ||
| status_str += 'failed: {0}'.format(failed) | ||
| self.cmdrun_status_display(status_str) | ||
| def control_devices(self, operate, args): | ||
| if len(args) < 1: | ||
| self.cmdrun_status_display('Usage error, usage: reset devices') | ||
| return False | ||
| operations = {'start':pkt.DEVICE_START, 'stop':pkt.DEVICE_STOP, 'reset':pkt.DEVICE_RESET} | ||
| operate = operations[operate] | ||
| succeed = []; failed = [] | ||
| for dev in args: | ||
| indexes = self.parse_device_index(dev) | ||
| if indexes == []: | ||
| self.cmdrun_status_display('invalid device index {0}'.format(dev)) | ||
| return False | ||
| for index in indexes: | ||
| dev_str = self.get_devstr_by_index(index) | ||
| self.send_packet(operate, dev_str) | ||
| self.wait_cmd_excute_done(1.5) | ||
| if self.cmd_excute_state == 'done': | ||
| succeed.append(index) | ||
| else: | ||
| failed.append(index) | ||
| self.cmd_excute_state = 'idle' | ||
| if dev_str not in self.using_list: | ||
| self.using_list.append(dev_str) | ||
| status_str = '' | ||
| if succeed != []: | ||
| status_str += 'succeed: {0}'.format(succeed) | ||
| if failed != []: | ||
| if status_str != '': | ||
| status_str += ', ' | ||
| status_str += 'failed: {0}'.format(failed) | ||
| self.cmdrun_status_display(status_str) | ||
| def log_on_off(self, args): | ||
| if len(args) < 2: | ||
| self.cmdrun_status_display('Usage error, usage: log on/off devices') | ||
| return False | ||
| if args[0] == 'on': | ||
| type = pkt.LOG_SUB | ||
| elif args[0] == 'off': | ||
| type = pkt.LOG_UNSUB | ||
| else: | ||
| self.cmdrun_status_display('Usage error, usage: log on/off devices') | ||
| return False | ||
| for dev in args[1:]: | ||
| indexes = self.parse_device_index(dev) | ||
| if not indexes: | ||
| self.cmdrun_status_display('invalid device index {0}'.format(dev)) | ||
| continue | ||
| for index in indexes: | ||
| dev_str = self.get_devstr_by_index(index) | ||
| if type == pkt.LOG_SUB and dev_str not in self.log_subscribed: | ||
| self.send_packet(type, dev_str) | ||
| self.log_subscribed.append(dev_str) | ||
| elif type == pkt.LOG_UNSUB and dev_str in self.log_subscribed: | ||
| self.send_packet(type, dev_str) | ||
| self.log_subscribed.remove(dev_str) | ||
| def log_download(self, args): | ||
| if len(args) < 1: | ||
| self.cmdrun_status_display('Usage error, usage: logdownload device0 [device1 ... deviceN]') | ||
| return False | ||
| succeed = []; failed = [] | ||
| for dev in args: | ||
| indexes = self.parse_device_index(dev) | ||
| if indexes == []: | ||
| self.cmdrun_status_display('invalid device index {0}'.format(dev)) | ||
| return False | ||
| for index in indexes: | ||
| device = self.get_devstr_by_index(index).split(',') | ||
| status_str = 'downloading log file for {0}.{1}:{2}...'.format(index, device[0], device[1].replace('/dev/', '')) | ||
| self.cmdrun_status_display(status_str) | ||
| content = ','.join(device) | ||
| self.send_packet(pkt.LOG_DOWNLOAD, content) | ||
| self.wait_cmd_excute_done(480) | ||
| if self.cmd_excute_state == 'done': | ||
| succeed.append(index) | ||
| else: | ||
| failed.append(index) | ||
| self.cmd_excute_state = 'idle' | ||
| status_str = '' | ||
| if succeed != []: | ||
| status_str += 'succeed: {0}'.format(succeed) | ||
| if failed != []: | ||
| if status_str != '': | ||
| status_str += ', ' | ||
| status_str += 'failed: {0}'.format(failed) | ||
| self.cmdrun_status_display(status_str) | ||
| return (len(failed) == 0) | ||
| def run_command(self, args, uselast = False): | ||
| if uselast == False: | ||
| if len(args) < 2: | ||
| self.cmdrun_status_display('Usage error, usage: runcmd device cmd_arg0 [cmd_arg1 cmd_arg2 ... cmd_argN]') | ||
| return False | ||
| indexes = self.parse_device_index(args[0]) | ||
| if indexes == []: | ||
| self.cmdrun_status_display('invalid device index {0}'.format(args[0])) | ||
| return False | ||
| args = args[1:] | ||
| self.last_runcmd_dev = self.get_devstr_by_index(indexes[-1]) | ||
| else: | ||
| if self.last_runcmd_dev == []: | ||
| self.cmdrun_status_display('Error: you have not excute any remote command with runcmd yet, no target remembered') | ||
| return False | ||
| if len(args) < 1: | ||
| self.cmdrun_status_display('Usage error, usage: !cmd_arg0 [cmd_arg1 cmd_arg2 ... cmd_argN]') | ||
| return False | ||
| if self.last_runcmd_dev not in list(self.device_list): | ||
| self.cmdrun_status_display('Error: remembered target no longer exists') | ||
| return False | ||
| indexes = [self.get_index_by_devstr(self.last_runcmd_dev)] | ||
| succeed = []; failed = [] | ||
| for index in indexes: | ||
| dev_str = self.get_devstr_by_index(index) | ||
| content = dev_str + ':' + '|'.join(args) | ||
| self.send_packet(pkt.DEVICE_CMD, content) | ||
| self.wait_cmd_excute_done(1.5) | ||
| if self.cmd_excute_state == 'done': | ||
| succeed.append(index) | ||
| else: | ||
| failed.append(index) | ||
| self.cmd_excute_state = 'idle' | ||
| if self.cmd_excute_return == None: | ||
| self.cmd_excute_return = 'timeout' | ||
| status_str = '{0} run: '.format(index) + ' '.join(args) + ', ' + self.cmd_excute_return | ||
| self.cmdrun_status_display(status_str) | ||
| if dev_str not in self.using_list: | ||
| self.using_list.append(dev_str) | ||
| if len(indexes) == 1: | ||
| return True | ||
| status_str = '' | ||
| if succeed != []: | ||
| status_str += 'succeed: {0}'.format(succeed) | ||
| if failed != []: | ||
| if status_str != '': | ||
| status_str += ', ' | ||
| status_str += 'failed: {0}'.format(failed) | ||
| self.cmdrun_status_display(status_str) | ||
| return (len(failed) == 0) | ||
| def run_debug(self, args, uselast = False): | ||
| if len(args) < 2 or args[0] not in ['start', 'stop']: | ||
| self.cmdrun_status_display('Usage error, usage: debug start/stop device1 [device2 .. deviceN] ') | ||
| return False | ||
| operation = args[0] | ||
| devs = args[1:] | ||
| indexes = []; devices = {} | ||
| for dev in devs: | ||
| indexes += self.parse_device_index(dev) | ||
| if indexes == []: | ||
| self.cmdrun_status_display('invalid device index {0}'.format(' '.join(devs))) | ||
| return False | ||
| indexes = list(set(indexes)) | ||
| for index in indexes: | ||
| devices[self.get_devstr_by_index(index)] = index | ||
| if operation == 'start': | ||
| type = pkt.DEVICE_DEBUG_START | ||
| else: | ||
| type = pkt.DEVICE_DEBUG_STOP | ||
| succeed = []; failed = [] | ||
| for device in list(devices): | ||
| if device not in self.device_list: | ||
| continue | ||
| self.send_packet(type, device) | ||
| self.wait_cmd_excute_done(2) | ||
| if self.cmd_excute_state == 'done': | ||
| succeed.append(devices[device]) | ||
| else: | ||
| failed.append(devices[device]) | ||
| status_str = '' | ||
| if succeed != []: | ||
| status_str += 'succeed: {0}'.format(succeed) | ||
| if failed != []: | ||
| if status_str != '': | ||
| status_str += ', ' | ||
| status_str += 'failed: {0}'.format(failed) | ||
| self.cmdrun_status_display(status_str) | ||
| return (len(failed) == 0) | ||
| def client_alias(self, args): | ||
| if len(args) < 1: | ||
| self.cmdrun_status_display('Usage error, usage: alias id0:name0 [id1:name1 ... idN:nameN]') | ||
| return False | ||
| for arg in args: | ||
| alias = arg.split(':') | ||
| if len(alias) != 2: | ||
| self.cmdrun_status_display('Usage error, unrecongnized alias tuple {0}'.format(arg)) | ||
| continue | ||
| self.alias_tuples[alias[0]] = alias[1] | ||
| try: | ||
| alias_file = path.join(path.expanduser('~'), '.udterminal', '.alias') | ||
| file = open(alias_file, 'wb') | ||
| json.dump(self.alias_tuples, file) | ||
| file.close() | ||
| except: | ||
| self.cmdrun_status_display('error: save alias record failed') | ||
| self.device_list_display() | ||
| def print_help_info(self): | ||
| self.log_display(time.time(), 'Supported commands and usage examples:') | ||
| self.log_display(time.time(), ' 1.erase [er]: erase flash of devices') | ||
| self.log_display(time.time(), ' example: erase 0 1 2-5') | ||
| self.log_display(time.time(), ' 2.program [pg]: program fireware to devices') | ||
| self.log_display(time.time(), ' example: program 0x40000 aos_esp32.bin 0 1 5-10') | ||
| self.log_display(time.time(), ' 3.reset [rs]: reset/restart devices') | ||
| self.log_display(time.time(), ' example: reset 0 1 3-8') | ||
| self.log_display(time.time(), ' 4.stop [sp]: stop devices') | ||
| self.log_display(time.time(), ' example: stop 0 5-7 9') | ||
| self.log_display(time.time(), ' 5.start [st]: start devices') | ||
| self.log_display(time.time(), ' example: start 3-5 0 1') | ||
| self.log_display(time.time(), ' 6.runcmd [rc]: run command at remote device') | ||
| self.log_display(time.time(), ' example: runcmd 0 ping fc:00:00:10:11:22:33:44') | ||
| self.log_display(time.time(), ' runcmd 0-10 umesh status') | ||
| self.log_display(time.time(), ' 7.^ : run command at latest (runcmd) remote device') | ||
| self.log_display(time.time(), ' example: ^ping fc:00:00:10:11:22:33:44') | ||
| self.log_display(time.time(), ' 8.debug [db]: start/stop debugging remote device') | ||
| self.log_display(time.time(), ' example: debug start 0') | ||
| self.log_display(time.time(), ' debug stop 1') | ||
| self.log_display(time.time(), ' 9.log [lg]: turn on/off log display for devices, eg.: log on 1') | ||
| self.log_display(time.time(), ' example: log on 1 2 5-8; log off 2-5 7') | ||
| self.log_display(time.time(), ' 10.logdownload[ld]: download log file of device from server') | ||
| self.log_display(time.time(), ' example: logdownload 0-2 5') | ||
| self.log_display(time.time(), ' 11.alias [al]: alias names to client ids') | ||
| self.log_display(time.time(), ' example: alias 1234567890123456:Pi1@HZ') | ||
| self.log_display(time.time(), ' 12.quit [q]: quit this terminal') | ||
| self.log_display(time.time(), ' 13.help [h]: print help infomation') | ||
| self.log_display(time.time(), ' ') | ||
| self.log_display(time.time(), 'Supported command editing shortcuts:') | ||
| self.log_display(time.time(), ' Ctrl + a - go to the start of the command line') | ||
| self.log_display(time.time(), ' Ctrl + e - go to the end of the command line') | ||
| self.log_display(time.time(), ' Ctrl + k - delete from cursor to the end of the command line') | ||
| self.log_display(time.time(), ' Ctrl + u - delete from cursor to the start of the command line') | ||
| def process_cmd(self, cmd): | ||
| if self.connected == False: | ||
| self.cmdrun_status_display("process command '{0}' failed, connection to server lost".format(cmd)) | ||
| cmd_argv = cmd.split(' ') | ||
| self.cmdrun_status_display('') | ||
| cmd = cmd_argv[0]; args = cmd_argv[1:] | ||
| if cmd == 'erase' or cmd == 'er': | ||
| self.erase_devices(args) | ||
| elif cmd == 'program' or cmd == 'pg': | ||
| self.program_devices(args) | ||
| elif cmd == 'reset' or cmd == 'rs': | ||
| self.control_devices('reset', args) | ||
| elif cmd == 'start' or cmd == 'st': | ||
| self.control_devices('start', args) | ||
| elif cmd == 'stop' or cmd == 'sp': | ||
| self.control_devices('stop', args) | ||
| elif cmd == 'log' or cmd == 'lg': | ||
| self.log_on_off(args) | ||
| elif cmd == 'logdownload' or cmd == 'ld': | ||
| self.log_download(args) | ||
| elif cmd == 'runcmd' or cmd == 'rc': | ||
| self.run_command(args, uselast=False) | ||
| elif cmd == 'debug' or cmd == 'db': | ||
| self.run_debug(args) | ||
| elif cmd[0] == '^': | ||
| self.run_command([cmd[1:]] + args, uselast=True) | ||
| elif cmd == 'alias' or cmd == 'al': | ||
| self.client_alias(args) | ||
| elif cmd == 'help' or cmd == 'h': | ||
| self.print_help_info() | ||
| else: | ||
| self.cmdrun_status_display('unknown command:' + cmd) | ||
| def user_interaction(self): | ||
| cmd = '' | ||
| saved_cmd = '' | ||
| history_index = -1 | ||
| p = 0 | ||
| while self.keep_running: | ||
| c = self.cmd_window.getch() | ||
| if c == ord('\n'): | ||
| if self.log_curr_line != -1: | ||
| self.log_curr_line = -1 | ||
| self.log_display(time.time(), '') | ||
| if cmd == 'quit' or cmd == 'q' : | ||
| self.keep_running = False | ||
| time.sleep(0.2) | ||
| break | ||
| elif cmd != '': | ||
| self.process_cmd(cmd) | ||
| self.cmd_history = [cmd] + self.cmd_history | ||
| cmd = '' | ||
| saved_cmd = '' | ||
| history_index = -1 | ||
| p = 0 | ||
| self.cmdrun_command_display(cmd, 0) | ||
| elif c == curses.KEY_BACKSPACE or c == 127 or c == 8: #DELETE | ||
| if cmd[0:p] == '': | ||
| continue | ||
| newcmd = cmd[0:p-1] + cmd[p:] | ||
| cmd = newcmd | ||
| p -= 1 | ||
| self.cmdrun_command_display(cmd, p) | ||
| elif c == 259 and len(self.cmd_history) > 0: #KEY_UP | ||
| if history_index == -1: | ||
| saved_cmd = cmd | ||
| if history_index < (len(self.cmd_history) - 1): | ||
| history_index += 1 | ||
| cmd = self.cmd_history[history_index] | ||
| p = len(cmd) | ||
| self.cmdrun_command_display(cmd, p) | ||
| elif c == 258 and len(self.cmd_history) > 0: #KEY_DOWN | ||
| if history_index <= -1: | ||
| history_index = -1 | ||
| continue | ||
| history_index -= 1 | ||
| if history_index >= 0: | ||
| cmd = self.cmd_history[history_index] | ||
| else: | ||
| cmd = saved_cmd | ||
| p = len(cmd) | ||
| self.cmdrun_command_display(cmd, p) | ||
| elif c == 260: #KEY_LEFT | ||
| if p > 0: | ||
| p -= 1 | ||
| self.cmdrun_command_display(cmd, p) | ||
| continue | ||
| elif c == 261: #KEY_RIGHT | ||
| if p < len(cmd): | ||
| p += 1 | ||
| self.cmdrun_command_display(cmd, p) | ||
| continue | ||
| elif c == 1: #ctrl-a: go to the start of the command line | ||
| p = 0 | ||
| self.cmdrun_command_display(cmd, p) | ||
| continue | ||
| elif c == 3: #ctrl-c: quit | ||
| self.keep_running = False | ||
| time.sleep(0.2) | ||
| break | ||
| elif c == 5: #ctrl-e: go to the end of the command line | ||
| p = len(cmd) | ||
| self.cmdrun_command_display(cmd, p) | ||
| continue | ||
| elif c == 11: #ctrl-k: delete from cursor to the end of the command line | ||
| cmd = cmd[0:p] | ||
| self.cmdrun_command_display(cmd, p) | ||
| continue | ||
| elif c == 21: #ctrl-u: delete from cursor to the start of the command line | ||
| cmd = cmd[p:] | ||
| p = 0 | ||
| self.cmdrun_command_display(cmd, p) | ||
| continue | ||
| elif c == curses.KEY_MOUSE: | ||
| mouse_state = curses.getmouse()[4] | ||
| if mouse_state == MOUSE_SCROLL_UP: | ||
| if self.log_curr_line == -1: | ||
| self.log_curr_line = len(self.log_content) - 2 | ||
| elif self.log_curr_line > 0: | ||
| self.log_curr_line -= 1 | ||
| self.log_display(time.time(), '') | ||
| continue | ||
| elif mouse_state == MOUSE_SCROLL_DOWN: | ||
| if self.log_curr_line == -1: | ||
| continue | ||
| self.log_curr_line += 1 | ||
| if self.log_curr_line >= len(self.log_content): | ||
| self.log_curr_line = -1 | ||
| self.log_display(time.time(), '') | ||
| continue | ||
| elif c == curses.KEY_RESIZE: | ||
| try: | ||
| self.window_redraw() | ||
| self.device_list_display() | ||
| self.log_display(time.time(), '') | ||
| self.cmdrun_command_display(cmd, p); | ||
| except: | ||
| if DEBUG: | ||
| raise | ||
| self.keep_running = False | ||
| break | ||
| else: | ||
| try: | ||
| c = str(unichr(c)) | ||
| newcmd = cmd[0:p] + c + cmd[p:] | ||
| cmd = newcmd | ||
| p += 1 | ||
| except: | ||
| self.cmdrun_status_display('Error: unsupported unicode character {0}'.format(c)) | ||
| continue | ||
| self.cmdrun_command_display(cmd, p) | ||
| def run(self, controller_ip, controller_port): | ||
| #connect to server | ||
| self.controller_ip = controller_ip | ||
| self.controller_port = controller_port | ||
| thread.start_new_thread(self.packet_send_thread, ()) | ||
| thread.start_new_thread(self.server_interaction, ()) | ||
| thread.start_new_thread(self.debug_interaction, ()) | ||
| while self.keep_running: | ||
| try: | ||
| self.user_interaction() | ||
| except KeyboardInterrupt: | ||
| self.keep_running = False | ||
| time.sleep(0.2) | ||
| except: | ||
| if DEBUG: | ||
| raise | ||
| def deinit(self): | ||
| curses.nocbreak() | ||
| self.cmd_window.keypad(0) | ||
| curses.echo() | ||
| curses.endwin() | ||
| if self.service_socket: | ||
| self.service_socket.close() | ||
| try: | ||
| if len(self.cmd_history) > 0: | ||
| cmd_history_file = path.join(path.expanduser('~'), '.udterminal', '.cmd_history') | ||
| file = open(cmd_history_file, 'wb') | ||
| json.dump(self.cmd_history, file) | ||
| file.close() | ||
| except: | ||
| self.cmdrun_status_display('error: save command history failed') | ||
| def terminal_func(self, controller_ip, controller_port): | ||
| ret = self.init() | ||
| if ret == 'read access key fail': | ||
| print '' | ||
| return | ||
| if ret == 'success': | ||
| self.run(controller_ip, controller_port) | ||
| if ret == 'UI failed': | ||
| print 'initilize UI window failed, try increase your terminal window size first' | ||
| self.deinit() |
-108
| #!/usr/bin/env python | ||
| import sys, os | ||
| DEFAULT_SERVER = '118.31.76.36' | ||
| DEFAULT_PORT = 2000 | ||
| def print_usage(): | ||
| print "Usage: {0} terminal/client [-s xxx.xxx.xx.xx] [-p xxxxx]\n".format(sys.argv[0]) | ||
| def try_install_curses(): | ||
| try: | ||
| import curses | ||
| return 'success' | ||
| except: | ||
| pass | ||
| if os.name != 'nt': | ||
| return 'fail' | ||
| print 'installing dependent package: curses ...' | ||
| import subprocess, urllib | ||
| try: | ||
| curses_32bit_url = 'http://alios-things-public.oss-cn-hangzhou.aliyuncs.com/curses-2.2-cp27-none-win32.whl' | ||
| curses_64bit_url = 'http://alios-things-public.oss-cn-hangzhou.aliyuncs.com/curses-2.2-cp27-none-win_amd64.whl' | ||
| urllib.urlretrieve(curses_32bit_url, 'curses-2.2-cp27-none-win32.whl') | ||
| urllib.urlretrieve(curses_64bit_url, 'curses-2.2-cp27-none-win_amd64.whl') | ||
| subprocess.call(['pip', 'install', 'wheel']) | ||
| subprocess.call(['pip', 'install', 'curses-2.2-cp27-none-win32.whl']) | ||
| subprocess.call(['pip', 'install', 'curses-2.2-cp27-none-win_amd64.whl']) | ||
| except: | ||
| pass | ||
| finally: | ||
| if os.path.isfile('curses-2.2-cp27-none-win32.whl'): | ||
| os.remove('curses-2.2-cp27-none-win32.whl') | ||
| if os.path.isfile('curses-2.2-cp27-none-win_amd64.whl'): | ||
| os.remove('curses-2.2-cp27-none-win_amd64.whl') | ||
| try: | ||
| import curses | ||
| print 'succeed' | ||
| return 'success' | ||
| except: | ||
| print 'failed' | ||
| return 'fail' | ||
| def main(): | ||
| if len(sys.argv) < 2: | ||
| print_usage() | ||
| sys.exit(1) | ||
| host_name = DEFAULT_SERVER | ||
| host_port = DEFAULT_PORT | ||
| mode = sys.argv[1] | ||
| i = 2; arg_num = len(sys.argv) | ||
| while i < arg_num: | ||
| if sys.argv[i] == '-s' and (i+1) < arg_num: | ||
| host_name = sys.argv[i+1] | ||
| i += 1 | ||
| elif sys.argv[i] == '-p' and (i+1) < arg_num: | ||
| try: | ||
| host_port = int(sys.argv[i+1]) | ||
| except: | ||
| print "error: valid port '{0}'".format(sys.argv[i+1]) | ||
| sys.exit(1) | ||
| i += 1 | ||
| else: | ||
| print "error: invalid argument '{0}'".format(' '.join(sys.argv[1:])) | ||
| print_usage() | ||
| sys.exit(1) | ||
| i += 1 | ||
| if os.name == 'posix': | ||
| tmpfile_folder = '/tmp/' | ||
| elif os.name == 'nt': | ||
| tmpfile_folder = os.path.expanduser('~') + '\\' | ||
| if mode == "client": | ||
| from client import Client | ||
| if host_name == None: | ||
| print_usage() | ||
| sys.exit(1) | ||
| tmpfile = tmpfile_folder + '.testbed_client' | ||
| if os.path.exists(tmpfile): | ||
| print "An udevice center client is already running" | ||
| sys.exit(1) | ||
| open(tmpfile, 'a').close() | ||
| client = Client() | ||
| client.client_func(host_name, host_port) | ||
| os.remove(tmpfile) | ||
| elif mode == "terminal": | ||
| if try_install_curses() == 'fail': | ||
| print "error: unable to install python curses module" | ||
| sys.exit(1) | ||
| from terminal import Terminal | ||
| if host_name == None: | ||
| print_usage() | ||
| sys.exit(1) | ||
| terminal = Terminal() | ||
| terminal.terminal_func(host_name, host_port) | ||
| else: | ||
| print_usage() | ||
| sys.exit(0) | ||
| if __name__ == '__main__': | ||
| main() |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
114382
-63.24%34
-26.09%2128
-67.25%