Mbed LS
lstools_base.py
Go to the documentation of this file.
1 """
2 mbed SDK
3 Copyright (c) 2011-2015 ARM Limited
4 
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8 
9  http://www.apache.org/licenses/LICENSE-2.0
10 
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16 """
17 
18 import re
19 import os
20 import sys
21 import functools
22 from os.path import expanduser
23 from io import open
24 import json
25 from os import listdir
26 from os.path import isfile, join, exists, isdir
27 import logging
28 from abc import ABCMeta, abstractmethod
29 
30 from .platform_database import PlatformDatabase, LOCAL_PLATFORM_DATABASE, \
31  LOCAL_MOCKS_DATABASE
32 mbedls_root_logger = logging.getLogger("mbedls")
33 mbedls_root_logger.setLevel(logging.WARNING)
34 
35 logger = logging.getLogger("mbedls.lstools_base")
36 logger.addHandler(logging.NullHandler())
37 
38 def deprecated(reason):
39  """Deprecate a function/method with a decorator"""
40  def actual_decorator(func):
41  @functools.wraps(func)
42  def new_func(*args, **kwargs):
43  logger.warning("Call to deprecated function %s. %s",
44  func.__name__, reason)
45  return func(*args, **kwargs)
46  return new_func
47  return actual_decorator
48 
50  BeforeFilter = 1
51  AfterFilter = 2
52  Never = 3
53 
55  """ Base class for mbed-lstools, defines mbed-ls tools interface for
56  mbed-enabled devices detection for various hosts
57  """
58 
59  __metaclass__ = ABCMeta
60 
61  # Which OSs are supported by this module
62  # Note: more than one OS can be supported by mbed-lstools_* module
63  os_supported = []
64 
65  # Directory where we will store global (OS user specific mocking)
66  HOME_DIR = expanduser("~")
67  MOCK_FILE_NAME = '.mbedls-mock'
68  RETARGET_FILE_NAME = 'mbedls.json'
69  DETAILS_TXT_NAME = 'DETAILS.TXT'
70  MBED_HTM_NAME = 'mbed.htm'
71 
72  VENDOR_ID_DEVICE_TYPE_MAP = {
73  '0483': 'stlink',
74  '0d28': 'daplink',
75  '1366': 'jlink',
76  '03eb': 'atmel'
77  }
78 
79  def __init__(self, list_unmounted=False, **kwargs):
80  """ ctor
81  """
82  self.retarget_dataretarget_data = {} # Used to retarget mbed-enabled platform properties
83 
84  platform_dbs = []
85  if isfile(self.MOCK_FILE_NAMEMOCK_FILE_NAME) or ("force_mock" in kwargs and kwargs['force_mock']):
86  platform_dbs.append(self.MOCK_FILE_NAMEMOCK_FILE_NAME)
87  elif isfile(LOCAL_MOCKS_DATABASE):
88  platform_dbs.append(LOCAL_MOCKS_DATABASE)
89  platform_dbs.append(LOCAL_PLATFORM_DATABASE)
90  self.plat_dbplat_db = PlatformDatabase(platform_dbs,
91  primary_database=platform_dbs[0])
92  self.list_unmountedlist_unmounted = list_unmounted
93 
94  if 'skip_retarget' not in kwargs or not kwargs['skip_retarget']:
95  self.retargetretarget()
96 
97  @abstractmethod
98  def find_candidates(self):
99  """Find all candidate devices connected to this computer
100 
101  Note: Should not open any files
102 
103  @return A dict with the keys 'mount_point', 'serial_port' and 'target_id_usb_id'
104  """
105  raise NotImplemented
106 
107  @deprecated("Functionality has been moved into 'list_mbeds'. " "Please use list_mbeds with 'unique_names=True' and " "'read_details_txt=True'")
108  def list_mbeds_ext(self):
109  """! Function adds extra information for each mbed device
110  @return Returns list of mbed devices plus extended data like 'platform_name_unique'
111  @details Get information about mbeds with extended parameters/info included
112  """
113 
114  return self.list_mbedslist_mbeds(unique_names=True, read_details_txt=True)
115 
117  self, fs_interaction=FSInteraction.BeforeFilter,
118  filter_function=None, unique_names=False,
119  read_details_txt=False):
120  """ List details of connected devices
121  @return Returns list of structures with detailed info about each mbed
122  @param fs_interaction A member of the FSInteraction class that picks the
123  trade of between quality of service and speed
124  @param filter_function Function that is passed each mbed candidate,
125  should return True if it should be included in the result
126  Ex. mbeds = list_mbeds(filter_function=lambda m: m['platform_name'] == 'K64F')
127  @param unique_names A boolean controlling the presence of the
128  'platform_unique_name' member of the output dict
129  @param read_details_txt A boolean controlling the presense of the
130  output dict attributes read from other files present on the 'mount_point'
131  @details Function returns list of dictionaries with mbed attributes 'mount_point', TargetID name etc.
132  Function returns mbed list with platform names if possible
133  """
134  platform_count = {}
135  candidates = list(self.find_candidatesfind_candidates())
136  logger.debug("Candidates for display %r", candidates)
137  result = []
138  for device in candidates:
139  device['device_type'] = self._detect_device_type_detect_device_type(device)
140  if ((not device['mount_point'] or
141  not self.mount_point_readymount_point_ready(device['mount_point'])) and
142  not self.list_unmountedlist_unmounted):
143  if (device['target_id_usb_id'] and device['serial_port']):
144  logger.warning(
145  "MBED with target id '%s' is connected, but not mounted. "
146  "Use the '-u' flag to include it in the list.",
147  device['target_id_usb_id'])
148  else:
149  platform_data = self.plat_dbplat_db.get(device['target_id_usb_id'][0:4],
150  device_type=device['device_type'] or 'daplink', verbose_data=True)
151  device.update(platform_data or {"platform_name": None})
152  maybe_device = {
153  FSInteraction.BeforeFilter: self._fs_before_id_check_fs_before_id_check,
154  FSInteraction.AfterFilter: self._fs_after_id_check_fs_after_id_check,
155  FSInteraction.Never: self._fs_never_fs_never
156  }[fs_interaction](device, filter_function, read_details_txt)
157  if maybe_device and (maybe_device['mount_point'] or self.list_unmountedlist_unmounted):
158  if unique_names:
159  name = device['platform_name']
160  platform_count.setdefault(name, -1)
161  platform_count[name] += 1
162  device['platform_name_unique'] = (
163  "%s[%d]" % (name, platform_count[name]))
164  try:
165  device.update(self.retarget_dataretarget_data[device['target_id']])
166  logger.debug("retargeting %s with %r",
167  device['target_id'],
168  self.retarget_dataretarget_data[device['target_id']])
169  except KeyError:
170  pass
171 
172  # This is done for API compatibility, would prefer for this to just be None
173  device['device_type'] = device['device_type'] if device['device_type'] else 'unknown'
174  result.append(maybe_device)
175 
176  return result
177 
178  def _fs_never(self, device, filter_function, read_details_txt):
179  """Filter device without touching the file system of the device"""
180  device['target_id'] = device['target_id_usb_id']
181  device['target_id_mbed_htm'] = None
182  if not filter_function or filter_function(device):
183  return device
184  else:
185  return None
186 
187  def _fs_before_id_check(self, device, filter_function, read_details_txt):
188  """Filter device after touching the file system of the device.
189  Said another way: Touch the file system before filtering
190  """
191 
192  device['target_id'] = device['target_id_usb_id']
193  self._update_device_from_fs_update_device_from_fs(device, read_details_txt)
194  if not filter_function or filter_function(device):
195  return device
196  else:
197  return None
198 
199  def _fs_after_id_check(self, device, filter_function, read_details_txt):
200  """Filter device before touching the file system of the device.
201  Said another way: Touch the file system after filtering
202  """
203  device['target_id'] = device['target_id_usb_id']
204  device['target_id_mbed_htm'] = None
205  if not filter_function or filter_function(device):
206  self._update_device_from_fs_update_device_from_fs(device, read_details_txt)
207  return device
208  else:
209  return None
210 
211  def _update_device_from_fs(self, device, read_details_txt):
212  """ Updates the device information based on files from its 'mount_point'
213  @param device Dictionary containing device information
214  @param read_details_txt A boolean controlling the presense of the
215  output dict attributes read from other files present on the 'mount_point'
216  """
217  if not device.get('mount_point', None):
218  return
219 
220  try:
221  directory_entries = os.listdir(device['mount_point'])
222  device['directory_entries'] = directory_entries
223  device['target_id'] = device['target_id_usb_id']
224 
225  # Always try to update using daplink compatible boards processself.
226  # This is done for backwards compatibility.
227  self._update_device_details_daplink_compatible_update_device_details_daplink_compatible(device, read_details_txt)
228 
229  if device.get('device_type') == 'jlink':
230  self._update_device_details_jlink_update_device_details_jlink(device, read_details_txt)
231 
232  if device.get('device_type') == 'atmel':
233  self._update_device_details_atmel_update_device_details_atmel(device, read_details_txt)
234 
235  except (OSError, IOError) as e:
236  logger.warning(
237  'Marking device with mount point "%s" as unmounted due to the '
238  'following error: %s', device['mount_point'], e)
239  device['mount_point'] = None
240 
241 
242  def _detect_device_type(self, device):
243  """ Returns a string of the device type
244  @param device Dictionary containing device information
245  @return Device type located in VENDOR_ID_DEVICE_TYPE_MAP or None if unknown
246  """
247 
248  return self.VENDOR_ID_DEVICE_TYPE_MAPVENDOR_ID_DEVICE_TYPE_MAP.get(device.get('vendor_id'))
249 
250 
251  def _update_device_details_daplink_compatible(self, device, read_details_txt):
252  """ Updates the daplink-specific device information based on files from its 'mount_point'
253  @param device Dictionary containing device information
254  @param read_details_txt A boolean controlling the presense of the
255  output dict attributes read from other files present on the 'mount_point'
256  """
257  lowercase_directory_entries = [e.lower() for e in device['directory_entries']]
258  if self.MBED_HTM_NAMEMBED_HTM_NAME.lower() in lowercase_directory_entries:
259  self._update_device_from_htm_update_device_from_htm(device)
260  elif not read_details_txt:
261  logger.debug('Since mbed.htm is not present, attempting to use '
262  'details.txt for the target id')
263  read_details_txt = True
264 
265  if read_details_txt and self.DETAILS_TXT_NAMEDETAILS_TXT_NAME.lower() in lowercase_directory_entries:
266  details_txt = self._details_txt_details_txt(device['mount_point']) or {}
267  device.update({"daplink_%s" % f.lower().replace(' ', '_'): v
268  for f, v in details_txt.items()})
269 
270  # If details.txt contains the target id, this is the most trusted source
271  if device.get('daplink_unique_id', None):
272  device['target_id'] = device['daplink_unique_id']
273 
274  if device['target_id']:
275  identifier = device['target_id'][0:4]
276  platform_data = self.plat_dbplat_db.get(identifier,
277  device_type='daplink',
278  verbose_data=True)
279  if not platform_data:
280  logger.warning('daplink entry: "%s" not found in platform database', identifier)
281  else:
282  device.update(platform_data)
283  else:
284  device['platform_name'] = None
285 
286  def _update_device_details_jlink(self, device, _):
287  """ Updates the jlink-specific device information based on files from its 'mount_point'
288  @param device Dictionary containing device information
289  """
290  lower_case_map = {e.lower(): e for e in device['directory_entries']}
291 
292  if 'board.html' in lower_case_map:
293  board_file_key = 'board.html'
294  elif 'user guide.html' in lower_case_map:
295  board_file_key = 'user guide.html'
296  else:
297  logger.warning('No valid file found to update JLink device details')
298  return
299 
300  board_file_path = os.path.join(device['mount_point'], lower_case_map[board_file_key])
301  with open(board_file_path, 'r') as board_file:
302  board_file_lines = board_file.readlines()
303 
304  for line in board_file_lines:
305  m = re.search(r'url=([\w\d\:\-/\\\?\.=-_]+)', line)
306  if m:
307  device['url'] = m.group(1).strip()
308  identifier = device['url'].split('/')[-1]
309  platform_data = self.plat_dbplat_db.get(identifier,
310  device_type='jlink',
311  verbose_data=True)
312  if not platform_data:
313  logger.warning('jlink entry: "%s", not found in platform database', identifier)
314  else:
315  device.update(platform_data)
316  break
317 
318  def _update_device_from_htm(self, device):
319  """Set the 'target_id', 'target_id_mbed_htm', 'platform_name' and
320  'daplink_*' attributes by reading from mbed.htm on the device
321  """
322  htm_target_id, daplink_info = self._read_htm_ids_read_htm_ids(device['mount_point'])
323  if daplink_info:
324  device.update({"daplink_%s" % f.lower().replace(' ', '_'): v
325  for f, v in daplink_info.items()})
326  if htm_target_id:
327  logger.debug("Found htm target id, %s, for usb target id %s",
328  htm_target_id, device['target_id_usb_id'])
329  device['target_id'] = htm_target_id
330  else:
331  logger.debug("Could not read htm on from usb id %s. "
332  "Falling back to usb id",
333  device['target_id_usb_id'])
334  device['target_id'] = device['target_id_usb_id']
335  device['target_id_mbed_htm'] = htm_target_id
336 
337  def _update_device_details_atmel(self, device, _):
338  """ Updates the Atmel device information based on files from its 'mount_point'
339  @param device Dictionary containing device information
340  @param read_details_txt A boolean controlling the presense of the
341  output dict attributes read from other files present on the 'mount_point'
342  """
343 
344  # Atmel uses a system similar to DAPLink, but there's no details.txt with a target ID
345  # to identify device we can use the serial, which is ATMLXXXXYYYYYYY
346  # where XXXX is the board identifier.
347  # This can be verified by looking at readme.htm, which also uses the board ID to redirect to platform page
348 
349  device['target_id'] = device['target_id_usb_id'][4:8]
350  platform_data = self.plat_dbplat_db.get(device['target_id'],
351  device_type='atmel',
352  verbose_data=True)
353 
354  device.update(platform_data or {"platform_name": None})
355 
356  def mock_manufacture_id(self, mid, platform_name, oper='+'):
357  """! Replace (or add if manufacture id doesn't exist) entry in self.manufacture_ids
358  @param oper '+' add new mock / override existing entry
359  '-' remove mid from mocking entry
360  @return Mocked structure (json format)
361  """
362  if oper == '+':
363  self.plat_dbplat_db.add(mid, platform_name, permanent=True)
364  elif oper == '-':
365  self.plat_dbplat_db.remove(mid, permanent=True)
366  else:
367  raise ValueError("oper can only be [+-]")
368 
369  @deprecated("List formatting methods are deprecated for a simpler API. " "Please use 'list_mbeds' instead.")
371  """! Creates list of all available mappings for target_id -> Platform
372  @return String with table formatted output
373  """
374  from prettytable import PrettyTable, HEADER
375 
376  columns = ['target_id_prefix', 'platform_name']
377  pt = PrettyTable(columns, junction_char="|", hrules=HEADER)
378  for col in columns:
379  pt.align[col] = 'l'
380 
381  for target_id_prefix, platform_name in sorted(self.plat_dbplat_db.items()):
382  pt.add_row([target_id_prefix, platform_name])
383 
384  return pt.get_string()
385 
386  def retarget_read(self):
387  """! Load retarget data from local file
388  @return Curent retarget configuration (dictionary)
389  """
390  if os.path.isfile(self.RETARGET_FILE_NAMERETARGET_FILE_NAME):
391  logger.debug("reading retarget file %s", self.RETARGET_FILE_NAMERETARGET_FILE_NAME)
392  try:
393  with open(self.RETARGET_FILE_NAMERETARGET_FILE_NAME, "r", encoding="utf-8") as f:
394  return json.load(f)
395  except IOError as e:
396  logger.exception(e)
397  except ValueError as e:
398  logger.exception(e)
399  return {}
400 
401  def retarget(self):
402  """! Enable retargeting
403  @details Read data from local retarget configuration file
404  @return Retarget data structure read from configuration file
405  """
406  self.retarget_dataretarget_data = self.retarget_readretarget_read()
407  return self.retarget_dataretarget_data
408 
409  def get_dummy_platform(self, platform_name):
410  """! Returns simple dummy platform """
411  if not hasattr(self, "dummy_counter"):
412  self.dummy_counterdummy_counter = {} # platform<str>: counter<int>
413 
414  if platform_name not in self.dummy_counterdummy_counter:
415  self.dummy_counterdummy_counter[platform_name] = 0
416 
417  platform = {
418  "platform_name": platform_name,
419  "platform_name_unique": "%s[%d]"% (platform_name, self.dummy_counterdummy_counter[platform_name]),
420  "mount_point": "DUMMY",
421  "serial_port": "DUMMY",
422  "target_id": "DUMMY",
423  "target_id_mbed_htm": "DUMMY",
424  "target_id_usb_id": "DUMMY",
425  "daplink_version": "DUMMY"
426  }
427  self.dummy_counterdummy_counter[platform_name] += 1
428  return platform
429 
430  def get_supported_platforms(self, device_type=None):
431  """! Return a dictionary of supported target ids and the corresponding platform name
432  @param device_type Filter which device entries are returned from the platform database
433  @return Dictionary of { 'target_id': 'platform_name', ... }
434  """
435  kwargs = {}
436  if device_type is not None:
437  kwargs['device_type'] = device_type
438 
439  items = self.plat_dbplat_db.items(**kwargs)
440  return {i[0]: i[1] for i in items}
441 
442  @deprecated("List formatting methods are deprecated to simplify the API. " "Please use 'list_mbeds' instead.")
443  def list_platforms(self):
444  """! Useful if you just want to know which platforms are currently available on the system
445  @return List of (unique values) available platforms
446  """
447  result = []
448  mbeds = self.list_mbedslist_mbeds()
449  for i, val in enumerate(mbeds):
450  platform_name = str(val['platform_name'])
451  if platform_name not in result:
452  result.append(platform_name)
453  return result
454 
455  @deprecated("List formatting methods are deprecated to simplify the API. " "Please use 'list_mbeds' instead.")
457  """! Useful if you just want to know how many platforms of each type are currently available on the system
458  @return Dict of platform: platform_count
459  """
460  result = {}
461  mbeds = self.list_mbedslist_mbeds()
462  for i, val in enumerate(mbeds):
463  platform_name = str(val['platform_name'])
464  if platform_name not in result:
465  result[platform_name] = 1
466  else:
467  result[platform_name] += 1
468  return result
469 
470  @deprecated("List formatting methods are deprecated to simplify the API. " "Please use 'list_mbeds' instead.")
472  """! Get information about mbeds with extended parameters/info included
473  @return Returns dictionary where keys are TargetIDs and values are mbed structures
474  @details Ordered by target id (key: target_id).
475  """
476  result = {}
477  mbed_list = self.list_mbeds_extlist_mbeds_ext()
478  for mbed in mbed_list:
479  target_id = mbed['target_id']
480  result[target_id] = mbed
481  return result
482 
483  def __str__(self):
484  """! Object to string casting
485 
486  @return Stringified class object should be prettytable formated string
487  """
488  return self.get_stringget_string()
489 
490  @deprecated("List formatting methods are deprecated to simplify the API. " "Please use 'list_mbeds' instead.")
491  def get_string(self, border=False, header=True, padding_width=1, sortby='platform_name'):
492  """! Printing with some sql table like decorators
493  @param border Table border visibility
494  @param header Table header visibility
495  @param padding_width Table padding
496  @param sortby Column used to sort results
497  @return Returns string which can be printed on console
498  """
499  from prettytable import PrettyTable, HEADER
500  result = ''
501  mbeds = self.list_mbedslist_mbeds(unique_names=True, read_details_txt=True)
502  if mbeds:
503  """ ['platform_name', 'mount_point', 'serial_port', 'target_id'] - columns generated from USB auto-detection
504  ['platform_name_unique', ...] - columns generated outside detection subsystem (OS dependent detection)
505  """
506  columns = ['platform_name', 'platform_name_unique', 'mount_point', 'serial_port', 'target_id', 'daplink_version']
507  pt = PrettyTable(columns, junction_char="|", hrules=HEADER)
508  for col in columns:
509  pt.align[col] = 'l'
510 
511  for mbed in mbeds:
512  row = []
513  for col in columns:
514  row.append(mbed[col] if col in mbed and mbed[col] else 'unknown')
515  pt.add_row(row)
516  result = pt.get_string(border=border, header=header, padding_width=padding_width, sortby=sortby)
517  return result
518 
519  # Private functions supporting API
520 
521  @deprecated("This method will be removed from the public API. " "Please use 'list_mbeds' instead")
522  def get_json_data_from_file(self, json_spec_filename, verbose=False):
523  """! Loads from file JSON formatted string to data structure
524  @return None if JSON can be loaded
525  """
526  try:
527  with open(json_spec_filename) as data_file:
528  try:
529  return json.load(data_file)
530  except ValueError as json_error_msg:
531  logger.error("Parsing file(%s): %s", json_spec_filename, json_error_msg)
532  return None
533  except IOError as fileopen_error_msg:
534  logger.warning(fileopen_error_msg)
535  return None
536 
537  @deprecated("This method will be removed from the public API. " "Please use 'list_mbeds' instead")
538  def get_htm_target_id(self, mount_point):
539  target_id, _ = self._read_htm_ids_read_htm_ids(mount_point)
540  return target_id
541 
542  @deprecated("This method will be removed from the public API. " "Please use 'list_mbeds' instead")
543  def get_mbed_htm(self, mount_point):
544  _, build_info = self._read_htm_ids_read_htm_ids(mount_point)
545  return build_info
546 
547  def _read_htm_ids(self, mount_point):
548  """! Function scans mbed.htm to get information about TargetID.
549  @param mount_point mbed mount point (disk / drive letter)
550  @return Function returns targetID, in case of failure returns None.
551  @details Note: This function should be improved to scan variety of boards' mbed.htm files
552  """
553  result = {}
554  target_id = None
555  for line in self._htm_lines_htm_lines(mount_point):
556  target_id = target_id or self._target_id_from_htm_target_id_from_htm(line)
557  ver_bld = self._mbed_htm_comment_section_ver_build_mbed_htm_comment_section_ver_build(line)
558  if ver_bld:
559  result['version'], result['build'] = ver_bld
560 
561  m = re.search(r'url=([\w\d\:/\\\?\.=-_]+)', line)
562  if m:
563  result['url'] = m.group(1).strip()
564  return target_id, result
565 
566  @deprecated("This method will be removed from the public API. " "Please use 'list_mbeds' instead")
568  return self._mbed_htm_comment_section_ver_build_mbed_htm_comment_section_ver_build(line)
569 
570  def _mbed_htm_comment_section_ver_build(self, line):
571  """! Check for Version and Build date of interface chip firmware im mbed.htm file
572  @return (version, build) tuple if successful, None if no info found
573  """
574  # <!-- Version: 0200 Build: Mar 26 2014 13:22:20 -->
575  m = re.search(r'^<!-- Version: (\d+) Build: ([\d\w: ]+) -->', line)
576  if m:
577  version_str, build_str = m.groups()
578  return (version_str.strip(), build_str.strip())
579 
580  # <!-- Version: 0219 Build: Feb 2 2016 15:20:54 Git Commit SHA: 0853ba0cdeae2436c52efcba0ba76a6434c200ff Git local mods:No-->
581  m = re.search(r'^<!-- Version: (\d+) Build: ([\d\w: ]+) Git Commit SHA', line)
582  if m:
583  version_str, build_str = m.groups()
584  return (version_str.strip(), build_str.strip())
585 
586  # <!-- Version: 0.14.3. build 471 -->
587  m = re.search(r'^<!-- Version: ([\d+\.]+)\. build (\d+) -->', line)
588  if m:
589  version_str, build_str = m.groups()
590  return (version_str.strip(), build_str.strip())
591  return None
592 
593  @deprecated("This method will be removed from the public API. " "Please use 'list_mbeds' instead")
594  def get_mbed_htm_lines(self, mount_point):
595  return self._htm_lines_htm_lines(mount_point)
596 
597  def _htm_lines(self, mount_point):
598  if mount_point:
599  mbed_htm_path = join(mount_point, self.MBED_HTM_NAMEMBED_HTM_NAME)
600  with open(mbed_htm_path, 'r') as f:
601  return f.readlines()
602 
603  @deprecated("This method will be removed from the public API. " "Please use 'list_mbeds' instead")
604  def get_details_txt(self, mount_point):
605  return self._details_txt_details_txt(mount_point)
606 
607  def _details_txt(self, mount_point):
608  """! Load DETAILS.TXT to dictionary:
609  DETAILS.TXT example:
610  Version: 0226
611  Build: Aug 24 2015 17:06:30
612  Git Commit SHA: 27a236b9fe39c674a703c5c89655fbd26b8e27e1
613  Git Local mods: Yes
614 
615  or:
616 
617  # DAPLink Firmware - see https://mbed.com/daplink
618  Unique ID: 0240000029164e45002f0012706e0006f301000097969900
619  HIF ID: 97969900
620  Auto Reset: 0
621  Automation allowed: 0
622  Daplink Mode: Interface
623  Interface Version: 0240
624  Git SHA: c765cbb590f57598756683254ca38b211693ae5e
625  Local Mods: 0
626  USB Interfaces: MSD, CDC, HID
627  Interface CRC: 0x26764ebf
628  """
629 
630  if mount_point:
631  path_to_details_txt = os.path.join(mount_point, self.DETAILS_TXT_NAMEDETAILS_TXT_NAME)
632  with open(path_to_details_txt, 'r') as f:
633  return self._parse_details_parse_details(f.readlines())
634  return None
635 
636  @deprecated("This method will be removed from the public API. " "Please use 'list_mbeds' instead")
637  def parse_details_txt(self, lines):
638  return self._parse_details_parse_details(lines)
639 
640  def _parse_details(self, lines):
641  result = {}
642  for line in lines:
643  if not line.startswith('#'):
644  key, _, value = line.partition(':')
645  if value:
646  result[key] = value.strip()
647  if 'Interface Version' in result:
648  result['Version'] = result['Interface Version']
649  return result
650 
651  @deprecated("This method will be removed from the public API. " "Please use 'list_mbeds' instead")
653  return self._target_id_from_htm_target_id_from_htm(line)
654 
655  def _target_id_from_htm(self, line):
656  """! Extract Target id from htm line.
657  @return Target id or None
658  """
659  # Detecting modern mbed.htm file format
660  m = re.search('\?code=([a-fA-F0-9]+)', line)
661  if m:
662  result = m.groups()[0]
663  logger.debug("Found target id %s in htm line %s", result, line)
664  return result
665  # Last resort, we can try to see if old mbed.htm format is there
666  m = re.search('\?auth=([a-fA-F0-9]+)', line)
667  if m:
668  result = m.groups()[0]
669  logger.debug("Found target id %s in htm line %s", result, line)
670  return result
671 
672  return None
673 
674  def mount_point_ready(self, path):
675  """! Check if a mount point is ready for file operations
676  """
677  return exists(path) and isdir(path)
678 
679  @staticmethod
680  @deprecated("This method will be removed from the public API. " "Please use 'list_mbeds' instead")
681  def run_cli_process(cmd, shell=True):
682  return MbedLsToolsBase._run_cli_process(cmd, shell)
683 
684  @staticmethod
685  def _run_cli_process(cmd, shell=True):
686  """! Runs command as a process and return stdout, stderr and ret code
687  @param cmd Command to execute
688  @return Tuple of (stdout, stderr, returncode)
689  """
690  from subprocess import Popen, PIPE
691 
692  p = Popen(cmd, shell=shell, stdout=PIPE, stderr=PIPE)
693  _stdout, _stderr = p.communicate()
694  return _stdout, _stderr, p.returncode
695 
def list_mbeds(self, fs_interaction=FSInteraction.BeforeFilter, filter_function=None, unique_names=False, read_details_txt=False)
def retarget_read(self)
Load retarget data from local file.
def _fs_after_id_check(self, device, filter_function, read_details_txt)
def list_mbeds_by_targetid(self)
Get information about mbeds with extended parameters/info included.
def _update_device_details_atmel(self, device, _)
def _update_device_details_daplink_compatible(self, device, read_details_txt)
def list_manufacture_ids(self)
Creates list of all available mappings for target_id -> Platform.
def retarget(self)
Enable retargeting.
def get_string(self, border=False, header=True, padding_width=1, sortby='platform_name')
Printing with some sql table like decorators.
def _fs_before_id_check(self, device, filter_function, read_details_txt)
def __init__(self, list_unmounted=False, **kwargs)
Definition: lstools_base.py:79
def _update_device_from_fs(self, device, read_details_txt)
def mock_manufacture_id(self, mid, platform_name, oper='+')
Replace (or add if manufacture id doesn't exist) entry in self.manufacture_ids.
def list_mbeds_ext(self)
Function adds extra information for each mbed device.
def get_json_data_from_file(self, json_spec_filename, verbose=False)
Loads from file JSON formatted string to data structure.
def list_platforms(self)
Useful if you just want to know which platforms are currently available on the system.
def __str__(self)
Object to string casting.
def get_dummy_platform(self, platform_name)
Returns simple dummy platform.
def get_mbed_htm_lines(self, mount_point)
def get_htm_target_id(self, mount_point)
def _fs_never(self, device, filter_function, read_details_txt)
def mount_point_ready(self, path)
Check if a mount point is ready for file operations.
def list_platforms_ext(self)
Useful if you just want to know how many platforms of each type are currently available on the system...
def _update_device_details_jlink(self, device, _)
def get_mbed_htm_comment_section_ver_build(self, line)
def get_supported_platforms(self, device_type=None)
Return a dictionary of supported target ids and the corresponding platform name.