33 #include "XLink/XLink.h"
34 #include "XLink/XLinkTime.h"
37 #include "spdlog/details/os.h"
38 #include "spdlog/fmt/bin_to_hex.h"
39 #include "spdlog/fmt/chrono.h"
40 #include "spdlog/sinks/stdout_color_sinks.h"
41 #include "spdlog/spdlog.h"
57 factoryPermissions =
true;
58 protectedPermissions =
false;
60 factoryPermissions =
false;
61 protectedPermissions =
true;
63 factoryPermissions =
true;
64 protectedPermissions =
true;
66 factoryPermissions =
false;
67 protectedPermissions =
false;
81 case spdlog::level::err:
85 case spdlog::level::off:
88 case spdlog::level::n_levels:
107 return spdlog::level::err;
111 return spdlog::level::off;
128 if(!searchTimeStr.empty()) {
131 defaultSearchTime = std::chrono::milliseconds{std::stoi(searchTimeStr)};
132 }
catch(
const std::invalid_argument& e) {
133 logger::warn(
"DEPTHAI_SEARCH_TIMEOUT value invalid: {}", e.what());
137 return defaultSearchTime;
145 using namespace std::chrono;
146 constexpr
auto POOL_SLEEP_TIME = milliseconds(100);
149 auto searchStartTime = steady_clock::now();
152 std::unordered_map<std::string, DeviceInfo> invalidDevices;
155 for(
auto searchState : {X_LINK_UNBOOTED, X_LINK_BOOTLOADER, X_LINK_FLASH_BOOTED}) {
156 for(
const auto& device : devices) {
157 if(device.state == searchState) {
158 if(device.status == X_LINK_SUCCESS) {
164 invalidDevices[device.name] = device;
176 if(timeout < POOL_SLEEP_TIME) {
178 std::this_thread::sleep_for(timeout);
181 std::this_thread::sleep_for(POOL_SLEEP_TIME);
183 }
while(steady_clock::now() - searchStartTime < timeout);
186 for(
const auto& invalidDevice : invalidDevices) {
187 const auto& invalidDeviceInfo = invalidDevice.second;
188 if(invalidDeviceInfo.status == X_LINK_INSUFFICIENT_PERMISSIONS) {
189 logger::warn(
"Insufficient permissions to communicate with {} device with name \"{}\". Make sure udev rules are set",
190 XLinkDeviceStateToStr(invalidDeviceInfo.state),
191 invalidDeviceInfo.name);
195 "Skipping {} device with name \"{}\" ({})", XLinkDeviceStateToStr(invalidDeviceInfo.state), invalidDeviceInfo.name, invalidDeviceInfo.mxid);
217 for(
auto searchState : {X_LINK_UNBOOTED, X_LINK_BOOTLOADER, X_LINK_FLASH_BOOTED}) {
218 for(
const auto& device : devices) {
219 if(device.state == searchState) {
220 return {
true, device};
229 std::vector<DeviceInfo> availableDevices;
231 for(
const auto& d : connectedDevices) {
232 if(d.state != X_LINK_BOOTED) availableDevices.push_back(d);
234 return availableDevices;
244 std::vector<DeviceInfo> availableDevices;
245 auto states = {X_LINK_UNBOOTED, X_LINK_BOOTLOADER};
248 for(
const auto& state : states) {
250 if(found)
return {
true, dev};
286 std::shared_ptr<spdlog::sinks::stdout_color_sink_mt> stdoutColorSink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
293 std::unique_ptr<nanorpc::core::client<nanorpc::packer::nlohmann_msgpack>>
rpcClient;
297 void setPattern(
const std::string& pattern);
301 logger.set_pattern(pattern);
308 logger.set_level(spdlogLevel);
327 if(numConnected > 0) {
328 throw std::runtime_error(
"No available devices (" +
std::to_string(numConnected) +
" connected, but in use)");
332 auto nonRVC2Devices = numDevicesAnyPlatform - numDevicesRVC2;
333 if(nonRVC2Devices > 0) {
334 throw std::runtime_error(
"No available RVC2 devices found, but found " +
std::to_string(nonRVC2Devices)
335 +
" non RVC2 device[s]. To use RVC4 devices, please update DepthAI to version v3.x or newer.");
343 init(version, maxUsbSpeed,
"");
350 init2(cfg, pathToCmd, {});
369 init(version, pathToCmd);
373 init(version, maxUsbSpeed);
382 init(pipeline, maxUsbSpeed);
387 init(pipeline, pathToCmd);
392 init(pipeline, devInfo);
397 init(pipeline, devInfo, maxUsbSpeed);
402 init(pipeline, devInfo, pathToCmd);
437 init2(cfg, pathToCmd, {});
442 init(version, maxUsbSpeed,
"");
450 init2(cfg,
"", pipeline);
455 init(pipeline, maxUsbSpeed,
"");
463 init2(cfg, pathToCmd, pipeline);
471 init2(cfg,
"", pipeline);
476 init(pipeline, maxUsbSpeed,
"");
484 init2(cfg, pathToCmd, pipeline);
517 std::unique_lock<std::mutex> lock(
closedMtx);
526 if(!timeoutStr.empty()) {
528 return std::stoi(timeoutStr) * 1000;
529 }
catch(
const std::invalid_argument& e) {
530 logger::warn(
"DEPTHAI_CRASHDUMP_TIMEOUT value invalid: {}", e.what());
537 using namespace std::chrono;
538 auto t1 = steady_clock::now();
539 bool shouldGetCrashDump =
false;
541 pimpl->logger.debug(
"Device about to be closed...");
548 bool isRunning =
pimpl->rpcClient->call(
"isRunning").as<
bool>();
549 shouldGetCrashDump = !isRunning;
551 pimpl->logger.debug(
"Shutdown {}", isRunning ?
"OK" :
"error");
553 }
catch(
const std::exception& ex) {
554 pimpl->logger.debug(
"shutdown call error: {}", ex.what());
555 shouldGetCrashDump =
true;
585 pimpl->rpcStream =
nullptr;
586 pimpl->rpcClient =
nullptr;
591 if(shouldGetCrashDump && timeout > 0) {
592 pimpl->logger.debug(
"Getting crash dump...");
593 auto t1 = steady_clock::now();
594 bool gotDump =
false;
599 if(found && (rebootingDeviceInfo.
state == X_LINK_UNBOOTED || rebootingDeviceInfo.
state == X_LINK_BOOTLOADER)) {
600 pimpl->logger.trace(
"Found rebooting device in {}ns", duration_cast<nanoseconds>(steady_clock::now() - t1).count());
607 pimpl->logger.warn(
"Device crashed, but no crash dump could be extracted.");
612 }
while(!found && steady_clock::now() - t1 < std::chrono::milliseconds(timeout));
614 pimpl->logger.error(
"Device likely crashed but did not reboot in time to get the crash dump");
616 }
else if(shouldGetCrashDump) {
617 pimpl->logger.warn(
"Device crashed. Crash dump retrieval disabled.");
620 pimpl->logger.debug(
"Device closed, {}", duration_cast<milliseconds>(steady_clock::now() - t1).count());
628 std::unique_lock<std::mutex> lock(
closedMtx);
639 throw std::runtime_error(
"Couldn't start the pipeline");
641 }
catch(
const std::exception&) {
655 init2(cfg, pathToMvcmd, {});
661 init2(cfg, pathToMvcmd, pipeline);
667 init2(cfg, pathToMvcmd, {});
688 XLinkDeviceState_t expectedBootState = X_LINK_BOOTED;
690 expectedBootState = X_LINK_BOOTED_NON_EXCLUSIVE;
695 deviceDesc_t foundDesc;
697 if(ret == X_LINK_SUCCESS) {
702 pimpl->logger.debug(
"Searched, but no actual device found by given DeviceInfo");
721 if(!watchdogMsStr.empty()) {
724 std::chrono::milliseconds watchdog{std::stoi(watchdogMsStr)};
726 watchdogTimeout = watchdog;
727 if(watchdogTimeout.count() == 0) {
728 pimpl->logger.warn(
"Watchdog disabled! In case of unclean exit, the device needs reset or power-cycle for next run", watchdogTimeout);
730 pimpl->logger.warn(
"Using a custom watchdog value of {}", watchdogTimeout);
732 }
catch(
const std::invalid_argument& e) {
733 pimpl->logger.warn(
"DEPTHAI_WATCHDOG value invalid: {}", e.what());
737 auto watchdogInitMsStr =
utility::getEnv(
"DEPTHAI_WATCHDOG_INITIAL_DELAY");
738 if(!watchdogInitMsStr.empty()) {
741 std::chrono::milliseconds watchdog{std::stoi(watchdogInitMsStr)};
743 pimpl->logger.warn(
"Watchdog initial delay set to {}", watchdog);
744 }
catch(
const std::invalid_argument& e) {
745 pimpl->logger.warn(
"DEPTHAI_WATCHDOG_INITIAL_DELAY value invalid: {}", e.what());
750 if(!deviceDebugStr.empty()) {
753 int deviceDebug{std::stoi(deviceDebugStr)};
755 }
catch(
const std::invalid_argument& e) {
756 pimpl->logger.warn(
"DEPTHAI_DEBUG value invalid: {}, should be a number (non-zero to enable)", e.what());
782 using namespace std::chrono;
784 auto t1 = steady_clock::now();
786 auto t2 = steady_clock::now();
787 pimpl->logger.debug(
"Booting FW with Bootloader. Version {}, Time taken: {}", version.toString(), duration_cast<milliseconds>(t2 - t1));
794 pimpl->logger.debug(
"Booting FW by jumping to USB ROM Bootloader first. Bootloader Version {}", version.toString());
808 throw std::runtime_error(
"Cannot find any device with given deviceInfo");
815 auto rpcStream =
pimpl->rpcStream;
820 std::unique_lock<std::mutex> lock(
pimpl->rpcMutex);
824 pimpl->logger.trace(
"RPC: {}", nlohmann::json::from_msgpack(request).dump());
829 rpcStream->write(std::move(request));
833 return rpcStream->read();
834 }
catch(
const std::exception& e) {
836 pimpl->logger.debug(
"RPC error: {}", e.what());
837 throw std::system_error(std::make_error_code(std::errc::io_error),
"Device already closed or disconnected");
844 if(watchdogTimeout > std::chrono::milliseconds(0)) {
855 std::vector<uint8_t> watchdogKeepalive = {0, 0, 0, 0};
857 stream.
write(watchdogKeepalive);
863 std::this_thread::sleep_for(watchdogTimeout / 2);
865 }
catch(
const std::exception& ex) {
867 pimpl->logger.debug(
"Watchdog thread exception caught: {}", ex.what());
878 std::this_thread::sleep_for(watchdogTimeout);
886 if(
watchdogRunning && std::chrono::steady_clock::now() - prevPingTime > watchdogTimeout * 2) {
910 }
catch(
const std::exception&) {
919 using namespace std::chrono;
925 XLinkTimespec timestamp;
926 stream.
read(timestamp);
929 stream.
write(×tamp,
sizeof(timestamp));
931 }
catch(
const std::exception& ex) {
933 pimpl->logger.debug(
"Timesync thread exception caught: {}", ex.what());
941 using namespace std::chrono;
942 std::vector<LogMessage> messages;
953 pimpl->logger.trace(
"Log vector decoded, size: {}", messages.size());
956 for(
const auto& msg : messages) {
957 pimpl->logger.logMessage(msg);
964 for(
const auto& msg : messages) {
966 const auto& cb = kv.second;
973 }
catch(
const nlohmann::json::exception& ex) {
974 pimpl->logger.error(
"Exception while parsing or calling callbacks for log message from device: {}", ex.what());
977 }
catch(
const std::exception& ex) {
979 pimpl->logger.debug(
"Log thread exception caught: {}", ex.what());
988 using namespace std::chrono;
997 w =
static_cast<long long>(w / rate);
998 r =
static_cast<long long>(r / rate);
1002 pimpl->logger.debug(
"Profiling write speed: {:.2f} MiB/s, read speed: {:.2f} MiB/s, total written: {:.2f} MiB, read: {:.2f} MiB",
1003 w / 1024.0f / 1024.0f,
1004 r / 1024.0f / 1024.0f,
1005 data.numBytesWritten / 1024.0f / 1024.0f,
1006 data.numBytesRead / 1024.0f / 1024.0f);
1008 std::this_thread::sleep_for(duration<float>(1) / rate);
1010 }
catch(
const std::exception& ex) {
1012 pimpl->logger.debug(
"Profiling thread exception caught: {}", ex.what());
1023 }
catch(
const std::exception&) {
1033 return pimpl->rpcClient->call(
"getMxId").as<std::string>();
1037 return pimpl->rpcClient->call(
"getConnectedCameras").as<std::vector<CameraBoardSocket>>();
1041 std::vector<dai::StereoPair> stereoPairs;
1046 throw std::runtime_error(
"No camera data found.");
1048 }
catch(
const std::exception&) {
1051 }
catch(
const std::exception&) {
1052 pimpl->logger.info(
"No calibration found.");
1058 auto camId1 = camIdAndInfo1.first;
1060 auto camId2 = camIdAndInfo2.first;
1063 auto baseline = std::abs(translationVector[0]) > std::abs(translationVector[1]) ? translationVector[0] : translationVector[1];
1064 auto leftSocket = baseline < 0 ? camId1 : camId2;
1065 auto rightSocket = leftSocket == camId1 ? camId2 : camId1;
1066 int baselineDiff = std::abs(
static_cast<int>(translationVector[0]) -
static_cast<int>(translationVector[1]));
1067 if(baselineDiff ==
static_cast<int>(std::abs(baseline))) {
1068 if(std::find_if(stereoPairs.begin(),
1070 [&leftSocket, &rightSocket](
const dai::StereoPair& pair) { return pair.left == leftSocket && pair.right == rightSocket; })
1071 == stereoPairs.end()) {
1072 stereoPairs.push_back(
dai::StereoPair{leftSocket, rightSocket, std::abs(baseline),
static_cast<int>(translationVector[0]) == 0});
1075 pimpl->logger.debug(
"Skipping diagonal pair, left: {}, right: {}.", leftSocket, rightSocket);
1077 }
catch(
const std::exception&) {
1085 std::vector<dai::StereoPair> filteredStereoPairs;
1087 stereoPairs.begin(), stereoPairs.end(), std::back_inserter(filteredStereoPairs), [
this, connectedCameras, deviceStereoPairs](
dai::StereoPair pair) {
1088 if(std::find(connectedCameras.begin(), connectedCameras.end(), pair.left) == connectedCameras.end()) {
1089 pimpl->logger.debug(
"Skipping calibrated stereo pair because, camera {} was not detected.", pair.left);
1091 }
else if(std::find(connectedCameras.begin(), connectedCameras.end(), pair.
right) == connectedCameras.end()) {
1092 pimpl->logger.debug(
"Skipping calibrated stereo pair because, camera {} was not detected.", pair.right);
1095 return std::find_if(deviceStereoPairs.begin(),
1096 deviceStereoPairs.end(),
1097 [pair](
dai::StereoPair devicePair) { return devicePair.left == pair.left && devicePair.right == pair.right; })
1098 != deviceStereoPairs.end();
1102 return filteredStereoPairs;
1106 return pimpl->rpcClient->call(
"getConnectionInterfaces").as<std::vector<ConnectionInterface>>();
1110 return pimpl->rpcClient->call(
"getConnectedCameraFeatures").as<std::vector<CameraFeatures>>();
1114 return pimpl->rpcClient->call(
"getStereoPairs").as<std::vector<StereoPair>>();
1118 return pimpl->rpcClient->call(
"getCameraSensorNames").as<std::unordered_map<CameraBoardSocket, std::string>>();
1122 return pimpl->rpcClient->call(
"getConnectedIMU").as<std::string>();
1126 std::string versionStr =
pimpl->rpcClient->call(
"getIMUFirmwareVersion").as<std::string>();
1130 }
catch(
const std::exception&) {
1137 std::string versionStr =
pimpl->rpcClient->call(
"getEmbeddedIMUFirmwareVersion").as<std::string>();
1141 }
catch(
const std::exception&) {
1148 return pimpl->rpcClient->call(
"startIMUFirmwareUpdate", forceUpdate).as<
bool>();
1152 return pimpl->rpcClient->call(
"getIMUFirmwareUpdateStatus").as<std::tuple<bool, unsigned int>>();
1165 return pimpl->rpcClient->call(
"getLeonCssHeapUsage").as<
MemoryInfo>();
1169 return pimpl->rpcClient->call(
"getLeonMssHeapUsage").as<
MemoryInfo>();
1177 return pimpl->rpcClient->call(
"getLeonCssCpuUsage").as<
CpuUsage>();
1181 return pimpl->rpcClient->call(
"getLeonMssCpuUsage").as<
CpuUsage>();
1185 return pimpl->rpcClient->call(
"getUsbSpeed").as<
UsbSpeed>();
1193 return pimpl->rpcClient->call(
"isPipelineRunning").as<
bool>();
1197 pimpl->rpcClient->call(
"setLogLevel", level);
1201 return pimpl->rpcClient->call(
"getLogLevel").as<
LogLevel>();
1205 pimpl->rpcClient->call(
"setXLinkChunkSize", sizeBytes);
1209 return pimpl->rpcClient->call(
"getXLinkChunkSize").as<
int>();
1213 pimpl->rpcClient->call(
"setXLinkRateLimit", maxRateBytesPerSecond, burstSize, waitUs);
1233 pimpl->setLogLevel(level);
1237 return pimpl->getLogLevel();
1241 return pimpl->rpcClient->call(
"setIrLaserDotProjectorBrightness", mA, mask,
false);
1245 return pimpl->rpcClient->call(
"setIrLaserDotProjectorBrightness", intensity, mask,
true);
1249 return pimpl->rpcClient->call(
"setIrFloodLightBrightness", mA, mask,
false);
1253 return pimpl->rpcClient->call(
"setIrFloodLightBrightness", intensity, mask,
true);
1257 return pimpl->rpcClient->call(
"getIrDrivers");
1265 return pimpl->rpcClient->call(
"hasCrashDump").as<
bool>();
1299 if(period < std::chrono::milliseconds(10)) {
1300 throw std::invalid_argument(
"Period must be greater or equal than 10ms");
1303 using namespace std::chrono;
1304 pimpl->rpcClient->call(
"setTimesync", duration_cast<milliseconds>(period).count(), numSamples, random);
1311 setTimesync(std::chrono::milliseconds(1000), 0,
false);
1316 pimpl->rpcClient->call(
"setSystemInformationLoggingRate", rateHz);
1320 return pimpl->rpcClient->call(
"getSystemInformationLoggingRate").as<
float>();
1324 return pimpl->rpcClient->call(
"isEepromAvailable").as<
bool>();
1337 bool factoryPermissions =
false;
1338 bool protectedPermissions =
false;
1340 pimpl->logger.debug(
"Flashing calibration. Factory permissions {}, Protected permissions {}", factoryPermissions, protectedPermissions);
1347 std::string errorMsg;
1348 std::tie(success, errorMsg) =
pimpl->rpcClient->call(
"storeToEeprom", calibrationDataHandler.
getEepromData(), factoryPermissions, protectedPermissions)
1349 .as<std::tuple<bool, std::string>>();
1352 throw std::runtime_error(errorMsg);
1358 std::string errorMsg;
1359 std::tie(success, errorMsg) =
pimpl->rpcClient->call(
"setCalibration", calibrationDataHandler.
getEepromData()).as<std::tuple<bool, std::string>>();
1361 throw std::runtime_error(errorMsg);
1367 std::string errorMsg;
1369 std::tie(success, errorMsg, eepromData) =
pimpl->rpcClient->call(
"getCalibration").as<std::tuple<bool, std::string, dai::EepromData>>();
1387 std::string errorMsg;
1389 std::tie(success, errorMsg, eepromData) =
pimpl->rpcClient->call(
"readFromEeprom").as<std::tuple<bool, std::string, dai::EepromData>>();
1401 bool factoryPermissions =
false;
1402 bool protectedPermissions =
false;
1404 pimpl->logger.debug(
"Flashing factory calibration. Factory permissions {}, Protected permissions {}", factoryPermissions, protectedPermissions);
1406 if(!factoryPermissions) {
1407 throw std::runtime_error(
"Calling factory API is not allowed in current configuration");
1415 std::string errorMsg;
1416 std::tie(success, errorMsg) =
1417 pimpl->rpcClient->call(
"storeToEepromFactory", calibrationDataHandler.
getEepromData(), factoryPermissions, protectedPermissions)
1418 .as<std::tuple<bool, std::string>>();
1426 std::string errorMsg;
1428 std::tie(success, errorMsg, eepromData) =
pimpl->rpcClient->call(
"readFromEepromFactory").as<std::tuple<bool, std::string, dai::EepromData>>();
1446 std::string errorMsg;
1447 std::tie(success, errorMsg) =
pimpl->rpcClient->call(
"eepromFactoryReset").as<std::tuple<bool, std::string>>();
1455 std::string errorMsg;
1456 std::vector<uint8_t> eepromDataRaw;
1457 std::tie(success, errorMsg, eepromDataRaw) =
pimpl->rpcClient->call(
"readFromEepromRaw").as<std::tuple<bool, std::string, std::vector<uint8_t>>>();
1461 return eepromDataRaw;
1466 std::string errorMsg;
1467 std::vector<uint8_t> eepromDataRaw;
1468 std::tie(success, errorMsg, eepromDataRaw) =
pimpl->rpcClient->call(
"readFromEepromFactoryRaw").as<std::tuple<bool, std::string, std::vector<uint8_t>>>();
1472 return eepromDataRaw;
1476 bool factoryPermissions =
false;
1477 bool protectedPermissions =
false;
1479 pimpl->logger.debug(
"Clearing User EEPROM contents. Factory permissions {}, Protected permissions {}", factoryPermissions, protectedPermissions);
1481 if(!protectedPermissions) {
1482 throw std::runtime_error(
"Calling EEPROM clear API is not allowed in current configuration");
1486 std::string errorMsg;
1487 std::tie(success, errorMsg) =
pimpl->rpcClient->call(
"eepromClear", protectedPermissions, factoryPermissions).as<std::tuple<bool, std::string>>();
1494 bool factoryPermissions =
false;
1495 bool protectedPermissions =
false;
1497 pimpl->logger.debug(
"Clearing User EEPROM contents. Factory permissions {}, Protected permissions {}", factoryPermissions, protectedPermissions);
1499 if(!protectedPermissions || !factoryPermissions) {
1500 throw std::runtime_error(
"Calling factory EEPROM clear API is not allowed in current configuration");
1504 std::string errorMsg;
1505 std::tie(success, errorMsg) =
pimpl->rpcClient->call(
"eepromFactoryClear", protectedPermissions, factoryPermissions).as<std::tuple<bool, std::string>>();
1519 throw std::runtime_error(
"Pipeline is already running");
1528 throw std::runtime_error(
"Device booted with different OpenVINO version that pipeline requires");
1534 std::vector<std::uint8_t> assetStorage;
1535 pipeline.
serialize(schema, assets, assetStorage);
1539 nlohmann::json jSchema = schema;
1540 pimpl->logger.debug(
"Schema dump: {}", jSchema.dump());
1541 nlohmann::json jAssets = assets;
1542 pimpl->logger.debug(
"Asset map dump: {}", jAssets.dump());
1546 pimpl->rpcClient->call(
"setPipelineSchema", schema);
1549 if(!assetStorage.empty()) {
1550 pimpl->rpcClient->call(
"setAssets", assets);
1553 const std::string streamAssetStorage =
"__stream_asset_storage";
1554 std::thread t1([
this, &streamAssetStorage, &assetStorage]() {
1559 stream.
write(&assetStorage[offset], toTransfer);
1560 offset += toTransfer;
1561 }
while(offset <
static_cast<int64_t
>(assetStorage.size()));
1564 pimpl->rpcClient->call(
"readAssetStorageFromXLink", streamAssetStorage, assetStorage.size());
1569 pimpl->rpcClient->call(
"printAssets");
1576 bool success =
false;
1577 std::string errorMsg;
1578 std::tie(success, errorMsg) =
pimpl->rpcClient->call(
"buildPipeline").as<std::tuple<bool, std::string>>();
1580 pimpl->rpcClient->call(
"startPipeline");
1582 throw std::runtime_error(errorMsg);