diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e0984ef20..40d28946c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,10 @@ HunterGate( LOCAL # Local config for dependencies ) +# TODO(themarpe) - might be more applicable to set as a target property +# Additional function information for 'backward' stacktrace +set(CMAKE_ENABLE_EXPORTS ON) + # Move binary dir if windows, to shorten the path if(WIN32) set(HUNTER_BINARY_DIR "${HUNTER_GATE_ROOT}/_bin" CACHE STRING "Hunter binary directory") @@ -181,6 +185,7 @@ add_library(${TARGET_CORE_NAME} src/pipeline/datatype/FeatureTrackerConfig.cpp src/utility/Initialization.cpp src/utility/Resources.cpp + src/utility/Platform.cpp src/xlink/XLinkConnection.cpp src/xlink/XLinkStream.cpp src/openvino/OpenVINO.cpp @@ -366,6 +371,8 @@ target_link_libraries(${TARGET_CORE_NAME} FP16::fp16 archive_static spdlog::spdlog + ZLIB::zlib + Backward::Backward ) # Add compile definitions diff --git a/cmake/Depthai/DepthaiBootloaderConfig.cmake b/cmake/Depthai/DepthaiBootloaderConfig.cmake index 780c2313f3..70e4f91e9b 100644 --- a/cmake/Depthai/DepthaiBootloaderConfig.cmake +++ b/cmake/Depthai/DepthaiBootloaderConfig.cmake @@ -1,5 +1,7 @@ # Maturity level "snapshot" / "release" -set(DEPTHAI_BOOTLOADER_MATURITY "release") +#set(DEPTHAI_BOOTLOADER_MATURITY "release") +set(DEPTHAI_BOOTLOADER_MATURITY "snapshot") # "version if applicable" -set(DEPTHAI_BOOTLOADER_VERSION "0.0.12") +#set(DEPTHAI_BOOTLOADER_VERSION "0.0.12") +set(DEPTHAI_BOOTLOADER_VERSION "870bec4ca0ef026ddb9c490a38dfe9d097614554") diff --git a/cmake/Depthai/DepthaiDeviceSideConfig.cmake b/cmake/Depthai/DepthaiDeviceSideConfig.cmake index 47bae7a119..b86aa6d3ea 100644 --- a/cmake/Depthai/DepthaiDeviceSideConfig.cmake +++ b/cmake/Depthai/DepthaiDeviceSideConfig.cmake @@ -2,7 +2,7 @@ set(DEPTHAI_DEVICE_SIDE_MATURITY "snapshot") # "full commit hash of device side binary" -set(DEPTHAI_DEVICE_SIDE_COMMIT "1928f3407397272cc9736aedcca667c6c7419057") +set(DEPTHAI_DEVICE_SIDE_COMMIT "6793bd877ae334ac468b884ad1c9db22667da5b8") # "version if applicable" set(DEPTHAI_DEVICE_SIDE_VERSION "") diff --git a/cmake/Hunter/config.cmake b/cmake/Hunter/config.cmake index af32b675d0..a2cf20dd25 100644 --- a/cmake/Hunter/config.cmake +++ b/cmake/Hunter/config.cmake @@ -7,9 +7,9 @@ hunter_config( hunter_config( XLink - VERSION "luxonis-2021.3-master" - URL "https://github.com/luxonis/XLink/archive/2c6cdb857f3d21088b34ec172a0ea8df16528d00.tar.gz" - SHA1 "736da6528515d9c969008e2334f1387428f91a3b" + VERSION "luxonis-2021.3-develop" + URL "https://github.com/luxonis/XLink/archive/4c149080d22c35a17ce285f5bca99f2b2fe05e46.tar.gz" + SHA1 "64b0a8bfeb1a91f909df88ea8b1d0b17885b92ff" ) hunter_config( @@ -74,4 +74,14 @@ hunter_config( CMAKE_ARGS FP16_BUILD_BENCHMARKS=OFF FP16_BUILD_TESTS=OFF +) + +# Backward - Stacktrace printer +hunter_config( + Backward + VERSION "1.6" + URL "https://github.com/bombela/backward-cpp/archive/refs/tags/v1.6.tar.gz" + SHA1 "4ecb711eabfd15bc88ff9dd9342907fc5da46b62" + CMAKE_ARGS + BACKWARD_TESTS=OFF ) \ No newline at end of file diff --git a/cmake/depthaiDependencies.cmake b/cmake/depthaiDependencies.cmake index 3a9ba9d184..4e303cbba5 100644 --- a/cmake/depthaiDependencies.cmake +++ b/cmake/depthaiDependencies.cmake @@ -12,11 +12,13 @@ else() hunter_add_package(FP16) hunter_add_package(libarchive-luxonis) hunter_add_package(spdlog) + hunter_add_package(ZLIB) + hunter_add_package(Backward) endif() # If library was build as static, find all dependencies if(NOT CONFIG_MODE OR (CONFIG_MODE AND NOT depthai_SHARED_LIBS)) - + # BZip2 (for bspatch) find_package(BZip2 ${_QUIET} CONFIG REQUIRED) @@ -26,10 +28,15 @@ if(NOT CONFIG_MODE OR (CONFIG_MODE AND NOT depthai_SHARED_LIBS)) # libarchive for firmware packages find_package(archive_static ${_QUIET} CONFIG REQUIRED) find_package(lzma ${_QUIET} CONFIG REQUIRED) + # ZLIB for compressing Apps + find_package(ZLIB CONFIG REQUIRED) # spdlog for library and device logging find_package(spdlog ${_QUIET} CONFIG REQUIRED) - + + # Backward + find_package(Backward ${_QUIET} CONFIG REQUIRED) + endif() # Add threads (c++) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index c977e61e95..e0300553b0 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -268,7 +268,7 @@ target_compile_definitions(object_tracker_video PRIVATE BLOB_PATH="${person_dete dai_add_example(queue_add_callback src/queue_add_callback.cpp ON) dai_add_example(bootloader_version src/bootloader_version.cpp ON) -#dai_add_example(flash_bootloader src/flash_bootloader.cpp OFF) +dai_add_example(flash_bootloader src/flash_bootloader.cpp OFF) dai_add_example(edge_detector src/edge_detector.cpp ON) dai_add_example(feature_tracker src/feature_tracker.cpp ON) @@ -289,4 +289,7 @@ dai_add_example(image_manip_tiling src/image_manip_tiling.cpp ON) target_compile_definitions(calibration_flash PRIVATE CALIB_PATH="${calib_v6}") target_compile_definitions(calibration_flash_version5 PRIVATE CALIB_PATH="${calib_v5}" BOARD_PATH="${device_config}") -target_compile_definitions(calibration_load PRIVATE CALIB_PATH="${calib_v6}") \ No newline at end of file +target_compile_definitions(calibration_load PRIVATE CALIB_PATH="${calib_v6}") + +# Bootloader configuration example +dai_add_example(bootloader_config src/bootloader_config.cpp OFF) diff --git a/examples/src/bootloader_config.cpp b/examples/src/bootloader_config.cpp new file mode 100644 index 0000000000..b2eb9564a3 --- /dev/null +++ b/examples/src/bootloader_config.cpp @@ -0,0 +1,68 @@ +#include "depthai/depthai.hpp" + +int main(int argc, char** argv) { + // Options + bool usage = false, read = true, clear = false; + std::string path = ""; + if(argc >= 2) { + std::string op = argv[1]; + if(op == "read") { + read = true; + } else if(op == "flash") { + read = false; + if(argc >= 3) { + path = argv[2]; + } + } else if(op == "clear") { + clear = true; + read = false; + } else if(op == "clear") { + clear = true; + read = false; + } else { + usage = true; + } + } else { + usage = true; + } + if(usage) { + std::cout << "Usage: " << argv[0] << " [read/flash/clear] [flash: path/to/config/json]" << std::endl; + return -1; + } + + // DeviceBootloader configuration + bool res = false; + dai::DeviceInfo info; + std::tie(res, info) = dai::DeviceBootloader::getFirstAvailableDevice(); + + if(res) { + std::cout << "Found device with name: " << info.desc.name << std::endl; + dai::DeviceBootloader bl(info); + + if(read) { + std::cout << "Current flashed configuration\n" << bl.readConfigData().dump(4) << std::endl; + } else { + bool success; + std::string error; + if(clear) { + std::tie(success, error) = bl.flashConfigClear(); + } else { + if(path.empty()) { + std::tie(success, error) = bl.flashConfig(dai::DeviceBootloader::Config{}); + } else { + std::tie(success, error) = bl.flashConfigFile(path); + } + } + if(success) { + std::cout << "Successfully flashed bootloader configuration\n"; + } else { + std::cout << "Error flashing bootloader configuration: " << error; + } + } + + } else { + std::cout << "No devices found" << std::endl; + } + + return 0; +} \ No newline at end of file diff --git a/examples/src/flash_bootloader.cpp b/examples/src/flash_bootloader.cpp new file mode 100644 index 0000000000..001145797e --- /dev/null +++ b/examples/src/flash_bootloader.cpp @@ -0,0 +1,68 @@ +#include +#include + +#include "depthai/depthai.hpp" + +int main(int argc, char** argv) { + using namespace std::chrono; + + dai::DeviceBootloader::Type blType = dai::DeviceBootloader::Type::AUTO; + if(argc > 1) { + std::string blTypeStr(argv[1]); + if(blTypeStr == "usb") { + blType = dai::DeviceBootloader::Type::USB; + } else if(blTypeStr == "network") { + blType = dai::DeviceBootloader::Type::NETWORK; + } else { + std::cout << "Specify either 'usb' or 'network' bootloader type\n"; + return 0; + } + } + + std::cout << "Warning! Flashing bootloader can potentially soft brick your device and should be done with caution." << std::endl; + std::cout << "Do not unplug your device while the bootloader is flashing." << std::endl; + std::cout << "Type 'y' and press enter to proceed, otherwise exits: "; + if(std::cin.get() != 'y') { + std::cout << "Prompt declined, exiting..." << std::endl; + return -1; + } + + bool found = false; + dai::DeviceInfo info; + std::tie(found, info) = dai::DeviceBootloader::getFirstAvailableDevice(); + if(!found) { + std::cout << "No device found to flash. Exiting." << std::endl; + return -1; + } + + // Open DeviceBootloader and allow flashing bootloader + std::cout << "Booting latest bootloader first, will take a tad longer..." << std::endl; + dai::DeviceBootloader bl(info, true); + auto currentBlType = bl.getType(); + + // Check if bootloader type is the same + if(blType != dai::DeviceBootloader::Type::AUTO && currentBlType != blType) { + std::cout << "Are you sure you want to flash '" << blType << "' bootloader over current '" << currentBlType << "' bootloader?" << std::endl; + std::cout << "Type 'y' and press enter to proceed, otherwise exits: "; + std::cin.ignore(); + if(std::cin.get() != 'y') { + std::cout << "Prompt declined, exiting..." << std::endl; + return -1; + } + } + + // Create a progress callback lambda + auto progress = [](float p) { std::cout << "Flashing Progress..." << p * 100 << "%" << std::endl; }; + + std::cout << "Flashing " << currentBlType << " bootloader..." << std::endl; + auto t1 = steady_clock::now(); + bool success = false; + std::string message; + std::tie(success, message) = bl.flashBootloader(dai::DeviceBootloader::Memory::FLASH, currentBlType, progress); + if(success) { + std::cout << "Flashing successful. Took " << duration_cast(steady_clock::now() - t1).count() << "ms" << std::endl; + } else { + std::cout << "Flashing failed: " << message << std::endl; + } + return 0; +} \ No newline at end of file diff --git a/examples/src/rgb_preview.cpp b/examples/src/rgb_preview.cpp index 911f7d1d95..be74991a03 100644 --- a/examples/src/rgb_preview.cpp +++ b/examples/src/rgb_preview.cpp @@ -25,11 +25,10 @@ int main() { camRgb->preview.link(xoutRgb->input); // Connect to device and start pipeline - dai::Device device(pipeline); + dai::Device device(pipeline, dai::UsbSpeed::SUPER); cout << "Connected cameras: "; for(const auto& cam : device.getConnectedCameras()) { - cout << static_cast(cam) << " "; cout << cam << " "; } cout << endl; @@ -48,7 +47,7 @@ int main() { int key = cv::waitKey(1); if(key == 'q' || key == 'Q') { - return 0; + break; } } return 0; diff --git a/include/depthai/common/CameraBoardSocket.hpp b/include/depthai/common/CameraBoardSocket.hpp index c9871c9185..9a2586c6b8 100644 --- a/include/depthai/common/CameraBoardSocket.hpp +++ b/include/depthai/common/CameraBoardSocket.hpp @@ -4,24 +4,21 @@ #include "depthai-shared/common/CameraBoardSocket.hpp" -namespace dai { - -inline std::ostream& operator<<(std::ostream& out, const CameraBoardSocket& socket) { +// Global namespace +inline std::ostream& operator<<(std::ostream& out, const dai::CameraBoardSocket& socket) { switch(socket) { - case CameraBoardSocket::AUTO: + case dai::CameraBoardSocket::AUTO: out << "AUTO"; break; - case CameraBoardSocket::RGB: + case dai::CameraBoardSocket::RGB: out << "RGB"; break; - case CameraBoardSocket::LEFT: + case dai::CameraBoardSocket::LEFT: out << "LEFT"; break; - case CameraBoardSocket::RIGHT: + case dai::CameraBoardSocket::RIGHT: out << "RIGHT"; break; } return out; } - -} // namespace dai \ No newline at end of file diff --git a/include/depthai/common/UsbSpeed.hpp b/include/depthai/common/UsbSpeed.hpp index e53346bb8e..aef2dc45ed 100644 --- a/include/depthai/common/UsbSpeed.hpp +++ b/include/depthai/common/UsbSpeed.hpp @@ -4,30 +4,27 @@ #include "depthai-shared/common/UsbSpeed.hpp" -namespace dai { - -inline std::ostream& operator<<(std::ostream& out, const UsbSpeed& speed) { +// Global namespace +inline std::ostream& operator<<(std::ostream& out, const dai::UsbSpeed& speed) { switch(speed) { - case UsbSpeed::UNKNOWN: + case dai::UsbSpeed::UNKNOWN: out << "UNKNOWN"; break; - case UsbSpeed::LOW: + case dai::UsbSpeed::LOW: out << "LOW"; break; - case UsbSpeed::FULL: + case dai::UsbSpeed::FULL: out << "FULL"; break; - case UsbSpeed::HIGH: + case dai::UsbSpeed::HIGH: out << "HIGH"; break; - case UsbSpeed::SUPER: + case dai::UsbSpeed::SUPER: out << "SUPER"; break; - case UsbSpeed::SUPER_PLUS: + case dai::UsbSpeed::SUPER_PLUS: out << "SUPER_PLUS"; break; } return out; } - -} // namespace dai diff --git a/include/depthai/device/Device.hpp b/include/depthai/device/Device.hpp index 94c47495a6..523363ae57 100644 --- a/include/depthai/device/Device.hpp +++ b/include/depthai/device/Device.hpp @@ -12,7 +12,6 @@ // project #include "DataQueue.hpp" #include "depthai/device/DeviceBase.hpp" -#include "depthai/pipeline/Pipeline.hpp" namespace dai { /** @@ -36,6 +35,13 @@ class Device : public DeviceBase { */ Device(const Pipeline& pipeline, bool usb2Mode); + /** + * Connects to any available device with a DEFAULT_SEARCH_TIME timeout. + * @param pipeline Pipeline to be executed on the device + * @param maxUsbSpeed Maximum allowed USB speed + */ + Device(const Pipeline& pipeline, UsbSpeed maxUsbSpeed); + /** * Connects to any available device with a DEFAULT_SEARCH_TIME timeout. * @param pipeline Pipeline to be executed on the device @@ -65,6 +71,14 @@ class Device : public DeviceBase { */ Device(const Pipeline& pipeline, const DeviceInfo& devInfo, bool usb2Mode); + /** + * Connects to device specified by devInfo. + * @param pipeline Pipeline to be executed on the device + * @param devInfo DeviceInfo which specifies which device to connect to + * @param maxUsbSpeed Maximum allowed USB speed + */ + Device(const Pipeline& pipeline, const DeviceInfo& devInfo, UsbSpeed maxUsbSpeed); + /** * Connects to device specified by devInfo. * @param pipeline Pipeline to be executed on the device @@ -77,7 +91,7 @@ class Device : public DeviceBase { * Connects to device specified by devInfo. * @param pipeline Pipeline to be executed on the device * @param devInfo DeviceInfo which specifies which device to connect to - * @param usb2Mode Path to custom device firmware + * @param pathToCmd Path to custom device firmware */ Device(const Pipeline& pipeline, const DeviceInfo& devInfo, const std::string& pathToCmd); diff --git a/include/depthai/device/DeviceBase.hpp b/include/depthai/device/DeviceBase.hpp index b2bee8edf9..a008f8b74d 100644 --- a/include/depthai/device/DeviceBase.hpp +++ b/include/depthai/device/DeviceBase.hpp @@ -17,7 +17,7 @@ #include "depthai/common/CameraBoardSocket.hpp" #include "depthai/common/UsbSpeed.hpp" #include "depthai/device/CalibrationHandler.hpp" -#include "depthai/pipeline/Pipeline.hpp" +#include "depthai/openvino/OpenVINO.hpp" #include "depthai/utility/Pimpl.hpp" #include "depthai/xlink/XLinkConnection.hpp" #include "depthai/xlink/XLinkStream.hpp" @@ -26,11 +26,15 @@ #include "depthai-shared/common/ChipTemperature.hpp" #include "depthai-shared/common/CpuUsage.hpp" #include "depthai-shared/common/MemoryInfo.hpp" +#include "depthai-shared/device/PrebootConfig.hpp" #include "depthai-shared/log/LogLevel.hpp" #include "depthai-shared/log/LogMessage.hpp" namespace dai { +// Forward declare Pipeline +class Pipeline; + /** * The core of depthai device for RAII, connects to device and maintains watchdog, timesync, ... */ @@ -42,6 +46,18 @@ class DeviceBase { static constexpr std::chrono::seconds DEFAULT_SEARCH_TIME{3}; /// Default rate at which system information is logged static constexpr float DEFAULT_SYSTEM_INFORMATION_LOGGING_RATE_HZ{1.0f}; + /// Default UsbSpeed for device connection + static constexpr UsbSpeed DEFAULT_USB_SPEED{UsbSpeed::SUPER}; + + // Structures + + /** + * Device specific configuration + */ + struct Config { + OpenVINO::Version version; + PrebootConfig preboot; + }; // static API @@ -86,7 +102,14 @@ class DeviceBase { * @param version Version of OpenVINO which firmware will support * @returns Firmware binary */ - static std::vector getEmbeddedDeviceBinary(bool usb2Mode, OpenVINO::Version version = Pipeline::DEFAULT_OPENVINO_VERSION); + static std::vector getEmbeddedDeviceBinary(bool usb2Mode, OpenVINO::Version version = OpenVINO::DEFAULT_VERSION); + + /** + * Gets device firmware binary for a specific configuration + * @param config FW with applied configuration + * @returns Firmware binary + */ + static std::vector getEmbeddedDeviceBinary(Config config); /** * Connects to any available device with a DEFAULT_SEARCH_TIME timeout. @@ -101,6 +124,13 @@ class DeviceBase { */ DeviceBase(const Pipeline& pipeline, bool usb2Mode); + /** + * Connects to any available device with a DEFAULT_SEARCH_TIME timeout. + * @param pipeline Pipeline to be executed on the device + * @param maxUsbSpeed Maximum allowed USB speed + */ + DeviceBase(const Pipeline& pipeline, UsbSpeed maxUsbSpeed); + /** * Connects to any available device with a DEFAULT_SEARCH_TIME timeout. * @param pipeline Pipeline to be executed on the device @@ -130,6 +160,14 @@ class DeviceBase { */ DeviceBase(const Pipeline& pipeline, const DeviceInfo& devInfo, bool usb2Mode); + /** + * Connects to device specified by devInfo. + * @param pipeline Pipeline to be executed on the device + * @param devInfo DeviceInfo which specifies which device to connect to + * @param maxUsbSpeed Maximum allowed USB speed + */ + DeviceBase(const Pipeline& pipeline, const DeviceInfo& devInfo, UsbSpeed maxUsbSpeed); + /** * Connects to device specified by devInfo. * @param pipeline Pipeline to be executed on the device @@ -165,6 +203,14 @@ class DeviceBase { */ DeviceBase(OpenVINO::Version version, bool usb2Mode); + /** + * Connects to device specified by devInfo. + * @param version OpenVINO version which the device will be booted with + * @param devInfo DeviceInfo which specifies which device to connect to + * @param maxUsbSpeed Maximum allowed USB speed + */ + DeviceBase(OpenVINO::Version version, UsbSpeed maxUsbSpeed); + /** * Connects to any available device with a DEFAULT_SEARCH_TIME timeout. * @param version OpenVINO version which the device will be booted with @@ -194,6 +240,14 @@ class DeviceBase { */ DeviceBase(OpenVINO::Version version, const DeviceInfo& devInfo, bool usb2Mode); + /** + * Connects to device specified by devInfo. + * @param version OpenVINO version which the device will be booted with + * @param devInfo DeviceInfo which specifies which device to connect to + * @param maxUsbSpeed Maximum allowed USB speed + */ + DeviceBase(OpenVINO::Version version, const DeviceInfo& devInfo, UsbSpeed maxUsbSpeed); + /** * Connects to device specified by devInfo. * @param version OpenVINO version which the device will be booted with @@ -210,6 +264,19 @@ class DeviceBase { */ DeviceBase(OpenVINO::Version version, const DeviceInfo& devInfo, const std::string& pathToCmd); + /** + * Connects to any available device with custom config. + * @param config Device custom configuration to boot with + */ + explicit DeviceBase(Config config); + + /** + * Connects to device 'devInfo' with custom config. + * @param devInfo DeviceInfo which specifies which device to connect to + * @param config Device custom configuration to boot with + */ + DeviceBase(Config config, const DeviceInfo& devInfo); + /** * Device destructor * @note In the destructor of the derived class, remember to call close() @@ -273,7 +340,7 @@ class DeviceBase { * * @return DeviceInfo of the current device in execution */ - DeviceInfo getDeviceInfo(); + DeviceInfo getDeviceInfo() const; /** * Get MxId of device @@ -419,10 +486,16 @@ class DeviceBase { */ bool isClosed() const; + /** + * Returns underlying XLinkConnection + */ std::shared_ptr getConnection() { return connection; } + /** + * Returns underlying XLinkConnection + */ std::shared_ptr getConnection() const { return connection; } @@ -459,10 +532,12 @@ class DeviceBase { virtual void closeImpl(); private: - // private static - void init(OpenVINO::Version version, bool embeddedMvcmd, bool usb2Mode, const std::string& pathToMvcmd); - void init(const Pipeline& pipeline, bool embeddedMvcmd, bool usb2Mode, const std::string& pathToMvcmd); - void init2(bool embeddedMvcmd, bool usb2Mode, const std::string& pathToMvcmd); + // private functions + void init(OpenVINO::Version version, bool usb2Mode, const std::string& pathToMvcmd); + void init(const Pipeline& pipeline, bool usb2Mode, const std::string& pathToMvcmd); + void init(OpenVINO::Version version, UsbSpeed maxUsbSpeed, const std::string& pathToMvcmd); + void init(const Pipeline& pipeline, UsbSpeed maxUsbSpeed, const std::string& pathToMvcmd); + void init2(Config cfg, const std::string& pathToMvcmd, tl::optional pipeline); DeviceInfo deviceInfo = {}; @@ -493,7 +568,7 @@ class DeviceBase { class Impl; Pimpl pimpl; - // OpenVINO version device was booted with - OpenVINO::Version openvinoVersion; + // Device config + Config config; }; } // namespace dai diff --git a/include/depthai/device/DeviceBootloader.hpp b/include/depthai/device/DeviceBootloader.hpp index fb5fcb40d9..5e07fccbf3 100644 --- a/include/depthai/device/DeviceBootloader.hpp +++ b/include/depthai/device/DeviceBootloader.hpp @@ -8,11 +8,13 @@ // project #include "CallbackHandler.hpp" #include "DataQueue.hpp" +#include "depthai/common/UsbSpeed.hpp" #include "depthai/pipeline/Pipeline.hpp" #include "depthai/xlink/XLinkConnection.hpp" #include "depthai/xlink/XLinkStream.hpp" // shared +#include "depthai-bootloader-shared/Config.hpp" #include "depthai-bootloader-shared/Memory.hpp" #include "depthai-bootloader-shared/Section.hpp" #include "depthai-bootloader-shared/Type.hpp" @@ -30,6 +32,50 @@ class DeviceBootloader { using Type = dai::bootloader::Type; using Memory = dai::bootloader::Memory; using Section = dai::bootloader::Section; + using UsbConfig = dai::bootloader::UsbConfig; + using NetworkConfig = dai::bootloader::NetworkConfig; + + // Derive and extend bootloader::Config for easier usage + struct Config : public bootloader::Config { + /// Setting a static IPv4 won't start DHCP client + void setStaticIPv4(std::string ip, std::string mask, std::string gateway); + /// Setting a dynamic IPv4 will set that IP as well as start DHCP client + void setDynamicIPv4(std::string ip, std::string mask, std::string gateway); + /// Get if static IPv4 configuration is set + bool isStaticIPV4(); + /// Get IPv4 + std::string getIPv4(); + /// Get IPv4 mask + std::string getIPv4Mask(); + /// Get IPv4 gateway + std::string getIPv4Gateway(); + /// Set IPv4 DNS options + void setDnsIPv4(std::string dns, std::string dnsAlt = ""); + /// Get primary IPv4 DNS server + std::string getDnsIPv4(); + /// Get alternate IPv4 DNS server + std::string getDnsAltIPv4(); + + /// Set USB timeout + void setUsbTimeout(std::chrono::milliseconds ms); + /// Get USB timeout + std::chrono::milliseconds getUsbTimeout(); + + /// Set NETWOR timeout + void setNetworkTimeout(std::chrono::milliseconds ms); + /// Get NETWORK timeout + std::chrono::milliseconds getNetworkTimeout(); + + /// Set MAC address if not flashed on controller + void setMacAddress(std::string mac); + /// Get MAC address if not flashed on controller + std::string getMacAddress(); + + /// Set maxUsbSpeed + void setUsbMaxSpeed(UsbSpeed speed); + /// Get maxUsbSpeed + UsbSpeed getUsbMaxSpeed(); + }; /// Bootloader version structure struct Version { @@ -39,16 +85,16 @@ class DeviceBootloader { Version(unsigned major, unsigned minor, unsigned patch); bool operator==(const Version& other) const; bool operator<(const Version& other) const; - inline bool operator!=(const Version& rhs) { + inline bool operator!=(const Version& rhs) const { return !(*this == rhs); } - inline bool operator>(const Version& rhs) { + inline bool operator>(const Version& rhs) const { return rhs < *this; } - inline bool operator<=(const Version& rhs) { + inline bool operator<=(const Version& rhs) const { return !(*this > rhs); } - inline bool operator>=(const Version& rhs) { + inline bool operator>=(const Version& rhs) const { return !(*this < rhs); } /// Convert Version to string @@ -80,17 +126,35 @@ class DeviceBootloader { * Creates application package which can be flashed to depthai device. * @param pipeline Pipeline from which to create the application package * @param pathToCmd Optional path to custom device firmware + * @param compress Optional boolean which specifies if contents should be compressed + * @returns Depthai application package + */ + static std::vector createDepthaiApplicationPackage(const Pipeline& pipeline, std::string pathToCmd = "", bool compress = false); + + /** + * Creates application package which can be flashed to depthai device. + * @param pipeline Pipeline from which to create the application package + * @param compress Specifies if contents should be compressed * @returns Depthai application package */ - static std::vector createDepthaiApplicationPackage(Pipeline& pipeline, std::string pathToCmd = ""); + static std::vector createDepthaiApplicationPackage(const Pipeline& pipeline, bool compress); /** * Saves application package to a file which can be flashed to depthai device. * @param path Path where to save the application package * @param pipeline Pipeline from which to create the application package * @param pathToCmd Optional path to custom device firmware + * @param compress Optional boolean which specifies if contents should be compressed */ - static void saveDepthaiApplicationPackage(std::string path, Pipeline& pipeline, std::string pathToCmd = ""); + static void saveDepthaiApplicationPackage(std::string path, const Pipeline& pipeline, std::string pathToCmd = "", bool compress = false); + + /** + * Saves application package to a file which can be flashed to depthai device. + * @param path Path where to save the application package + * @param pipeline Pipeline from which to create the application package + * @param compress Specifies if contents should be compressed + */ + static void saveDepthaiApplicationPackage(std::string path, const Pipeline& pipeline, bool compress); /** * @returns Embedded bootloader version @@ -107,36 +171,45 @@ class DeviceBootloader { /** * Connects to or boots device in bootloader mode depending on devInfo state. * @param devInfo DeviceInfo of which to boot or connect to + * @param allowFlashingBootloader Set to true to allow flashing the devices bootloader. Defaults to false */ - explicit DeviceBootloader(const DeviceInfo& devInfo); + explicit DeviceBootloader(const DeviceInfo& devInfo, bool allowFlashingBootloader = false); /** * Connects to device in bootloader of specified type. Throws if it wasn't possible. * This constructor will automatically boot into specified bootloader type if not already running * @param devInfo DeviceInfo of which to boot or connect to * @param type Type of bootloader to boot/connect to. + * @param allowFlashingBootloader Set to true to allow flashing the devices bootloader. Defaults to false */ - DeviceBootloader(const DeviceInfo& devInfo, Type type); + DeviceBootloader(const DeviceInfo& devInfo, Type type, bool allowFlashingBootloader = false); /** * Connects to or boots device in bootloader mode depending on devInfo state with a custom bootloader firmware. * @param devInfo DeviceInfo of which to boot or connect to * @param pathToBootloader Custom bootloader firmware to boot + * @param allowFlashingBootloader Set to true to allow flashing the devices bootloader. Defaults to false */ - DeviceBootloader(const DeviceInfo& devInfo, const std::string& pathToBootloader); + DeviceBootloader(const DeviceInfo& devInfo, const std::string& pathToBootloader, bool allowFlashingBootloader = false); /** * @overload */ - DeviceBootloader(const DeviceInfo& devInfo, const char* pathToBootloader); + DeviceBootloader(const DeviceInfo& devInfo, const char* pathToBootloader, bool allowFlashingBootloader = false); ~DeviceBootloader(); /** - * Flashes a give pipeline to the board. + * Flashes a given pipeline to the device. * @param progressCallback Callback that sends back a value between 0..1 which signifies current flashing progress * @param pipeline Pipeline to flash to the board */ - std::tuple flash(std::function progressCallback, Pipeline& pipeline); + std::tuple flash(std::function progressCallback, const Pipeline& pipeline, bool compress = false); + + /** + * Flashes a given pipeline to the device. + * @param pipeline Pipeline to flash to the board + */ + std::tuple flash(const Pipeline& pipeline, bool compress = false); /** * Flashes a specific depthai application package that was generated using createDepthaiApplicationPackage or saveDepthaiApplicationPackage @@ -145,6 +218,12 @@ class DeviceBootloader { */ std::tuple flashDepthaiApplicationPackage(std::function progressCallback, std::vector package); + /** + * Flashes a specific depthai application package that was generated using createDepthaiApplicationPackage or saveDepthaiApplicationPackage + * @param package Depthai application package to flash to the board + */ + std::tuple flashDepthaiApplicationPackage(std::vector package); + /** * Flashes bootloader to the current board * @param progressCallback Callback that sends back a value between 0..1 which signifies current flashing progress @@ -170,16 +249,87 @@ class DeviceBootloader { */ // std::tuple flashCustom(Memory memory, uint32_t offset, std::function progressCb, std::vector data); + /** + * Reads configuration data from bootloader + * @returns Unstructured configuration data + * @param memory Optional - from which memory to read configuration data + * @param type Optional - from which type of bootloader to read configuration data + */ + nlohmann::json readConfigData(Memory memory = Memory::AUTO, Type type = Type::AUTO); + + /** + * Flashes configuration data to bootloader + * @param configData Unstructured configuration data + * @param memory Optional - to which memory flash configuration + * @param type Optional - for which type of bootloader to flash configuration + */ + std::tuple flashConfigData(nlohmann::json configData, Memory memory = Memory::AUTO, Type type = Type::AUTO); + + /** + * Flashes configuration data to bootloader + * @param configPath Unstructured configuration data + * @param memory Optional - to which memory flash configuration + * @param type Optional - for which type of bootloader to flash configuration + */ + std::tuple flashConfigFile(std::string configPath, Memory memory = Memory::AUTO, Type type = Type::AUTO); + + /** + * Clears configuration data + * @param memory Optional - on which memory to clear configuration data + * @param type Optional - for which type of bootloader to clear configuration data + */ + std::tuple flashConfigClear(Memory memory = Memory::AUTO, Type type = Type::AUTO); + + /** + * Reads configuration from bootloader + * @param memory Optional - from which memory to read configuration + * @param type Optional - from which type of bootloader to read configuration + * @returns Configuration structure + */ + Config readConfig(Memory memory = Memory::AUTO, Type type = Type::AUTO); + + /** + * Flashes configuration to bootloader + * @param configData Configuration structure + * @param memory Optional - to which memory flash configuration + * @param type Optional - for which type of bootloader to flash configuration + */ + std::tuple flashConfig(const Config& config, Memory memory = Memory::AUTO, Type type = Type::AUTO); + + /** + * Boots a custom FW in memory + * @param fw + * @throws A runtime exception if there are any communication issues + */ + void bootMemory(const std::vector& fw); + + /** + * Boots into integrated ROM bootloader in USB mode + * @throws A runtime exception if there are any communication issues + */ + void bootUsbRomBootloader(); + /** * @returns Version of current running bootloader */ - Version getVersion(); + Version getVersion() const; /** - * @returns True whether the bootloader running is flashed or booted by library + * @returns True when bootloader was booted using latest bootloader integrated in the library. + * False when bootloader is already running on the device and just connected to. */ bool isEmbeddedVersion() const; + /** + * @returns Type of currently connected bootloader + */ + Type getType() const; + + /** + * @returns True if allowed to flash bootloader + */ + bool isAllowedFlashingBootloader() const; + /** * Explicitly closes connection to device. * @note This function does not need to be explicitly called @@ -195,10 +345,19 @@ class DeviceBootloader { private: // private static - // private variables - void init(bool embeddedMvcmd, const std::string& pathToMvcmd, tl::optional type); + // private methods + void init(bool embeddedMvcmd, const std::string& pathToMvcmd, tl::optional type, bool allowBlFlash); void checkClosed() const; + template + bool sendRequest(const T& request); + bool receiveResponseData(std::vector& data); + template + bool parseResponse(const std::vector& data, T& response); + template + bool receiveResponse(T& response); + Version requestVersion(); + // private variables std::shared_ptr connection; DeviceInfo deviceInfo = {}; @@ -214,6 +373,64 @@ class DeviceBootloader { // bootloader stream std::unique_ptr stream; + + // Allow flashing bootloader flag + bool allowFlashingBootloader = false; + + // Current connected bootloader version + Version version{0, 0, 2}; }; } // namespace dai + +// Global namespace +inline std::ostream& operator<<(std::ostream& out, const dai::DeviceBootloader::Type& type) { + switch(type) { + case dai::DeviceBootloader::Type::AUTO: + out << "AUTO"; + break; + case dai::DeviceBootloader::Type::USB: + out << "USB"; + break; + case dai::DeviceBootloader::Type::NETWORK: + out << "NETWORK"; + break; + } + return out; +} + +inline std::ostream& operator<<(std::ostream& out, const dai::DeviceBootloader::Memory& memory) { + switch(memory) { + case dai::DeviceBootloader::Memory::AUTO: + out << "AUTO"; + break; + case dai::DeviceBootloader::Memory::FLASH: + out << "FLASH"; + break; + case dai::DeviceBootloader::Memory::EMMC: + out << "EMMC"; + break; + } + return out; +} + +inline std::ostream& operator<<(std::ostream& out, const dai::DeviceBootloader::Section& type) { + switch(type) { + case dai::DeviceBootloader::Section::AUTO: + out << "AUTO"; + break; + case dai::DeviceBootloader::Section::HEADER: + out << "HEADER"; + break; + case dai::DeviceBootloader::Section::BOOTLOADER: + out << "BOOTLOADER"; + break; + case dai::DeviceBootloader::Section::BOOTLOADER_CONFIG: + out << "BOOTLOADER_CONFIG"; + break; + case dai::DeviceBootloader::Section::APPLICATION: + out << "APPLICATION"; + break; + } + return out; +} \ No newline at end of file diff --git a/include/depthai/openvino/OpenVINO.hpp b/include/depthai/openvino/OpenVINO.hpp index beb9da4a41..228821c1db 100644 --- a/include/depthai/openvino/OpenVINO.hpp +++ b/include/depthai/openvino/OpenVINO.hpp @@ -15,6 +15,9 @@ class OpenVINO { /// OpenVINO Version supported version information enum Version { VERSION_2020_3, VERSION_2020_4, VERSION_2021_1, VERSION_2021_2, VERSION_2021_3, VERSION_2021_4 }; + /// Main OpenVINO version + constexpr static const Version DEFAULT_VERSION = VERSION_2021_4; + /** * @returns Supported versions */ diff --git a/include/depthai/pipeline/Pipeline.hpp b/include/depthai/pipeline/Pipeline.hpp index dbedfadcb4..8ab72f0f4e 100644 --- a/include/depthai/pipeline/Pipeline.hpp +++ b/include/depthai/pipeline/Pipeline.hpp @@ -10,9 +10,11 @@ #include "AssetManager.hpp" #include "Node.hpp" #include "depthai/device/CalibrationHandler.hpp" +#include "depthai/device/Device.hpp" #include "depthai/openvino/OpenVINO.hpp" // shared +#include "depthai-shared/device/PrebootConfig.hpp" #include "depthai-shared/pipeline/PipelineSchema.hpp" #include "depthai-shared/properties/GlobalProperties.hpp" @@ -34,7 +36,9 @@ class PipelineImpl { // Functions Node::Id getNextUniqueId(); PipelineSchema getPipelineSchema() const; - OpenVINO::Version getPipelineOpenVINOVersion() const; + tl::optional getPipelineOpenVINOVersion() const; + bool isOpenVINOVersionCompatible(OpenVINO::Version version) const; + Device::Config getDeviceConfig() const; void setCameraTuningBlobPath(const std::string& path); void setXLinkChunkSize(int sizeBytes); @@ -44,7 +48,7 @@ class PipelineImpl { std::shared_ptr getNode(Node::Id id) const; std::shared_ptr getNode(Node::Id id); - void serialize(PipelineSchema& schema, Assets& assets, std::vector& assetStorage, OpenVINO::Version& version) const; + void serialize(PipelineSchema& schema, Assets& assets, std::vector& assetStorage) const; void remove(std::shared_ptr node); std::vector getConnections() const; @@ -57,8 +61,6 @@ class PipelineImpl { Node::Id latestId = 0; // Pipeline asset manager AssetManager assetManager; - // Default version - constexpr static auto DEFAULT_OPENVINO_VERSION = OpenVINO::Version::VERSION_2021_4; // Optionally forced version tl::optional forceRequiredOpenVINOVersion; // Global pipeline properties @@ -107,9 +109,6 @@ class Pipeline { /// Clone the pipeline (Creates a copy) Pipeline clone() const; - /// Default Pipeline openvino version - constexpr static auto DEFAULT_OPENVINO_VERSION = PipelineImpl::DEFAULT_OPENVINO_VERSION; - /** * @returns Global properties of current pipeline */ @@ -121,8 +120,8 @@ class Pipeline { PipelineSchema getPipelineSchema(); // void loadAssets(AssetManager& assetManager); - void serialize(PipelineSchema& schema, Assets& assets, std::vector& assetStorage, OpenVINO::Version& version) const { - impl()->serialize(schema, assets, assetStorage, version); + void serialize(PipelineSchema& schema, Assets& assets, std::vector& assetStorage) const { + impl()->serialize(schema, assets, assetStorage); } /** @@ -232,8 +231,13 @@ class Pipeline { return impl()->getCalibrationData(); } - /// Get required OpenVINO version to run this pipeline + /// Get possible OpenVINO version to run this pipeline OpenVINO::Version getOpenVINOVersion() const { + return impl()->getPipelineOpenVINOVersion().value_or(OpenVINO::DEFAULT_VERSION); + } + + /// Get required OpenVINO version to run this pipeline. Can be none + tl::optional getRequiredOpenVINOVersion() const { return impl()->getPipelineOpenVINOVersion(); } @@ -250,6 +254,16 @@ class Pipeline { void setXLinkChunkSize(int sizeBytes) { impl()->setXLinkChunkSize(sizeBytes); } + + /// Checks whether a given OpenVINO version is compatible with the pipeline + bool isOpenVINOVersionCompatible(OpenVINO::Version version) const { + return impl()->isOpenVINOVersionCompatible(version); + } + + /// Get device configuration needed for this pipeline + Device::Config getDeviceConfig() const { + return impl()->getDeviceConfig(); + } }; } // namespace dai diff --git a/include/depthai/pipeline/datatype/Tracklets.hpp b/include/depthai/pipeline/datatype/Tracklets.hpp index 81d77bdec1..8dd483ecc4 100644 --- a/include/depthai/pipeline/datatype/Tracklets.hpp +++ b/include/depthai/pipeline/datatype/Tracklets.hpp @@ -31,22 +31,23 @@ class Tracklets : public Buffer { std::vector& tracklets; }; -inline std::ostream& operator<<(std::ostream& out, const Tracklet::TrackingStatus& status) { +} // namespace dai + +// Global namespace +inline std::ostream& operator<<(std::ostream& out, const dai::Tracklet::TrackingStatus& status) { switch(status) { - case Tracklet::TrackingStatus::NEW: + case dai::Tracklet::TrackingStatus::NEW: out << "NEW"; break; - case Tracklet::TrackingStatus::TRACKED: + case dai::Tracklet::TrackingStatus::TRACKED: out << "TRACKED"; break; - case Tracklet::TrackingStatus::LOST: + case dai::Tracklet::TrackingStatus::LOST: out << "LOST"; break; - case Tracklet::TrackingStatus::REMOVED: + case dai::Tracklet::TrackingStatus::REMOVED: out << "REMOVED"; break; } return out; -} - -} // namespace dai +} \ No newline at end of file diff --git a/include/depthai/pipeline/node/Script.hpp b/include/depthai/pipeline/node/Script.hpp index b20c376860..2640bfc170 100644 --- a/include/depthai/pipeline/node/Script.hpp +++ b/include/depthai/pipeline/node/Script.hpp @@ -48,12 +48,14 @@ class Script : public Node { /** * Sets script data to be interpreted * @param script Script string to be interpreted + * @param name Optionally set a name of this script */ void setScript(const std::string& script, const std::string& name = ""); /** * Sets script data to be interpreted * @param data Binary data that represents the script to be interpreted + * @param name Optionally set a name of this script */ void setScript(const std::vector& data, const std::string& name = ""); diff --git a/include/depthai/utility/Initialization.hpp b/include/depthai/utility/Initialization.hpp index 348d7f34ae..d9c420c681 100644 --- a/include/depthai/utility/Initialization.hpp +++ b/include/depthai/utility/Initialization.hpp @@ -4,6 +4,6 @@ namespace dai { -bool initialize(std::string additionalInfo = ""); +bool initialize(std::string additionalInfo = "", bool installSignalHandler = true); } // namespace dai diff --git a/include/depthai/xlink/XLinkConnection.hpp b/include/depthai/xlink/XLinkConnection.hpp index 4b2d16696b..d845a6f6c6 100644 --- a/include/depthai/xlink/XLinkConnection.hpp +++ b/include/depthai/xlink/XLinkConnection.hpp @@ -33,16 +33,12 @@ struct DeviceInfo { * Represents connection between host and device over XLink protocol */ class XLinkConnection { - static std::atomic xlinkGlobalInitialized; - static XLinkGlobalHandler_t xlinkGlobalHandler; - static void initXLinkGlobal(); - static std::mutex xlinkStreamOperationMutex; - public: // static API static std::vector getAllConnectedDevices(XLinkDeviceState_t state = X_LINK_ANY_STATE); static std::tuple getFirstDevice(XLinkDeviceState_t state = X_LINK_ANY_STATE); static std::tuple getDeviceByMxId(std::string, XLinkDeviceState_t state = X_LINK_ANY_STATE); + static DeviceInfo bootBootloader(const DeviceInfo& devInfo); XLinkConnection(const DeviceInfo& deviceDesc, std::vector mvcmdBinary, XLinkDeviceState_t expectedState = X_LINK_BOOTED); XLinkConnection(const DeviceInfo& deviceDesc, std::string pathToMvcmd, XLinkDeviceState_t expectedState = X_LINK_BOOTED); diff --git a/include/depthai/xlink/XLinkStream.hpp b/include/depthai/xlink/XLinkStream.hpp index fdd8d07515..e26e20f891 100644 --- a/include/depthai/xlink/XLinkStream.hpp +++ b/include/depthai/xlink/XLinkStream.hpp @@ -24,7 +24,6 @@ class XLinkStream { // static constexpr static int STREAM_OPEN_RETRIES = 5; constexpr static std::chrono::milliseconds WAIT_FOR_STREAM_RETRY{50}; - static std::mutex xlinkStreamOperationMutex; std::string streamName; streamId_t streamId{INVALID_STREAM_ID}; diff --git a/shared/depthai-bootloader-shared b/shared/depthai-bootloader-shared index 7efb3e1887..1a4da6776a 160000 --- a/shared/depthai-bootloader-shared +++ b/shared/depthai-bootloader-shared @@ -1 +1 @@ -Subproject commit 7efb3e188715844d7b2b0e50861e4bfc36087370 +Subproject commit 1a4da6776a1c019ee2698ab7587a5c7e81f89d60 diff --git a/shared/depthai-bootloader-shared.cmake b/shared/depthai-bootloader-shared.cmake index 2f1c56ed4c..18435fb8b3 100644 --- a/shared/depthai-bootloader-shared.cmake +++ b/shared/depthai-bootloader-shared.cmake @@ -2,6 +2,7 @@ set(DEPTHAI_BOOTLOADER_SHARED_FOLDER ${CMAKE_CURRENT_LIST_DIR}/depthai-bootloade set(DEPTHAI_BOOTLOADER_SHARED_SOURCES ${DEPTHAI_BOOTLOADER_SHARED_FOLDER}/src/SBR.c + ${DEPTHAI_BOOTLOADER_SHARED_FOLDER}/src/Bootloader.cpp ) set(DEPTHAI_BOOTLOADER_SHARED_PUBLIC_INCLUDE @@ -26,8 +27,8 @@ if(GIT_FOUND AND NOT DEPTHAI_DOWNLOADED_SOURCES) string(SUBSTRING ${statusCommit} 0 1 status) if(${status} STREQUAL "-") message(FATAL_ERROR "Submodule 'depthai-bootloader-shared' not initialized/updated. Run 'git submodule update --init --recursive' first") - endif() - + endif() + # Get depthai-bootloader-shared current commit execute_process( COMMAND ${GIT_EXECUTABLE} rev-parse HEAD diff --git a/shared/depthai-shared b/shared/depthai-shared index a33b3e3f62..ade4e25144 160000 --- a/shared/depthai-shared +++ b/shared/depthai-shared @@ -1 +1 @@ -Subproject commit a33b3e3f62af68303ba909968d5bd9806e492262 +Subproject commit ade4e2514441b8b1a6b396282814174eb9ea8d9d diff --git a/shared/depthai-shared.cmake b/shared/depthai-shared.cmake index 3e2f95285e..39722c9b1b 100644 --- a/shared/depthai-shared.cmake +++ b/shared/depthai-shared.cmake @@ -4,6 +4,7 @@ set(DEPTHAI_SHARED_3RDPARTY_HEADERS_PATH "depthai-shared/3rdparty") set(DEPTHAI_SHARED_SOURCES ${DEPTHAI_SHARED_FOLDER}/src/datatype/DatatypeEnum.cpp + ${DEPTHAI_SHARED_FOLDER}/src/utility/Checksum.cpp ) set(DEPTHAI_SHARED_PUBLIC_INCLUDE @@ -21,7 +22,7 @@ set(DEPTHAI_SHARED_INCLUDE # Try retriving depthai-shared commit hash (if cloned and not sources only) find_package(Git) if(GIT_FOUND AND NOT DEPTHAI_DOWNLOADED_SOURCES) - + # Check that submodule is initialized and updated execute_process( COMMAND ${GIT_EXECUTABLE} submodule status ${DEPTHAI_SHARED_FOLDER} @@ -32,7 +33,7 @@ if(GIT_FOUND AND NOT DEPTHAI_DOWNLOADED_SOURCES) string(SUBSTRING ${statusCommit} 0 1 status) if(${status} STREQUAL "-") message(FATAL_ERROR "Submodule 'depthai-shared' not initialized/updated. Run 'git submodule update --init --recursive' first") - endif() + endif() # Get depthai-shared current commit execute_process( diff --git a/src/device/CallbackHandler.cpp b/src/device/CallbackHandler.cpp index f31d81db8b..df012bc2e6 100644 --- a/src/device/CallbackHandler.cpp +++ b/src/device/CallbackHandler.cpp @@ -18,7 +18,7 @@ CallbackHandler::CallbackHandler(std::shared_ptr conn, t = std::thread([this, streamName]() { try { // open stream with 1B write size (no writing will happen here) - XLinkStream stream(*connection, streamName, XLINK_USB_BUFFER_MAX_SIZE); + XLinkStream stream(*connection, streamName, device::XLINK_USB_BUFFER_MAX_SIZE); while(running) { // read packet diff --git a/src/device/DataQueue.cpp b/src/device/DataQueue.cpp index 971da9a4f9..4c6eb6bace 100644 --- a/src/device/DataQueue.cpp +++ b/src/device/DataQueue.cpp @@ -171,7 +171,7 @@ bool DataOutputQueue::removeCallback(int callbackId) { DataInputQueue::DataInputQueue(const std::shared_ptr& conn, const std::string& streamName, unsigned int maxSize, bool blocking) : queue(maxSize, blocking), name(streamName) { // open stream with default XLINK_USB_BUFFER_MAX_SIZE write size - XLinkStream stream(*conn, name, dai::XLINK_USB_BUFFER_MAX_SIZE); + XLinkStream stream(*conn, name, device::XLINK_USB_BUFFER_MAX_SIZE); writingThread = std::thread([this, stream = std::move(stream)]() mutable { std::uint64_t numPacketsSent = 0; diff --git a/src/device/Device.cpp b/src/device/Device.cpp index 8810a0fdce..1f4534dff5 100644 --- a/src/device/Device.cpp +++ b/src/device/Device.cpp @@ -15,7 +15,6 @@ #include "depthai/pipeline/node/XLinkIn.hpp" #include "depthai/pipeline/node/XLinkOut.hpp" #include "pipeline/Pipeline.hpp" -#include "utility/BootloaderHelper.hpp" #include "utility/Initialization.hpp" #include "utility/Resources.hpp" @@ -32,6 +31,10 @@ Device::Device(const Pipeline& pipeline, bool usb2Mode) : DeviceBase(pipeline.ge tryStartPipeline(pipeline); } +Device::Device(const Pipeline& pipeline, UsbSpeed maxUsbSpeed) : DeviceBase(pipeline.getOpenVINOVersion(), maxUsbSpeed) { + tryStartPipeline(pipeline); +} + Device::Device(const Pipeline& pipeline, const char* pathToCmd) : DeviceBase(pipeline.getOpenVINOVersion(), pathToCmd) { tryStartPipeline(pipeline); } @@ -57,6 +60,10 @@ Device::Device(const Pipeline& pipeline, const DeviceInfo& devInfo, const std::s tryStartPipeline(pipeline); } +Device::Device(const Pipeline& pipeline, const DeviceInfo& devInfo, UsbSpeed maxUsbSpeed) : DeviceBase(pipeline.getOpenVINOVersion(), devInfo, maxUsbSpeed) { + tryStartPipeline(pipeline); +} + Device::Device() : DeviceBase() {} Device::~Device() { diff --git a/src/device/DeviceBase.cpp b/src/device/DeviceBase.cpp index 695ac40e3d..82f3b66c1c 100644 --- a/src/device/DeviceBase.cpp +++ b/src/device/DeviceBase.cpp @@ -18,14 +18,15 @@ #include "depthai/pipeline/node/XLinkIn.hpp" #include "depthai/pipeline/node/XLinkOut.hpp" #include "pipeline/Pipeline.hpp" -#include "utility/BootloaderHelper.hpp" #include "utility/Initialization.hpp" #include "utility/PimplImpl.hpp" #include "utility/Resources.hpp" // libraries +#include "XLink/XLink.h" #include "nanorpc/core/client.h" #include "nanorpc/packer/nlohmann_msgpack.h" +#include "spdlog/details/os.h" #include "spdlog/fmt/chrono.h" #include "spdlog/sinks/stdout_color_sinks.h" #include "spdlog/spdlog.h" @@ -86,6 +87,7 @@ template std::tuple DeviceBase::getAnyAvailableDevice(std::chr template std::tuple DeviceBase::getAnyAvailableDevice(std::chrono::seconds); constexpr std::chrono::seconds DeviceBase::DEFAULT_SEARCH_TIME; constexpr float DeviceBase::DEFAULT_SYSTEM_INFORMATION_LOGGING_RATE_HZ; +constexpr UsbSpeed DeviceBase::DEFAULT_USB_SPEED; template std::tuple DeviceBase::getAnyAvailableDevice(std::chrono::duration timeout) { @@ -97,7 +99,7 @@ std::tuple DeviceBase::getAnyAvailableDevice(std::chrono::dura bool found = false; DeviceInfo deviceInfo; do { - for(auto searchState : {X_LINK_UNBOOTED, X_LINK_BOOTLOADER}) { + for(auto searchState : {X_LINK_UNBOOTED, X_LINK_BOOTLOADER, X_LINK_FLASH_BOOTED}) { std::tie(found, deviceInfo) = XLinkConnection::getFirstDevice(searchState); if(found) break; } @@ -134,6 +136,9 @@ std::tuple DeviceBase::getFirstAvailableDevice() { if(!found) { std::tie(found, dev) = XLinkConnection::getFirstDevice(X_LINK_BOOTLOADER); } + if(!found) { + std::tie(found, dev) = XLinkConnection::getFirstDevice(X_LINK_FLASH_BOOTED); + } return {found, dev}; } @@ -164,6 +169,10 @@ std::vector DeviceBase::getEmbeddedDeviceBinary(bool usb2Mode, Ope return Resources::getInstance().getDeviceFirmware(usb2Mode, version); } +std::vector DeviceBase::getEmbeddedDeviceBinary(Config config) { + return Resources::getInstance().getDeviceFirmware(config); +} + /* std::vector DeviceBase::getAllConnectedDevices(){ return XLinkConnection::getAllConnectedDevices(); @@ -220,18 +229,22 @@ LogLevel DeviceBase::Impl::getLogLevel() { DeviceBase::DeviceBase(OpenVINO::Version version, const DeviceInfo& devInfo) : DeviceBase(version, devInfo, false) {} DeviceBase::DeviceBase(OpenVINO::Version version, const DeviceInfo& devInfo, bool usb2Mode) : deviceInfo(devInfo) { - init(version, true, usb2Mode, ""); + init(version, usb2Mode, ""); +} + +DeviceBase::DeviceBase(OpenVINO::Version version, const DeviceInfo& devInfo, UsbSpeed maxUsbSpeed) : deviceInfo(devInfo) { + init(version, maxUsbSpeed, ""); } DeviceBase::DeviceBase(OpenVINO::Version version, const DeviceInfo& devInfo, const char* pathToCmd) : deviceInfo(devInfo) { - init(version, false, false, std::string(pathToCmd)); + init(version, false, std::string(pathToCmd)); } DeviceBase::DeviceBase(OpenVINO::Version version, const DeviceInfo& devInfo, const std::string& pathToCmd) : deviceInfo(devInfo) { - init(version, false, false, pathToCmd); + init(version, false, pathToCmd); } -DeviceBase::DeviceBase() : DeviceBase(Pipeline::DEFAULT_OPENVINO_VERSION) {} +DeviceBase::DeviceBase() : DeviceBase(OpenVINO::DEFAULT_VERSION) {} DeviceBase::DeviceBase(OpenVINO::Version version) { // Searches for any available device for 'default' timeout @@ -240,7 +253,7 @@ DeviceBase::DeviceBase(OpenVINO::Version version) { // If no device found, throw if(!found) throw std::runtime_error("No available devices"); - init(version, true, false, ""); + init(version, false, ""); } DeviceBase::DeviceBase(OpenVINO::Version version, const char* pathToCmd) { @@ -251,7 +264,7 @@ DeviceBase::DeviceBase(OpenVINO::Version version, const char* pathToCmd) { // If no device found, throw if(!found) throw std::runtime_error("No available devices"); - init(version, false, false, std::string(pathToCmd)); + init(version, false, std::string(pathToCmd)); } DeviceBase::DeviceBase(OpenVINO::Version version, const std::string& pathToCmd) { @@ -261,7 +274,7 @@ DeviceBase::DeviceBase(OpenVINO::Version version, const std::string& pathToCmd) // If no device found, throw if(!found) throw std::runtime_error("No available devices"); - init(version, false, false, pathToCmd); + init(version, false, pathToCmd); } DeviceBase::DeviceBase(OpenVINO::Version version, bool usb2Mode) { @@ -271,7 +284,17 @@ DeviceBase::DeviceBase(OpenVINO::Version version, bool usb2Mode) { // If no device found, throw if(!found) throw std::runtime_error("No available devices"); - init(version, true, usb2Mode, ""); + init(version, usb2Mode, ""); +} + +DeviceBase::DeviceBase(OpenVINO::Version version, UsbSpeed maxUsbSpeed) { + // Searches for any available device for 'default' timeout + bool found = false; + std::tie(found, deviceInfo) = getAnyAvailableDevice(); + + // If no device found, throw + if(!found) throw std::runtime_error("No available devices"); + init(version, maxUsbSpeed, ""); } DeviceBase::DeviceBase(const Pipeline& pipeline) : DeviceBase(pipeline.getOpenVINOVersion()) { @@ -282,6 +305,10 @@ DeviceBase::DeviceBase(const Pipeline& pipeline, bool usb2Mode) : DeviceBase(pip tryStartPipeline(pipeline); } +DeviceBase::DeviceBase(const Pipeline& pipeline, UsbSpeed maxUsbSpeed) : DeviceBase(pipeline.getOpenVINOVersion(), maxUsbSpeed) { + tryStartPipeline(pipeline); +} + DeviceBase::DeviceBase(const Pipeline& pipeline, const char* pathToCmd) : DeviceBase(pipeline.getOpenVINOVersion(), pathToCmd) { tryStartPipeline(pipeline); } @@ -296,6 +323,11 @@ DeviceBase::DeviceBase(const Pipeline& pipeline, const DeviceInfo& devInfo, bool tryStartPipeline(pipeline); } +DeviceBase::DeviceBase(const Pipeline& pipeline, const DeviceInfo& devInfo, UsbSpeed maxUsbSpeed) + : DeviceBase(pipeline.getOpenVINOVersion(), devInfo, maxUsbSpeed) { + tryStartPipeline(pipeline); +} + DeviceBase::DeviceBase(const Pipeline& pipeline, const DeviceInfo& devInfo, const char* pathToCmd) : DeviceBase(pipeline.getOpenVINOVersion(), devInfo, pathToCmd) { tryStartPipeline(pipeline); @@ -306,6 +338,20 @@ DeviceBase::DeviceBase(const Pipeline& pipeline, const DeviceInfo& devInfo, cons tryStartPipeline(pipeline); } +DeviceBase::DeviceBase(Config config) { + // Searches for any available device for 'default' timeout + bool found = false; + std::tie(found, deviceInfo) = getAnyAvailableDevice(); + + // If no device found, throw + if(!found) throw std::runtime_error("No available devices"); + init2(config, {}, {}); +} + +DeviceBase::DeviceBase(Config config, const DeviceInfo& devInfo) : deviceInfo(devInfo) { + init2(config, {}, {}); +} + void DeviceBase::close() { // Only allow to close once if(closed.exchange(true)) return; @@ -318,11 +364,12 @@ void DeviceBase::closeImpl() { auto t1 = steady_clock::now(); spdlog::debug("Device about to be closed..."); - // Close connection first (so queues unblock) + // Close connection first + // Resets device as well and queues unblock connection->close(); connection = nullptr; - // Stop watchdog + // Stop various threads watchdogRunning = false; timesyncRunning = false; loggingRunning = false; @@ -366,142 +413,113 @@ void DeviceBase::tryStartPipeline(const Pipeline& pipeline) { } } -void DeviceBase::init(OpenVINO::Version version, bool embeddedMvcmd, bool usb2Mode, const std::string& pathToMvcmd) { - // Initalize depthai library if not already - initialize(); - +void DeviceBase::init(OpenVINO::Version version, bool usb2Mode, const std::string& pathToMvcmd) { + Config cfg; + // Specify usb speed + cfg.preboot.usb.maxSpeed = usb2Mode ? UsbSpeed::HIGH : DeviceBase::DEFAULT_USB_SPEED; // Specify the OpenVINO version - openvinoVersion = version; - - spdlog::debug("Device - OpenVINO version: {}", OpenVINO::getVersionName(openvinoVersion)); - - init2(embeddedMvcmd, usb2Mode, pathToMvcmd); + cfg.version = version; + init2(cfg, pathToMvcmd, {}); +} +void DeviceBase::init(const Pipeline& pipeline, bool usb2Mode, const std::string& pathToMvcmd) { + Config cfg = pipeline.getDeviceConfig(); + // Modify usb speed + cfg.preboot.usb.maxSpeed = usb2Mode ? UsbSpeed::HIGH : DeviceBase::DEFAULT_USB_SPEED; + init2(cfg, pathToMvcmd, pipeline); +} +void DeviceBase::init(OpenVINO::Version version, UsbSpeed maxUsbSpeed, const std::string& pathToMvcmd) { + Config cfg; + // Specify usb speed + cfg.preboot.usb.maxSpeed = maxUsbSpeed; + // Specify the OpenVINO version + cfg.version = version; + init2(cfg, pathToMvcmd, {}); +} +void DeviceBase::init(const Pipeline& pipeline, UsbSpeed maxUsbSpeed, const std::string& pathToMvcmd) { + Config cfg = pipeline.getDeviceConfig(); + // Modify usb speed + cfg.preboot.usb.maxSpeed = maxUsbSpeed; + init2(cfg, pathToMvcmd, pipeline); } -void DeviceBase::init(const Pipeline& pipeline, bool embeddedMvcmd, bool usb2Mode, const std::string& pathToMvcmd) { +void DeviceBase::init2(Config cfg, const std::string& pathToMvcmd, tl::optional pipeline) { // Initalize depthai library if not already initialize(); - // Mark the OpenVINO version - openvinoVersion = pipeline.getOpenVINOVersion(); - - spdlog::debug("Device - pipeline serialized, OpenVINO version: {}", OpenVINO::getVersionName(openvinoVersion)); + // Specify cfg + config = cfg; - init2(embeddedMvcmd, usb2Mode, pathToMvcmd); -} + if(pipeline) { + spdlog::debug("Device - pipeline serialized, OpenVINO version: {}", OpenVINO::getVersionName(config.version)); + } else { + spdlog::debug("Device - OpenVINO version: {}", OpenVINO::getVersionName(config.version)); + } -void DeviceBase::init2(bool embeddedMvcmd, bool usb2Mode, const std::string& pathToMvcmd) { // Set logging pattern of device (device id + shared pattern) pimpl->setPattern(fmt::format("[{}] {}", deviceInfo.getMxId(), LOG_DEFAULT_PATTERN)); - // Get embedded mvcmd - std::vector embeddedFw = Resources::getInstance().getDeviceFirmware(usb2Mode, openvinoVersion); + // Check if WD env var is set + std::chrono::milliseconds watchdogTimeout = device::XLINK_WATCHDOG_TIMEOUT; + auto watchdogMsStr = spdlog::details::os::getenv("DEPTHAI_WATCHDOG"); + if(!watchdogMsStr.empty()) { + // Try parsing the string as a number + try { + std::chrono::milliseconds watchdog{std::stoi(watchdogMsStr)}; + config.preboot.watchdogTimeoutMs = watchdog.count(); + watchdogTimeout = watchdog; + spdlog::debug("Using a custom watchdog value of {}", watchdogTimeout); + } catch(const std::invalid_argument& e) { + spdlog::warn("DEPTHAI_WATCHDOG value invalid: {}", e.what()); + } + } + + // Get embedded mvcmd or external with applied config + std::vector fwWithConfig = Resources::getInstance().getDeviceFirmware(config, pathToMvcmd); // Init device (if bootloader, handle correctly - issue USB boot command) if(deviceInfo.state == X_LINK_UNBOOTED) { // Unbooted device found, boot and connect with XLinkConnection constructor - if(embeddedMvcmd) { - connection = std::make_shared(deviceInfo, embeddedFw); - } else { - connection = std::make_shared(deviceInfo, pathToMvcmd); + connection = std::make_shared(deviceInfo, fwWithConfig); + } else if(deviceInfo.state == X_LINK_BOOTLOADER || deviceInfo.state == X_LINK_FLASH_BOOTED) { + // If device is in flash booted state, reset to bootloader and then continue by booting appropriate FW + if(deviceInfo.state == X_LINK_FLASH_BOOTED) { + // Boot bootloader and set current deviceInfo to new device state + deviceInfo = XLinkConnection::bootBootloader(deviceInfo); } - } else if(deviceInfo.state == X_LINK_BOOTLOADER) { - // Scope so bootloaderConnection is desctructed and XLink cleans its state + // Scope so DeviceBootloader is disconnected { - // TODO(themarpe) - move this logic to DeviceBootloader - - // Bootloader state, proceed by issuing a command to bootloader - XLinkConnection bootloaderConnection(deviceInfo, X_LINK_BOOTLOADER); - - // Open stream - XLinkStream stream(bootloaderConnection, bootloader::XLINK_CHANNEL_BOOTLOADER, bootloader::XLINK_STREAM_MAX_SIZE); - - // prepare watchdog thread, which will keep device alive - std::atomic watchdogRunning{true}; - watchdogThread = std::thread([&]() { - // prepare watchdog thread - XLinkStream stream(bootloaderConnection, bootloader::XLINK_CHANNEL_WATCHDOG, 64); - std::vector watchdogKeepalive = {0, 0, 0, 0}; - while(watchdogRunning) { - try { - stream.write(watchdogKeepalive); - } catch(const std::exception&) { - break; - } - // Ping with a period half of that of the watchdog timeout - std::this_thread::sleep_for(bootloader::XLINK_WATCHDOG_TIMEOUT / 2); - } - }); - - // Send request for bootloader version - if(!sendBootloaderRequest(stream.getStreamId(), bootloader::request::GetBootloaderVersion{})) { - throw std::runtime_error("Error trying to connect to device"); - } - // Receive response - bootloader::response::BootloaderVersion ver; - if(!receiveBootloaderResponse(stream.getStreamId(), ver)) throw std::runtime_error("Error trying to connect to device"); - DeviceBootloader::Version version(ver.major, ver.minor, ver.patch); + DeviceBootloader bl(deviceInfo); + auto version = bl.getVersion(); // If version is >= 0.0.12 then boot directly, otherwise jump to USB ROM bootloader // Check if version is recent enough for this operation if(version >= DeviceBootloader::Version(0, 0, 12)) { - // Send request to boot firmware directly from bootloader - dai::bootloader::request::BootMemory bootMemory; - bootMemory.totalSize = static_cast(embeddedFw.size()); - bootMemory.numPackets = ((static_cast(embeddedFw.size()) - 1) / bootloader::XLINK_STREAM_MAX_SIZE) + 1; - if(!sendBootloaderRequest(stream.getStreamId(), bootMemory)) { - throw std::runtime_error("Error trying to connect to device"); - } - using namespace std::chrono; + // Boot the given FW auto t1 = steady_clock::now(); - // After that send numPackets of data - stream.writeSplit(embeddedFw.data(), embeddedFw.size(), bootloader::XLINK_STREAM_MAX_SIZE); - spdlog::debug( - "Booting FW with Bootloader. Version {}, Time taken: {}", version.toString(), duration_cast(steady_clock::now() - t1)); + bl.bootMemory(fwWithConfig); + auto t2 = steady_clock::now(); + spdlog::debug("Booting FW with Bootloader. Version {}, Time taken: {}", version.toString(), duration_cast(t2 - t1)); // After that the state will be BOOTED deviceInfo.state = X_LINK_BOOTED; - - // TODO(themarpe) - Possibility of switching to another port for cleaner transitions - // strcat(deviceInfo.desc.name, ":11492"); - } else { + // Boot into USB ROM BOOTLOADER + bl.bootUsbRomBootloader(); spdlog::debug("Booting FW by jumping to USB ROM Bootloader first. Bootloader Version {}", version.toString()); - // Send request to jump to USB bootloader - // Boot into USB ROM BOOTLOADER NOW - if(!sendBootloaderRequest(stream.getStreamId(), dai::bootloader::request::UsbRomBoot{})) { - throw std::runtime_error("Error trying to connect to device"); - } - // After that the state will be UNBOOTED deviceInfo.state = X_LINK_UNBOOTED; } - - watchdogRunning = false; - watchdogThread.join(); - - // Dummy read, until link falls down and it returns an error code - streamPacketDesc_t* pPacket; - XLinkReadData(stream.getStreamId(), &pPacket); } // Boot and connect with XLinkConnection constructor - if(embeddedMvcmd) { - connection = std::make_shared(deviceInfo, embeddedFw); - } else { - connection = std::make_shared(deviceInfo, pathToMvcmd); - } + connection = std::make_shared(deviceInfo, fwWithConfig); } else if(deviceInfo.state == X_LINK_BOOTED) { // Connect without booting - if(embeddedMvcmd) { - connection = std::make_shared(deviceInfo, embeddedFw); - } else { - connection = std::make_shared(deviceInfo, pathToMvcmd); - } + connection = std::make_shared(deviceInfo, fwWithConfig); } else { throw std::runtime_error("Cannot find any device with given deviceInfo"); } @@ -509,7 +527,7 @@ void DeviceBase::init2(bool embeddedMvcmd, bool usb2Mode, const std::string& pat deviceInfo.state = X_LINK_BOOTED; // prepare rpc for both attached and host controlled mode - pimpl->rpcStream = std::make_unique(*connection, dai::XLINK_CHANNEL_MAIN_RPC, dai::XLINK_USB_BUFFER_MAX_SIZE); + pimpl->rpcStream = std::make_unique(*connection, device::XLINK_CHANNEL_MAIN_RPC, device::XLINK_USB_BUFFER_MAX_SIZE); pimpl->rpcClient = std::make_unique>([this](nanorpc::core::type::buffer request) { // Lock for time of the RPC call, to not mix the responses between calling threads. @@ -530,16 +548,19 @@ void DeviceBase::init2(bool embeddedMvcmd, bool usb2Mode, const std::string& pat }); // prepare watchdog thread, which will keep device alive - watchdogThread = std::thread([this]() { - std::shared_ptr conn = this->connection; - while(watchdogRunning) { - try { - pimpl->rpcClient->call("watchdogKeepalive"); - } catch(const std::exception&) { - break; + // separate stream so it doesn't miss between potentially long RPC calls + watchdogThread = std::thread([this, watchdogTimeout]() { + try { + XLinkStream stream(*this->connection, device::XLINK_CHANNEL_WATCHDOG, 128); + std::vector watchdogKeepalive = {0, 0, 0, 0}; + while(watchdogRunning) { + stream.write(watchdogKeepalive); + // Ping with a period half of that of the watchdog timeout + std::this_thread::sleep_for(watchdogTimeout / 2); } - // Ping with a period half of that of the watchdog timeout - std::this_thread::sleep_for(XLINK_WATCHDOG_TIMEOUT / 2); + } catch(const std::exception& ex) { + // ignore + spdlog::debug("Watchdog thread exception caught: {}", ex.what()); } // Watchdog ended. Useful for checking disconnects @@ -551,7 +572,7 @@ void DeviceBase::init2(bool embeddedMvcmd, bool usb2Mode, const std::string& pat using namespace std::chrono; try { - XLinkStream stream(*this->connection, XLINK_CHANNEL_TIMESYNC, 128); + XLinkStream stream(*this->connection, device::XLINK_CHANNEL_TIMESYNC, 128); Timestamp timestamp = {}; while(timesyncRunning) { // Block @@ -578,7 +599,7 @@ void DeviceBase::init2(bool embeddedMvcmd, bool usb2Mode, const std::string& pat using namespace std::chrono; std::vector messages; try { - XLinkStream stream(*this->connection, XLINK_CHANNEL_LOG, 128); + XLinkStream stream(*this->connection, device::XLINK_CHANNEL_LOG, 128); while(loggingRunning) { // Block auto log = stream.read(); @@ -734,7 +755,7 @@ int DeviceBase::getXLinkChunkSize() { return pimpl->rpcClient->call("getXLinkChunkSize").as(); } -DeviceInfo DeviceBase::getDeviceInfo() { +DeviceInfo DeviceBase::getDeviceInfo() const { return deviceInfo; } @@ -819,16 +840,16 @@ bool DeviceBase::startPipeline(const Pipeline& pipeline) { } bool DeviceBase::startPipelineImpl(const Pipeline& pipeline) { + // Check openvino version + if(!pipeline.isOpenVINOVersionCompatible(config.version)) { + throw std::runtime_error("Device booted with different OpenVINO version that pipeline requires"); + } + + // Serialize the pipeline PipelineSchema schema; Assets assets; std::vector assetStorage; - - // Mark the OpenVINO version and serialize the pipeline - OpenVINO::Version pipelineOpenvinoVersion; - pipeline.serialize(schema, assets, assetStorage, pipelineOpenvinoVersion); - if(openvinoVersion != pipelineOpenvinoVersion) { - throw std::runtime_error("Device booted with different OpenVINO version that pipeline requires"); - } + pipeline.serialize(schema, assets, assetStorage); // if debug if(spdlog::get_level() == spdlog::level::debug) { @@ -851,10 +872,10 @@ bool DeviceBase::startPipelineImpl(const Pipeline& pipeline) { // Transfer the whole assetStorage in a separate thread const std::string streamAssetStorage = "__stream_asset_storage"; std::thread t1([this, &streamAssetStorage, &assetStorage]() { - XLinkStream stream(*connection, streamAssetStorage, XLINK_USB_BUFFER_MAX_SIZE); + XLinkStream stream(*connection, streamAssetStorage, device::XLINK_USB_BUFFER_MAX_SIZE); int64_t offset = 0; do { - int64_t toTransfer = std::min(static_cast(XLINK_USB_BUFFER_MAX_SIZE), static_cast(assetStorage.size() - offset)); + int64_t toTransfer = std::min(static_cast(device::XLINK_USB_BUFFER_MAX_SIZE), static_cast(assetStorage.size() - offset)); stream.write(&assetStorage[offset], toTransfer); offset += toTransfer; } while(offset < static_cast(assetStorage.size())); diff --git a/src/device/DeviceBootloader.cpp b/src/device/DeviceBootloader.cpp index 6e3653261a..02fa3784e3 100644 --- a/src/device/DeviceBootloader.cpp +++ b/src/device/DeviceBootloader.cpp @@ -6,6 +6,7 @@ // shared #include "depthai-bootloader-shared/Bootloader.hpp" #include "depthai-bootloader-shared/SBR.h" +#include "depthai-bootloader-shared/Structure.hpp" #include "depthai-bootloader-shared/XLinkConstants.hpp" #include "depthai-shared/datatype/RawImgFrame.hpp" #include "depthai-shared/pipeline/Assets.hpp" @@ -14,11 +15,13 @@ // project #include "device/Device.hpp" #include "pipeline/Pipeline.hpp" -#include "utility/BootloaderHelper.hpp" +#include "utility/Platform.hpp" #include "utility/Resources.hpp" // libraries +#include "spdlog/fmt/chrono.h" #include "spdlog/spdlog.h" +#include "zlib.h" // Resource compiled assets (cmds) #ifdef DEPTHAI_RESOURCE_COMPILED_BINARIES @@ -28,6 +31,10 @@ CMRC_DECLARE(depthai); namespace dai { +// Using +namespace Request = bootloader::request; +namespace Response = bootloader::response; + // constants constexpr const DeviceBootloader::Type DeviceBootloader::DEFAULT_TYPE; @@ -41,6 +48,9 @@ std::tuple DeviceBootloader::getFirstAvailableDevice() { if(!found) { std::tie(found, dev) = XLinkConnection::getFirstDevice(X_LINK_BOOTLOADER); } + if(!found) { + std::tie(found, dev) = XLinkConnection::getFirstDevice(X_LINK_FLASH_BOOTED); + } return {found, dev}; } @@ -54,13 +64,15 @@ std::vector DeviceBootloader::getAllAvailableDevices() { return availableDevices; } -std::vector DeviceBootloader::createDepthaiApplicationPackage(Pipeline& pipeline, std::string pathToCmd) { +std::vector DeviceBootloader::createDepthaiApplicationPackage(const Pipeline& pipeline, std::string pathToCmd, bool compress) { // Serialize the pipeline PipelineSchema schema; Assets assets; std::vector assetStorage; - OpenVINO::Version version; - pipeline.serialize(schema, assets, assetStorage, version); + pipeline.serialize(schema, assets, assetStorage); + + // Get openvino version + OpenVINO::Version version = pipeline.getOpenVINOVersion(); // Prepare device firmware std::vector deviceFirmware; @@ -98,12 +110,47 @@ std::vector DeviceBootloader::createDepthaiApplicationPackage(Pipeline& return ((((S) + (SECTION_ALIGNMENT_SIZE)-1)) & ~((SECTION_ALIGNMENT_SIZE)-1)); }; + // Should compress firmware? + if(compress) { + using namespace std::chrono; + + auto t1 = steady_clock::now(); + auto compressBufferSize = compressBound(deviceFirmware.size()); + std::vector compressBuffer(compressBufferSize); + // Chosen impirically + constexpr int COMPRESSION_LEVEL = 9; + if(compress2(compressBuffer.data(), &compressBufferSize, deviceFirmware.data(), deviceFirmware.size(), COMPRESSION_LEVEL) != Z_OK) { + throw std::runtime_error("Error while compressing device firmware\n"); + } + + // Resize output buffer + compressBuffer.resize(compressBufferSize); + + // Set the compressed firmware + auto prevSize = deviceFirmware.size(); + deviceFirmware = std::move(compressBuffer); + + auto diff = duration_cast(steady_clock::now() - t1); + spdlog::debug("Compressed firmware for Dephai Application Package. Took {}, size reduced from {:.2f}MiB to {:.2f}MiB", + diff, + prevSize / (1024.0f * 1024.0f), + deviceFirmware.size() / (1024.0f * 1024.0f)); + } + // First section, MVCMD, name '__firmware' sbr_section_set_name(fwSection, "__firmware"); sbr_section_set_bootable(fwSection, true); sbr_section_set_size(fwSection, static_cast(deviceFirmware.size())); sbr_section_set_checksum(fwSection, sbr_compute_checksum(deviceFirmware.data(), static_cast(deviceFirmware.size()))); sbr_section_set_offset(fwSection, SBR_RAW_SIZE); + // Ignore checksum to allow faster booting (images are verified after flashing, low risk) + sbr_section_set_ignore_checksum(fwSection, true); + // Set compression flags + if(compress) { + sbr_section_set_compression(fwSection, SBR_COMPRESSION_ZLIB); + } else { + sbr_section_set_compression(fwSection, SBR_NO_COMPRESSION); + } // Second section, pipeline schema, name 'pipeline' sbr_section_set_name(pipelineSection, "pipeline"); @@ -133,32 +180,49 @@ std::vector DeviceBootloader::createDepthaiApplicationPackage(Pipeline& sbr_serialize(&sbr, fwPackage.data(), static_cast(fwPackage.size())); // Write to fwPackage - for(unsigned i = 0; i < deviceFirmware.size(); i++) fwPackage[fwSection->offset + i] = deviceFirmware[i]; - for(unsigned i = 0; i < pipelineBinary.size(); i++) fwPackage[pipelineSection->offset + i] = pipelineBinary[i]; - for(unsigned i = 0; i < assetsBinary.size(); i++) fwPackage[assetsSection->offset + i] = assetsBinary[i]; - for(unsigned i = 0; i < assetStorage.size(); i++) fwPackage[assetStorageSection->offset + i] = assetStorage[i]; + for(std::size_t i = 0; i < deviceFirmware.size(); i++) fwPackage[fwSection->offset + i] = deviceFirmware[i]; + for(std::size_t i = 0; i < pipelineBinary.size(); i++) fwPackage[pipelineSection->offset + i] = pipelineBinary[i]; + for(std::size_t i = 0; i < assetsBinary.size(); i++) fwPackage[assetsSection->offset + i] = assetsBinary[i]; + for(std::size_t i = 0; i < assetStorage.size(); i++) fwPackage[assetStorageSection->offset + i] = assetStorage[i]; return fwPackage; } -DeviceBootloader::DeviceBootloader(const DeviceInfo& devInfo) : deviceInfo(devInfo) { - init(true, "", tl::nullopt); +std::vector DeviceBootloader::createDepthaiApplicationPackage(const Pipeline& pipeline, bool compress) { + return createDepthaiApplicationPackage(pipeline, "", compress); +} + +void DeviceBootloader::saveDepthaiApplicationPackage(std::string path, const Pipeline& pipeline, std::string pathToCmd, bool compress) { + auto dap = createDepthaiApplicationPackage(pipeline, pathToCmd, compress); + std::ofstream outfile(path, std::ios::binary); + outfile.write(reinterpret_cast(dap.data()), dap.size()); +} + +void DeviceBootloader::saveDepthaiApplicationPackage(std::string path, const Pipeline& pipeline, bool compress) { + auto dap = createDepthaiApplicationPackage(pipeline, compress); + std::ofstream outfile(path, std::ios::binary); + outfile.write(reinterpret_cast(dap.data()), dap.size()); } -DeviceBootloader::DeviceBootloader(const DeviceInfo& devInfo, Type type) : deviceInfo(devInfo) { - init(true, "", type); +DeviceBootloader::DeviceBootloader(const DeviceInfo& devInfo, bool allowFlashingBootloader) : deviceInfo(devInfo) { + init(true, "", tl::nullopt, allowFlashingBootloader); } -DeviceBootloader::DeviceBootloader(const DeviceInfo& devInfo, const char* pathToBootloader) : deviceInfo(devInfo) { - init(false, std::string(pathToBootloader), tl::nullopt); +DeviceBootloader::DeviceBootloader(const DeviceInfo& devInfo, Type type, bool allowFlashingBootloader) : deviceInfo(devInfo) { + init(true, "", type, allowFlashingBootloader); } -DeviceBootloader::DeviceBootloader(const DeviceInfo& devInfo, const std::string& pathToBootloader) : deviceInfo(devInfo) { - init(false, pathToBootloader, tl::nullopt); +DeviceBootloader::DeviceBootloader(const DeviceInfo& devInfo, const char* pathToBootloader, bool allowFlashingBootloader) : deviceInfo(devInfo) { + init(false, std::string(pathToBootloader), tl::nullopt, allowFlashingBootloader); } -void DeviceBootloader::init(bool embeddedMvcmd, const std::string& pathToMvcmd, tl::optional type) { +DeviceBootloader::DeviceBootloader(const DeviceInfo& devInfo, const std::string& pathToBootloader, bool allowFlashingBootloader) : deviceInfo(devInfo) { + init(false, pathToBootloader, tl::nullopt, allowFlashingBootloader); +} + +void DeviceBootloader::init(bool embeddedMvcmd, const std::string& pathToMvcmd, tl::optional type, bool allowBlFlash) { stream = nullptr; + allowFlashingBootloader = allowBlFlash; bootloaderType = type.value_or(DEFAULT_TYPE); @@ -171,9 +235,21 @@ void DeviceBootloader::init(bool embeddedMvcmd, const std::string& pathToMvcmd, connection = std::make_shared(deviceInfo, pathToMvcmd, X_LINK_BOOTLOADER); } + // prepare bootloader stream + stream = std::make_unique(*connection, bootloader::XLINK_CHANNEL_BOOTLOADER, bootloader::XLINK_STREAM_MAX_SIZE); + + // Retrieve bootloader version + version = requestVersion(); + // Device wasn't already in bootloader, that means that embedded bootloader is booted isEmbedded = true; - } else if(deviceInfo.state == X_LINK_BOOTLOADER) { + } else if(deviceInfo.state == X_LINK_BOOTLOADER || deviceInfo.state == X_LINK_FLASH_BOOTED) { + // If device is in flash booted state, reset to bootloader and then continue by booting appropriate FW + if(deviceInfo.state == X_LINK_FLASH_BOOTED) { + // Boot bootloader and set current deviceInfo to new device state + deviceInfo = XLinkConnection::bootBootloader(deviceInfo); + } + // In this case boot specified bootloader only if current bootloader isn't of correct type // Check version first, if >= 0.0.12 then check type and then either bootmemory to correct BL or continue as is @@ -182,33 +258,28 @@ void DeviceBootloader::init(bool embeddedMvcmd, const std::string& pathToMvcmd, connection = std::make_shared(deviceInfo, X_LINK_BOOTLOADER); // If type is specified, try to boot into that BL type - stream = std::unique_ptr(new XLinkStream(*connection, bootloader::XLINK_CHANNEL_BOOTLOADER, bootloader::XLINK_STREAM_MAX_SIZE)); + stream = std::make_unique(*connection, bootloader::XLINK_CHANNEL_BOOTLOADER, bootloader::XLINK_STREAM_MAX_SIZE); - // Send request for bootloader version - if(!sendBootloaderRequest(stream->getStreamId(), bootloader::request::GetBootloaderVersion{})) { - throw std::runtime_error("Error trying to connect to device"); - } - - // Receive response - bootloader::response::BootloaderVersion ver; - if(!receiveBootloaderResponse(stream->getStreamId(), ver)) throw std::runtime_error("Error trying to connect to device"); - DeviceBootloader::Version version(ver.major, ver.minor, ver.patch); - - // If version is adequite + // Retrieve bootloader version + version = requestVersion(); if(version >= Version(0, 0, 12)) { + // If version is adequate, do an in memory boot. + // Send request for bootloader type - if(!sendBootloaderRequest(stream->getStreamId(), bootloader::request::GetBootloaderType{})) { + if(!sendRequest(Request::GetBootloaderType{})) { throw std::runtime_error("Error trying to connect to device"); } // Receive response - bootloader::response::BootloaderType runningBootloaderType; - if(!receiveBootloaderResponse(stream->getStreamId(), runningBootloaderType)) throw std::runtime_error("Error trying to connect to device"); + Response::BootloaderType runningBootloaderType; + if(!receiveResponse(runningBootloaderType)) throw std::runtime_error("Error trying to connect to device"); // Modify actual bootloader type bootloaderType = runningBootloaderType.type; - // Boot memory correct type of BL - if(type && runningBootloaderType.type != *type) { + Type desiredBootloaderType = type.value_or(bootloaderType); + + // If not correct type OR if allowFlashingBootloader is set, then boot internal (latest) bootloader of correct type + if((desiredBootloaderType != bootloaderType) || allowFlashingBootloader) { // prepare watchdog thread, which will keep device alive std::atomic wdRunning{true}; std::thread wd = std::thread([&]() { @@ -227,66 +298,88 @@ void DeviceBootloader::init(bool embeddedMvcmd, const std::string& pathToMvcmd, }); // Send request to boot firmware directly from bootloader - dai::bootloader::request::BootMemory bootMemory; - auto binary = getEmbeddedBootloaderBinary(*type); + Request::BootMemory bootMemory; + auto binary = getEmbeddedBootloaderBinary(desiredBootloaderType); bootMemory.totalSize = static_cast(binary.size()); bootMemory.numPackets = ((static_cast(binary.size()) - 1) / bootloader::XLINK_STREAM_MAX_SIZE) + 1; - if(!sendBootloaderRequest(stream->getStreamId(), bootMemory)) { + if(!sendRequest(bootMemory)) { throw std::runtime_error("Error trying to connect to device"); } // After that send numPackets of data stream->writeSplit(binary.data(), binary.size(), bootloader::XLINK_STREAM_MAX_SIZE); - + // Close existing stream first + stream = nullptr; // Stop watchdog wdRunning = false; wd.join(); + // Close connection + connection->close(); - // Dummy read, until link falls down and it returns an error code - streamPacketDesc_t* pPacket; - XLinkReadData(stream->getStreamId(), &pPacket); - - // Now connect + // Now reconnect connection = std::make_shared(deviceInfo, X_LINK_BOOTLOADER); + // prepare new bootloader stream + stream = std::make_unique(*connection, bootloader::XLINK_CHANNEL_BOOTLOADER, bootloader::XLINK_STREAM_MAX_SIZE); - isEmbedded = false; - } else { + // Retrieve bootloader version + version = requestVersion(); + + // The type of bootloader is now 'desiredBootloaderType' + bootloaderType = desiredBootloaderType; + + // Embedded bootloader was used to boot, set to true isEmbedded = true; + } else { + // Just connected to existing bootloader on device. Set embedded to false + isEmbedded = false; } } else { - if(type && *type != Type::USB) { + // If version isn't adequate to do an in memory boot - do regular Bootloader -> USB ROM -> Boot transition. + Type desiredBootloaderType = type.value_or(Type::USB); + + // If not correct type OR if allowFlashingBootloader is set, then boot internal (latest) bootloader of correct type + if((desiredBootloaderType != Type::USB) || allowFlashingBootloader) { // Send request to jump to USB bootloader // Boot into USB ROM BOOTLOADER NOW - if(!sendBootloaderRequest(stream->getStreamId(), dai::bootloader::request::UsbRomBoot{})) { + if(!sendRequest(Request::UsbRomBoot{})) { throw std::runtime_error("Error trying to connect to device"); } + // Close existing stream first + stream = nullptr; + // Close connection + connection->close(); - // Dummy read, until link falls down and it returns an error code - streamPacketDesc_t* pPacket; - XLinkReadData(stream->getStreamId(), &pPacket); - + // Now reconnect // Unbooted device found, boot to BOOTLOADER and connect with XLinkConnection constructor if(embeddedMvcmd) { - connection = std::make_shared(deviceInfo, getEmbeddedBootloaderBinary(*type), X_LINK_BOOTLOADER); + connection = std::make_shared(deviceInfo, getEmbeddedBootloaderBinary(desiredBootloaderType), X_LINK_BOOTLOADER); } else { connection = std::make_shared(deviceInfo, pathToMvcmd, X_LINK_BOOTLOADER); } - bootloaderType = *type; + // prepare bootloader stream + stream = std::make_unique(*connection, bootloader::XLINK_CHANNEL_BOOTLOADER, bootloader::XLINK_STREAM_MAX_SIZE); - // Device wasn't already in bootloader, that means that embedded bootloader is booted + // Retrieve bootloader version + version = requestVersion(); + + // The type of bootloader is now 'desiredBootloaderType' + bootloaderType = desiredBootloaderType; + + // Embedded bootloader was used to boot, set to true isEmbedded = true; } else { bootloaderType = dai::bootloader::Type::USB; - // Device was already in bootloader, that means that embedded isn't running + + // Just connected to existing bootloader on device. Set embedded to false isEmbedded = false; } } } else { - throw std::runtime_error("Device not in UNBOOTED or BOOTLOADER state"); + throw std::runtime_error("Device not in UNBOOTED, BOOTLOADER or FLASH_BOOTED state"); } deviceInfo.state = X_LINK_BOOTLOADER; @@ -320,11 +413,6 @@ void DeviceBootloader::init(bool embeddedMvcmd, const std::string& pathToMvcmd, // Sleep a bit, so device isn't available anymore std::this_thread::sleep_for(std::chrono::milliseconds(500)); }); - - // prepare bootloader stream - if(stream == nullptr) { - stream = std::unique_ptr(new XLinkStream(*connection, bootloader::XLINK_CHANNEL_BOOTLOADER, bootloader::XLINK_STREAM_MAX_SIZE)); - } } void DeviceBootloader::close() { @@ -367,17 +455,19 @@ DeviceBootloader::Version DeviceBootloader::getEmbeddedBootloaderVersion() { return DeviceBootloader::Version(DEPTHAI_BOOTLOADER_VERSION); } -DeviceBootloader::Version DeviceBootloader::getVersion() { - streamId_t streamId = stream->getStreamId(); +DeviceBootloader::Version DeviceBootloader::getVersion() const { + return version; +} +DeviceBootloader::Version DeviceBootloader::requestVersion() { // Send request to jump to USB bootloader - if(!sendBootloaderRequest(streamId, bootloader::request::GetBootloaderVersion{})) { + if(!sendRequest(Request::GetBootloaderVersion{})) { throw std::runtime_error("Couldn't get bootloader version"); } // Receive response - dai::bootloader::response::BootloaderVersion ver; - if(!receiveBootloaderResponse(streamId, ver)) { + Response::BootloaderVersion ver; + if(!receiveResponse(ver)) { throw std::runtime_error("Couldn't get bootloader version"); } @@ -385,49 +475,53 @@ DeviceBootloader::Version DeviceBootloader::getVersion() { return DeviceBootloader::Version(ver.major, ver.minor, ver.patch); } -std::tuple DeviceBootloader::flash(std::function progressCb, Pipeline& pipeline) { - return flashDepthaiApplicationPackage(progressCb, createDepthaiApplicationPackage(pipeline)); +DeviceBootloader::Type DeviceBootloader::getType() const { + return bootloaderType; } -void DeviceBootloader::saveDepthaiApplicationPackage(std::string path, Pipeline& pipeline, std::string pathToCmd) { - auto dap = createDepthaiApplicationPackage(pipeline, pathToCmd); - std::ofstream outfile(path, std::ios::binary); - outfile.write(reinterpret_cast(dap.data()), dap.size()); +bool DeviceBootloader::isAllowedFlashingBootloader() const { + return allowFlashingBootloader; } -std::tuple DeviceBootloader::flashDepthaiApplicationPackage(std::function progressCb, std::vector package) { - streamId_t streamId = stream->getStreamId(); +std::tuple DeviceBootloader::flash(std::function progressCb, const Pipeline& pipeline, bool compress) { + return flashDepthaiApplicationPackage(progressCb, createDepthaiApplicationPackage(pipeline, compress)); +} +std::tuple DeviceBootloader::flash(const Pipeline& pipeline, bool compress) { + return flashDepthaiApplicationPackage(createDepthaiApplicationPackage(pipeline, compress)); +} + +std::tuple DeviceBootloader::flashDepthaiApplicationPackage(std::function progressCb, std::vector package) { // Bug in NETWORK bootloader in version 0.0.12 < 0.1.0 - flashing can cause a soft brick auto version = getVersion(); - if(bootloaderType == Type::NETWORK && version < Version(0, 1, 0)) { + if(bootloaderType == Type::NETWORK && version < Version(0, 0, 14)) { throw std::invalid_argument("Network bootloader requires version 0.1.0 or higher to flash applications. Current version: " + version.toString()); } // send request to FLASH BOOTLOADER - dai::bootloader::request::UpdateFlash updateFlash; - updateFlash.storage = dai::bootloader::request::UpdateFlash::SBR; + Request::UpdateFlash updateFlash; + updateFlash.storage = Request::UpdateFlash::SBR; updateFlash.totalSize = static_cast(package.size()); updateFlash.numPackets = ((static_cast(package.size()) - 1) / bootloader::XLINK_STREAM_MAX_SIZE) + 1; - if(!sendBootloaderRequest(streamId, updateFlash)) return {false, "Couldn't send bootloader flash request"}; + if(!sendRequest(updateFlash)) return {false, "Couldn't send bootloader flash request"}; // After that send numPackets of data stream->writeSplit(package.data(), package.size(), bootloader::XLINK_STREAM_MAX_SIZE); // Then wait for response by bootloader // Wait till FLASH_COMPLETE response - dai::bootloader::response::FlashComplete result; + Response::FlashComplete result; do { std::vector data; - if(!receiveBootloaderResponseData(streamId, data)) return {false, "Couldn't receive bootloader response"}; + if(!receiveResponseData(data)) return {false, "Couldn't receive bootloader response"}; - dai::bootloader::response::FlashStatusUpdate update; - if(parseBootloaderResponse(data, update)) { + Response::FlashStatusUpdate update; + if(parseResponse(data, update)) { // if progress callback is set if(progressCb != nullptr) { progressCb(update.progress); } - } else if(parseBootloaderResponse(data, result)) { + } else if(parseResponse(data, result)) { break; } else { // Unknown response, shouldn't happen @@ -440,16 +534,25 @@ std::tuple DeviceBootloader::flashDepthaiApplicationPackage(s return {result.success, result.errorMsg}; } +std::tuple DeviceBootloader::flashDepthaiApplicationPackage(std::vector package) { + return flashDepthaiApplicationPackage(nullptr, package); +} + std::tuple DeviceBootloader::flashBootloader(std::function progressCb, std::string path) { return flashBootloader(Memory::FLASH, bootloaderType, progressCb, path); } std::tuple DeviceBootloader::flashBootloader(Memory memory, Type type, std::function progressCb, std::string path) { + // Check if 'allowFlashingBootloader' is set to true. + if(!allowFlashingBootloader) { + throw std::invalid_argument("DeviceBootloader wasn't initialized to allow flashing bootloader. Set 'allowFlashingBootloader' in constructor"); + } + // Only flash memory is supported for now if(memory != Memory::FLASH) { throw std::invalid_argument("Only FLASH memory is supported for now"); } - if(bootloaderType != type && getVersion() < Version(0, 0, 12)) { + if(bootloaderType != type && getVersion() < Version(Request::UpdateFlashEx2::VERSION)) { std::runtime_error("Current bootloader version doesn't support flashing different type of bootloader"); } @@ -462,29 +565,26 @@ std::tuple DeviceBootloader::flashBootloader(Memory memory, T package = getEmbeddedBootloaderBinary(type); } - // get streamId - streamId_t streamId = stream->getStreamId(); - // If booted and desired bootloader types don't match // Use UpdateFlashEx2 instead to properly flash if(bootloaderType == type) { // Old command // send request to FLASH BOOTLOADER - dai::bootloader::request::UpdateFlash updateFlash; - updateFlash.storage = dai::bootloader::request::UpdateFlash::BOOTLOADER; + Request::UpdateFlash updateFlash; + updateFlash.storage = Request::UpdateFlash::BOOTLOADER; updateFlash.totalSize = static_cast(package.size()); updateFlash.numPackets = ((static_cast(package.size()) - 1) / bootloader::XLINK_STREAM_MAX_SIZE) + 1; - if(!sendBootloaderRequest(streamId, updateFlash)) return {false, "Couldn't send bootloader flash request"}; + if(!sendRequest(updateFlash)) return {false, "Couldn't send bootloader flash request"}; } else { // send request to FLASH BOOTLOADER - dai::bootloader::request::UpdateFlashEx2 updateFlashEx2; + Request::UpdateFlashEx2 updateFlashEx2; updateFlashEx2.memory = memory; updateFlashEx2.offset = dai::bootloader::getStructure(type).offset.at(Section::BOOTLOADER); updateFlashEx2.totalSize = static_cast(package.size()); updateFlashEx2.numPackets = ((static_cast(package.size()) - 1) / bootloader::XLINK_STREAM_MAX_SIZE) + 1; - if(!sendBootloaderRequest(streamId, updateFlashEx2)) return {false, "Couldn't send bootloader flash request"}; + if(!sendRequest(updateFlashEx2)) return {false, "Couldn't send bootloader flash request"}; } // After that send numPackets of data @@ -492,19 +592,19 @@ std::tuple DeviceBootloader::flashBootloader(Memory memory, T // Then wait for response by bootloader // Wait till FLASH_COMPLETE response - dai::bootloader::response::FlashComplete result; + Response::FlashComplete result; do { std::vector data; - if(!receiveBootloaderResponseData(streamId, data)) return {false, "Couldn't receive bootloader response"}; + if(!receiveResponseData(data)) return {false, "Couldn't receive bootloader response"}; - dai::bootloader::response::FlashStatusUpdate update; - if(parseBootloaderResponse(data, update)) { + Response::FlashStatusUpdate update; + if(parseResponse(data, update)) { // if progress callback is set if(progressCb != nullptr) { progressCb(update.progress); } // if flash complete response arrived, break from while loop - } else if(parseBootloaderResponse(data, result)) { + } else if(parseResponse(data, result)) { break; } else { // Unknown response, shouldn't happen @@ -531,24 +631,24 @@ std::tuple DeviceBootloader::flashCustom(Memory memory, uint3 streamId_t streamId = stream->getStreamId(); // send request to FLASH BOOTLOADER - dai::bootloader::request::UpdateFlashEx2 updateFlashEx2; + Request::UpdateFlashEx2 updateFlashEx2; updateFlashEx2.memory = memory; updateFlashEx2.offset = offset; updateFlashEx2.totalSize = static_cast(data.size()); updateFlashEx2.numPackets = ((static_cast(data.size()) - 1) / bootloader::XLINK_STREAM_MAX_SIZE) + 1; - if(!sendBootloaderRequest(streamId, updateFlashEx2)) return {false, "Couldn't send bootloader flash request"}; + if(!sendRequest(updateFlashEx2)) return {false, "Couldn't send bootloader flash request"}; // After that send numPackets of data stream->writeSplit(data.data(), data.size(), bootloader::XLINK_STREAM_MAX_SIZE); // Then wait for response by bootloader // Wait till FLASH_COMPLETE response - dai::bootloader::response::FlashComplete result; + Response::FlashComplete result; do { std::vector data; - if(!receiveBootloaderResponseData(streamId, data)) return {false, "Couldn't receive bootloader response"}; + if(!receiveResponseData(data)) return {false, "Couldn't receive bootloader response"}; - dai::bootloader::response::FlashStatusUpdate update; + Response::FlashStatusUpdate update; if(parseBootloaderResponse(data, update)) { // if progress callback is set if(progressCb != nullptr) { @@ -569,6 +669,137 @@ std::tuple DeviceBootloader::flashCustom(Memory memory, uint3 } */ +nlohmann::json DeviceBootloader::readConfigData(Memory memory, Type type) { + // Send request to GET_BOOTLOADER_CONFIG + Request::GetBootloaderConfig getConfigReq; + getConfigReq.memory = memory; + + if(type != Type::AUTO) { + const auto confStructure = bootloader::getStructure(type); + getConfigReq.offset = confStructure.offset.at(bootloader::Section::BOOTLOADER_CONFIG); + getConfigReq.maxSize = confStructure.size.at(bootloader::Section::BOOTLOADER_CONFIG); + } else { + // leaves as default values, which correspond to AUTO + } + + if(!sendRequest(getConfigReq)) return {false, "Couldn't send request to get configuration data"}; + + // Get response + Response::GetBootloaderConfig resp; + receiveResponse(resp); + + if(resp.success) { + // Read back bootloader config (1 packet max) + auto bsonConfig = stream->read(); + // Parse from BSON + return nlohmann::json::from_bson(bsonConfig); + } else { + return {}; + } +} + +std::tuple DeviceBootloader::flashConfigClear(Memory memory, Type type) { + // send request to SET_BOOTLOADER_CONFIG + Request::SetBootloaderConfig setConfigReq; + setConfigReq.memory = memory; + if(type != Type::AUTO) { + setConfigReq.offset = bootloader::getStructure(type).offset.at(bootloader::Section::BOOTLOADER_CONFIG); + } + + setConfigReq.numPackets = 0; + setConfigReq.totalSize = 0; + setConfigReq.clearConfig = 1; + if(!sendRequest(setConfigReq)) return {false, "Couldn't send request to flash configuration data"}; + + // Read back response + Response::FlashComplete result; + receiveResponse(result); + + // Return if flashing was successful + return {result.success, result.errorMsg}; +} + +std::tuple DeviceBootloader::flashConfigData(nlohmann::json configData, Memory memory, Type type) { + // Parse to BSON + auto bson = nlohmann::json::to_bson(configData); + + // Send request to SET_BOOTLOADER_CONFIG + Request::SetBootloaderConfig setConfigReq; + setConfigReq.memory = memory; + if(type != Type::AUTO) { + setConfigReq.offset = bootloader::getStructure(type).offset.at(bootloader::Section::BOOTLOADER_CONFIG); + } + setConfigReq.numPackets = 1; + setConfigReq.totalSize = bson.size(); + setConfigReq.clearConfig = 0; + if(!sendRequest(setConfigReq)) return {false, "Couldn't send request to flash configuration data"}; + + // Send 1 packet, of bson config data + stream->write(bson); + + // Read back response + Response::FlashComplete result; + receiveResponse(result); + + // Return if flashing was successful + return {result.success, result.errorMsg}; +} + +std::tuple DeviceBootloader::flashConfigFile(std::string configPath, Memory memory, Type type) { + // read a JSON file + std::ifstream configInputStream(configPath); + if(!configInputStream.is_open()) throw std::runtime_error("Cannot flash configuration, JSON at path: " + configPath + " doesn't exist"); + nlohmann::json configJson; + configInputStream >> configJson; + return flashConfigData(configJson, memory, type); +} + +DeviceBootloader::Config DeviceBootloader::readConfig(Memory memory, Type type) { + auto json = readConfigData(memory, type); + // Implicit parse from json to Config + return json; +} + +std::tuple DeviceBootloader::flashConfig(const Config& config, Memory memory, Type type) { + // Implicit parse from Config to json + return flashConfigData(config, memory, type); +} + +// Boot memory +void DeviceBootloader::bootMemory(const std::vector& embeddedFw) { + // Send request to boot firmware directly from bootloader + Request::BootMemory bootMemory; + bootMemory.totalSize = static_cast(embeddedFw.size()); + bootMemory.numPackets = ((static_cast(embeddedFw.size()) - 1) / bootloader::XLINK_STREAM_MAX_SIZE) + 1; + if(!sendRequest(bootMemory)) { + throw std::runtime_error("Error trying to connect to device"); + } + + // After that send numPackets of data + stream->writeSplit(embeddedFw.data(), embeddedFw.size(), bootloader::XLINK_STREAM_MAX_SIZE); + + // Then wait for the link to fall down + try { + stream->read(); + } catch(const std::exception& ex) { + // ignore + } +} + +void DeviceBootloader::bootUsbRomBootloader() { + // Boot into USB ROM BOOTLOADER now + if(!sendRequest(Request::UsbRomBoot{})) { + throw std::runtime_error("Error trying to connect to device"); + } + + // Then wait for the link to fall down + try { + stream->read(); + } catch(const std::exception& ex) { + // ignore + } +} + bool DeviceBootloader::isEmbeddedVersion() const { return isEmbedded; } @@ -582,11 +813,7 @@ DeviceBootloader::Version::Version(const std::string& v) : versionMajor(0), vers if(std::sscanf(v.c_str(), "%u.%u.%u", &versionMajor, &versionMinor, &versionPatch) != 3) throw std::runtime_error("Cannot parse version: " + v); } -DeviceBootloader::Version::Version(unsigned vmajor, unsigned vminor, unsigned vpatch) { - this->versionMajor = vmajor; - this->versionMinor = vminor; - this->versionPatch = vpatch; -} +DeviceBootloader::Version::Version(unsigned vmajor, unsigned vminor, unsigned vpatch) : versionMajor(vmajor), versionMinor(vminor), versionPatch(vpatch) {} bool DeviceBootloader::Version::operator==(const Version& other) const { if(versionMajor == other.versionMajor && versionMinor == other.versionMinor && versionPatch == other.versionPatch) return true; @@ -612,4 +839,149 @@ std::string DeviceBootloader::Version::toString() const { return std::to_string(versionMajor) + "." + std::to_string(versionMinor) + "." + std::to_string(versionPatch); } +template +bool DeviceBootloader::sendRequest(const T& request) { + if(stream == nullptr) return false; + + // Do a version check beforehand + if(getVersion() < Version(T::VERSION)) { + throw std::runtime_error( + fmt::format("Bootloader version {} required to send request '{}'. Current version {}", T::VERSION, T::NAME, getVersion().toString())); + } + + try { + stream->write((uint8_t*)&request, sizeof(T)); + } catch(const std::exception& ex) { + return false; + } + + return true; +} + +bool DeviceBootloader::receiveResponseData(std::vector& data) { + if(stream == nullptr) return false; + + data = stream->read(); + return true; +} + +template +bool DeviceBootloader::parseResponse(const std::vector& data, T& response) { + // Checks that 'data' is type T + Response::Command command; + if(data.size() < sizeof(command)) return false; + memcpy(&command, data.data(), sizeof(command)); + if(response.cmd != command) return false; + if(data.size() < sizeof(response)) return false; + + // If yes, memcpy to response + memcpy(&response, data.data(), sizeof(response)); + return true; +} + +template +bool DeviceBootloader::receiveResponse(T& response) { + if(stream == nullptr) return false; + // Receive data first + std::vector data; + if(!receiveResponseData(data)) return false; + + // Then try to parse + if(!parseResponse(data, response)) return false; + + return true; +} + +// Config functions +void DeviceBootloader::Config::setStaticIPv4(std::string ip, std::string mask, std::string gateway) { + network.ipv4 = platform::getIPv4AddressAsBinary(ip); + network.ipv4Mask = platform::getIPv4AddressAsBinary(mask); + network.ipv4Gateway = platform::getIPv4AddressAsBinary(gateway); + network.staticIpv4 = true; +} +void DeviceBootloader::Config::setDynamicIPv4(std::string ip, std::string mask, std::string gateway) { + network.ipv4 = platform::getIPv4AddressAsBinary(ip); + network.ipv4Mask = platform::getIPv4AddressAsBinary(mask); + network.ipv4Gateway = platform::getIPv4AddressAsBinary(gateway); + network.staticIpv4 = false; +} + +bool DeviceBootloader::Config::isStaticIPV4() { + return network.staticIpv4; +} + +std::string DeviceBootloader::Config::getIPv4() { + return platform::getIPv4AddressAsString(network.ipv4); +} +std::string DeviceBootloader::Config::getIPv4Mask() { + return platform::getIPv4AddressAsString(network.ipv4Mask); +} +std::string DeviceBootloader::Config::getIPv4Gateway() { + return platform::getIPv4AddressAsString(network.ipv4Gateway); +} + +void DeviceBootloader::Config::setDnsIPv4(std::string dns, std::string dnsAlt) { + network.ipv4Dns = platform::getIPv4AddressAsBinary(dns); + network.ipv4DnsAlt = platform::getIPv4AddressAsBinary(dnsAlt); +} + +std::string DeviceBootloader::Config::getDnsIPv4() { + return platform::getIPv4AddressAsString(network.ipv4Dns); +} + +std::string DeviceBootloader::Config::getDnsAltIPv4() { + return platform::getIPv4AddressAsString(network.ipv4DnsAlt); +} + +void DeviceBootloader::Config::setUsbTimeout(std::chrono::milliseconds ms) { + usb.timeoutMs = ms.count(); +} + +std::chrono::milliseconds DeviceBootloader::Config::getUsbTimeout() { + return std::chrono::milliseconds(usb.timeoutMs); +} + +void DeviceBootloader::Config::setNetworkTimeout(std::chrono::milliseconds ms) { + network.timeoutMs = ms.count(); +} + +std::chrono::milliseconds DeviceBootloader::Config::getNetworkTimeout() { + return std::chrono::milliseconds(network.timeoutMs); +} + +void DeviceBootloader::Config::setUsbMaxSpeed(UsbSpeed speed) { + usb.maxUsbSpeed = static_cast(speed); +} + +UsbSpeed DeviceBootloader::Config::getUsbMaxSpeed() { + return static_cast(usb.maxUsbSpeed); +} + +void DeviceBootloader::Config::setMacAddress(std::string mac) { + std::array a; + int last = -1; + int rc = std::sscanf(mac.c_str(), "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx%n", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5], &last); + if(rc != 6 || static_cast(mac.size()) != last) { + throw std::invalid_argument("Invalid MAC address format " + mac); + } + + // Set the parsed mac address + network.mac = a; +} +std::string DeviceBootloader::Config::getMacAddress() { + // 32 characters is adequite for MAC address representation + std::array macStr = {}; + std::snprintf(macStr.data(), + macStr.size(), + "%02X:%02X:%02X:%02X:%02X:%02X", + network.mac[0], + network.mac[1], + network.mac[2], + network.mac[3], + network.mac[4], + network.mac[5]); + + return std::string(macStr.data()); +} + } // namespace dai diff --git a/src/openvino/OpenVINO.cpp b/src/openvino/OpenVINO.cpp index d2f4d8cd47..816cd77065 100644 --- a/src/openvino/OpenVINO.cpp +++ b/src/openvino/OpenVINO.cpp @@ -11,6 +11,9 @@ namespace dai { +// Definition +constexpr OpenVINO::Version OpenVINO::DEFAULT_VERSION; + // static member init // {{major, minor}, 'latest openvino version to support it'} // major and minor represent openvino NN blob version information diff --git a/src/pipeline/Pipeline.cpp b/src/pipeline/Pipeline.cpp index 62daf1cfe1..2e5e0a33d8 100644 --- a/src/pipeline/Pipeline.cpp +++ b/src/pipeline/Pipeline.cpp @@ -12,9 +12,6 @@ namespace dai { -constexpr OpenVINO::Version PipelineImpl::DEFAULT_OPENVINO_VERSION; -constexpr OpenVINO::Version Pipeline::DEFAULT_OPENVINO_VERSION; - Node::Id PipelineImpl::getNextUniqueId() { return latestId++; } @@ -82,7 +79,7 @@ std::vector> PipelineImpl::getAllNodes() { return nodes; } -void PipelineImpl::serialize(PipelineSchema& schema, Assets& assets, std::vector& assetStorage, OpenVINO::Version& version) const { +void PipelineImpl::serialize(PipelineSchema& schema, Assets& assets, std::vector& assetStorage) const { // Set schema schema = getPipelineSchema(); @@ -95,10 +92,8 @@ void PipelineImpl::serialize(PipelineSchema& schema, Assets& assets, std::vector for(const auto& kv : nodeMap) { kv.second->getAssetManager().serialize(mutableAssets, assetStorage, fmt::format("/node/{}/", kv.second->id)); } - assets = mutableAssets; - // detect and set openvino version - version = getPipelineOpenVINOVersion(); + assets = mutableAssets; } PipelineSchema PipelineImpl::getPipelineSchema() const { @@ -183,7 +178,16 @@ PipelineSchema PipelineImpl::getPipelineSchema() const { return schema; } -OpenVINO::Version PipelineImpl::getPipelineOpenVINOVersion() const { +bool PipelineImpl::isOpenVINOVersionCompatible(OpenVINO::Version version) const { + auto ver = getPipelineOpenVINOVersion(); + if(ver) { + return OpenVINO::areVersionsBlobCompatible(version, *ver); + } else { + return true; + } +} + +tl::optional PipelineImpl::getPipelineOpenVINOVersion() const { // Loop over nodes, and get the required information tl::optional version; std::string lastNodeNameWithRequiredVersion = ""; @@ -223,17 +227,24 @@ OpenVINO::Version PipelineImpl::getPipelineOpenVINOVersion() const { } } - // After iterating over, set openvinoVersion - OpenVINO::Version openvinoVersion = DEFAULT_OPENVINO_VERSION; + // After iterating over, return appropriate version if(forceRequiredOpenVINOVersion) { - // set to forced version - openvinoVersion = *forceRequiredOpenVINOVersion; + // Return forced version + return forceRequiredOpenVINOVersion; } else if(version) { - // set to detected version - openvinoVersion = *version; + // Return detected version + return version; + } else { + // Return null + return tl::nullopt; } +} - return openvinoVersion; +Device::Config PipelineImpl::getDeviceConfig() const { + Device::Config config; + config.version = getPipelineOpenVINOVersion().value_or(OpenVINO::DEFAULT_VERSION); + // TODO(themarpe) - fill out rest of preboot config + return config; } void PipelineImpl::setCameraTuningBlobPath(const std::string& path) { diff --git a/src/utility/BootloaderHelper.hpp b/src/utility/BootloaderHelper.hpp deleted file mode 100644 index 8fd11b3f49..0000000000 --- a/src/utility/BootloaderHelper.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -// std -#include -#include - -// libraries -#include - -// shared -#include "depthai-bootloader-shared/Bootloader.hpp" - -namespace dai -{ - - template - bool sendBootloaderRequest(streamId_t streamId, T request){ - if(XLinkWriteData(streamId, (uint8_t*) &request, sizeof(T)) != X_LINK_SUCCESS) return false; - return true; - } - - inline bool receiveBootloaderResponseData(streamId_t streamId, std::vector& data){ - data = std::vector(); - - streamPacketDesc_t* pPacket; - if(XLinkReadData(streamId, &pPacket) != X_LINK_SUCCESS) return false; - - // Resize vector - data.resize(pPacket->length); - - // copy data - memcpy(data.data(), pPacket->data, pPacket->length); - - // release data - if(XLinkReleaseData(streamId) != X_LINK_SUCCESS) return false; - - return true; - } - - template - bool parseBootloaderResponse(const std::vector& data, T& response){ - // Checks that 'data' is type T - dai::bootloader::response::Command command; - if(data.size() < sizeof(command)) return false; - memcpy(&command, data.data(), sizeof(command)); - if(response.cmd != command) return false; - if(data.size() < sizeof(response)) return false; - - // If yes, memcpy to response - memcpy(&response, data.data(), sizeof(response)); - return true; - } - - template - bool receiveBootloaderResponse(streamId_t streamId, T& response){ - // Receive data first - std::vector data; - if(!receiveBootloaderResponseData(streamId, data)) return false; - - // Then try to parse - if(!parseBootloaderResponse(data, response)) return false; - - return true; - } - -} // namespace dai - - diff --git a/src/utility/Initialization.cpp b/src/utility/Initialization.cpp index 7060f2d824..5b7930acb0 100644 --- a/src/utility/Initialization.cpp +++ b/src/utility/Initialization.cpp @@ -5,10 +5,15 @@ #include "utility/Resources.hpp" // libraries +#include "XLink/XLink.h" +#include "backward.hpp" #include "spdlog/cfg/env.h" #include "spdlog/cfg/helpers.h" #include "spdlog/details/os.h" #include "spdlog/spdlog.h" +extern "C" { +#include "XLink/XLinkLog.h" +} // For easier access to dai namespaced symbols namespace dai { @@ -29,12 +34,20 @@ namespace { } // namespace -bool initialize(std::string additionalInfo) { +// Backward library stacktrace handling +static backward::SignalHandling* pSignalHandler; + +bool initialize(std::string additionalInfo, bool installSignalHandler) { // atomic bool for checking whether depthai was already initialized static std::atomic initialized{false}; - if(initialized.exchange(true)) return true; + // install backward if specified + auto envSignalHandler = spdlog::details::os::getenv("DEPTHAI_INSTALL_SIGNAL_HANDLER"); + if(installSignalHandler && envSignalHandler != "0") { + pSignalHandler = new backward::SignalHandling; + } + // Set global logging level from ENV variable 'DEPTHAI_LEVEL' // Taken from spdlog, to replace with DEPTHAI_LEVEL instead of SPDLOG_LEVEL // spdlog::cfg::load_env_levels(); @@ -46,11 +59,6 @@ bool initialize(std::string additionalInfo) { spdlog::set_level(spdlog::level::warn); } - // auto debugger_val = spdlog::details::os::getenv("DEPTHAI_DEBUGGER"); - // if(!debugger_val.empty()){ - // // TODO(themarpe) - instruct Device class that first available device is also a booted device - // } - // Print core commit and build datetime if(!additionalInfo.empty()) { spdlog::debug("{}", additionalInfo); @@ -63,6 +71,16 @@ bool initialize(std::string additionalInfo) { // Preload Resources (getting instance causes some internal lazy loading to start) Resources::getInstance(); + // Static global handler + static XLinkGlobalHandler_t xlinkGlobalHandler = {}; + xlinkGlobalHandler.protocol = X_LINK_USB_VSC; + auto status = XLinkInitialize(&xlinkGlobalHandler); + if(X_LINK_SUCCESS != status) { + throw std::runtime_error("Couldn't initialize XLink"); + } + // Suppress XLink related errors + mvLogDefaultLevelSet(MVLOG_LAST); + spdlog::debug("Initialize - finished"); return true; diff --git a/src/utility/Platform.cpp b/src/utility/Platform.cpp new file mode 100644 index 0000000000..7847a6f44e --- /dev/null +++ b/src/utility/Platform.cpp @@ -0,0 +1,52 @@ +#include "Platform.hpp" + +// Platform specific +#if defined(_WIN32) || defined(__USE_W32_SOCKETS) + #include + #ifdef _MSC_VER + #pragma comment(lib, "Ws2_32.lib") + #endif +#else + #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) + #include + #endif + #include +#endif + +namespace dai { +namespace platform { + +uint32_t getIPv4AddressAsBinary(std::string address) { + uint32_t binary = 0; + if(address == "") { + // inet_addr returns 0xFFFFFFFF if addr is "" + return 0; + } + +#if defined(_WIN32) || defined(__USE_W32_SOCKETS) + #if(_WIN32_WINNT <= 0x0501) + binary = inet_addr(address.c_str()); // for XP + #else + inet_pton(AF_INET, address.c_str(), &binary); // for Vista or higher + #endif +#else + inet_pton(AF_INET, address.c_str(), &binary); +#endif + + return binary; +} + +std::string getIPv4AddressAsString(std::uint32_t binary) { + char address[INET_ADDRSTRLEN] = {0}; + +#if defined(_WIN32) || defined(__USE_W32_SOCKETS) + InetNtopA(AF_INET, &binary, address, sizeof(address)); +#else + inet_ntop(AF_INET, &binary, address, sizeof(address)); +#endif + + return {address}; +} + +} // namespace platform +} // namespace dai diff --git a/src/utility/Platform.hpp b/src/utility/Platform.hpp new file mode 100644 index 0000000000..ef299164b3 --- /dev/null +++ b/src/utility/Platform.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +namespace dai { +namespace platform { + +uint32_t getIPv4AddressAsBinary(std::string address); +std::string getIPv4AddressAsString(std::uint32_t binary); + +} +} diff --git a/src/utility/Resources.cpp b/src/utility/Resources.cpp index 3f8e00f19b..cb13f41141 100644 --- a/src/utility/Resources.cpp +++ b/src/utility/Resources.cpp @@ -16,6 +16,10 @@ #include "spdlog/fmt/chrono.h" #include "spdlog/spdlog.h" +// shared +#include "depthai-shared/device/PrebootConfig.hpp" +#include "depthai-shared/utility/Checksum.hpp" + extern "C" { #include "bspatch/bspatch.h" } @@ -28,157 +32,143 @@ CMRC_DECLARE(depthai); namespace dai { +static std::vector createPrebootHeader(const std::vector& payload, uint32_t magic1, uint32_t magic2); + constexpr static auto CMRC_DEPTHAI_DEVICE_TAR_XZ = "depthai-device-fwp-" DEPTHAI_DEVICE_VERSION ".tar.xz"; // Main FW constexpr static auto DEPTHAI_CMD_OPENVINO_2021_4_PATH = "depthai-device-openvino-2021.4-" DEPTHAI_DEVICE_VERSION ".cmd"; +constexpr static auto MAIN_FW_PATH = DEPTHAI_CMD_OPENVINO_2021_4_PATH; +constexpr static auto& MAIN_FW_VERSION = OpenVINO::DEFAULT_VERSION; // Patches from Main FW - constexpr static auto DEPTHAI_CMD_OPENVINO_2020_3_PATCH_PATH = "depthai-device-openvino-2020.3-" DEPTHAI_DEVICE_VERSION ".patch"; constexpr static auto DEPTHAI_CMD_OPENVINO_2020_4_PATCH_PATH = "depthai-device-openvino-2020.4-" DEPTHAI_DEVICE_VERSION ".patch"; constexpr static auto DEPTHAI_CMD_OPENVINO_2021_1_PATCH_PATH = "depthai-device-openvino-2021.1-" DEPTHAI_DEVICE_VERSION ".patch"; constexpr static auto DEPTHAI_CMD_OPENVINO_2021_2_PATCH_PATH = "depthai-device-openvino-2021.2-" DEPTHAI_DEVICE_VERSION ".patch"; constexpr static auto DEPTHAI_CMD_OPENVINO_2021_3_PATCH_PATH = "depthai-device-openvino-2021.3-" DEPTHAI_DEVICE_VERSION ".patch"; -// Usb2 patches -constexpr static auto DEPTHAI_CMD_OPENVINO_2020_3_USB2_PATCH_PATH = "depthai-device-usb2-patch-openvino-2020.3-" DEPTHAI_DEVICE_VERSION ".patch"; -constexpr static auto DEPTHAI_CMD_OPENVINO_2020_4_USB2_PATCH_PATH = "depthai-device-usb2-patch-openvino-2020.4-" DEPTHAI_DEVICE_VERSION ".patch"; -constexpr static auto DEPTHAI_CMD_OPENVINO_2021_1_USB2_PATCH_PATH = "depthai-device-usb2-patch-openvino-2021.1-" DEPTHAI_DEVICE_VERSION ".patch"; -constexpr static auto DEPTHAI_CMD_OPENVINO_2021_2_USB2_PATCH_PATH = "depthai-device-usb2-patch-openvino-2021.2-" DEPTHAI_DEVICE_VERSION ".patch"; -constexpr static auto DEPTHAI_CMD_OPENVINO_2021_3_USB2_PATCH_PATH = "depthai-device-usb2-patch-openvino-2021.3-" DEPTHAI_DEVICE_VERSION ".patch"; -constexpr static auto DEPTHAI_CMD_OPENVINO_2021_4_USB2_PATCH_PATH = "depthai-device-usb2-patch-openvino-2021.4-" DEPTHAI_DEVICE_VERSION ".patch"; - -constexpr static std::array RESOURCE_LIST_DEVICE = { - DEPTHAI_CMD_OPENVINO_2020_3_PATCH_PATH, - DEPTHAI_CMD_OPENVINO_2020_4_PATCH_PATH, - DEPTHAI_CMD_OPENVINO_2021_1_PATCH_PATH, - DEPTHAI_CMD_OPENVINO_2021_2_PATCH_PATH, - DEPTHAI_CMD_OPENVINO_2021_3_PATCH_PATH, - DEPTHAI_CMD_OPENVINO_2021_4_PATH, - DEPTHAI_CMD_OPENVINO_2020_3_USB2_PATCH_PATH, - DEPTHAI_CMD_OPENVINO_2020_4_USB2_PATCH_PATH, - DEPTHAI_CMD_OPENVINO_2021_1_USB2_PATCH_PATH, - DEPTHAI_CMD_OPENVINO_2021_2_USB2_PATCH_PATH, - DEPTHAI_CMD_OPENVINO_2021_3_USB2_PATCH_PATH, - DEPTHAI_CMD_OPENVINO_2021_4_USB2_PATCH_PATH, +// Creates std::array without explicitly needing to state the size +template +static constexpr auto array_of(T&&... t) -> std::array { + return {{std::forward(t)...}}; +} -}; +constexpr static auto RESOURCE_LIST_DEVICE = array_of(DEPTHAI_CMD_OPENVINO_2021_4_PATH, + DEPTHAI_CMD_OPENVINO_2020_3_PATCH_PATH, + DEPTHAI_CMD_OPENVINO_2020_4_PATCH_PATH, + DEPTHAI_CMD_OPENVINO_2021_1_PATCH_PATH, + DEPTHAI_CMD_OPENVINO_2021_2_PATCH_PATH, + DEPTHAI_CMD_OPENVINO_2021_3_PATCH_PATH); + +std::vector Resources::getDeviceFirmware(Device::Config config, std::string pathToMvcmd) { + // Acquire mutex (this mutex signifies that lazy load is complete) + // It is necessary when accessing resourceMap variable + std::unique_lock lock(mtxDevice); -std::vector Resources::getDeviceBinary(OpenVINO::Version version, bool usb2Mode) { - std::vector finalCmd; + std::vector finalFwBinary; - // Check if env variable DEPTHAI_DEVICE_BINARY is set + // Get OpenVINO version + auto& version = config.version; + + // Check if pathToMvcmd variable is set + std::string finalFwBinaryPath = ""; + if(!pathToMvcmd.empty()) { + finalFwBinaryPath = pathToMvcmd; + } + // Override if env variable DEPTHAI_DEVICE_BINARY is set auto fwBinaryPath = spdlog::details::os::getenv("DEPTHAI_DEVICE_BINARY"); if(!fwBinaryPath.empty()) { + finalFwBinaryPath = fwBinaryPath; + } + // Return binary from file if any of above paths are present + if(!finalFwBinaryPath.empty()) { // Load binary file at path - std::ifstream stream(fwBinaryPath, std::ios::binary); + std::ifstream stream(finalFwBinaryPath, std::ios::binary); if(!stream.is_open()) { // Throw an error // TODO(themarpe) - Unify exceptions into meaningful groups throw std::runtime_error(fmt::format("File at path {} pointed to by DEPTHAI_DEVICE_BINARY doesn't exist.", fwBinaryPath)); } + spdlog::warn("Overriding firmware: {}", fwBinaryPath); // Read the file and return its contents - return std::vector(std::istreambuf_iterator(stream), {}); - } - + finalFwBinary = std::vector(std::istreambuf_iterator(stream), {}); + } else { // Binaries are resource compiled #ifdef DEPTHAI_RESOURCE_COMPILED_BINARIES - // Temporary binary - std::vector tmpDepthaiBinary; - // Main FW - std::vector depthaiBinary = resourceMapDevice[DEPTHAI_CMD_OPENVINO_2021_4_PATH]; - // Patch from main to specified - std::vector depthaiPatch = resourceMapDevice[DEPTHAI_CMD_OPENVINO_2021_3_PATCH_PATH]; - // Patch from specified to usb2 specified - std::vector depthaiUsb2Patch = resourceMapDevice[DEPTHAI_CMD_OPENVINO_2021_4_USB2_PATCH_PATH]; - - switch(version) { - case OpenVINO::VERSION_2020_3: - depthaiPatch = resourceMapDevice[DEPTHAI_CMD_OPENVINO_2020_3_PATCH_PATH]; - depthaiUsb2Patch = resourceMapDevice[DEPTHAI_CMD_OPENVINO_2020_3_USB2_PATCH_PATH]; - break; + // Main FW + std::vector depthaiBinary; + // Patch from main to specified + std::vector depthaiPatch; - case OpenVINO::VERSION_2020_4: - depthaiPatch = resourceMapDevice[DEPTHAI_CMD_OPENVINO_2020_4_PATCH_PATH]; - depthaiUsb2Patch = resourceMapDevice[DEPTHAI_CMD_OPENVINO_2020_4_USB2_PATCH_PATH]; - break; - - case OpenVINO::VERSION_2021_1: - depthaiPatch = resourceMapDevice[DEPTHAI_CMD_OPENVINO_2021_1_PATCH_PATH]; - depthaiUsb2Patch = resourceMapDevice[DEPTHAI_CMD_OPENVINO_2021_1_USB2_PATCH_PATH]; - break; - - case OpenVINO::VERSION_2021_2: - depthaiPatch = resourceMapDevice[DEPTHAI_CMD_OPENVINO_2021_2_PATCH_PATH]; - depthaiUsb2Patch = resourceMapDevice[DEPTHAI_CMD_OPENVINO_2021_2_USB2_PATCH_PATH]; - break; - - case OpenVINO::VERSION_2021_3: - depthaiPatch = resourceMapDevice[DEPTHAI_CMD_OPENVINO_2021_3_PATCH_PATH]; - depthaiUsb2Patch = resourceMapDevice[DEPTHAI_CMD_OPENVINO_2021_3_USB2_PATCH_PATH]; - break; - - case OpenVINO::VERSION_2021_4: - depthaiBinary = resourceMapDevice[DEPTHAI_CMD_OPENVINO_2021_4_PATH]; - depthaiUsb2Patch = resourceMapDevice[DEPTHAI_CMD_OPENVINO_2021_4_USB2_PATCH_PATH]; - break; - } - - // is patching required? - if(version != OpenVINO::VERSION_2021_4) { - spdlog::debug("Patching OpenVINO FW version from {} to {}", OpenVINO::getVersionName(OpenVINO::VERSION_2021_4), OpenVINO::getVersionName(version)); - - // Get new size - int64_t patchedSize = bspatch_mem_get_newsize(depthaiPatch.data(), depthaiPatch.size()); + switch(version) { + case OpenVINO::VERSION_2020_3: + depthaiPatch = resourceMapDevice[DEPTHAI_CMD_OPENVINO_2020_3_PATCH_PATH]; + break; - // Reserve space for patched binary - tmpDepthaiBinary.resize(patchedSize); + case OpenVINO::VERSION_2020_4: + depthaiPatch = resourceMapDevice[DEPTHAI_CMD_OPENVINO_2020_4_PATCH_PATH]; + break; - // Patch - int error = bspatch_mem(depthaiBinary.data(), depthaiBinary.size(), depthaiPatch.data(), depthaiPatch.size(), tmpDepthaiBinary.data()); + case OpenVINO::VERSION_2021_1: + depthaiPatch = resourceMapDevice[DEPTHAI_CMD_OPENVINO_2021_1_PATCH_PATH]; + break; - // if patch not successful - if(error > 0) throw std::runtime_error("Error while patching cmd for usb2 mode"); + case OpenVINO::VERSION_2021_2: + depthaiPatch = resourceMapDevice[DEPTHAI_CMD_OPENVINO_2021_2_PATCH_PATH]; + break; - // Change depthaiBinary to tmpDepthaiBinary - depthaiBinary = tmpDepthaiBinary; - } - - if(usb2Mode) { - #ifdef DEPTHAI_PATCH_ONLY_MODE + case OpenVINO::VERSION_2021_3: + depthaiPatch = resourceMapDevice[DEPTHAI_CMD_OPENVINO_2021_3_PATCH_PATH]; + break; - spdlog::debug("Patching FW version {} to USB2 mode", OpenVINO::getVersionName(version)); + case MAIN_FW_VERSION: + depthaiBinary = resourceMapDevice[MAIN_FW_PATH]; + break; + } - // Get new size - int64_t patchedSize = bspatch_mem_get_newsize(depthaiUsb2Patch.data(), depthaiUsb2Patch.size()); + // is patching required? + if(!depthaiPatch.empty()) { + spdlog::debug("Patching OpenVINO FW version from {} to {}", OpenVINO::getVersionName(MAIN_FW_VERSION), OpenVINO::getVersionName(version)); - // Reserve space for patched binary - finalCmd.resize(patchedSize); + // Load full binary for patch + depthaiBinary = resourceMapDevice[MAIN_FW_PATH]; - // Patch - int error = bspatch_mem(depthaiBinary.data(), depthaiBinary.size(), depthaiUsb2Patch.data(), depthaiUsb2Patch.size(), finalCmd.data()); + // Get new size + int64_t patchedSize = bspatch_mem_get_newsize(depthaiPatch.data(), depthaiPatch.size()); - // if patch not successful - if(error > 0) throw std::runtime_error("Error while patching cmd for usb2 mode"); + // Reserve space for patched binary + std::vector tmpDepthaiBinary{}; + tmpDepthaiBinary.resize(patchedSize); - #else + // Patch + int error = bspatch_mem(depthaiBinary.data(), depthaiBinary.size(), depthaiPatch.data(), depthaiPatch.size(), tmpDepthaiBinary.data()); - static_assert("Unsupported currently"); + // if patch not successful + if(error > 0) { + throw std::runtime_error(fmt::format( + "Error while patching OpenVINO FW version from {} to {}", OpenVINO::getVersionName(MAIN_FW_VERSION), OpenVINO::getVersionName(version))); + } - #endif + // Change depthaiBinary to tmpDepthaiBinary + depthaiBinary = std::move(tmpDepthaiBinary); + } - } else { - return depthaiBinary; - } + finalFwBinary = std::move(depthaiBinary); #else - // Binaries from default path (TODO) + // Binaries from default path (TODO) #endif + } - return finalCmd; + // Prepend preboot config + auto prebootHeader = createPrebootHeader(nlohmann::json::to_msgpack(config.preboot), PREBOOT_CONFIG_MAGIC1, PREBOOT_CONFIG_MAGIC2); + finalFwBinary.insert(finalFwBinary.begin(), prebootHeader.begin(), prebootHeader.end()); + + // Return created firmware + return finalFwBinary; } constexpr static auto CMRC_DEPTHAI_BOOTLOADER_TAR_XZ = "depthai-bootloader-fwp-" DEPTHAI_BOOTLOADER_VERSION ".tar.xz"; @@ -191,11 +181,36 @@ constexpr static std::array RESOURCE_LIST_BOOTLOADER = { }; std::vector Resources::getBootloaderFirmware(dai::bootloader::Type type) { + // Check if env variable DEPTHAI_BOOTLOADER_BINARY_USB/_ETH is set + std::string blEnvVar; + if(type == dai::bootloader::Type::USB) { + blEnvVar = "DEPTHAI_BOOTLOADER_BINARY_USB"; + } else if(type == dai::bootloader::Type::NETWORK) { + blEnvVar = "DEPTHAI_BOOTLOADER_BINARY_ETH"; + } + auto blBinaryPath = spdlog::details::os::getenv(blEnvVar.c_str()); + if(!blBinaryPath.empty()) { + // Load binary file at path + std::ifstream stream(blBinaryPath, std::ios::binary); + if(!stream.is_open()) { + // Throw an error + // TODO(themarpe) - Unify exceptions into meaningful groups + throw std::runtime_error(fmt::format("File at path {} pointed to by {} doesn't exist.", blBinaryPath, blEnvVar)); + } + spdlog::warn("Overriding bootloader {}: {}", blEnvVar, blBinaryPath); + // Read the file and return its content + return std::vector(std::istreambuf_iterator(stream), {}); + } + // Acquire mutex (this mutex signifies that lazy load is complete) // It is necessary when accessing resourceMap variable std::unique_lock lock(mtxBootloader); switch(type) { + case dai::bootloader::Type::AUTO: + throw std::invalid_argument("DeviceBootloader::Type::AUTO not allowed, when getting bootloader firmware."); + break; + case dai::bootloader::Type::USB: return resourceMapBootloader[DEVICE_BOOTLOADER_USB_PATH]; break; @@ -353,12 +368,65 @@ Resources::~Resources() { // Get device firmware std::vector Resources::getDeviceFirmware(bool usb2Mode, OpenVINO::Version version) { - // Acquire mutex (this mutex signifies that lazy load is complete) - // It is necessary when accessing resourceMapDevice variable - std::unique_lock lock(mtxDevice); + Device::Config cfg; + if(usb2Mode) { + cfg.preboot.usb.maxSpeed = UsbSpeed::HIGH; + } else { + cfg.preboot.usb.maxSpeed = Device::DEFAULT_USB_SPEED; + } + cfg.version = version; + + return getDeviceFirmware(cfg); +} + +std::vector createPrebootHeader(const std::vector& payload, uint32_t magic1, uint32_t magic2) { + const std::uint8_t HEADER[] = {77, + 65, + 50, + 120, + 0x8A, + static_cast((magic1 >> 0) & 0xFF), + static_cast((magic1 >> 8) & 0xFF), + static_cast((magic1 >> 16) & 0xFF), + static_cast((magic1 >> 24) & 0xFF)}; + + // Store the constructed preboot information + std::vector prebootHeader; + + // Store initial header + prebootHeader.insert(prebootHeader.begin(), std::begin(HEADER), std::end(HEADER)); + + // Calculate size + std::size_t totalPayloadSize = payload.size() + sizeof(magic2) + sizeof(uint32_t) + sizeof(uint32_t); + std::size_t toAddBytes = 0; + if(totalPayloadSize % 4 != 0) { + toAddBytes = 4 - (totalPayloadSize % 4); + } + std::size_t totalSize = totalPayloadSize + toAddBytes; + std::size_t totalSizeWord = totalSize / 4; + + // Write size in words in little endian + prebootHeader.push_back((totalSizeWord >> 0) & 0xFF); + prebootHeader.push_back((totalSizeWord >> 8) & 0xFF); + + // Compute payload checksum + auto checksum = utility::checksum(payload.data(), payload.size()); + + // Write checksum & payload size as uint32_t LE + for(const auto& field : {magic2, checksum, static_cast(payload.size())}) { + for(int i = 0; i < 4; i++) { + prebootHeader.push_back((field >> (i * 8)) & 0xFF); + } + } + + // Copy payload + prebootHeader.insert(prebootHeader.end(), payload.begin(), payload.end()); + // Add missing bytes + for(std::size_t i = 0; i < toAddBytes; i++) { + prebootHeader.push_back(0x00); + } - // Return device firmware - return getDeviceBinary(version, usb2Mode); + return prebootHeader; } } // namespace dai diff --git a/src/utility/Resources.hpp b/src/utility/Resources.hpp index 1ebb9051fc..57f6e8cdbb 100644 --- a/src/utility/Resources.hpp +++ b/src/utility/Resources.hpp @@ -9,10 +9,10 @@ // project #include +#include #include -namespace dai -{ +namespace dai { class Resources { // private constructor @@ -27,7 +27,7 @@ class Resources { std::thread lazyThreadBootloader; std::unordered_map> resourceMapBootloader; - std::vector getDeviceBinary(OpenVINO::Version version, bool usb2Mode); + std::vector getDeviceBinary(Device::Config config); public: static Resources& getInstance(); @@ -35,7 +35,8 @@ class Resources { void operator=(Resources const&) = delete; // Available resources - std::vector getDeviceFirmware(bool usb2Mode, OpenVINO::Version version = OpenVINO::VERSION_2021_4); + std::vector getDeviceFirmware(bool usb2Mode, OpenVINO::Version version = OpenVINO::DEFAULT_VERSION); + std::vector getDeviceFirmware(Device::Config config, std::string pathToMvcmd = ""); std::vector getBootloaderFirmware(DeviceBootloader::Type type = DeviceBootloader::Type::USB); }; diff --git a/src/xlink/XLinkConnection.cpp b/src/xlink/XLinkConnection.cpp index 2fb435e781..cd34b3f810 100644 --- a/src/xlink/XLinkConnection.cpp +++ b/src/xlink/XLinkConnection.cpp @@ -9,6 +9,9 @@ #include #include +// project +#include "depthai/utility/Initialization.hpp" + // libraries #include extern "C" { @@ -53,30 +56,14 @@ static DeviceInfo deviceInfoFix(const DeviceInfo& d, XLinkDeviceState_t state); constexpr std::chrono::milliseconds XLinkConnection::WAIT_FOR_BOOTUP_TIMEOUT; constexpr std::chrono::milliseconds XLinkConnection::WAIT_FOR_CONNECT_TIMEOUT; -void XLinkConnection::initXLinkGlobal() { - if(xlinkGlobalInitialized.exchange(true)) return; - - xlinkGlobalHandler.protocol = X_LINK_USB_VSC; - auto status = XLinkInitialize(&xlinkGlobalHandler); - if(X_LINK_SUCCESS != status) { - throw std::runtime_error("Couldn't initialize XLink"); - } - - // Suppress XLink related errors - mvLogDefaultLevelSet(MVLOG_LAST); -} - -std::atomic XLinkConnection::xlinkGlobalInitialized{false}; -XLinkGlobalHandler_t XLinkConnection::xlinkGlobalHandler = {}; - std::vector XLinkConnection::getAllConnectedDevices(XLinkDeviceState_t state) { - initXLinkGlobal(); + initialize(); std::vector devices; std::vector states; if(state == X_LINK_ANY_STATE) { - states = {X_LINK_UNBOOTED, X_LINK_BOOTLOADER, X_LINK_BOOTED}; + states = {X_LINK_UNBOOTED, X_LINK_BOOTLOADER, X_LINK_BOOTED, X_LINK_FLASH_BOOTED}; } else { states = {state}; } @@ -123,6 +110,37 @@ std::tuple XLinkConnection::getDeviceByMxId(std::string mxId, return {false, DeviceInfo()}; } +DeviceInfo XLinkConnection::bootBootloader(const DeviceInfo& deviceInfo) { + using namespace std::chrono; + + // Device is in flash booted state. Boot to bootloader first + XLinkBootBootloader(&deviceInfo.desc); + + // Fix deviceInfo for BOOTLOADER state + DeviceInfo deviceToWait = deviceInfoFix(deviceInfo, X_LINK_BOOTLOADER); + + // Device desc if found + deviceDesc_t foundDeviceDesc = {}; + + // Wait for device to get to bootloader state + XLinkError_t rc; + auto tstart = steady_clock::now(); + do { + rc = XLinkFindFirstSuitableDevice(X_LINK_BOOTLOADER, deviceToWait.desc, &foundDeviceDesc); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + if(rc == X_LINK_SUCCESS) break; + } while(steady_clock::now() - tstart < WAIT_FOR_BOOTUP_TIMEOUT); + + // If device not found + if(rc != X_LINK_SUCCESS) { + throw std::runtime_error("Failed to find device (" + std::string(deviceToWait.desc.name) + "), error message: " + convertErrorCodeToString(rc)); + } + + deviceToWait.state = X_LINK_BOOTLOADER; + deviceToWait.desc = foundDeviceDesc; + return deviceToWait; +} + XLinkConnection::XLinkConnection(const DeviceInfo& deviceDesc, std::vector mvcmdBinary, XLinkDeviceState_t expectedState) { bootDevice = true; bootWithPath = false; @@ -208,7 +226,7 @@ bool XLinkConnection::bootAvailableDevice(const deviceDesc_t& deviceToBoot, std: } void XLinkConnection::initDevice(const DeviceInfo& deviceToInit, XLinkDeviceState_t expectedState) { - initXLinkGlobal(); + initialize(); assert(deviceLinkId == -1); XLinkError_t rc = X_LINK_ERROR; @@ -319,6 +337,11 @@ std::string XLinkConnection::convertErrorCodeToString(XLinkError_t errorCode) { } DeviceInfo deviceInfoFix(const DeviceInfo& dev, XLinkDeviceState_t state) { + if(dev.desc.protocol == X_LINK_TCP_IP) { + // X_LINK_TCP_IP doesn't need a fix + return dev; + } + DeviceInfo fixed(dev); // Remove everything after dash diff --git a/src/xlink/XLinkStream.cpp b/src/xlink/XLinkStream.cpp index 6c44805a4a..70161f2583 100644 --- a/src/xlink/XLinkStream.cpp +++ b/src/xlink/XLinkStream.cpp @@ -12,7 +12,6 @@ namespace dai { // static constexpr std::chrono::milliseconds XLinkStream::WAIT_FOR_STREAM_RETRY; constexpr int XLinkStream::STREAM_OPEN_RETRIES; -std::mutex XLinkStream::xlinkStreamOperationMutex; XLinkStream::XLinkStream(const XLinkConnection& conn, const std::string& name, std::size_t maxWriteSize) : streamName(name) { if(name.empty()) throw std::invalid_argument("Cannot create XLinkStream using empty stream name"); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6b258c53bb..c6770577d9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -99,5 +99,8 @@ target_compile_definitions(openvino_blob PRIVATE OPENVINO_2021_4_BLOB_PATH="${openvino_2021_4_blob}" ) +# Bootloader configuration tests +dai_add_test(bootloader_config_test src/bootloader_config_test.cpp) - +# Device USB Speed test +dai_add_test(device_usbspeed_test src/device_usbspeed_test.cpp) diff --git a/tests/src/bootloader_config_test.cpp b/tests/src/bootloader_config_test.cpp new file mode 100644 index 0000000000..e177e612ac --- /dev/null +++ b/tests/src/bootloader_config_test.cpp @@ -0,0 +1,51 @@ +#define CATCH_CONFIG_MAIN +#include + +// std +#include + +// Include depthai library +#include + +TEST_CASE("Bootloader Config") { + dai::DeviceBootloader::Config config; + + // By default IPv4 is 0.0.0.0 (invalid) + REQUIRE("0.0.0.0" == config.getIPv4()); + + std::string ipv4 = "192.168.1.150"; + std::string ipv4Mask = "255.255.255.0"; + std::string ipv4Gateway = "192.168.1.1"; + + config.setStaticIPv4(ipv4, ipv4Mask, ipv4Gateway); + + std::array ipv4InMemory = {192, 168, 1, 150}; + for(int i = 0; i < ipv4InMemory.size(); i++) { + REQUIRE(ipv4InMemory[i] == reinterpret_cast(&config.network.ipv4)[i]); + } + + REQUIRE(ipv4 == config.getIPv4()); + REQUIRE(ipv4Mask == config.getIPv4Mask()); + REQUIRE(ipv4Gateway == config.getIPv4Gateway()); + REQUIRE(config.isStaticIPV4() == true); + + std::string dns = "1.1.1.1"; + std::string dnsAlt = "8.8.8.8"; + + config.setDnsIPv4(dns); + + REQUIRE(config.getDnsIPv4() == dns); + REQUIRE(config.network.ipv4DnsAlt == 0); + + config.setDnsIPv4(dns, dnsAlt); + + REQUIRE(config.getDnsIPv4() == dns); + REQUIRE(config.getDnsAltIPv4() == dnsAlt); + + // MAC address + std::string mac = "FF:AA:BB:CC:00:11"; + config.setMacAddress(mac); + // std::cout << "Orig mac address: " << mac << " len: " << mac.length() << std::endl; + // std::cout << "Get mac address: " << config.getMacAddress() << " len: " << config.getMacAddress().length() << std::endl; + REQUIRE(config.getMacAddress() == mac); +} \ No newline at end of file diff --git a/tests/src/device_usbspeed_test.cpp b/tests/src/device_usbspeed_test.cpp new file mode 100644 index 0000000000..7df9e61e21 --- /dev/null +++ b/tests/src/device_usbspeed_test.cpp @@ -0,0 +1,24 @@ +#define CATCH_CONFIG_MAIN +#include + +// std +#include +#include + +// Include depthai library +#include + +TEST_CASE("UsbSpeed::HIGH") { + dai::Pipeline p; + dai::Device d(p, dai::UsbSpeed::HIGH); +} + +TEST_CASE("UsbSpeed::SUPER") { + dai::Pipeline p; + dai::Device d(p, dai::UsbSpeed::SUPER); +} + +TEST_CASE("UsbSpeed::SUPER_PLUS") { + dai::Pipeline p; + dai::Device d(p, dai::UsbSpeed::SUPER_PLUS); +}