From d47350b08f029db4fbeeb439fa07632a478910b3 Mon Sep 17 00:00:00 2001 From: junhuanchen Date: Wed, 12 Jan 2022 19:34:02 +0800 Subject: [PATCH] [0.4.0] release --- MANIFEST.in | 5 + README.md | 14 +- docs/develop_en.md | 12 +- envs/linux_desktop.py | 15 +- envs/maix_r329.py | 13 +- envs/maix_v83x.py | 1 + ext_modules/_maix_camera/_camera_pybind.cpp | 8 +- .../_maix_camera/include/_maix_camera.h | 26 +- ext_modules/_maix_image/_maix_image.cpp | 12 +- ext_modules/_maix_image/include/maix_image.h | 3 +- ext_modules/_maix_image/py_maix_image.cpp | 1 + ext_modules/_maix_nn/example/load_forward.py | 3 +- ext_modules/_maix_nn/example/yolo2_camera.py | 52 ++- ext_modules/_maix_nn/src/py_maix_nn.c | 52 +-- ext_modules/_maix_nn/src/py_maix_nn_app.c | 1 + ext_modules/_maix_nn/src/py_maix_nn_decoder.c | 1 + .../.py_maix_nn_decoder_yolo2.c | 356 ------------------ ext_modules/libmaix | 2 +- maix/__init__.py | 12 + maix/camera.py | 6 +- maix/display.py | 18 +- maix/i2c.py | 20 +- maix/nn/app/__init__.py | 3 + maix/zbar.py | 70 ++++ setup.py | 3 +- tests/maix_v831/usage/usage_v831_adc.py | 32 ++ tests/maix_v831/usage/usage_v831_watchdog.py | 168 +++++++++ 27 files changed, 434 insertions(+), 475 deletions(-) create mode 100644 MANIFEST.in delete mode 100644 ext_modules/_maix_nn_decoder_/.py_maix_nn_decoder_yolo2.c create mode 100644 maix/zbar.py create mode 100644 tests/maix_v831/usage/usage_v831_adc.py create mode 100644 tests/maix_v831/usage/usage_v831_watchdog.py diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..de4d047 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,5 @@ +prune .git + +recursive-include ext_modules/libmaix/components/libmaix/include *.h +recursive-include ext_modules/libmaix/components/maix_cv_image/include *.h +recursive-include ext_modules/libmaix/components/libmaix/lib/arch/desktop/ * diff --git a/README.md b/README.md index 7797fd4..b6f8a2e 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,19 @@ Install [rpyc_ikernel](https://github.com/sipeed/rpyc_ikernel) kernel in jupyter | **Platform** | **Status** | **Config** | **Progress** | | -------------- | ---------- | ----------- | ------------ | -| linux_pc & RPi_2B | working | [linux_desktop](./envs/linux_desktop.py) | 50% +| linux_pc & RPi_2B | working | [linux_desktop](./envs/linux_desktop.py) | 70% | maix_v83x | working | [maix_v83x](./envs/maix_v83x.py) | 90% -| maix_r329 | working | [maix_r329](./envs/maix_r329.py) | 70% +| maix_r329 | working | [maix_r329](./envs/maix_r329.py) | 80% + +Estimated Workload + +- 30% Python3.7↑ & toolchain environment adaptation Pillow spidev evdev gpiod pyserial zbarlight numpy + +- 20% base library adaptation camera display image nn speech + +- 10% Peripheral library adapted to gpio spi i2c zbar pwm event + +- 40% AI application adaptation resnet yolo classifier FaceRecognize > The development progress is in no particular order. diff --git a/docs/develop_en.md b/docs/develop_en.md index b08d337..fc145fc 100644 --- a/docs/develop_en.md +++ b/docs/develop_en.md @@ -73,25 +73,27 @@ auto run test_xxxx.py your x86 -- python setup.py build +- python3 setup.py bdist_wheel your v83x - source /opt/v83x_linux_x86_python3.8_toolchain/envsetup.sh -- python3.8 setup.py build maix_v83x +- python3.8 setup.py bdist_wheel maix_v83x - adb shell "pip install /root/maixpy3-0.3.5-cp38-cp38-linux_armv7l.whl --upgrade" -- python3.8 setup.py clean --all bdist_wheel maix_v83x && adb push ./dist/maixpy3-0.3.5-cp38-cp38-linux_armv7l.whl /root/ && adb shell "pip install /root/maixpy3-0.3.5-cp38-cp38-linux_armv7l.whl --upgrade" +- python3.8 setup.py clean --all bdist_wheel maix_v83x && adb push ./dist/maixpy3-0.3.8-cp38-cp38-linux_armv7l.whl /root/ && adb shell "pip install /root/maixpy3-0.3.8-cp38-cp38-linux_armv7l.whl --upgrade" your r329 - source /opt/r329_linux_x86_python3.9_toolchain/envsetup.sh -- python3.9 setup.py clean --all bdist_wheel maix_r329 && scp ./dist/maixpy3-0.3.5-cp39*.whl root@192.168.0.5:~/ +- python3.8 setup.py bdist_wheel maix_r329 + +- python3.9 setup.py clean --all bdist_wheel maix_r329 && scp ./dist/maixpy3-0.3.6-cp39*.whl root@192.168.0.17:~/ -- pip uninstall maixpy3 -y && pip install ~/maixpy3-0.3.5-cp39*.whl +- pip uninstall maixpy3 -y && pip install ~/maixpy3-0.3.6-cp39*.whl > pypi upload manylinux2014_aarch64 diff --git a/envs/linux_desktop.py b/envs/linux_desktop.py index 789a940..c020f51 100644 --- a/envs/linux_desktop.py +++ b/envs/linux_desktop.py @@ -21,7 +21,7 @@ _maix_image_module = Pybind11Extension( name = "_maix_image", - include_dirs=[ '/usr/include/opencv4/', + include_dirs=[ '/usr/include/opencv4/', '/usr/local/include/opencv4/', get_incs( 'ext_modules/libmaix/components/libmaix/include'), get_incs( @@ -44,7 +44,8 @@ ], library_dirs=[ "/usr/lib/", - "/usr/lib/x86_64-linux-gnu/" + "/usr/lib/x86_64-linux-gnu/", + "/usr/local/lib64/", ], extra_link_args=[ ], @@ -53,7 +54,7 @@ _maix_camera_module = Pybind11Extension( name = '_maix_camera', - include_dirs=[ '/usr/include/opencv4/', + include_dirs=[ '/usr/include/opencv4/', '/usr/local/include/opencv4/', get_incs( 'ext_modules/libmaix/components/libmaix/include'), get_incs( @@ -79,7 +80,8 @@ ], library_dirs=[ "/usr/lib/", - "/usr/lib/x86_64-linux-gnu/" + "/usr/lib/x86_64-linux-gnu/", + "/usr/local/lib64/", ], extra_link_args=[ ], @@ -88,7 +90,7 @@ _maix_display_module = Pybind11Extension( name = "_maix_display", - include_dirs=[ '/usr/include/opencv4/', + include_dirs=[ '/usr/include/opencv4/', '/usr/local/include/opencv4/', get_incs( 'ext_modules/libmaix/components/libmaix/include'), get_incs( @@ -114,7 +116,8 @@ ], library_dirs=[ "/usr/lib/", - "/usr/lib/x86_64-linux-gnu/" + "/usr/lib/x86_64-linux-gnu/", + "/usr/local/lib64/", ], extra_link_args=[ ], diff --git a/envs/maix_r329.py b/envs/maix_r329.py index 9887694..062554d 100644 --- a/envs/maix_r329.py +++ b/envs/maix_r329.py @@ -85,16 +85,16 @@ max_nn_srcs = get_srcs('ext_modules/_maix_nn/src') max_nn_srcs.extend(get_srcs('ext_modules/libmaix/components/libmaix/src')) max_nn_srcs.remove("ext_modules/libmaix/components/libmaix/src/libmaix.c") -max_nn_srcs.remove("ext_modules/_maix_nn/src/py_maix_nn_app.c") -max_nn_srcs.remove("ext_modules/_maix_nn/src/py_maix_nn_app_classifier.c") -max_nn_srcs.remove("ext_modules/_maix_nn/src/py_maix_nn_app_FaceRecognize.c") -max_nn_srcs.remove("ext_modules/_maix_nn/src/py_maix_nn_decoder_yolo2.c") -max_nn_srcs.remove("ext_modules/_maix_nn/src/py_maix_nn_decoder.c") +# max_nn_srcs.remove("ext_modules/_maix_nn/src/py_maix_nn_app.c") +# max_nn_srcs.remove("ext_modules/_maix_nn/src/py_maix_nn_app_classifier.c") +# max_nn_srcs.remove("ext_modules/_maix_nn/src/py_maix_nn_app_FaceRecognize.c") +# max_nn_srcs.remove("ext_modules/_maix_nn/src/py_maix_nn_decoder_yolo2.c") +# max_nn_srcs.remove("ext_modules/_maix_nn/src/py_maix_nn_decoder.c") # max_nn_srcs.remove("ext_modules/_maix_nn/src/py_maix_nn.c") _maix_nn_module = Extension('_maix_nn', include_dirs=['ext_modules/_maix_nn/include', 'ext_modules/libmaix/components/libmaix/include'], sources=max_nn_srcs, libraries=[ - "maix_utils", "maix_nn", + "maix_utils", "maix_nn", "maix_nn_decoder", "maix_nn_app" ], library_dirs=[ ext_so, ], # extra_link_args = [ "-Wl,-z,origin", "-Wl,-rpath='$ORIGIN/maix'" ] @@ -150,6 +150,7 @@ ] _maix_py_modules = [ + "numpy", "Pillow", "rpyc", "gpiod", diff --git a/envs/maix_v83x.py b/envs/maix_v83x.py index fad8485..5a24bd5 100644 --- a/envs/maix_v83x.py +++ b/envs/maix_v83x.py @@ -184,6 +184,7 @@ ] _maix_py_modules = [ + "numpy", "Pillow", "rpyc", "gpiod", diff --git a/ext_modules/_maix_camera/_camera_pybind.cpp b/ext_modules/_maix_camera/_camera_pybind.cpp index 70e7e6d..e01ca59 100644 --- a/ext_modules/_maix_camera/_camera_pybind.cpp +++ b/ext_modules/_maix_camera/_camera_pybind.cpp @@ -34,7 +34,7 @@ PYBIND11_MODULE(_maix_camera, m) .doc() = "_maix_Camera"; pybind11::class_<_camera>(m, "Camera") - .def(py::init(), py::arg("w") = 240, py::arg("h") = 240, py::arg("dev_id") = 0) + .def(py::init(), py::arg("w") = 240, py::arg("h") = 240, py::arg("dev_id") = 0, py::arg("m") = 0, py::arg("f") = 0) .def_readwrite("width", &_camera::width) .def_readwrite("height", &_camera::height) .def("read", &_camera::read) @@ -67,7 +67,7 @@ void v_init(_camera *tp) libmaix_camera_module_init(); libmaix_image_module_init(); tp->rgb_img = NULL; - tp->cam = libmaix_cam_create(tp->dev_id, tp->width, tp->height, 0, 0); + tp->cam = libmaix_cam_create(tp->dev_id, tp->width, tp->height, tp->m, tp->f); if (NULL != tp->cam) { int ret = tp->cam->start_capture(tp->cam); @@ -91,11 +91,13 @@ void v_init(_camera *tp) #endif // VirtualCamera } -_camera::_camera(int w, int h, int dev_id) +_camera::_camera(int w, int h, int dev_id, int m, int f) { this->width = w; this->height = h; this->dev_id = dev_id; + this->m = m; + this->f = f; v_init(this); } diff --git a/ext_modules/_maix_camera/include/_maix_camera.h b/ext_modules/_maix_camera/include/_maix_camera.h index 30d71e3..8afc5e3 100644 --- a/ext_modules/_maix_camera/include/_maix_camera.h +++ b/ext_modules/_maix_camera/include/_maix_camera.h @@ -33,12 +33,8 @@ class _camera { private: public: - int width, height, dev_id; - // width operator=(const int num) //如果使用的话,可能需要重载赋值运算符 - // { - // if(num < 640 || num > 480) - // } - _camera(int w, int h, int dev_id); + int width, height, dev_id, m, f; + _camera(int w, int h, int dev_id, int m, int f); ~_camera(); pybind11::list read(); void close(); @@ -46,25 +42,7 @@ class _camera void __exit__(); libmaix_cam_t *cam; - libmaix_cam_t *cam1; libmaix_image_t *rgb_img; - - -// #ifdef R329Camera -// public: -// libmaix_cam_t *cam; -// uint8_t *img_buff; - -// #endif - -// #ifdef V83xCamera -// public: -// libmaix_cam_t *cam; -// libmaix_image_t *yuv_img; -// libmaix_image_t *rgb_img; - -// #endif - }; diff --git a/ext_modules/_maix_image/_maix_image.cpp b/ext_modules/_maix_image/_maix_image.cpp index 5240cd1..c0f4bc0 100644 --- a/ext_modules/_maix_image/_maix_image.cpp +++ b/ext_modules/_maix_image/_maix_image.cpp @@ -122,7 +122,7 @@ std::string maix_image::str__() { std::string info_str; std::stringstream ss; - ss << "<_maix_image.Image \"width\":" << this->_maix_image_width + ss << "<_maix_image.Image " << this->_img << " \" width\":" << this->_maix_image_width << ", \"height\":" << this->_maix_image_height << ", \"type\"=" << this->_maix_image_type << ", \"size\":" << this->_maix_image_size @@ -345,13 +345,19 @@ py::object maix_image::_to_py(std::string im) } } #endif + return py::none(); } -void maix_image::_clear() +void maix_image::_delete() { this->v_close(); } - +maix_image &maix_image::_clear() +{ + uint8_t* img_p_t = (uint8_t*)this->_img->data; + memset(img_p_t, 0, this->_maix_image_size); + return *this; +} void maix_image::_load_freetype(std::string path) { libmaix_cv_image_load_freetype(path.c_str()); diff --git a/ext_modules/_maix_image/include/maix_image.h b/ext_modules/_maix_image/include/maix_image.h index c6cc873..72309ad 100644 --- a/ext_modules/_maix_image/include/maix_image.h +++ b/ext_modules/_maix_image/include/maix_image.h @@ -96,7 +96,8 @@ class maix_image : virtual public any_image, public maix_version, public maix_cu maix_image &_load(py::object data, std::vector size, std::string mode); maix_image &_open_file(std::string path); py::object _to_py(std::string im); - void _clear(); + void _delete(); + maix_image &_clear(); void _load_freetype(std::string path); py::tuple _get_string_size(std::string str, double scale, int thickness); maix_image &_resize(int w, int h, int func); diff --git a/ext_modules/_maix_image/py_maix_image.cpp b/ext_modules/_maix_image/py_maix_image.cpp index 3d55804..828a256 100644 --- a/ext_modules/_maix_image/py_maix_image.cpp +++ b/ext_modules/_maix_image/py_maix_image.cpp @@ -46,6 +46,7 @@ PYBIND11_MODULE(_maix_image, mo) .def("open", &maix_image::_open_file) .def("copy", &maix_image::_to_py, py::arg("img") = "maix_image") .def("clear", &maix_image::_clear) + .def("delete", &maix_image::_delete) .def("load_freetype", &maix_image::_load_freetype, py::arg("path")) .def("get_string_size", &maix_image::_get_string_size, py::arg("str"), py::arg("scale") = 1.0, py::arg("thickness") = 1) .def("save", &maix_image::_save, py::arg("path"), py::arg("format") = "jpeg") diff --git a/ext_modules/_maix_nn/example/load_forward.py b/ext_modules/_maix_nn/example/load_forward.py index 8308f90..eed81cd 100644 --- a/ext_modules/_maix_nn/example/load_forward.py +++ b/ext_modules/_maix_nn/example/load_forward.py @@ -30,8 +30,9 @@ while True: img = camera.capture() out = m.forward(img.tobytes(), quantize=True) + + print(out.shape) out2 = nn.F.softmax(out) msg = "{:.2f}: {}".format(out2.max(), labels[out.argmax()]) - print(msg) img.draw_string(0,0,msg,scale = 0.5 , color = (183,127,221)) display.show(img) \ No newline at end of file diff --git a/ext_modules/_maix_nn/example/yolo2_camera.py b/ext_modules/_maix_nn/example/yolo2_camera.py index 3cb9756..dc896e0 100644 --- a/ext_modules/_maix_nn/example/yolo2_camera.py +++ b/ext_modules/_maix_nn/example/yolo2_camera.py @@ -2,32 +2,34 @@ -from maix import nn -from PIL import Image, ImageDraw, ImageFont -from maix import display, camera +from maix import nn, display, camera , image import time from maix.nn import decoder +import numpy as np -def draw_rectangle_with_title(draw, box, disp_str, bg_color=(255, 0, 0, 255), font_color=(255, 255, 255, 255)): - # draw = ImageDraw.Draw(img) - font = ImageFont.load_default() +def draw_rectangle_with_title(img, box, disp_str, bg_color=(255, 0, 0, 255), font_color=(255, 255, 255, 255)): + # # draw = ImageDraw.Draw(img) + # font_w, font_h = font.getsize(disp_str) + # draw.rectangle((box[0], box[1], box[0] + box[2], box[1] + box[3]), fill=None, outline=bg_color, width=2) + # draw.rectangle((box[0], box[1] - font_h, box[0] + font_w, box[1]), fill=bg_color) + # draw.text((box[0], box[1] - font_h), disp_str, fill=font_color, font=font) - font_w, font_h = font.getsize(disp_str) - draw.rectangle((box[0], box[1], box[0] + box[2], box[1] + box[3]), fill=None, outline=bg_color, width=2) - draw.rectangle((box[0], box[1] - font_h, box[0] + font_w, box[1]), fill=bg_color) - draw.text((box[0], box[1] - font_h), disp_str, fill=font_color, font=font) + # tmp.draw_rectangle(i["x"], i["y"], i["x"] + i["w"], + # i["y"] + i["h"], color=(255, 0, 0), thickness=1) + img.draw_rectangle(box[0], box[1], box[0] + box[2], box[1] + box[3],color=(255, 0, 0), thickness=1) + img.draw_string(box[0], box[1]+ box[3] ,disp_str, scale=0.5,color=(0, 0, 255), thickness=1) + - -camera.config(size=(224, 224)) + model = { - # "param": "/root/models/yolo2_face_awnn.param", - # "bin": "/root/models/yolo2_face_awnn.bin" - "bin":"/root/dections/yolo2.bin" + "param": "/home/model/face/yolo2_face_awnn.param", + "bin": "/home/model/face/yolo2_face_awnn.bin" + # "bin":"./aipu_onnx_cards_224_35.bin" } options = { - "model_type": "normal", + "model_type": "awnn", "inputs": { "input0": (224, 224, 3) }, @@ -48,21 +50,31 @@ def draw_rectangle_with_title(draw, box, disp_str, bg_color=(255, 0, 0, 255), fo print("-- read image ok") +# labels = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "mouse", "microbit", "ruler", "cat", "peer", "ship", "apple", "car", "pan", "dog", "umbrella", "airplane", "clock", "grape", "cup", "left", "right", "front", "stop", "back"] +# anchors = [2.44, 2.25, 5.03, 4.91, 3.5, 3.53, 4.16, 3.94, 2.97, 2.84] + labels = ["person"] anchors = [1.19, 1.98, 2.79, 4.59, 4.53, 8.92, 8.06, 5.29, 10.32, 10.65] + yolo2_decoder = decoder.Yolo2(len(labels), anchors, net_in_size=(w, h), net_out_size=(7, 7)) + + + +camera.config((224, 224)) while 1: img = camera.capture() if not img: time.sleep(0.01) continue t = time.time() - out = m.forward(img.tobytes(), quantize=True, layout="hwc") + + out = m.forward(img.tobytes(), quantize=True) print("-- forward: ", time.time() - t ) + t = time.time() - boxes, probs = yolo2_decoder.run(out, nms=0.3, threshold=0.5, img_size=(240, 240)) + boxes, probs = yolo2_decoder.run(out, nms=0.5, threshold=0.5, img_size=(224,224)) print("-- decode: ", time.time() - t ) t = time.time() @@ -70,11 +82,11 @@ def draw_rectangle_with_title(draw, box, disp_str, bg_color=(255, 0, 0, 255), fo class_id = probs[i][0] prob = probs[i][1][class_id] disp_str = "{}:{:.2f}%".format(labels[class_id], prob*100) - draw_rectangle_with_title(display.get_draw(), box, disp_str) + draw_rectangle_with_title(img, box, disp_str) print("-- draw: ", time.time() - t ) t = time.time() - display.show() + display.show(img) print("-- show: ", time.time() - t ) diff --git a/ext_modules/_maix_nn/src/py_maix_nn.c b/ext_modules/_maix_nn/src/py_maix_nn.c index e2996ce..e7bf3fb 100644 --- a/ext_modules/_maix_nn/src/py_maix_nn.c +++ b/ext_modules/_maix_nn/src/py_maix_nn.c @@ -1,7 +1,7 @@ #include "py_maix_nn.h" -// #include "py_maix_nn_classifier.h" -// #include "py_maix_nn_FaceRecognize.h" +#include "py_maix_nn_classifier.h" +#include "py_maix_nn_FaceRecognize.h" #include "string.h" #include "libmaix_nn.h" @@ -95,15 +95,15 @@ PyMODINIT_FUNC PyInit__maix_nn(void) if (PyType_Ready(&PyMaix_NN_Model_Type) < 0) { return NULL; } - // if (PyType_Ready(&PyMaix_NN_Decoder_Yolo2_Type) < 0) { - // return NULL; - // } - // if (PyType_Ready(&PyMaix_NN_app_Classifier_Type) < 0) { - // return NULL; - // } - // if (PyType_Ready(&PyMaix_NN_app_FaceRecognize_Type) < 0) { - // return NULL; - // } + if (PyType_Ready(&PyMaix_NN_Decoder_Yolo2_Type) < 0) { + return NULL; + } + if (PyType_Ready(&PyMaix_NN_app_Classifier_Type) < 0) { + return NULL; + } + if (PyType_Ready(&PyMaix_NN_app_FaceRecognize_Type) < 0) { + return NULL; + } /* Add maix.nn.F module*/ PyObject *functional_module = PyModule_Create(&maix_nn_functional_module); @@ -111,21 +111,21 @@ PyMODINIT_FUNC PyInit__maix_nn(void) Py_INCREF(functional_module); PyModule_AddObject(module, "functional", functional_module); - // /* Add maix.nn.decoder module, - // add maix.nn.decoder.Yolo2 class*/ - // PyObject *decoder_module = PyModule_Create(&maix_nn_decoder_module); - // Py_INCREF(&PyMaix_NN_Decoder_Yolo2_Type); - // PyModule_AddObject(decoder_module, "Yolo2", (PyObject*)&PyMaix_NN_Decoder_Yolo2_Type); - // PyModule_AddObject(module, "decoder", decoder_module); - - // /* Add maix.nn._app module, - // add maix.nn._app.classifier class*/ - // PyObject *app_module = PyModule_Create(&maix_nn_app_module); - // Py_INCREF(&PyMaix_NN_app_Classifier_Type); - // PyModule_AddObject(app_module, "Classifier", (PyObject*)&PyMaix_NN_app_Classifier_Type); - // Py_INCREF(&PyMaix_NN_app_FaceRecognize_Type); - // PyModule_AddObject(app_module, "FaceRecognize", (PyObject*)&PyMaix_NN_app_FaceRecognize_Type); - // PyModule_AddObject(module, "_app", app_module); + /* Add maix.nn.decoder module, + add maix.nn.decoder.Yolo2 class*/ + PyObject *decoder_module = PyModule_Create(&maix_nn_decoder_module); + Py_INCREF(&PyMaix_NN_Decoder_Yolo2_Type); + PyModule_AddObject(decoder_module, "Yolo2", (PyObject*)&PyMaix_NN_Decoder_Yolo2_Type); + PyModule_AddObject(module, "decoder", decoder_module); + + /* Add maix.nn._app module, + add maix.nn._app.classifier class*/ + PyObject *app_module = PyModule_Create(&maix_nn_app_module); + Py_INCREF(&PyMaix_NN_app_Classifier_Type); + PyModule_AddObject(app_module, "Classifier", (PyObject*)&PyMaix_NN_app_Classifier_Type); + Py_INCREF(&PyMaix_NN_app_FaceRecognize_Type); + PyModule_AddObject(app_module, "FaceRecognize", (PyObject*)&PyMaix_NN_app_FaceRecognize_Type); + PyModule_AddObject(module, "_app", app_module); return module; } diff --git a/ext_modules/_maix_nn/src/py_maix_nn_app.c b/ext_modules/_maix_nn/src/py_maix_nn_app.c index ca75d67..7ad86df 100644 --- a/ext_modules/_maix_nn/src/py_maix_nn_app.c +++ b/ext_modules/_maix_nn/src/py_maix_nn_app.c @@ -3,6 +3,7 @@ #include "py_maix_nn.h" #include "py_maix_nn_classifier.h" #include "libmaix_nn_classifier.h" +#include "libmaix_nn_face.h" #include #include diff --git a/ext_modules/_maix_nn/src/py_maix_nn_decoder.c b/ext_modules/_maix_nn/src/py_maix_nn_decoder.c index 9ae3c2d..b90dfc9 100644 --- a/ext_modules/_maix_nn/src/py_maix_nn_decoder.c +++ b/ext_modules/_maix_nn/src/py_maix_nn_decoder.c @@ -7,6 +7,7 @@ static PyMethodDef _maix_nn_decoder_methods[] = { + {NULL, NULL, 0, NULL} }; diff --git a/ext_modules/_maix_nn_decoder_/.py_maix_nn_decoder_yolo2.c b/ext_modules/_maix_nn_decoder_/.py_maix_nn_decoder_yolo2.c deleted file mode 100644 index b60601c..0000000 --- a/ext_modules/_maix_nn_decoder_/.py_maix_nn_decoder_yolo2.c +++ /dev/null @@ -1,356 +0,0 @@ -#include "py_maix_nn.h" -#include -#include "libmaix_nn.h" -#include "stdlib.h" -#include "libmaix_nn_decoder_yolo2.h" - - -typedef struct -{ - PyObject_HEAD; - PyObject* m_numpy; - libmaix_nn_decoder_yolo2_config_t config; - libmaix_nn_decoder_t* decoder; - bool init; -} DecoderYolo2Object; - -PyDoc_STRVAR(decoder_yolo2_type_doc, "neural network model Yolo2 decoder object.\n"); - -static int max_index(float *a, int n) -{ - int i, max_i = 0; - float max = a[0]; - - for (i = 1; i < n; ++i) - { - if (a[i] > max) - { - max = a[i]; - max_i = i; - } - } - return max_i; -} - -static int decoder_yolo2_init(DecoderYolo2Object *self, PyObject *args, PyObject *kwds); - -static PyObject* decoder_yolo2_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - DecoderYolo2Object* self = (DecoderYolo2Object*)type->tp_alloc(type, 0); - if (!self) - { - PyErr_NoMemory(); - return NULL; - } - return (PyObject*)self; -} - - -static void decoder_yolo2_del(DecoderYolo2Object *self) -{ - if(self->config.anchors) - { - free(self->config.anchors); - self->config.anchors = NULL; - } - Py_DECREF(self->m_numpy); - if(self->decoder) - { - self->decoder->deinit(self->decoder); - libmaix_nn_decoder_yolo2_destroy(&self->decoder); - } - self->init = false; -} - -static int decoder_yolo2_init(DecoderYolo2Object *self, PyObject *args, PyObject *kwds) -{ - if(self->init) - { - PyErr_SetString(PyExc_Exception, "already initialized"); - return -1; - } - libmaix_err_t err = LIBMAIX_ERR_NONE; - static char *kwlist[] = {"class_num", "anchors", "net_in_size", "net_out_size", NULL}; - - PyObject *anchors = NULL; - PyObject *net_in_zie = NULL; - PyObject *net_out_zie = NULL; - bool anchors_nedd_decref = false; - - self->m_numpy = PyImport_ImportModule("numpy"); - if(!self->m_numpy) - { - PyErr_SetString(PyExc_Exception, "need numpy module"); - return -1; - } - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "IO|$OO:__init__", kwlist, - &self->config.classes_num, &anchors, &net_in_zie, &net_out_zie)) - { - return -1; - } - if(PyList_Check(anchors)) - { - anchors = PyList_AsTuple(anchors); - anchors_nedd_decref = true; - } - self->config.anchors = (float*)malloc(PyTuple_Size(anchors) * sizeof(float)); - if(!self->config.anchors) - { - PyErr_NoMemory(); - return -1; - } - self->config.anchors_num = PyTuple_Size(anchors) / 2; - for(int i = 0; i < PyTuple_Size(anchors); ++i) - { - self->config.anchors[i] = (float)PyFloat_AsDouble(PyTuple_GetItem(anchors, i)); - } - if(anchors_nedd_decref) - { - Py_DECREF(anchors); - } - if(net_in_zie) - { - self->config.net_in_width = PyLong_AsLong(PyTuple_GetItem(net_in_zie, 0)); - self->config.net_in_height = PyLong_AsLong(PyTuple_GetItem(net_in_zie, 1)); - } - else - { - self->config.net_in_width = 224; - self->config.net_in_height = 224; - } - if(net_out_zie) - { - self->config.net_out_width = PyLong_AsLong(PyTuple_GetItem(net_out_zie, 0)); - self->config.net_out_height = PyLong_AsLong(PyTuple_GetItem(net_out_zie, 1)); - } - else - { - self->config.net_out_width = 7; - self->config.net_out_height = 7; - } - self->config.input_width = self->config.net_in_width; - self->config.input_height = self->config.net_in_height; - self->config.nms_value = 0.3; - self->config.threshold = 0.5; - - self->decoder = libmaix_nn_decoder_yolo2_create(); - if(!self->decoder) - { - PyErr_SetString(PyExc_Exception, "create yolo2 decoder fail"); - return -1; - } - err = self->decoder->init(self->decoder, (void*)&self->config); - if(err != LIBMAIX_ERR_NONE) - { - PyErr_Format(PyExc_Exception, "yolo2 decoder init fail, code: %d", err); - return -1; - } - - self->init = true; - return 0; -} - -static PyObject *decoder_yolo2_str(PyObject *object) -{ - char desc[] = "Yolo2 decoder object"; - PyObject *dev_desc = NULL; - - dev_desc = PyUnicode_FromString(desc); - - return dev_desc; -} - -PyDoc_STRVAR(decoder_yolo2_method_run_doc, "run decode process.\n"); -static PyObject* decoder_yolo2_method_run(DecoderYolo2Object *self, PyObject *args, PyObject *kw_args) -{ - if(!self->init) - { - PyErr_SetString(PyExc_Exception, "not initialize"); - return NULL; - } - - libmaix_err_t err = LIBMAIX_ERR_NONE; - static char *kwlist[] = {"fmap", "nms", "threshold", "img_size", NULL}; - PyObject *fmap = NULL; - float nms = 0.3; - float threshold = 0.5; - PyObject *img_size = NULL; - bool fmap_need_decref = false; - if (!PyArg_ParseTupleAndKeywords(args, kw_args, "O|$ffO:__init__", kwlist, - &fmap, &nms, &threshold, &img_size)) - { - return NULL; - } - if(img_size) - { - self->config.input_width = PyLong_AsLong(PyTuple_GetItem(img_size, 0)); - self->config.input_height = PyLong_AsLong(PyTuple_GetItem(img_size, 1)); - } - else - { - self->config.input_width = self->config.net_in_width; - self->config.input_height = self->config.net_in_height; - } - self->config.nms_value = nms; - self->config.threshold = threshold; - - libmaix_nn_layer_t out_fmap = { - .w = self->config.net_out_width, - .h = self->config.net_out_height, - .c = (self->config.classes_num + 5) * self->config.anchors_num, - .dtype = LIBMAIX_NN_DTYPE_FLOAT, - .layout = LIBMAIX_NN_LAYOUT_HWC, - .data = NULL - }; - PyObject* o_out_fmap = NULL; - if(strstr(fmap->ob_type->tp_name, "ndarray") > 0) - { - o_out_fmap = PyObject_CallMethod(fmap, "tobytes", NULL); - if(o_out_fmap == NULL) - { - PyErr_SetString(PyExc_ValueError, "ndarray tobytes() error"); - return NULL; - } - fmap_need_decref = true; - } - else if(PyBytes_Check(fmap)) - { - o_out_fmap = fmap; - } - else - { - PyErr_SetString(PyExc_ValueError, "not supported fmap object, should be bytes or numpy.ndarray"); - return NULL; - } - int expected_size = out_fmap.w * out_fmap.h * out_fmap.c * sizeof(float); - if(PyBytes_Size(o_out_fmap) != expected_size) - { - PyErr_Format(PyExc_ValueError, "fmap size error, need: %d, but: %d", expected_size, PyBytes_Size(o_out_fmap)); - if(fmap_need_decref) - Py_DECREF(o_out_fmap); - return NULL; - } - out_fmap.data = PyBytes_AsString(o_out_fmap); - - libmaix_nn_decoder_yolo2_result_t yolo2_result; - err = self->decoder->decode(self->decoder, &out_fmap, (void*)&yolo2_result); - if(fmap_need_decref) - Py_DECREF(o_out_fmap); - if(err != LIBMAIX_ERR_NONE) - { - PyErr_Format(PyExc_Exception, "decode fail, code: %d", err); - return NULL; - } - PyObject* boxes = PyList_New(0); - PyObject* probs = PyList_New(0); - if(yolo2_result.boxes_num > 0) - { - for (uint32_t i = 0; i < yolo2_result.boxes_num; ++i) - { - int class_id = max_index(yolo2_result.probs[i], self->config.classes_num); - float prob = yolo2_result.probs[i][class_id]; - if (prob > threshold) - { - libmaix_nn_decoder_yolo2_box_t *b = yolo2_result.boxes + i; - if(img_size) - { - uint32_t x1 = (uint32_t)(b->x * self->config.input_width - (b->w * self->config.input_width / 2.)); - uint32_t y1 = (uint32_t)(b->y * self->config.input_height - (b->h * self->config.input_height / 2.)); - uint32_t w = (uint32_t)(b->w * self->config.input_width); - uint32_t h = (uint32_t)(b->h * self->config.input_height); - PyObject* box = PyList_New(4); - PyObject* tmp_o = PyLong_FromLong(x1); - printf("yinyong1 :%d \r\n",tmp_o->ob_refcnt); - PyList_SetItem(box, 0, tmp_o); - printf("yinyong2 :%d \r\n",tmp_o->ob_refcnt); - PyList_SetItem(box, 1, PyLong_FromLong(y1)); - PyList_SetItem(box, 2, PyLong_FromLong(w)); - PyList_SetItem(box, 3, PyLong_FromLong(h)); - PyList_Append(boxes, box); - Py_DECREF(box); - } - else - { - PyObject* box = PyList_New(4); - PyList_SetItem(box, 0, PyFloat_FromDouble(b->x - b->w / 2.)); - PyList_SetItem(box, 1, PyFloat_FromDouble(b->y - b->h / 2.)); - PyList_SetItem(box, 2, PyFloat_FromDouble(b->w)); - PyList_SetItem(box, 3, PyFloat_FromDouble(b->h)); - PyList_Append(boxes, box); - Py_DECREF(box); - } - PyObject* o_probs = PyList_New(0); - for(uint32_t j=0; jconfig.classes_num; ++j) - { - PyObject* tmp_o = PyFloat_FromDouble(yolo2_result.probs[i][j]); - PyList_Append(o_probs, tmp_o); - Py_DECREF(tmp_o); - } - PyObject* o_probs_item = PyList_New(2); - PyList_SetItem(o_probs_item, 0, PyLong_FromLong(class_id)); - PyList_SetItem(o_probs_item, 1, o_probs); - PyList_Append(probs, o_probs_item); - // Py_DECREF(o_probs); - Py_DECREF(o_probs_item); - } - } - } - - PyObject* final_result = PyList_New(0); - PyList_Append(final_result, boxes); - PyList_Append(final_result, probs); - Py_DECREF(boxes); - Py_DECREF(probs); - return final_result; -} - - - -static PyMethodDef decoder_yolo2_methods[] = { - {"run", (PyCFunction)decoder_yolo2_method_run, METH_VARARGS | METH_KEYWORDS, decoder_yolo2_method_run_doc}, - {NULL, NULL, 0, NULL}, -}; - - -PyTypeObject PyMaix_NN_Decoder_Yolo2_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - maix_nn_decoder_yolo2_type_name, /* tp_name */ - sizeof(DecoderYolo2Object), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)decoder_yolo2_del, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - decoder_yolo2_str, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - decoder_yolo2_type_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - decoder_yolo2_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)decoder_yolo2_init, /* tp_init */ - 0, /* tp_alloc */ - decoder_yolo2_new, /* tp_new */ -}; - diff --git a/ext_modules/libmaix b/ext_modules/libmaix index 4c964d8..e9d10ee 160000 --- a/ext_modules/libmaix +++ b/ext_modules/libmaix @@ -1 +1 @@ -Subproject commit 4c964d8dc28b2582995a6b0b0b25da6c91667f7d +Subproject commit e9d10eeb69159d1cb508d02620f49c4ad8bf23cf diff --git a/maix/__init__.py b/maix/__init__.py index c5be1ef..5b670b9 100644 --- a/maix/__init__.py +++ b/maix/__init__.py @@ -18,3 +18,15 @@ __all__.extend(['utils']) except ModuleNotFoundError as e: pass + +try: + import spidev as spi + __all__.extend(['spi']) +except ModuleNotFoundError as e: + pass + +try: + import serial + __all__.extend(['serial']) +except ModuleNotFoundError as e: + pass diff --git a/maix/camera.py b/maix/camera.py index 936821c..de7d2ae 100644 --- a/maix/camera.py +++ b/maix/camera.py @@ -76,15 +76,13 @@ def __init__(self, source="/v831"): self.source = source self.cam = None - def config(self, size=None): + def config(self, size=None, video=0, horizontal=1, vertical=1): if self.cam == None: if size == None: from maix import display size = (display.__width__, display.__height__) super(SpMaixVideo, self).__init__(size) - self.cam = Camera(self.width(), self.height(), 0) - import time - time.sleep(0.2) # wait init + self.cam = Camera(self.width(), self.height(), video, horizontal, vertical) print('[camera] config input size(%d, %d)' % (self.width(), self.height())) diff --git a/maix/display.py b/maix/display.py index f21fb82..8ae3aa2 100644 --- a/maix/display.py +++ b/maix/display.py @@ -1,22 +1,24 @@ __width__, __height__, __mode__ = 240, 240, "RGB" +def config(size=(240, 240)): + global __width__, __height__ + if size[0] > 0 and size[1] > 0: + __width__, __height__ = size[0], size[1] + try: __fastview__ = None from _maix_display import Display __fastview__ = Display() - __width__, __height__ = __fastview__.width, __fastview__.height - + config(size=(__fastview__.width, __fastview__.height)) def __draw__(img): from PIL import Image - global __fastview__ + global __fastview__, __width__, __height__ if isinstance(img, bytes): - __fastview__.draw(img, __fastview__.width, __fastview__.height) + __fastview__.draw(img, __width__, __height__) elif isinstance(img, Image.Image): - img = img.resize( - (__fastview__.width, __fastview__.height), Image.ANTIALIAS) - __fastview__.draw(img.tobytes(), __fastview__.width, - __fastview__.height) + img = img.resize((__width__, __height__), Image.ANTIALIAS) + __fastview__.draw(img.tobytes(), __width__, __height__) except ModuleNotFoundError as e: pass except Exception as e: diff --git a/maix/i2c.py b/maix/i2c.py index 2008372..0c99ec9 100644 --- a/maix/i2c.py +++ b/maix/i2c.py @@ -25,22 +25,26 @@ def scan(path='/dev/i2c-2'): addrs = [] for addr in range(0x00, 0x7F): try: - i2c = I2C(path, addr) - i2c.ioctl_read(0, 1) + tmp = None + tmp = I2C(path, addr) + tmp.ioctl_read(0, 1) addrs.append(addr) except OSError as e: pass finally: - i2c.close() + if tmp: + tmp.close() for addr in range(0x7F, 0xFF): try: - i2c = I2C(path, addr, tenbit=True) - i2c.ioctl_read(0, 1) + tmp = None + tmp = I2C(path, addr, tenbit=True) + tmp.ioctl_read(0, 1) addrs.append(addr) except OSError as e: pass finally: - i2c.close() + if tmp: + tmp.close() return addrs except ModuleNotFoundError as e: pass @@ -48,8 +52,8 @@ def scan(path='/dev/i2c-2'): if __name__ == '__main__': print(scan()) bus, addr = '/dev/i2c-2', 0x26 - i2c = I2C(bus, addr) - i2c.close() + tmp = I2C(bus, addr) + tmp.close() # i2c = I2CDevice(bus, addr, tenbit=False, iaddr_bytes=1, page_bytes=8, delay=1, flags=0) # iaddr_bytes(defualt 1 byte internal address) I2C device internal(word) address bytes, such as: 24C04 1 byte, 24C64 2 bytes # page_bytes(default 8 bytes per page) I2C max number of bytes per page, 1K/2K 8, 4K/8K/16K 16, 32K/64K 32 etc diff --git a/maix/nn/app/__init__.py b/maix/nn/app/__init__.py index e69de29..fe6120e 100644 --- a/maix/nn/app/__init__.py +++ b/maix/nn/app/__init__.py @@ -0,0 +1,3 @@ + +# from . import classifier +# from . import face diff --git a/maix/zbar.py b/maix/zbar.py new file mode 100644 index 0000000..2f7cf81 --- /dev/null +++ b/maix/zbar.py @@ -0,0 +1,70 @@ +# https://online-barcode-reader.inliteresearch.com/ +# http://zbar.sourceforge.net/ + +class LazyImport: + def __init__(self, module_name): + self.module_name = module_name + self.module = None + def __getattr__(self, name): + if self.module is None: + self.module = __import__(self.module_name) + return getattr(self.module, name) + +zbarlight = LazyImport("zbarlight") + +import warnings + +class UnknownSymbologieError(Exception): + pass + + +def scan_codes(code_types, img): + """ + Get *code_type* codes from a maix_image. + *code_type* can be any of zbar supported code type [#zbar_symbologies]_: + - **EAN/UPC**: EAN-13 (`ean13`), UPC-A (`upca`), EAN-8 (`ean8`) and UPC-E (`upce`) + - **Linear barcode**: Code 128 (`code128`), Code 93 (`code93`), Code 39 (`code39`), Interleaved 2 of 5 (`i25`), + DataBar (`databar`) and DataBar Expanded (`databar-exp`) + - **2D**: QR Code (`qrcode`) + - **Undocumented**: `ean5`, `ean2`, `composite`, `isbn13`, `isbn10`, `codabar`, `pdf417` + .. [#zbar_symbologies] http://zbar.sourceforge.net/iphone/userguide/symbologies.html + Args: + code_types (list(str)): Code type(s) to search (see ``zbarlight.Symbologies`` for supported values). + image (maix_image): Image to scan + returns: + A list of *code_type* code values or None + """ + if isinstance(code_types, str): + code_types = [code_types] + warnings.warn( + 'Using a str for code_types is deprecated, please use a list of str instead', + DeprecationWarning, + ) + + from zbarlight import _zbarlight + + # Translate symbologies + symbologies = [ + _zbarlight.Symbologies.get(code_type.upper()) + for code_type in set(code_types) + ] + + # Check that all symbologies are known + if None in symbologies: + bad_code_types = [code_type for code_type in code_types if code_type.upper() not in _zbarlight.Symbologies] + raise UnknownSymbologieError('Unknown Symbologies: %s' % bad_code_types) + + # Convert the image to be used by c-extension + from maix import image + if not isinstance(img, image.Image): + raise RuntimeError('Bad or unknown image type') + + return _zbarlight.zbar_code_scanner(symbologies, img.copy().convert('L').tobytes(), img.width, img.height) + +if __name__ == '__main__': + from maix import camera, display, zbar + while True: + tmp = camera.capture() + result = zbar.scan_codes(["qrcode", "code39"], tmp) + print(str(result)) + display.show(tmp.draw_string(10, 10, str(result), 2.0, (255, 0, 0))) diff --git a/setup.py b/setup.py index be49e42..0a8460f 100644 --- a/setup.py +++ b/setup.py @@ -32,7 +32,7 @@ setup( name='maixpy3', - version='0.3.5', + version='0.4.0', license='MIT', author='Sipeed', author_email="support@sipeed.com", @@ -55,6 +55,7 @@ include_package_data=True, package_data = { '': ['*.so'], + '': ['*.h'], }, data_files=data_files, ) diff --git a/tests/maix_v831/usage/usage_v831_adc.py b/tests/maix_v831/usage/usage_v831_adc.py new file mode 100644 index 0000000..a03771a --- /dev/null +++ b/tests/maix_v831/usage/usage_v831_adc.py @@ -0,0 +1,32 @@ + +class v83x_ADC(): + def __init__(self, addr=b"0x05070080") -> None: + self.addr = addr + self.path = "/sys/class/sunxi_dump/dump" + self.file = open(self.path, "wb+") + self.last = self.value() + def __del__(self): + try: + if self.file: + self.file.close() + del self.file + except Exception as e: + pass + def value(self): + self.file.write(b"0x05070080") + self.file.seek(0) + return int(self.file.read()[:-1], 16) + +if __name__ == '__main__': + import time + from maix import display, image + v831_adc0 = v83x_ADC() + while True: + time.sleep(0.1) + tmp = image.Image().new((240, 240), (0x2c, 0x3e, 0x50), "RGB") + val = v831_adc0.value() + print(val) + img = image.Image().open('/home/res/logo.png') + tmp.draw_image(img, 50, 40, alpha=1).draw_string(20, 200, "adc0 : " + str(val), 1, (0xbd, 0xc3, 0xc7)) + display.show(tmp) + diff --git a/tests/maix_v831/usage/usage_v831_watchdog.py b/tests/maix_v831/usage/usage_v831_watchdog.py new file mode 100644 index 0000000..0207ae6 --- /dev/null +++ b/tests/maix_v831/usage/usage_v831_watchdog.py @@ -0,0 +1,168 @@ +# Copyright 2014 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Provides Linux kernel Watchdog interface. +https://www.kernel.org/doc/Documentation/watchdog/watchdog-api.txt +""" + +import array +import fcntl +import struct + +IO_WRITE = 0x40000000 +IO_READ = 0x80000000 +IO_READ_WRITE = 0xC0000000 +IO_SIZE_INT = 0x00040000 +IO_SIZE_40 = 0x00280000 +IO_TYPE_WATCHDOG = ord('W') << 8 + +WDR_INT = IO_READ | IO_SIZE_INT | IO_TYPE_WATCHDOG +WDR_40 = IO_READ | IO_SIZE_40 | IO_TYPE_WATCHDOG +WDWR_INT = IO_READ_WRITE | IO_SIZE_INT | IO_TYPE_WATCHDOG + +WDIOC_GETSUPPORT = 0 | WDR_40 +WDIOC_GETSTATUS = 1 | WDR_INT +WDIOC_GETBOOTSTATUS = 2 | WDR_INT +WDIOC_GETTEMP = 3 | WDR_INT +WDIOC_SETOPTIONS = 4 | WDWR_INT +WDIOC_KEEPALIVE = 5 | WDR_INT +WDIOC_SETTIMEOUT = 6 | WDWR_INT +WDIOC_GETTIMEOUT = 7 | WDR_INT +WDIOC_SETPRETIMEOUT = 8 | WDWR_INT +WDIOC_GETPRETIMEOUT = 9 | WDR_INT +WDIOC_GETTIMELEFT = 10 | WDR_INT + +WDIOF_OVERHEAT = 0x0001 +WDIOF_FANFAULT = 0x0002 +WDIOF_EXTERN1 = 0x0004 +WDIOF_EXTERN2 = 0x0008 +WDIOF_POWERUNDER = 0x0010 +WDIOF_CARDRESET = 0x0020 +WDIOF_POWEROVER = 0x0040 +WDIOF_SETTIMEOUT = 0x0080 +WDIOF_MAGICCLOSE = 0x0100 +WDIOF_PRETIMEOUT = 0x0200 +WDIOF_ALARMONLY = 0x0400 +WDIOF_KEEPALIVEPING = 0x8000 + +WDIOS_DISABLECARD = 0x0001 +WDIOS_ENABLECARD = 0x0002 +WDIOS_TEMPPANIC = 0x0004 + +WATCHDOG_DEVICE = '/dev/watchdog' +WATCHDOG_STOP = 'V' +WATCHDOG_START = 'S' + + +class Watchdog: + """Linux Kernel watchdog class.""" + + def __init__(self, stop=True): + super(Watchdog, self).__init__() + self.fd = open(WATCHDOG_DEVICE, 'w') + self.stop = stop + if stop: + self.Stop() + + def __del__(self) -> None: # for v831 + try: + self.fd.close() + import os + os.system("echo V > " + WATCHDOG_DEVICE) + except Exception as e: + pass + + def Stop(self): + self.fd.write(WATCHDOG_STOP) + + def Start(self): + self.stop = False + self.fd.write(WATCHDOG_START) + + def _IoctlInt(self, cmd, write_value=0): + """Writes and reads integer. + Args: + cmd: ioctl command contains read/write flag. + write_value: the 32bit value to write. + Returns: + 32bit integer from ioctl mutable buffer. + """ + buf = array.array('I', [write_value]) + start_temporary = self.stop + # For stopped watchdog, we need to start it temporary to set a value as + # some hardware accepts commands only when watchdog is running. + if start_temporary: + self.Start() + fcntl.ioctl(self.fd, cmd, buf, True) + if start_temporary: + self.Stop() + return buf[0] + + def GetStatus(self): + """Gets watchdog status flags. + The status and boot status are used to fetch the current status, and the + status at the last reboot. Enabled options are described in GetSupport() + return value. Status/option WDIOF_flags: + WDIOF_OVERHEAT: CPU overheat triggered. + WDIOF_FANFAULT: Fan failed triggered. + WDIOF_EXTERN1/2: External source triggered. + WDIOF_POWEROVER: Overvoltage detected. + WDIOF_KEEPALIVEPING: Got a keep alive ping after last query. + WDIOF_TIMEOUT: Reset due to timeout. + """ + return self._IoctlInt(WDIOC_GETSTATUS) + + def GetBootStatus(self): + return self._IoctlInt(WDIOC_GETBOOTSTATUS) + + def GetTemp(self): + return self._IoctlInt(WDIOC_GETTEMP) + + def SetOptions(self, options): + return self._IoctlInt(WDIOC_SETOPTIONS, options) + + def KeepAlive(self): + if self.stop: + self.Start() + return self._IoctlInt(WDIOC_KEEPALIVE) + + def SetTimeout(self, timeout): + timeout = int(timeout) + if timeout <= 0: + raise ValueError('timeout <= 0') + return self._IoctlInt(WDIOC_SETTIMEOUT, timeout) + + def GetTimeout(self): + return self._IoctlInt(WDIOC_GETTIMEOUT) + + def SetPreTimeout(self, timeout): + timeout = int(timeout) + if timeout <= 0 or timeout > 16: + raise ValueError('0 < timeout <= 16') + return self._IoctlInt(WDIOC_SETPRETIMEOUT, timeout) + + def GetPreTimeout(self): + return self._IoctlInt(WDIOC_GETPRETIMEOUT) + + def GetTimeLeft(self): + return self._IoctlInt(WDIOC_GETTIMELEFT) + +if __name__ == '__main__': + import wtd + t = wtd.Watchdog() + t.SetTimeout(1) + import time + t.KeepAlive() + time.sleep(0.5) + t.KeepAlive() + time.sleep(0.5) + t.__del__() + del t + t = wtd.Watchdog() + del t + t = wtd.Watchdog() + t.SetTimeout(2) + time.sleep(2) + del t +