Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

aos-cube

Package Overview
Dependencies
Maintainers
1
Versions
105
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

aos-cube - npm Package Compare versions

Comparing version
0.3.7
to
0.3.8
+130
aos/download.py
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)
+0
-1
[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
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)

@@ -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:

@@ -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)

@@ -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)

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 @@

@@ -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 @@

@@ -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():

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

@@ -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 @@ ]

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
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'
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

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()
#!/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()
#!/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()