Skip to content

Commit

Permalink
Improved pybind_mkdoc and added device bindings
Browse files Browse the repository at this point in the history
  • Loading branch information
themarpe committed Apr 26, 2021
1 parent 59ed8ab commit 93214ad
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 40 deletions.
22 changes: 11 additions & 11 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,26 @@ jobs:

# Job which builds docstrings for the rest of the wheel builds
build-docstrings:
runs-on: macos-latest
runs-on: ubuntu-latest
steps:
- name: Cache .hunter folder
uses: actions/cache@v2
with:
path: ~/.hunter
key: hunter-macos-latest
key: hunter-ubuntu-latest
- name: List .hunter cache directory
run: ls -a -l ~/.hunter/_Base/ || true
- uses: actions/checkout@v2
with:
submodules: 'recursive'
- name: Set up Python
uses: actions/setup-python@v2
with:
with:
python-version: 3.8
- name: Install dependencies
run: |
python -m pip install --upgrade pip
brew install libusb
sudo apt install libusb-1.0-0-dev
python -m pip install git+git://github.com/luxonis/pybind11_mkdoc.git@master
- name: Configure project
run: cmake -S . -B build -DDEPTHAI_PYTHON_FORCE_DOCSTRINGS=ON -DDEPTHAI_PYTHON_DOCSTRINGS_OUTPUT="$PWD/docstrings/depthai_python_docstring.hpp"
Expand All @@ -62,7 +62,7 @@ jobs:
build-linux-armhf:
needs: build-docstrings
runs-on: luxonis-armhf
container:
container:
image: registry.gitlab.com/luxonis/depthai-crosscompile/debian-buster
# Mount local hunter cache directory, instead of transfering to Github and back
volumes:
Expand All @@ -73,7 +73,7 @@ jobs:
- uses: actions/checkout@v2
with:
submodules: 'recursive'

