modm_data.html.stmicro.document

  1# Copyright 2022, Niklas Hauser
  2# SPDX-License-Identifier: MPL-2.0
  3
  4import json
  5from collections import defaultdict
  6from ...html import Document
  7from ...utils import cache_path, ext_path
  8from .datasheet import DatasheetMicro, DatasheetSensor
  9from .reference import ReferenceManual
 10from ...owl import DeviceIdentifier
 11from ...owl.stmicro import did_from_string
 12
 13
 14MAP_DEVICE_DOC_FILE = cache_path("stmicro-did-doc.json")
 15DOCUMENT_CACHE = None
 16
 17
 18def load_documents() -> list:
 19    documents = defaultdict(dict)
 20    for path in sorted(ext_path("stmicro/html").glob("*-v*")):
 21        # This doc is parsed wrongly since it has a DRAFT background
 22        if "DS12960-v5" in path.stem: continue
 23        # This doc has a preliminary ordering information STM32WBA52CGU6TR
 24        if "DS14127" in path.stem: continue
 25        doc = Document(path)
 26        if "DS" in doc.name and (chap := doc.chapters("chapter 0")):
 27            # FIXME: Better detection that DS13252 is a STM32WB55 module, not a chip!
 28            if any("STM32" in h.html for h in chap[0].headings()) and \
 29                "DS13252" not in doc.name and "DS14096" not in doc.name:
 30                documents[doc.name][doc.version] = DatasheetMicro(path)
 31            else:
 32                documents[doc.name][doc.version] = DatasheetSensor(path)
 33        elif "RM" in doc.name:
 34            documents[doc.name][doc.version] = ReferenceManual(path)
 35    return documents
 36
 37
 38def load_document_devices(use_cached=True) -> tuple[dict[DeviceIdentifier, DatasheetMicro],
 39                                                    dict[DeviceIdentifier, ReferenceManual]]:
 40    global DOCUMENT_CACHE
 41    if DOCUMENT_CACHE is not None:
 42        return DOCUMENT_CACHE
 43
 44    global MAP_DEVICE_DOC_FILE
 45    if MAP_DEVICE_DOC_FILE.exists() and use_cached:
 46        with MAP_DEVICE_DOC_FILE.open('r', encoding='utf-8') as fh:
 47            json_data = json.load(fh)
 48
 49        docs = {}
 50        for path in set(json_data["ds"].values()):
 51            docs[path] = DatasheetMicro(path)
 52        for path in set(json_data["rm"].values()):
 53            docs[path] = ReferenceManual(path)
 54        datasheets = {did_from_string(did): docs[path]
 55                      for did, path in json_data["ds"].items()}
 56        reference_manuals = {did_from_string(did): docs[path]
 57                             for did, path in json_data["rm"].items()}
 58    else:
 59        dss = defaultdict(set)
 60        rms = defaultdict(set)
 61        for name, versions in load_documents().items():
 62            # Always choose the latest version
 63            doc = list(versions.values())[-1]
 64            # print(doc.path_pdf.relative_to(Path().cwd()), doc.path.relative_to(Path().cwd()))
 65            # print(doc.devices)
 66            if isinstance(doc, DatasheetMicro):
 67                if not doc.devices:
 68                    raise ValueError(f"{doc} has no associated devices!")
 69                for dev in doc.devices:
 70                    dss[dev].add(doc)
 71            elif isinstance(doc, ReferenceManual):
 72                if not doc.devices:
 73                    raise ValueError(f"{doc} has no associated devices!")
 74                for dev in doc.devices:
 75                    rms[dev].add(doc)
 76
 77        for dev, docs in dss.items():
 78            if len(docs) != 1:
 79                raise ValueError(f"One device points to multiple datasheets! {dev} -> {docs}")
 80        datasheets = {did:list(ds)[0] for did, ds in dss.items()}
 81        # print(len(datasheets.keys()), sorted(list(d.string for d in datasheets.keys())))
 82
 83        manuals = defaultdict(set)
 84        for dev, docs in rms.items():
 85            if len(docs) != 1:
 86                raise ValueError(f"One device points to multiple reference manuals! {dev} -> {docs}")
 87            for dev in list(docs)[0].filter_devices(datasheets.keys()):
 88                manuals[dev].add(list(docs)[0])
 89
 90        for dev, docs in manuals.items():
 91            if len(docs) != 1:
 92                raise ValueError(f"One device points to multiple reference manuals! {dev} -> {docs}")
 93
 94        reference_manuals = {did:list(rm)[0] for did, rm in manuals.items()}
 95
 96        # Cache the results for the next call
 97        json_data = {
 98            "ds": {did.string: str(doc.path) for did, doc in datasheets.items()},
 99            "rm": {did.string: str(doc.path) for did, doc in reference_manuals.items()}
100        }
101        MAP_DEVICE_DOC_FILE.parent.mkdir(parents=True, exist_ok=True)
102        with MAP_DEVICE_DOC_FILE.open('w', encoding='utf-8') as fh:
103            json.dump(json_data, fh, indent=4)
104
105    DOCUMENT_CACHE = (datasheets, reference_manuals)
106    return datasheets, reference_manuals
107
108
109def _document_for_device(did: DeviceIdentifier, documents):
110    if did in documents:
111        return documents[did]
112
113    # Find the first device without temperature key that matches
114    did = did.copy()
115    for temp in ["7", "6", "3"]:
116        did.set("temperature", temp)
117        if did in documents:
118            return documents[did]
119
120    return None
121
122
123def datasheet_for_device(did: DeviceIdentifier) -> DatasheetMicro:
124    datasheets, _ = load_document_devices()
125    return _document_for_device(did, datasheets)
126
127
128def reference_manual_for_device(did: DeviceIdentifier) -> ReferenceManual:
129    _, reference_manual = load_document_devices()
130    return _document_for_device(did, reference_manual)
MAP_DEVICE_DOC_FILE = PosixPath('/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/ext/cache/stmicro-did-doc.json')
DOCUMENT_CACHE = None
def load_documents() -> list:
19def load_documents() -> list:
20    documents = defaultdict(dict)
21    for path in sorted(ext_path("stmicro/html").glob("*-v*")):
22        # This doc is parsed wrongly since it has a DRAFT background
23        if "DS12960-v5" in path.stem: continue
24        # This doc has a preliminary ordering information STM32WBA52CGU6TR
25        if "DS14127" in path.stem: continue
26        doc = Document(path)
27        if "DS" in doc.name and (chap := doc.chapters("chapter 0")):
28            # FIXME: Better detection that DS13252 is a STM32WB55 module, not a chip!
29            if any("STM32" in h.html for h in chap[0].headings()) and \
30                "DS13252" not in doc.name and "DS14096" not in doc.name:
31                documents[doc.name][doc.version] = DatasheetMicro(path)
32            else:
33                documents[doc.name][doc.version] = DatasheetSensor(path)
34        elif "RM" in doc.name:
35            documents[doc.name][doc.version] = ReferenceManual(path)
36    return documents
 39def load_document_devices(use_cached=True) -> tuple[dict[DeviceIdentifier, DatasheetMicro],
 40                                                    dict[DeviceIdentifier, ReferenceManual]]:
 41    global DOCUMENT_CACHE
 42    if DOCUMENT_CACHE is not None:
 43        return DOCUMENT_CACHE
 44
 45    global MAP_DEVICE_DOC_FILE
 46    if MAP_DEVICE_DOC_FILE.exists() and use_cached:
 47        with MAP_DEVICE_DOC_FILE.open('r', encoding='utf-8') as fh:
 48            json_data = json.load(fh)
 49
 50        docs = {}
 51        for path in set(json_data["ds"].values()):
 52            docs[path] = DatasheetMicro(path)
 53        for path in set(json_data["rm"].values()):
 54            docs[path] = ReferenceManual(path)
 55        datasheets = {did_from_string(did): docs[path]
 56                      for did, path in json_data["ds"].items()}
 57        reference_manuals = {did_from_string(did): docs[path]
 58                             for did, path in json_data["rm"].items()}
 59    else:
 60        dss = defaultdict(set)
 61        rms = defaultdict(set)
 62        for name, versions in load_documents().items():
 63            # Always choose the latest version
 64            doc = list(versions.values())[-1]
 65            # print(doc.path_pdf.relative_to(Path().cwd()), doc.path.relative_to(Path().cwd()))
 66            # print(doc.devices)
 67            if isinstance(doc, DatasheetMicro):
 68                if not doc.devices:
 69                    raise ValueError(f"{doc} has no associated devices!")
 70                for dev in doc.devices:
 71                    dss[dev].add(doc)
 72            elif isinstance(doc, ReferenceManual):
 73                if not doc.devices:
 74                    raise ValueError(f"{doc} has no associated devices!")
 75                for dev in doc.devices:
 76                    rms[dev].add(doc)
 77
 78        for dev, docs in dss.items():
 79            if len(docs) != 1:
 80                raise ValueError(f"One device points to multiple datasheets! {dev} -> {docs}")
 81        datasheets = {did:list(ds)[0] for did, ds in dss.items()}
 82        # print(len(datasheets.keys()), sorted(list(d.string for d in datasheets.keys())))
 83
 84        manuals = defaultdict(set)
 85        for dev, docs in rms.items():
 86            if len(docs) != 1:
 87                raise ValueError(f"One device points to multiple reference manuals! {dev} -> {docs}")
 88            for dev in list(docs)[0].filter_devices(datasheets.keys()):
 89                manuals[dev].add(list(docs)[0])
 90
 91        for dev, docs in manuals.items():
 92            if len(docs) != 1:
 93                raise ValueError(f"One device points to multiple reference manuals! {dev} -> {docs}")
 94
 95        reference_manuals = {did:list(rm)[0] for did, rm in manuals.items()}
 96
 97        # Cache the results for the next call
 98        json_data = {
 99            "ds": {did.string: str(doc.path) for did, doc in datasheets.items()},
100            "rm": {did.string: str(doc.path) for did, doc in reference_manuals.items()}
101        }
102        MAP_DEVICE_DOC_FILE.parent.mkdir(parents=True, exist_ok=True)
103        with MAP_DEVICE_DOC_FILE.open('w', encoding='utf-8') as fh:
104            json.dump(json_data, fh, indent=4)
105
106    DOCUMENT_CACHE = (datasheets, reference_manuals)
107    return datasheets, reference_manuals
124def datasheet_for_device(did: DeviceIdentifier) -> DatasheetMicro:
125    datasheets, _ = load_document_devices()
126    return _document_for_device(did, datasheets)
129def reference_manual_for_device(did: DeviceIdentifier) -> ReferenceManual:
130    _, reference_manual = load_document_devices()
131    return _document_for_device(did, reference_manual)