Mbed LS
platform_database.py
Go to the documentation of this file.
1"""
2mbed SDK
3Copyright (c) 2017 ARM Limited
4
5Licensed under the Apache License, Version 2.0 (the "License");
6you may not use this file except in compliance with the License.
7You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11Unless required by applicable law or agreed to in writing, software
12distributed under the License is distributed on an "AS IS" BASIS,
13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14See the License for the specific language governing permissions and
15limitations under the License.
16"""
17
18"""Functions that manage a platform database"""
19
20import datetime
21import json
22import re
23from collections import OrderedDict, defaultdict
24from copy import copy
25from io import open
26from os import makedirs
27from os.path import join, dirname, getmtime
28from platformdirs import user_data_dir
29from fasteners import InterProcessLock
30
31try:
32 unicode
33except NameError:
34 unicode = str
35
36
37import logging
38logger = logging.getLogger("mbedls.platform_database")
39logger.addHandler(logging.NullHandler())
40del logging
41
42LOCAL_PLATFORM_DATABASE = join(user_data_dir("mbedls"), "platforms.json")
43LOCAL_MOCKS_DATABASE = join(user_data_dir("mbedls"), "mock.json")
44
45DEFAULT_PLATFORM_DB = {
46 u'daplink': {
47 u'0001': u'LPC2368',
48 u'0002': u'LPC2368',
49 u'0003': u'LPC2368',
50 u'0004': u'LPC2368',
51 u'0005': u'LPC2368',
52 u'0006': u'LPC2368',
53 u'0007': u'LPC2368',
54 u'0100': u'LPC2368',
55 u'0183': u'UBLOX_C027',
56 u'0200': u'KL25Z',
57 u'0201': u'KW41Z',
58 u'0210': u'KL05Z',
59 u'0214': u'HEXIWEAR',
60 u'0217': u'K82F',
61 u'0218': u'KL82Z',
62 u'0220': u'KL46Z',
63 u'0227': u'MIMXRT1050_EVK',
64 u'0228': u'RAPIDIOT_K64F',
65 u'0230': u'K20D50M',
66 u'0231': u'K22F',
67 u'0234': u'RAPIDIOT_KW41Z',
68 u'0240': u'K64F',
69 u'0245': u'K64F',
70 u'0250': u'KW24D',
71 u'0261': u'KL27Z',
72 u'0262': u'KL43Z',
73 u'0300': u'MTS_GAMBIT',
74 u'0305': u'MTS_MDOT_F405RG',
75 u'0310': u'MTS_DRAGONFLY_F411RE',
76 u'0311': u'K66F',
77 u'0312': u'MTS_DRAGONFLY_L471QG',
78 u'0315': u'MTS_MDOT_F411RE',
79 u'0350': u'XDOT_L151CC',
80 u'0400': u'MAXWSNENV',
81 u'0405': u'MAX32600MBED',
82 u'0406': u'MAX32620MBED',
83 u'0407': u'MAX32620HSP',
84 u'0408': u'MAX32625NEXPAQ',
85 u'0409': u'MAX32630FTHR',
86 u'0410': u'ETTEPLAN_LORA',
87 u'0415': u'MAX32625MBED',
88 u'0416': u'MAX32625PICO',
89 u'0417': u'MAX32630MBED',
90 u'0418': u'MAX32620FTHR',
91 u'0419': u'MAX35103EVKIT2',
92 u'0420': u'MAX32630HSP3',
93 u'0450': u'MTB_UBLOX_ODIN_W2',
94 u'0451': u'MTB_MXCHIP_EMW3166',
95 u'0452': u'MTB_LAIRD_BL600',
96 u'0453': u'MTB_MTS_XDOT',
97 u'0454': u'MTB_MTS_DRAGONFLY',
98 u'0455': u'MTB_UBLOX_NINA_B1',
99 u'0456': u'MTB_MURATA_ABZ',
100 u'0457': u'MTB_RAK811',
101 u'0458': u'MTB_ADV_WISE_1510',
102 u'0459': u'MTB_ADV_WISE_1530',
103 u'0460': u'MTB_ADV_WISE_1570',
104 u'0461': u'MTB_LAIRD_BL652',
105 u'0462': u'MTB_USI_WM_BN_BM_22',
106 u'0465': u'MTB_LAIRD_BL654',
107 u'0466': u'MTB_MURATA_WSM_BL241',
108 u'0472': u'MTB_ACONNO_ACN52832',
109 u'0500': u'SPANSION_PLACEHOLDER',
110 u'0505': u'SPANSION_PLACEHOLDER',
111 u'0510': u'SPANSION_PLACEHOLDER',
112 u'0602': u'EV_COG_AD3029LZ',
113 u'0603': u'EV_COG_AD4050LZ',
114 u'0700': u'NUCLEO_F103RB',
115 u'0705': u'NUCLEO_F302R8',
116 u'0710': u'NUCLEO_L152RE',
117 u'0715': u'NUCLEO_L053R8',
118 u'0720': u'NUCLEO_F401RE',
119 u'0725': u'NUCLEO_F030R8',
120 u'0730': u'NUCLEO_F072RB',
121 u'0735': u'NUCLEO_F334R8',
122 u'0740': u'NUCLEO_F411RE',
123 u'0742': u'NUCLEO_F413ZH',
124 u'0743': u'DISCO_F413ZH',
125 u'0744': u'NUCLEO_F410RB',
126 u'0745': u'NUCLEO_F303RE',
127 u'0746': u'DISCO_F303VC',
128 u'0747': u'NUCLEO_F303ZE',
129 u'0750': u'NUCLEO_F091RC',
130 u'0755': u'NUCLEO_F070RB',
131 u'0760': u'NUCLEO_L073RZ',
132 u'0764': u'DISCO_L475VG_IOT01A',
133 u'0765': u'NUCLEO_L476RG',
134 u'0766': u'SILICA_SENSOR_NODE',
135 u'0770': u'NUCLEO_L432KC',
136 u'0774': u'DISCO_L4R9I',
137 u'0775': u'NUCLEO_F303K8',
138 u'0776': u'NUCLEO_L4R5ZI',
139 u'0777': u'NUCLEO_F446RE',
140 u'0778': u'NUCLEO_F446ZE',
141 u'0779': u'NUCLEO_L433RC_P',
142 u'0780': u'NUCLEO_L011K4',
143 u'0781': u'NUCLEO_L4R5ZI_P',
144 u'0785': u'NUCLEO_F042K6',
145 u'0788': u'DISCO_F469NI',
146 u'0790': u'NUCLEO_L031K6',
147 u'0791': u'NUCLEO_F031K6',
148 u'0795': u'DISCO_F429ZI',
149 u'0796': u'NUCLEO_F429ZI',
150 u'0797': u'NUCLEO_F439ZI',
151 u'0799': u'ST_PLACEHOLDER',
152 u'0805': u'DISCO_L053C8',
153 u'0810': u'DISCO_F334C8',
154 u'0812': u'NUCLEO_F722ZE',
155 u'0813': u'NUCLEO_H743ZI',
156 u'0815': u'DISCO_F746NG',
157 u'0816': u'NUCLEO_F746ZG',
158 u'0817': u'DISCO_F769NI',
159 u'0818': u'NUCLEO_F767ZI',
160 u'0820': u'DISCO_L476VG',
161 u'0821': u'NUCLEO_L452RE',
162 u'0822': u'DISCO_L496AG',
163 u'0823': u'NUCLEO_L496ZG',
164 u'0824': u'LPC824',
165 u'8012': u'TT_M3HQ',
166 u'0826': u'NUCLEO_F412ZG',
167 u'0827': u'NUCLEO_L486RG',
168 u'0828': u'NUCLEO_L496ZG_P',
169 u'0829': u'NUCLEO_L452RE_P',
170 u'0830': u'DISCO_F407VG',
171 u'0833': u'DISCO_L072CZ_LRWAN1',
172 u'0835': u'NUCLEO_F207ZG',
173 u'0839': u'NUCLEO_WB55RG',
174 u'0840': u'B96B_F446VE',
175 u'0879': u'NUCLEO_F756ZG',
176 u'0900': u'XPRO_SAMR21',
177 u'0905': u'XPRO_SAMW25',
178 u'0910': u'XPRO_SAML21',
179 u'0915': u'XPRO_SAMD21',
180 u'1000': u'LPC2368',
181 u'1001': u'LPC2368',
182 u'1010': u'LPC1768',
183 u'1017': u'HRM1017',
184 u'1018': u'SSCI824',
185 u'1019': u'TY51822R3',
186 u'1022': u'RO359B',
187 u'1034': u'LPC11U34',
188 u'1040': u'LPC11U24',
189 u'1045': u'LPC11U24',
190 u'1050': u'LPC812',
191 u'1054': u'LPC54114',
192 u'1056': u'LPC546XX',
193 u'1060': u'LPC4088',
194 u'1061': u'LPC11U35_401',
195 u'1062': u'LPC4088_DM',
196 u'1070': u'NRF51822',
197 u'1075': u'NRF51822_OTA',
198 u'1080': u'OC_MBUINO',
199 u'1090': u'RBLAB_NRF51822',
200 u'1093': u'RBLAB_BLENANO2',
201 u'1095': u'RBLAB_BLENANO',
202 u'1100': u'NRF51_DK',
203 u'1101': u'NRF52_DK',
204 u'1102': u'NRF52840_DK',
205 u'1105': u'NRF51_DK_OTA',
206 u'1114': u'LPC1114',
207 u'1120': u'NRF51_DONGLE',
208 u'1130': u'NRF51822_SBK',
209 u'1140': u'WALLBOT_BLE',
210 u'1168': u'LPC11U68',
211 u'1200': u'NCS36510',
212 u'1234': u'UBLOX_C027',
213 u'1235': u'UBLOX_C027',
214 u'1236': u'UBLOX_EVK_ODIN_W2',
215 u'1237': u'UBLOX_EVK_NINA_B1',
216 u'1300': u'NUC472-NUTINY',
217 u'1301': u'NUMBED',
218 u'1302': u'NUMAKER_PFM_NUC472',
219 u'1303': u'NUMAKER_PFM_M453',
220 u'1304': u'NUMAKER_PFM_M487',
221 u'1305': u'NUMAKER_PFM_M2351',
222 u'1306': u'NUMAKER_PFM_NANO130',
223 u'1307': u'NUMAKER_PFM_NUC240',
224 u'1308': u'NUMAKER_IOT_M487',
225 u'1549': u'LPC1549',
226 u'1600': u'LPC4330_M4',
227 u'1605': u'LPC4330_M4',
228 u'1701': u'GD32_F307VG',
229 u'1702': u'GD32_F450ZI',
230 u'2000': u'EFM32_G8XX_STK',
231 u'2005': u'EFM32HG_STK3400',
232 u'2010': u'EFM32WG_STK3800',
233 u'2015': u'EFM32GG_STK3700',
234 u'2020': u'EFM32LG_STK3600',
235 u'2025': u'EFM32TG_STK3300',
236 u'2030': u'EFM32ZG_STK3200',
237 u'2035': u'EFM32PG_STK3401',
238 u'2040': u'EFM32PG12_STK3402',
239 u'2041': u'TB_SENSE_12',
240 u'2045': u'TB_SENSE_1',
241 u'2100': u'XBED_LPC1768',
242 u'2201': u'WIZWIKI_W7500',
243 u'2202': u'WIZWIKI_W7500ECO',
244 u'2203': u'WIZWIKI_W7500P',
245 u'2500': u'ADV_WISE_1570',
246 u'3001': u'LPC11U24',
247 u'3101': u'SDT32620B',
248 u'3102': u'SDT32625B',
249 u'3103': u'SDT51822B',
250 u'3104': u'SDT52832B',
251 u'3105': u'SDT64B',
252 u'3106': u'SDT8195B',
253 u'3108': u'SDT32429B',
254 u'3110': u'SDT32439B',
255 u'3300': u'CC3220SF',
256 u'4000': u'LPC11U35_Y5_MBUG',
257 u'4005': u'NRF51822_Y5_MBUG',
258 u'4100': u'MOTE_L152RC',
259 u'4337': u'LPC4337',
260 u'4500': u'DELTA_DFCM_NNN40',
261 u'4501': u'DELTA_DFBM_NQ620',
262 u'4502': u'DELTA_DFCM_NNN50',
263 u'4600': u'REALTEK_RTL8195AM',
264 u'5000': u'ARM_MPS2',
265 u'5001': u'ARM_MPS2_M0',
266 u'5002': u'ARM_BEETLE_SOC',
267 u'5003': u'ARM_MPS2_M0P',
268 u'5005': u'ARM_MPS2_M0DS',
269 u'5006': u'ARM_MUSCA_A1',
270 u'5007': u'ARM_MPS2_M1',
271 u'5009': u'ARM_MPS2_M3',
272 u'5011': u'ARM_MPS2_M4',
273 u'5015': u'ARM_MPS2_M7',
274 u'5020': u'HOME_GATEWAY_6LOWPAN',
275 u'5500': u'RZ_A1H',
276 u'5501': u'GR_LYCHEE',
277 u'6000': u'FUTURE_SEQUANA',
278 u'6660': u'NZ32_SC151',
279 u'7010': u'BLUENINJA_CDP_TZ01B',
280 u'7011': u'TMPM066',
281 u'7013': u'TMPM46B',
282 u'7402': u'MBED_BR_HAT',
283 u'7778': u'TEENSY3_1',
284 u'8001': u'UNO_91H',
285 u'8002': u'UNO_81C',
286 u'8003': u'UNO_81AM',
287 u'8004': u'UNO_81A',
288 u'8080': u'FF1705_L151CC',
289 u'8081': u'FF_LPC546XX',
290 u'9001': u'LPC1347',
291 u'9002': u'LPC11U24',
292 u'9003': u'LPC1347',
293 u'9004': u'ARCH_PRO',
294 u'9006': u'LPC11U24',
295 u'9007': u'LPC11U35_501',
296 u'9008': u'XADOW_M0',
297 u'9009': u'ARCH_BLE',
298 u'9010': u'ARCH_GPRS',
299 u'9011': u'ARCH_MAX',
300 u'9012': u'SEEED_TINY_BLE',
301 u'9014': u'WIO_3G',
302 u'9015': u'WIO_BG96',
303 u'9900': u'NRF51_MICROBIT',
304 u'C002': u'VK_RZ_A1H',
305 u'C005': u'MTM_MTCONNECT04S',
306 u'C006': u'VBLUNO51',
307 u'C008': u'SAKURAIO_EVB_01',
308 u'C030': u'UBLOX_C030_U201',
309 u'C031': u'UBLOX_C030_N211',
310 u'C032': u'UBLOX_C030_R404M',
311 u'C033': u'UBLOX_C030_R410M',
312 u'C034': u'UBLOX_C030_S200',
313 u'C035': u'UBLOX_C030_R3121',
314 u'FFFF': u'K20 BOOTLOADER',
315 u'RIOT': u'RIOT',
316 },
317 u'jlink': {
318 u'X729475D28G': {
319 u'platform_name': u'NRF51_DK',
320 u'jlink_device_name': u'nRF51422_xxAC'
321 },
322 u'X349858SLYN': {
323 u'platform_name': u'NRF52_DK',
324 u'jlink_device_name': u'nRF52832_xxaa'
325 },
326 u'FRDM-KL25Z': {
327 u'platform_name': u'KL25Z',
328 u'jlink_device_name': u'MKL25Z128xxx4'
329 },
330 u'FRDM-KL27Z': {
331 u'platform_name': u'KL27Z',
332 u'jlink_device_name': u'MKL27Z64xxx4'
333 },
334 u'FRDM-KL43Z': {
335 u'platform_name': u'KL43Z',
336 u'jlink_device_name': u'MKL43Z256xxx4'
337 }
338 },
339 u'atmel': {
340 u'2241': 'SAML21J18A'
341 }
342}
343
344
346 try:
347 mtime = getmtime(path)
348 except OSError:
349 mtime = 0
350 return datetime.datetime.fromtimestamp(mtime)
351
352
354 return _get_modified_time(path) < _get_modified_time(__file__)
355
356
357def _modify_data_format(data, verbose_data, simple_data_key='platform_name'):
358 if isinstance(data, dict):
359 if verbose_data:
360 return data
361
362 return data[simple_data_key]
363 else:
364 if verbose_data:
365 return {
366 simple_data_key: data
367 }
368
369 return data
370
371
373 try:
374 if db is LOCAL_PLATFORM_DATABASE and _older_than_me(db):
375 raise ValueError("Platform Database is out of date")
376 with open(db, encoding="utf-8") as db_in:
377 return json.load(db_in)
378 except (IOError, ValueError) as exc:
379 if db is LOCAL_PLATFORM_DATABASE:
380 logger.warning(
381 "Error loading database %s: %s; Recreating", db, str(exc))
382 try:
383 makedirs(dirname(db))
384 except OSError:
385 pass
386 try:
387 with open(db, "w", encoding="utf-8") as out:
388 out.write(unicode(json.dumps(DEFAULT_PLATFORM_DB)))
389 except IOError:
390 pass
391 return copy(DEFAULT_PLATFORM_DB)
392 else:
393 return {}
394
395
397 """Represents a union of multiple platform database files.
398 Handles inter-process synchronization of database files.
399 """
400
401 target_id_pattern = re.compile(r'^[a-fA-F0-9]{4}$')
402
403 def __init__(self, database_files, primary_database=None):
404 """Construct a PlatformDatabase object from a series of platform database files"""
405 self._prim_db = primary_database
406 if not self._prim_db and len(database_files) == 1:
407 self._prim_db = database_files[0]
408 self._dbs = OrderedDict()
409 self._keys = defaultdict(set)
410 for db in database_files:
411 new_db = _overwrite_or_open(db)
412 first_value = None
413 if new_db.values():
414 first_value = next(iter(new_db.values()))
415 if not isinstance(first_value, dict):
416 new_db = {
417 'daplink': new_db
418 }
419
420 if new_db:
421 for device_type in new_db:
422 duplicates = self._keys[device_type].intersection(set(new_db[device_type].keys()))
423 duplicates = set(["%s.%s" % (device_type, k) for k in duplicates])
424 if duplicates:
425 logger.warning(
426 "Duplicate platform ids found: %s,"
427 " ignoring the definitions from %s",
428 " ".join(duplicates), db)
429 self._dbs[db] = new_db
430 self._keys[device_type] = self._keys[device_type].union(new_db[device_type].keys())
431 else:
432 self._dbs[db] = new_db
433
434
435 def items(self, device_type='daplink'):
436 for db in self._dbs.values():
437 for entry in db.get(device_type, {}).items():
438 yield entry
439
440 def all_ids(self, device_type='daplink'):
441 return iter(self._keys[device_type])
442
443 def get(self, index, default=None, device_type='daplink', verbose_data=False):
444 """Standard lookup function. Works exactly like a dict. If 'verbose_data'
445 is True, all data for the platform is returned as a dict."""
446 for db in self._dbs.values():
447 if device_type in db:
448 maybe_answer = db[device_type].get(index, None)
449 if maybe_answer:
450 return _modify_data_format(maybe_answer, verbose_data)
451
452 return default
453
454 def _update_db(self):
455 if self._prim_db:
456 lock = InterProcessLock("%s.lock" % self._prim_db)
457 acquired = lock.acquire(blocking=False)
458 if not acquired:
459 logger.debug("Waiting 60 seconds for file lock")
460 acquired = lock.acquire(blocking=True, timeout=60)
461 if acquired:
462 try:
463 with open(self._prim_db, "w", encoding="utf-8") as out:
464 out.write(unicode(
465 json.dumps(self._dbs[self._prim_db])))
466 return True
467 finally:
468 lock.release()
469 else:
470 logger.error("Could not update platform database: "
471 "Lock acquire failed after 60 seconds")
472 return False
473 else:
474 logger.error("Can't update platform database: "
475 "destination database is ambiguous")
476 return False
477
478 def add(self, id, platform_name, permanent=False, device_type='daplink'):
479 """Add a platform to this database, optionally updating an origin
480 database
481 """
482 if self.target_id_pattern.match(id):
483 if self._prim_db:
484 if device_type not in self._dbs[self._prim_db]:
485 self._dbs[self._prim_db][device_type] = {}
486 self._dbs[self._prim_db][device_type][id] = platform_name
487 else:
488 cur_db = next(iter(self._dbs.values()))
489 if device_type not in cur_db:
490 cur_db[device_type] = {}
491 cur_db[device_type][id] = platform_name
492 self._keys[device_type].add(id)
493 if permanent:
494 self._update_db()
495 else:
496 raise ValueError("Invald target id: %s" % id)
497
498 def remove(self, id, permanent=False, device_type='daplink', verbose_data=False):
499 """Remove a platform from this database, optionally updating an origin
500 database. If 'verbose_data' is True, all data for the platform is returned
501 as a dict.
502 """
503 logger.debug("Trying remove of %s", id)
504 if id == '*' and device_type in self._dbs[self._prim_db]:
505 self._dbs[self._prim_db][device_type] = {}
506 for db in self._dbs.values():
507 if device_type in db and id in db[device_type]:
508 logger.debug("Removing id...")
509 removed = db[device_type][id]
510 del db[device_type][id]
511 self._keys[device_type].remove(id)
512 if permanent:
513 self._update_db()
514
515 return _modify_data_format(removed, verbose_data)
add(self, id, platform_name, permanent=False, device_type='daplink')
get(self, index, default=None, device_type='daplink', verbose_data=False)
__init__(self, database_files, primary_database=None)
remove(self, id, permanent=False, device_type='daplink', verbose_data=False)
_modify_data_format(data, verbose_data, simple_data_key='platform_name')