- uses: actions/download-artifact@v2
with:
name: 'docstrings'
Expand Down Expand Up @@ -129,7 +129,7 @@ jobs:

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
with:
python-version: ${{ matrix.python-version }}
architecture: ${{ matrix.python-architecture }}
- name: Append build hash if not a tagged commit
Expand Down Expand Up @@ -182,7 +182,7 @@ jobs:

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
with:
python-version: ${{ matrix.python-version }}
- name: Append build hash if not a tagged commit
if: startsWith(github.ref, 'refs/tags/v') != true
Expand All @@ -197,7 +197,7 @@ jobs:
- name: Building wheels
run: python -m pip wheel . -w ./wheelhouse/ --verbose
- name: Auditing wheels
run: ci/repair-whl-macos.sh `pwd`/wheelhouse/* `pwd`/wheelhouse/audited
run: ci/repair-whl-macos.sh `pwd`/wheelhouse/* `pwd`/wheelhouse/audited
- name: Archive wheel artifacts
uses: actions/upload-artifact@v2
with:
Expand Down Expand Up @@ -336,12 +336,12 @@ jobs:
if: startsWith(github.ref, 'refs/tags/v')
needs: [build-linux-armhf, build-windows-x86_64, build-macos-x86_64, build-linux-x86_64, build-linux-arm64]
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
with:
submodules: 'recursive'

- name: Get tag version
id: tag
uses: battila7/get-version-action@v2
Expand Down
27 changes: 15 additions & 12 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,13 @@ endif()
hunter_add_package(pybind11)

# Disable LTO if MINGW compiler
if(MINGW)
if(MINGW)
set(PYBIND11_LTO_CXX_FLAGS "" CACHE STRING "" FORCE)
endif()
find_package(pybind11 CONFIG REQUIRED)

# Add files for python module
pybind11_add_module(${TARGET_NAME}
pybind11_add_module(${TARGET_NAME}
src/py_bindings.cpp
src/XLinkConnectionBindings.cpp
src/DeviceBindings.cpp
Expand All @@ -104,8 +104,8 @@ endif()

if(DEPTHAI_PYTHON_DOCSTRINGS_OUTPUT)
# If output is specified set both input and output to same the path
set(docstring_input_path ${DEPTHAI_PYTHON_DOCSTRINGS_OUTPUT})
set(docstring_output_path ${DEPTHAI_PYTHON_DOCSTRINGS_OUTPUT})
set(docstring_input_path ${DEPTHAI_PYTHON_DOCSTRINGS_OUTPUT})
set(docstring_output_path ${DEPTHAI_PYTHON_DOCSTRINGS_OUTPUT})
else()
# If input docstrings explicitly specified, use those and disable building
if(DEPTHAI_PYTHON_DOCSTRINGS_INPUT)
Expand All @@ -114,11 +114,11 @@ else()
set(DEPTHAI_PYTHON_BUILD_DOCSTRINGS OFF CACHE BOOL "Generate docstrings from header files if module 'pybind11_mkdoc' available" FORCE)
else()
# Otherwise set default location as input
set(docstring_input_path ${DEFAULT_DOCSTRINGS_OUTPUT})
set(docstring_input_path ${DEFAULT_DOCSTRINGS_OUTPUT})
endif()

# Set default output location
set(docstring_output_path ${DEFAULT_DOCSTRINGS_OUTPUT})
set(docstring_output_path ${DEFAULT_DOCSTRINGS_OUTPUT})
endif()

if(DEPTHAI_PYTHON_BUILD_DOCSTRINGS)
Expand All @@ -130,11 +130,14 @@ configure_file(cmake/docstring.hpp.in ${DOCSTRINGS_INCLUDE_PLACEHOLDER_PATH})
# Add target to generate docstrings
if (DEPTHAI_PYTHON_BUILD_DOCSTRINGS)
include(pybind11-mkdoc)

# Check if pybind11_mkdoc available and create target
target_pybind11_mkdoc_setup(${docstring_output_path} depthai::core ${DEPTHAI_PYTHON_FORCE_DOCSTRINGS})

if(NOT TARGET pybind11_mkdoc)

if(TARGET pybind11_mkdoc)
# Add dependency to mkdoc target (makes sure that mkdoc is executed, and docstrings available)
add_dependencies(${TARGET_NAME} pybind11_mkdoc)
else()
# Generate default docstrings to OUTPUT path
configure_file(cmake/default_docstring.hpp.in ${docstring_output_path} COPYONLY)
endif()
Expand All @@ -147,16 +150,16 @@ endif()
target_include_directories(${TARGET_NAME} PRIVATE src ${DOCSTRINGS_INCLUDE_PLACEHOLDER_DIR})

# Link with libraries
target_link_libraries(${TARGET_NAME}
PUBLIC
target_link_libraries(${TARGET_NAME}
PUBLIC
# pybind11
pybind11::pybind11
depthai::core # Use non-opencv target as we use opencv-python in bindings
hedley
)

# Add default commit hash ('dev') if not build by CI
if(NOT DEFINED ENV{CI} AND NOT DEPTHAI_PYTHON_COMMIT_HASH)
if(NOT DEFINED ENV{CI} AND NOT DEPTHAI_PYTHON_COMMIT_HASH)
set(DEPTHAI_PYTHON_COMMIT_HASH "dev")
endif()

Expand Down
3 changes: 0 additions & 3 deletions cmake/pybind11-mkdoc.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,4 @@ function(pybind11_mkdoc_setup_internal target output_path mkdoc_headers enforce)
# Force target build
file(TOUCH_NOCREATE ${mkdoc_headers})

# Add dependency to mkdoc target (makes sure that mkdoc is executed, and docstrings available)
add_dependencies(${target} ${PYBIND11_MKDOC_TARGET_NAME})

endfunction()
77 changes: 64 additions & 13 deletions src/DeviceBindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ static std::unique_ptr<dai::Device> deviceConstructorHelper(const dai::Pipeline&
bool found;
dai::DeviceInfo deviceInfo = {};
do {
{
{
// releases python GIL
py::gil_scoped_release release;
std::tie(found, deviceInfo) = dai::Device::getFirstAvailableDevice();
// Check if found
if(found){
break;
} else {
// block for 100ms
// block for 100ms
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
Expand All @@ -45,7 +45,46 @@ static std::unique_ptr<dai::Device> deviceConstructorHelper(const dai::Pipeline&
return std::unique_ptr<dai::Device>(new dai::Device(pipeline, deviceInfo, pathToCmd));
}
return nullptr;
}
}

// Searches for available devices (as Device constructor)
// but pooling, to check for python interrupts, and releases GIL in between
static std::unique_ptr<dai::Device> deviceConstructorHelper(dai::OpenVINO::Version version, const std::string& pathToCmd = "", bool usb2Mode = false){
auto startTime = std::chrono::steady_clock::now();
bool found;
dai::DeviceInfo deviceInfo = {};
do {
{
// releases python GIL
py::gil_scoped_release release;
std::tie(found, deviceInfo) = dai::Device::getFirstAvailableDevice();
// Check if found
if(found){
break;
} else {
// block for 100ms
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
// reacquires python GIL for PyErr_CheckSignals call
// check if interrupt triggered in between
if (PyErr_CheckSignals() != 0) throw py::error_already_set();
} while(std::chrono::steady_clock::now() - startTime < dai::Device::DEFAULT_SEARCH_TIME);

// If neither UNBOOTED nor BOOTLOADER were found (after 'DEFAULT_SEARCH_TIME'), try BOOTED
if(!found) std::tie(found, deviceInfo) = dai::XLinkConnection::getFirstDevice(X_LINK_BOOTED);

// if no devices found, then throw
if(!found) throw std::runtime_error("No available devices");

// Check if pathToCmd supplied
if(pathToCmd.empty()){
return std::unique_ptr<dai::Device>(new dai::Device(version, deviceInfo, usb2Mode));
} else {
return std::unique_ptr<dai::Device>(new dai::Device(version, deviceInfo, pathToCmd));
}
return nullptr;
}


std::vector<std::string> deviceGetQueueEventsHelper(dai::Device& d, const std::vector<std::string>& queueNames, std::size_t maxNumEvents, std::chrono::microseconds timeout){
Expand All @@ -55,7 +94,7 @@ std::vector<std::string> deviceGetQueueEventsHelper(dai::Device& d, const std::v
bool unlimitedTimeout = timeout < microseconds(0);
auto startTime = steady_clock::now();
do {
{
{
// releases python GIL
py::gil_scoped_release release;
// block for 100ms
Expand All @@ -66,8 +105,8 @@ std::vector<std::string> deviceGetQueueEventsHelper(dai::Device& d, const std::v
// check if interrupt triggered in between
if (PyErr_CheckSignals() != 0) throw py::error_already_set();
} while(unlimitedTimeout || steady_clock::now() - startTime < timeout);
return std::vector<std::string>();

return std::vector<std::string>();
}


Expand All @@ -91,16 +130,16 @@ void DeviceBindings::bind(pybind11::module& m){
.def_static("getAllAvailableDevices", &Device::getAllAvailableDevices, DOC(dai, Device, getAllAvailableDevices))
.def_static("getEmbeddedDeviceBinary", &Device::getEmbeddedDeviceBinary, py::arg("usb2Mode"), py::arg("version") = Pipeline::DEFAULT_OPENVINO_VERSION, DOC(dai, Device, getEmbeddedDeviceBinary))
.def_static("getDeviceByMxId", &Device::getDeviceByMxId, py::arg("mxId"), DOC(dai, Device, getDeviceByMxId))

// methods
.def(py::init([](const Pipeline& pipeline){ return deviceConstructorHelper(pipeline); }), py::arg("pipeline"), DOC(dai, Device, Device))
.def(py::init([](const Pipeline& pipeline, bool usb2Mode){
.def(py::init([](const Pipeline& pipeline, bool usb2Mode){
// Blocking constructor
return deviceConstructorHelper(pipeline, std::string(""), usb2Mode);
return deviceConstructorHelper(pipeline, std::string(""), usb2Mode);
}), py::arg("pipeline"), py::arg("usb2Mode"), DOC(dai, Device, Device, 2))
.def(py::init([](const Pipeline& pipeline, const std::string& pathToCmd){
// Blocking constructor
return deviceConstructorHelper(pipeline, pathToCmd);
return deviceConstructorHelper(pipeline, pathToCmd);
}), py::arg("pipeline"), py::arg("pathToCmd"), DOC(dai, Device, Device, 3))
.def(py::init([](const Pipeline& pipeline, const DeviceInfo& deviceInfo, bool usb2Mode){
// Non blocking constructor
Expand All @@ -111,8 +150,19 @@ void DeviceBindings::bind(pybind11::module& m){
return std::unique_ptr<Device>(new Device(pipeline, deviceInfo, pathToCmd));
}), py::arg("pipeline"), py::arg("deviceDesc"), py::arg("pathToCmd"), DOC(dai, Device, Device, 5))

.def(py::init([](OpenVINO::Version version){ return deviceConstructorHelper(version); }), py::arg("version") = Pipeline::DEFAULT_OPENVINO_VERSION, DOC(dai, Device, Device, 6))
.def(py::init([](OpenVINO::Version version, bool usb2Mode){
// Blocking constructor
return deviceConstructorHelper(version, std::string(""), usb2Mode);
}), py::arg("version"), py::arg("usb2Mode"), DOC(dai, Device, Device, 7))
.def(py::init([](OpenVINO::Version version, const std::string& pathToCmd){
// Blocking constructor
return deviceConstructorHelper(version, pathToCmd);
}), py::arg("version"), py::arg("pathToCmd"), DOC(dai, Device, Device, 8))

.def("isPipelineRunning", &Device::isPipelineRunning, DOC(dai, Device, isPipelineRunning))
.def("startPipeline", &Device::startPipeline, DOC(dai, Device, startPipeline))
.def("startPipeline", py::overload_cast<>(&Device::startPipeline), DOC(dai, Device, startPipeline))
.def("startPipeline", py::overload_cast<const Pipeline&>(&Device::startPipeline), DOC(dai, Device, startPipeline, 2))

.def("getOutputQueue", static_cast<std::shared_ptr<DataOutputQueue>(Device::*)(const std::string&)>(&Device::getOutputQueue), py::arg("name"), DOC(dai, Device, getOutputQueue))
.def("getOutputQueue", static_cast<std::shared_ptr<DataOutputQueue>(Device::*)(const std::string&, unsigned int, bool)>(&Device::getOutputQueue), py::arg("name"), py::arg("maxSize"), py::arg("blocking") = true, DOC(dai, Device, getOutputQueue, 2))
Expand All @@ -137,13 +187,13 @@ void DeviceBindings::bind(pybind11::module& m){
if(events.empty()) return std::string("");
return events[0];
}, py::arg("queueNames"), py::arg("timeout") = std::chrono::microseconds(-1), DOC(dai, Device, getQueueEvent))

.def("getQueueEvent", [](Device& d, std::string queueName, std::chrono::microseconds timeout) {
auto events = deviceGetQueueEventsHelper(d, std::vector<std::string>{queueName}, std::numeric_limits<std::size_t>::max(), timeout);
if(events.empty()) return std::string("");
return events[0];
}, py::arg("queueName"), py::arg("timeout") = std::chrono::microseconds(-1), DOC(dai, Device, getQueueEvent, 3))

.def("getQueueEvent", [](Device& d, std::chrono::microseconds timeout) {
auto events = deviceGetQueueEventsHelper(d, d.getOutputQueueNames(), std::numeric_limits<std::size_t>::max(), timeout);
if(events.empty()) return std::string("");
Expand All @@ -155,6 +205,7 @@ void DeviceBindings::bind(pybind11::module& m){
.def("getLogLevel", &Device::getLogLevel, DOC(dai, Device, getLogLevel))
.def("setSystemInformationLoggingRate", &Device::setSystemInformationLoggingRate, py::arg("rateHz"), DOC(dai, Device, setSystemInformationLoggingRate))
.def("getSystemInformationLoggingRate", &Device::getSystemInformationLoggingRate, DOC(dai, Device, getSystemInformationLoggingRate))
.def("getConnectedCameras", &Device::getConnectedCameras, DOC(dai, Device, getConnectedCameras))
.def("getDdrMemoryUsage", &Device::getDdrMemoryUsage, DOC(dai, Device, getDdrMemoryUsage))
.def("getCmxMemoryUsage", &Device::getCmxMemoryUsage, DOC(dai, Device, getCmxMemoryUsage))
.def("getLeonCssHeapUsage", &Device::getLeonCssHeapUsage, DOC(dai, Device, getLeonCssHeapUsage))
Expand Down

0 comments on commit 93214ad

Please sign in to comment.