diff --git a/components/maix/CMakeLists.txt b/components/maix/CMakeLists.txt index f86887a5..c63cb2b1 100644 --- a/components/maix/CMakeLists.txt +++ b/components/maix/CMakeLists.txt @@ -79,7 +79,7 @@ if(EXISTS ${maixpy_wrapper_src}) file(REMOVE ${maixpy_wrapper_src}) endif() add_custom_command(OUTPUT ${maixpy_wrapper_src} - COMMAND ${python} -u ${CMAKE_CURRENT_SOURCE_DIR}/gen_api.py -o ${maixpy_wrapper_src} --doc ${PROJECT_PATH}/docs/api --sdk_path ${SDK_PATH} + COMMAND ${python} -u ${CMAKE_CURRENT_SOURCE_DIR}/gen_api_cpp.py -o ${maixpy_wrapper_src} --sdk_path ${SDK_PATH} COMMENT "Generating maixpy_wrapper.cpp" VERBATIM ) diff --git a/components/maix/gen_api.py b/components/maix/gen_api.py index fcf95e95..e43c335e 100644 --- a/components/maix/gen_api.py +++ b/components/maix/gen_api.py @@ -31,13 +31,90 @@ def sort_headers(headers): headers = sorted(headers, key = lambda x: headers_priority.index(os.path.basename(x)) if os.path.basename(x) in headers_priority else len(headers_priority)) return headers +def parse_pyi(path): + items = { + "class": {}, + "func": [] + } + with open(path) as f: + lines = f.readlines() + class_item = None + for i, line in enumerate(lines): + if class_item: + if line[0] != " ": + items["class"][class_item["name"]] = class_item + class_item = None + continue + line = line.strip() + if line.startswith("def"): + class_item["func"].append(line.rsplit(":", 1)[0]) + + if line.startswith("def"): + items["func"].append(line.rsplit(":", 1)[0]) + if line.startswith("class"): + class_item = { + "name": line.replace("class", "").replace(":", "").strip(), + "func": [] + } + return items + +def find_func_def(items, name): + for item in items: + # def check_bool_raise(ok: bool, msg: str = '') -> None: + funcname = item.replace("def", "").strip().split("(")[0] + if funcname == name: + return item + return None + +def find_class_func_def(items, mc_k, name, debug): + item = items["class"].get(mc_k, {}) + if debug: + print(items, items["class"], mc_k) + if not item: + return None + # {"name": "", "func": {}} + return find_func_def(item["func"], name) + +def update_py_def_from_stub_files(api_tree, stub): + ''' + parse stub files, add definition to api_tree + ''' + maix_pyi_root = os.path.join(stub, "maix", "_maix") + for k, v in api_tree["members"]["maix"]["members"].items(): + def parse_module(pyi_path, k, v): + items = parse_pyi(pyi_path) + for m_k, m_v in v["members"].items(): + if m_v["type"] == "func": + name = m_v["name"] + func_def = find_func_def(items, name) + if func_def: + m_v["py_def"] = func_def.replace("maix._maix", "maix") + elif m_v["type"] == "class": + for mc_k, mc_v in m_v["members"].items(): + if mc_v["type"] == "func": + func_def = find_class_func_def(items, m_k, mc_k, m_v["name"] == "Tensors") + if func_def: + mc_v["py_def"] = func_def.replace("maix._maix", "maix") + module_dir = os.path.join(maix_pyi_root, k) + if os.path.isdir(module_dir): + for m_k, m_v in v["members"].items(): + path = os.path.join(maix_pyi_root, k, f"{m_k}.pyi") + # TODO: optimize speed + if os.path.exists(path): + parse_module(path, m_k, m_v) + else: + parse_module(os.path.join(maix_pyi_root, k, f"__init__.pyi"), m_k, m_v) + else: + parse_module(os.path.join(maix_pyi_root, f"{k}.pyi"), k, v) + return api_tree + if __name__ == "__main__": print("-- Generate MaixPy C/C++ API") parser = argparse.ArgumentParser(description='Generate MaixPy C/C++ API') parser.add_argument('--vars', type=str, default="", help="CMake global variables file") - parser.add_argument('-o', '--output', type=str, default="", help="API wrapper output file") parser.add_argument('--sdk_path', type=str, default="", help="MaixPy SDK path") parser.add_argument('--doc', type=str, default="", help="API documentation output file") + parser.add_argument('--stub', type=str, default="stub", help="stub dir") args = parser.parse_args() t = time.time() @@ -91,11 +168,7 @@ def sort_headers(headers): for r in rm: headers.remove(r) - # generate API cpp file - content = generate_api_cpp(api_tree, headers) - with open(args.output, "w", encoding="utf-8") as f: - f.write(content) - print("-- Generate MaixPy C/C++ API done") + api_tree = update_py_def_from_stub_files(api_tree, args.stub) # generate API documenation according to api_tree print("-- Generating MaixPy API documentation") @@ -126,8 +199,8 @@ def sort_headers(headers): } sidebar["items"].append(doc_maix_sidebar) start_comment_template = ''' +> You can use `{}` to access this module with MaixPy > This module is generated from [MaixCDK](https://github.com/sipeed/MaixCDK) -> You can use `{}` to access this module. ''' top_api_keys = ["maix"] diff --git a/components/maix/gen_api_cpp.py b/components/maix/gen_api_cpp.py index 65624eef..3daa3800 100644 --- a/components/maix/gen_api_cpp.py +++ b/components/maix/gen_api_cpp.py @@ -6,6 +6,9 @@ ''' import os +import argparse +import time +import sys def generate_api_cpp(api_tree, headers, out_path = None): content = ''' @@ -109,3 +112,83 @@ def gen_members(members, _code, parent_var, parent_name, parent_type, parent_nam with open(out_path, "w", encoding="utf-8") as f: f.write(content) return content + +def sort_headers(headers): + # read headers_priority.txt + headers_priority = [] + priority_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "headers_priority.txt") + with open(priority_file, "r", encoding="utf-8") as f: + for line in f.readlines(): + line = line.strip() + if line.startswith("#"): + continue + headers_priority.append(line) + # sort headers + headers = sorted(headers, key = lambda x: headers_priority.index(os.path.basename(x)) if os.path.basename(x) in headers_priority else len(headers_priority)) + return headers + +if __name__ == "__main__": + print("-- Generate MaixPy C/C++ API") + parser = argparse.ArgumentParser(description='Generate MaixPy C/C++ API') + parser.add_argument('--vars', type=str, default="", help="CMake global variables file") + parser.add_argument('-o', '--output', type=str, default="", help="API wrapper output file") + parser.add_argument('--sdk_path', type=str, default="", help="MaixPy SDK path") + args = parser.parse_args() + + t = time.time() + + sys.path.insert(0, os.path.join(args.sdk_path, "tools")) + from doc_tool.gen_api import get_headers_recursive, parse_api_from_header + from doc_tool.gen_markdown import module_to_md + + # get header files + headers = [] + if args.vars: + with open(args.vars, "r", encoding="utf-8") as f: + vars = json.load(f) + for include_dir in vars["includes"]: + headers += get_headers_recursive(include_dir) + else: # add sdk_path/components all .h and .hpp header files, except 3rd_party components + except_dirs = ["3rd_party"] + curr_dir = os.path.dirname(os.path.abspath(__file__)) + project_components_dir = os.path.abspath(os.path.join(curr_dir, "..")) + componets_dirs = [os.path.join(args.sdk_path, "components"), project_components_dir] + for componets_dir in componets_dirs: + for root, dirs, files in os.walk(componets_dir): + ignored = False + for except_dir in except_dirs: + if os.path.join(componets_dir, except_dir) in root: + ignored = True + break + if ignored: + continue + for name in files: + path = os.path.join(root, name) + if path.endswith(".h") or path.endswith(".hpp"): + headers.append(path) + # check each header file to find MaixPy API + api_tree = {} + rm = [] + all_keys = {} + + headers = sort_headers(headers) + + for header in headers: + api_tree, updated, keys = parse_api_from_header(header, api_tree, for_sdk = "maixpy") + if not updated: + rm.append(header) + for h, ks in all_keys.items(): + for k in ks: + if k in keys: + raise Exception("API {} multiple defined in {} and {}".format(k, h, header)) + all_keys[header] = keys + + for r in rm: + headers.remove(r) + + # generate API cpp file + content = generate_api_cpp(api_tree, headers) + with open(args.output, "w", encoding="utf-8") as f: + f.write(content) + print("-- Generate MaixPy C/C++ API done") + diff --git a/docs/doc/zh/README.md b/docs/doc/zh/README.md index cc06e3d9..d32b9e75 100644 --- a/docs/doc/zh/README.md +++ b/docs/doc/zh/README.md @@ -40,7 +40,8 @@ title: MaixPy 快速开始 ### 准备 TF 镜像卡和插入到设备 -如果你买的套餐里面有 TF 卡,里面已经有出厂镜像了,需要先小心打开外壳(注意里面有排线连接不要扯断了),然后插入 TF 卡。 +如果你买的**套餐里面有 TF 卡**,里面已经有出厂镜像了,需要先小心打开外壳(注意里面有排线连接不要扯断了),然后插入 TF 卡。 +> 出厂的镜像 如果没买 TF 卡,则需要将系统烧录进自备的 TF 卡中,烧录方法请看[升级和烧录系统](./basic/os.md),然后再安装到板子。 @@ -50,8 +51,10 @@ title: MaixPy 快速开始 ![maixcam](/static/image/maixcam_font.png) -> 如果屏幕没有显示,请确认购买了配套的 TF 卡,如果确认有 TF 卡,可以尝试[更新系统](./basic/os.md)。 -> 如果你没有购买 TF 卡套餐,你需要按照[升级和烧录系统](./basic/os.md)的方法烧录最新的系统到 TF 卡。 +如果屏幕没有显示 +* 请确认购买了配套的 TF 卡,如果确认有 TF 卡,并且已经插入到设备,可以**尝试[更新到最新的系统](./basic/os.md)**。 +* 如果你没有购买 TF 卡套餐,你需要按照[升级和烧录系统](./basic/os.md)的方法烧录最新的系统到 TF 卡。 +* 另外请确认屏幕和摄像头的排线没有松动,屏幕的排线在拆开外壳时很容易脱落,需要注意。 ### 联网 @@ -71,6 +74,8 @@ title: MaixPy 快速开始 * 设备上点击 `设置`(`Settings`),选择`安装运行库`。 * 安装完成后可以看到更新到了最新版本,然后退出即可。 +如果显示`Request failed` 或者`请求失败`,请先检查网络是否已经连接,需要能连接到互联网,如果还不行,请拍照联系客服处理即可。 + ### 使用内置应用 @@ -80,7 +85,9 @@ title: MaixPy 快速开始 Classifier Result video -其它的请自行摸索,以后还会更新更多应用,之后会在 [MaixHub 应用商店](https://maixhub.com/app) 更新。 +其它的请自行摸索,以后还会更新更多应用,使用文档以及应用更新请看 [MaixHub 应用商店](https://maixhub.com/app) 。 + +**注意:应用只包含了 MaixPy 能实现的一部分功能,使用 MaixPy 能创造更多功能**。 ## 作为串口模块使用 diff --git a/setup.py b/setup.py index 6da9e4b2..f0196fd1 100644 --- a/setup.py +++ b/setup.py @@ -118,6 +118,15 @@ def get_python_version(): os.makedirs(os.path.dirname(dst), exist_ok=True) shutil.copy(os.path.join(root, name), dst) +# generate api documentation + # COMMAND ${python} -u ${CMAKE_CURRENT_SOURCE_DIR}/gen_api.py -o ${maixpy_wrapper_src} --doc ${PROJECT_PATH}/docs/api --sdk_path ${SDK_PATH} +maixcdk_path = os.environ.get("MAIXCDK_PATH", None) +if not maixcdk_path: + raise Exception("No environment variable MAIXCDK_PATH, please set first by `export MAIXCDK_PATH=xxxxx`") +ret = os.system(f"python -u components/maix/gen_api.py --doc docs/api --sdk_path {maixcdk_path}") +if ret != 0: + raise Exception("Generate doc file failed") + # requirement packages requirements = []