RodAndBarPositionApp 的姿态问题
This commit is contained in:
parent
71abd70e2d
commit
9aa39794b7
@ -6,7 +6,7 @@
|
||||
#define RODANDBARPOSITION_APP_NAME "棒材定位"
|
||||
|
||||
#define RODANDBARPOSITION_VERSION_STRING "1.1.4"
|
||||
#define RODANDBARPOSITION_BUILD_STRING "1"
|
||||
#define RODANDBARPOSITION_BUILD_STRING "2"
|
||||
#define RODANDBARPOSITION_FULL_VERSION_STRING "V" RODANDBARPOSITION_VERSION_STRING "_" RODANDBARPOSITION_BUILD_STRING
|
||||
|
||||
// 获取版本信息的便捷函数
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
# 1.1.4 2026-06-26
|
||||
## build_2
|
||||
1. 限定了欧拉角的输出范围
|
||||
|
||||
## build_1
|
||||
1. 修改工具的参数配置
|
||||
|
||||
|
||||
@ -7,17 +7,21 @@
|
||||
| 1 | 自动拆包 | GrabBag | 1.3.6.2 |
|
||||
| 2 | 皮带撕裂 | BeltTearingServer | 2.0.8.1 |
|
||||
| 3 | 孔洞检测 | HoleDetection | 1.1.6.2 |
|
||||
| 4 | 棒材定位 | RodAndBarPosition | 1.1.0.1 |
|
||||
| 5 | 车轮拱高测量 | WheelMeasure | 1.2.0.1 |
|
||||
| 4 | 棒材定位 | RodAndBarPosition | 1.1.4.2 |
|
||||
| 5 | 车轮拱高测量 | WheelMeasure | 1.2.2.2 |
|
||||
| 6 | 定子定位 | StatorPosition | 1.0.0.1 |
|
||||
| 7 | 焊接 | LapWeld | 1.0.0.1 |
|
||||
| 8 | 工件定位 | Workpiece | 1.0.3.0 |
|
||||
| 9 | 颗粒尺寸检测 | ParticleSize | 1.0.0.0 |
|
||||
| 10 | 双目标记检测 | BinocularMarkServer | 1.0.0.4 |
|
||||
| 11 | 铁路隧道槽道测量 | TunnelChannel | 1.0.0.3 |
|
||||
| 12 | 螺杆定位 | ScrewPosition | 1.1.10.1 |
|
||||
| 12 | 螺杆定位 | ScrewPosition | 1.2.4.1 |
|
||||
| 13 | 包裹拆线位置定位 | BagThreadPosition | 1.0.0.4 |
|
||||
| 14 | 工件孔定位 | WorkpieceHole | 1.1.5.1 |
|
||||
| 14 | 工件孔定位 | WorkpieceHole | 1.1.6.1 |
|
||||
| 15 | 轮胎孔定位 | TireHolePose | 1.0.1.1 |
|
||||
| 16 | 坑孔定位 | HolePitPosition | 无 |
|
||||
| 17 | 钢筋焊缝定位 | RodWeldSeam | 1.0.0.1 |
|
||||
| 18 | 无人机螺杆检测服务端 | DroneScrewServer | 1.0.0.1 |
|
||||
| 19 | 无人机螺杆检测控制端 | DroneScrewCtrlApp | 1.0.0.1 |
|
||||
| 20 | 砂轮盘孔定位 | DiscHolePose | 1.0.0.1 |
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
|
||||
|
||||
#define MyAppName "点云查看器"
|
||||
#define MyAppVersion "1.1.4"
|
||||
#define MyAppVersion "1.1.6"
|
||||
#define MyAppPublisher ""
|
||||
#define MyAppURL ""
|
||||
#define MyAppExeName "CloudView.exe"
|
||||
|
||||
@ -27,7 +27,7 @@ Device.depends = Utils AppUtils
|
||||
AppUtils.depends = Utils Module
|
||||
App.depends = Utils VrNets Module Device AppUtils
|
||||
Tools.depends = Module Utils Robot
|
||||
Test.depends = Device Module
|
||||
Test.depends = Utils Module Device
|
||||
|
||||
# Test 测试
|
||||
Test.file = ../Test/Test.pro
|
||||
|
||||
44
GrabBagPrj/GrabBagPrj.sln
Normal file
44
GrabBagPrj/GrabBagPrj.sln
Normal file
@ -0,0 +1,44 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.5.33516.290
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ThreadPool", "..\Module\ThreadPool\ThreadPool.vcxproj", "{881F41B5-0FB2-4890-AB1F-F5F2013A22F7}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestThreadPool", "..\Test\TestThreadPool\TestThreadPool.vcxproj", "{0DBD887F-4AAC-4DB6-9706-4C6A43C3BB38}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{881F41B5-0FB2-4890-AB1F-F5F2013A22F7} = {881F41B5-0FB2-4890-AB1F-F5F2013A22F7}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{881F41B5-0FB2-4890-AB1F-F5F2013A22F7}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{881F41B5-0FB2-4890-AB1F-F5F2013A22F7}.Debug|x64.Build.0 = Debug|x64
|
||||
{881F41B5-0FB2-4890-AB1F-F5F2013A22F7}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{881F41B5-0FB2-4890-AB1F-F5F2013A22F7}.Debug|x86.Build.0 = Debug|Win32
|
||||
{881F41B5-0FB2-4890-AB1F-F5F2013A22F7}.Release|x64.ActiveCfg = Release|x64
|
||||
{881F41B5-0FB2-4890-AB1F-F5F2013A22F7}.Release|x64.Build.0 = Release|x64
|
||||
{881F41B5-0FB2-4890-AB1F-F5F2013A22F7}.Release|x86.ActiveCfg = Release|Win32
|
||||
{881F41B5-0FB2-4890-AB1F-F5F2013A22F7}.Release|x86.Build.0 = Release|Win32
|
||||
{0DBD887F-4AAC-4DB6-9706-4C6A43C3BB38}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{0DBD887F-4AAC-4DB6-9706-4C6A43C3BB38}.Debug|x64.Build.0 = Debug|x64
|
||||
{0DBD887F-4AAC-4DB6-9706-4C6A43C3BB38}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{0DBD887F-4AAC-4DB6-9706-4C6A43C3BB38}.Debug|x86.Build.0 = Debug|Win32
|
||||
{0DBD887F-4AAC-4DB6-9706-4C6A43C3BB38}.Release|x64.ActiveCfg = Release|x64
|
||||
{0DBD887F-4AAC-4DB6-9706-4C6A43C3BB38}.Release|x64.Build.0 = Release|x64
|
||||
{0DBD887F-4AAC-4DB6-9706-4C6A43C3BB38}.Release|x86.ActiveCfg = Release|Win32
|
||||
{0DBD887F-4AAC-4DB6-9706-4C6A43C3BB38}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {9CA02FFA-9326-4C71-A14C-B54AB2F579D9}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
23
GrabBagPrj/TireHolePose.iss
Normal file
23
GrabBagPrj/TireHolePose.iss
Normal file
@ -0,0 +1,23 @@
|
||||
; TireHolePose Inno Setup 脚本
|
||||
; 轮胎孔定位应用程序打包配置
|
||||
|
||||
[Setup]
|
||||
AppName=轮胎孔定位
|
||||
AppVersion=1.0.0
|
||||
AppPublisher=ShangGu
|
||||
DefaultDirName={pf}\TireHolePose
|
||||
DefaultGroupName=轮胎孔定位
|
||||
OutputBaseFilename=TireHolePose_Setup
|
||||
Compression=lzma
|
||||
SolidCompression=yes
|
||||
UninstallDisplayIcon={app}\TireHolePoseApp.exe
|
||||
|
||||
[Files]
|
||||
Source: "buildwin\TireHolePose\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
|
||||
[Icons]
|
||||
Name: "{group}\轮胎孔定位"; Filename: "{app}\TireHolePoseApp.exe"
|
||||
Name: "{group}\卸载轮胎孔定位"; Filename: "{uninstallexe}"
|
||||
|
||||
[Run]
|
||||
Filename: "{app}\TireHolePoseApp.exe"; Description: "运行轮胎孔定位"; Flags: nowait postinstall skipifsilent
|
||||
@ -110,8 +110,10 @@ fi
|
||||
JOBS="${JOBS:-$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 8)}"
|
||||
|
||||
echo "=========================================="
|
||||
echo "一次性编译,并行度 ${JOBS}"
|
||||
echo "(若内存不足出现 cc1plus 被 Killed,降低并行度重试:JOBS=4 ./buildArmPrj.sh 或 JOBS=2 ./buildArmPrj.sh)"
|
||||
|
||||
echo "执行 make -j${JOBS}"
|
||||
# 一次性编译全部目标(并行度由 JOBS 控制,默认取 CPU 核心数)
|
||||
BUILD_RESULT=0
|
||||
make -j"${JOBS}" || BUILD_RESULT=$?
|
||||
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
@echo off
|
||||
setlocal
|
||||
|
||||
set PRJ_PATH=c:\project\QT\GrabBag\GrabBagPrj
|
||||
set PRJ_PATH=%~dp0
|
||||
set PRJ_PATH=%PRJ_PATH:~0,-1%
|
||||
|
||||
if not exist "%PRJ_PATH%\buildwin\AuthRegister" mkdir "%PRJ_PATH%\buildwin\AuthRegister"
|
||||
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
@echo off
|
||||
setlocal
|
||||
|
||||
set PRJ_PATH=c:\project\QT\GrabBag\GrabBagPrj
|
||||
set PRJ_PATH=%~dp0
|
||||
set PRJ_PATH=%PRJ_PATH:~0,-1%
|
||||
|
||||
if not exist "%PRJ_PATH%\buildwin\BeltTearingApp" mkdir "%PRJ_PATH%\buildwin\BeltTearingApp"
|
||||
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
@echo off
|
||||
setlocal
|
||||
|
||||
set PRJ_PATH=c:\project\QT\GrabBag\GrabBagPrj
|
||||
set PRJ_PATH=%~dp0
|
||||
set PRJ_PATH=%PRJ_PATH:~0,-1%
|
||||
|
||||
cd /d "%PRJ_PATH%\buildwin\BinocularMarkTest"
|
||||
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
@echo off
|
||||
setlocal
|
||||
|
||||
set PRJ_PATH=c:\project\QT\GrabBag\GrabBagPrj
|
||||
set PRJ_PATH=%~dp0
|
||||
set PRJ_PATH=%PRJ_PATH:~0,-1%
|
||||
|
||||
if not exist "%PRJ_PATH%\buildwin\CalibView" mkdir "%PRJ_PATH%\buildwin\CalibView"
|
||||
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
@echo off
|
||||
setlocal
|
||||
|
||||
set PRJ_PATH=c:\project\QT\GrabBag\GrabBagPrj
|
||||
set PRJ_PATH=%~dp0
|
||||
set PRJ_PATH=%PRJ_PATH:~0,-1%
|
||||
|
||||
if not exist "%PRJ_PATH%\buildwin\CloudView" mkdir "%PRJ_PATH%\buildwin\CloudView"
|
||||
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
@echo off
|
||||
setlocal
|
||||
|
||||
set PRJ_PATH=c:\project\QT\GrabBag\GrabBagPrj
|
||||
set PRJ_PATH=%~dp0
|
||||
set PRJ_PATH=%PRJ_PATH:~0,-1%
|
||||
|
||||
if not exist "%PRJ_PATH%\buildwin\ControlApp" mkdir "%PRJ_PATH%\buildwin\ControlApp"
|
||||
|
||||
|
||||
42
GrabBagPrj/pkg_threadpool.bat
Normal file
42
GrabBagPrj/pkg_threadpool.bat
Normal file
@ -0,0 +1,42 @@
|
||||
@echo off
|
||||
setlocal
|
||||
|
||||
REM project root = parent of this script's directory
|
||||
cd /d "%~dp0.."
|
||||
set PRJ_PATH=%cd%
|
||||
|
||||
set PUB_PATH=%PRJ_PATH%\Publish\ThreadPool
|
||||
|
||||
echo ========================================
|
||||
echo pkg ThreadPool
|
||||
echo ========================================
|
||||
|
||||
if exist "%PUB_PATH%" (
|
||||
echo [WARN] dir already exists: %PUB_PATH%
|
||||
echo [WARN] please delete it manually, then re-run.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
mkdir "%PUB_PATH%"
|
||||
|
||||
REM 1. copy ThreadPool source (keep Inc / _Inc / Src structure)
|
||||
echo [copy] ThreadPool source...
|
||||
xcopy "%PRJ_PATH%\Module\ThreadPool\Inc\*.h" "%PUB_PATH%\Inc\" /Y /I /Q
|
||||
xcopy "%PRJ_PATH%\Module\ThreadPool\_Inc\*.h" "%PUB_PATH%\_Inc\" /Y /I /Q
|
||||
xcopy "%PRJ_PATH%\Module\ThreadPool\Src\*.cpp" "%PUB_PATH%\Src\" /Y /I /Q
|
||||
|
||||
REM 2. copy demo
|
||||
echo [copy] demo...
|
||||
xcopy "%PRJ_PATH%\Test\TestThreadPool\main.cpp" "%PUB_PATH%\demo\" /Y /I /Q
|
||||
|
||||
REM 3. copy project files
|
||||
echo [copy] project files...
|
||||
xcopy "%PRJ_PATH%\Module\ThreadPool\ThreadPool.pro" "%PUB_PATH%\" /Y /I /Q
|
||||
xcopy "%PRJ_PATH%\Module\ThreadPool\ThreadPool.vcxproj" "%PUB_PATH%\" /Y /I /Q
|
||||
xcopy "%PRJ_PATH%\Module\ThreadPool\ThreadPool.vcxproj.filters" "%PUB_PATH%\" /Y /I /Q
|
||||
|
||||
echo ========================================
|
||||
echo done: %PUB_PATH%
|
||||
echo ========================================
|
||||
dir /s /b "%PUB_PATH%"
|
||||
endlocal
|
||||
25
GrabBagPrj/pkg_tireholepose.bat
Normal file
25
GrabBagPrj/pkg_tireholepose.bat
Normal file
@ -0,0 +1,25 @@
|
||||
@echo off
|
||||
setlocal
|
||||
|
||||
set PRJ_PATH=%~dp0
|
||||
set PRJ_PATH=%PRJ_PATH:~0,-1%
|
||||
|
||||
if not exist "%PRJ_PATH%\buildwin\TireHolePose" mkdir "%PRJ_PATH%\buildwin\TireHolePose"
|
||||
|
||||
cd /d "%PRJ_PATH%\buildwin\TireHolePose"
|
||||
|
||||
powershell -Command "Remove-Item * -Recurse -Force"
|
||||
|
||||
copy "..\..\build\App\TireHolePose\TireHolePoseApp\release\TireHolePoseApp.exe" .\
|
||||
|
||||
"C:\tools\Qt\5.15.2\msvc2019_64\bin\windeployqt.exe" "TireHolePoseApp.exe"
|
||||
|
||||
cd ..\..
|
||||
|
||||
set ISCC_PATH=C:\Program Files (x86)\Inno Setup 6\ISCC.exe
|
||||
|
||||
"%ISCC_PATH%" "TireHolePose.iss"
|
||||
|
||||
echo "finish"
|
||||
|
||||
endlocal
|
||||
52
GrabBagPrj/pkg_wdremotereceivertest.bat
Normal file
52
GrabBagPrj/pkg_wdremotereceivertest.bat
Normal file
@ -0,0 +1,52 @@
|
||||
@echo off
|
||||
setlocal
|
||||
|
||||
REM ============================================================
|
||||
REM WDRemoteReceiver 模块测试工具 - Windows 打包脚本
|
||||
REM
|
||||
REM 输出:%PRJ_PATH%\buildwin\WDRemoteReceiverTest\
|
||||
REM ============================================================
|
||||
|
||||
set PRJ_PATH=%~dp0
|
||||
set PRJ_PATH=%PRJ_PATH:~0,-1%
|
||||
set OUT_DIR=%PRJ_PATH%\buildwin\WDRemoteReceiverTest
|
||||
set BUILD_DIR=%PRJ_PATH%\build\Test\WDRemoteReceiverTest\release
|
||||
set WD_RECEIVER_DIR=%PRJ_PATH%\build\Module\WDRemoteReceiver\release
|
||||
|
||||
if not exist "%OUT_DIR%" mkdir "%OUT_DIR%"
|
||||
cd /d "%OUT_DIR%"
|
||||
|
||||
REM 1) 拷贝可执行
|
||||
copy "%BUILD_DIR%\WDRemoteReceiverTest.exe" .\
|
||||
if errorlevel 1 (
|
||||
echo [ERROR] not found: %BUILD_DIR%\WDRemoteReceiverTest.exe
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
REM 2) Qt 依赖部署
|
||||
REM 2) WDRemoteReceiver runtime
|
||||
copy "%WD_RECEIVER_DIR%\WDRemoteReceiver.dll" .\
|
||||
if errorlevel 1 (
|
||||
echo [ERROR] not found: %WD_RECEIVER_DIR%\WDRemoteReceiver.dll
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
"C:\tools\Qt\5.15.2\msvc2019_64\bin\windeployqt.exe" "WDRemoteReceiverTest.exe"
|
||||
|
||||
REM 3) 拷贝 ZeroMQ Runtime
|
||||
copy "%PRJ_PATH%\..\SDK\ZeroMQ\Windows\x64\Release\libzmq*.dll" .\
|
||||
|
||||
REM 4) 拷贝 Protobuf Runtime(如果需要)
|
||||
REM 5) OpenCV runtime used by WDRemoteReceiver
|
||||
copy "%PRJ_PATH%\..\SDK\OpenCV320\Windows\vc14\Release\opencv_world320.dll" .\
|
||||
|
||||
copy "%PRJ_PATH%\..\SDK\Protobuf_3_20\Windows\x64\Release\libprotobuf*.dll" .\ 2>nul
|
||||
|
||||
cd /d "%PRJ_PATH%"
|
||||
|
||||
echo.
|
||||
echo === WDRemoteReceiverTest Windows 打包完成 ===
|
||||
echo 输出目录: %OUT_DIR%
|
||||
echo.
|
||||
|
||||
endlocal
|
||||
@ -27,6 +27,21 @@ _array_contains() {
|
||||
|
||||
_trim_leading_spaces() { printf '%s' "${1#"${1%%[![:space:]]*}"}"; }
|
||||
|
||||
# 将逗号分隔的多条 spec 字符串拆分到指定数组里(空段忽略,自动去前导空格)
|
||||
# 使用 read -ra 避免对 spec 中的 glob (*) 触发路径名展开
|
||||
# 用法: _split_csv_specs "<csv>" out_array
|
||||
_split_csv_specs() {
|
||||
local _csv=$1
|
||||
local -n _out=$2
|
||||
local _items _item _trimmed
|
||||
[ -z "$_csv" ] && return 0
|
||||
IFS=',' read -ra _items <<< "$_csv"
|
||||
for _item in "${_items[@]}"; do
|
||||
_trimmed="$(_trim_leading_spaces "$_item")"
|
||||
[ -n "$_trimmed" ] && _out+=("$_trimmed")
|
||||
done
|
||||
}
|
||||
|
||||
set_project_meta() { PROJECT_META["$1:$2"]=${3-}; }
|
||||
get_project_meta() { printf '%s' "${PROJECT_META["$1:$2"]:-}"; }
|
||||
|
||||
@ -71,6 +86,7 @@ load_package_project_meta() {
|
||||
PACKAGE_SERVICE_DEST_REL="$(get_project_meta "$project" package_service_dest_rel)"
|
||||
PACKAGE_LD_CONF_EXTRA_LINES="$(get_project_meta "$project" package_ld_conf_extra_lines)"
|
||||
PACKAGE_OPENCV_PROFILE="$(get_project_meta "$project" package_opencv_profile)"
|
||||
PACKAGE_DEPENDS="$(get_project_meta "$project" package_depends)"
|
||||
|
||||
local serialized
|
||||
serialized="${PROJECT_COPY_SPECS[$project]:-}"
|
||||
@ -154,14 +170,25 @@ _finalize_project_meta() {
|
||||
fi
|
||||
fi
|
||||
|
||||
: "${meta_ref[package_icon_source_rel]:=${meta_ref[app_dir_rel]}/resource/logo.png}"
|
||||
|
||||
if [ "${meta_ref[package_kind]}" = "desktop" ]; then
|
||||
: "${meta_ref[package_icon_dest_rel]:=usr/share/pixmaps/${package_slug}.png}"
|
||||
local default_icon_rel="${meta_ref[app_dir_rel]}/resource/logo.png"
|
||||
if [ -z "${meta_ref[package_icon_source_rel]}" ] && [ -f "${SCRIPT_DIR}/../${default_icon_rel}" ]; then
|
||||
meta_ref[package_icon_source_rel]="${default_icon_rel}"
|
||||
fi
|
||||
if [ -n "${meta_ref[package_icon_source_rel]}" ]; then
|
||||
: "${meta_ref[package_icon_dest_rel]:=usr/share/pixmaps/${package_slug}.png}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "${meta_ref[package_kind]}" = "service" ]; then
|
||||
: "${meta_ref[package_service_source_rel]:=${meta_ref[app_dir_rel]}/${package_slug}.service}"
|
||||
# 优先使用 service/ 子目录下的 service 文件,不存在则回退到 app_dir 根目录
|
||||
local _svc_in_service_dir="${meta_ref[app_dir_rel]}/service/${package_slug}.service"
|
||||
local _svc_in_root="${meta_ref[app_dir_rel]}/${package_slug}.service"
|
||||
if [ -f "${SCRIPT_DIR}/../${_svc_in_service_dir}" ]; then
|
||||
: "${meta_ref[package_service_source_rel]:=$_svc_in_service_dir}"
|
||||
else
|
||||
: "${meta_ref[package_service_source_rel]:=$_svc_in_root}"
|
||||
fi
|
||||
: "${meta_ref[package_service_dest_rel]:=opt/${package_slug}/${package_slug}.service}"
|
||||
fi
|
||||
fi
|
||||
@ -196,6 +223,7 @@ _register_project() {
|
||||
[package_service_dest_rel]=""
|
||||
[package_ld_conf_extra_lines]=""
|
||||
[package_opencv_profile]=""
|
||||
[package_depends]=""
|
||||
)
|
||||
|
||||
local kv key value
|
||||
@ -269,7 +297,7 @@ _parse_kv_overrides() {
|
||||
|
||||
_register_desktop_projects() {
|
||||
local entry fields name algo
|
||||
local app_dir version_macro opencv algo_copy device_copy extra_copy optional_copy ld_conf description comment
|
||||
local app_dir version_macro opencv algo_copy device_copy extra_copy optional_copy ld_conf description comment package_depends
|
||||
|
||||
for entry in "${DESKTOP_PROJECTS[@]}"; do
|
||||
IFS=';' read -ra fields <<< "$entry"
|
||||
@ -280,12 +308,14 @@ _register_desktop_projects() {
|
||||
app_dir="App/${name}/${name}App"
|
||||
version_macro=$(printf '%s' "$name" | tr '[:lower:]' '[:upper:]')
|
||||
opencv="" algo_copy="" device_copy="$COPY_VZNLSDK" extra_copy="" optional_copy=""
|
||||
ld_conf="" description="" comment=""
|
||||
ld_conf="" description="" comment="" package_depends=""
|
||||
|
||||
_parse_kv_overrides fields 2 \
|
||||
app_dir version_macro opencv algo_copy device_copy extra_copy optional_copy ld_conf description comment
|
||||
app_dir version_macro opencv algo_copy device_copy extra_copy optional_copy ld_conf description comment package_depends
|
||||
|
||||
: "${algo_copy:=glob|AppAlgo/${algo}/Arm/aarch64/*.so|usr/lib}"
|
||||
if [ -z "$algo_copy" ] && [ "$algo" != "none" ]; then
|
||||
algo_copy="glob|AppAlgo/${algo}/Arm/aarch64/*.so|usr/lib"
|
||||
fi
|
||||
|
||||
# register
|
||||
local reg_args=()
|
||||
@ -293,18 +323,22 @@ _register_desktop_projects() {
|
||||
[ -n "$ld_conf" ] && reg_args+=("package_ld_conf_extra_lines=$ld_conf")
|
||||
[ -n "$description" ] && reg_args+=("package_description=$description")
|
||||
[ -n "$comment" ] && reg_args+=("package_comment=$comment")
|
||||
[ -n "$package_depends" ] && reg_args+=("package_depends=$package_depends")
|
||||
|
||||
_register_desktop_project "$name" "$app_dir" "$version_macro" "${reg_args[@]}"
|
||||
|
||||
# copy specs
|
||||
local specs=("$algo_copy")
|
||||
local specs=()
|
||||
[ -n "$algo_copy" ] && [ "$algo_copy" != "none" ] && specs+=("$algo_copy")
|
||||
[ -n "$device_copy" ] && [ "$device_copy" != "none" ] && specs+=("$device_copy")
|
||||
[ -n "$extra_copy" ] && specs+=("$extra_copy")
|
||||
_split_csv_specs "$extra_copy" specs
|
||||
_set_copy_specs "$name" "${specs[@]}"
|
||||
|
||||
# optional copy
|
||||
if [ -n "$optional_copy" ]; then
|
||||
_set_optional_copy_specs "$name" "$optional_copy"
|
||||
local opt_specs=()
|
||||
_split_csv_specs "$optional_copy" opt_specs
|
||||
if [ ${#opt_specs[@]} -gt 0 ]; then
|
||||
_set_optional_copy_specs "$name" "${opt_specs[@]}"
|
||||
elif [ "$device_copy" != "$COPY_VZNLSDK" ]; then
|
||||
_set_optional_copy_specs "$name" "$COPY_VZNLSDK"
|
||||
fi
|
||||
@ -313,7 +347,7 @@ _register_desktop_projects() {
|
||||
|
||||
_register_service_projects() {
|
||||
local entry fields name algo
|
||||
local app_suffix version_macro opencv device_copy description
|
||||
local app_dir app_suffix version_macro opencv device_copy extra_copy optional_copy description package_depends
|
||||
|
||||
for entry in "${SERVICE_PROJECTS[@]}"; do
|
||||
IFS=';' read -ra fields <<< "$entry"
|
||||
@ -321,30 +355,45 @@ _register_service_projects() {
|
||||
algo="$(_trim_leading_spaces "${fields[1]}")"
|
||||
|
||||
# 默认值
|
||||
app_suffix="Server" version_macro="" opencv=""
|
||||
device_copy="$COPY_VZNLSDK" description=""
|
||||
app_dir="" app_suffix="Server" version_macro="" opencv=""
|
||||
device_copy="$COPY_VZNLSDK" extra_copy="" optional_copy="" description="" package_depends=""
|
||||
|
||||
_parse_kv_overrides fields 2 \
|
||||
app_suffix version_macro opencv device_copy description
|
||||
app_dir app_suffix version_macro opencv device_copy extra_copy optional_copy description package_depends
|
||||
|
||||
: "${app_dir:=App/${name}/${name}${app_suffix}}"
|
||||
: "${version_macro:=$(printf '%s' "$name" | tr '[:lower:]' '[:upper:]')}"
|
||||
|
||||
# register
|
||||
local reg_args=()
|
||||
[ -n "$opencv" ] && reg_args+=("package_opencv_profile=$opencv")
|
||||
[ -n "$description" ] && reg_args+=("package_description=$description")
|
||||
[ -n "$package_depends" ] && reg_args+=("package_depends=$package_depends")
|
||||
|
||||
_register_service_project "$name" "App/${name}/${name}${app_suffix}" "$version_macro" "${reg_args[@]}"
|
||||
_set_copy_specs "$name" "glob|AppAlgo/${algo}/Arm/aarch64/*.so|usr/lib" "$device_copy"
|
||||
_register_service_project "$name" "$app_dir" "$version_macro" "${reg_args[@]}"
|
||||
|
||||
# copy specs
|
||||
local specs=()
|
||||
[ "$algo" != "none" ] && specs+=("glob|AppAlgo/${algo}/Arm/aarch64/*.so|usr/lib")
|
||||
[ -n "$device_copy" ] && [ "$device_copy" != "none" ] && specs+=("$device_copy")
|
||||
_split_csv_specs "$extra_copy" specs
|
||||
_set_copy_specs "$name" "${specs[@]}"
|
||||
|
||||
# optional copy
|
||||
local opt_specs=()
|
||||
_split_csv_specs "$optional_copy" opt_specs
|
||||
|
||||
# 自动检测 config 目录
|
||||
local app_dir="App/${name}/${name}${app_suffix}"
|
||||
if [ -d "${SCRIPT_DIR}/../${app_dir}/config" ]; then
|
||||
local pkg_name
|
||||
pkg_name=$(get_project_meta "$name" package_name)
|
||||
local slug
|
||||
slug=$(printf '%s' "$pkg_name" | tr '[:upper:]' '[:lower:]')
|
||||
_set_optional_copy_specs "$name" "dir|${app_dir}/config|opt/${slug}/config"
|
||||
opt_specs+=("dir|${app_dir}/config|opt/${slug}/config")
|
||||
fi
|
||||
|
||||
if [ ${#opt_specs[@]} -gt 0 ]; then
|
||||
_set_optional_copy_specs "$name" "${opt_specs[@]}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
@ -16,6 +16,21 @@ QT_LIB_PATH="/opt/sysroot/firefly-arm64-sysroot-20.04/lib/aarch64-linux-gnu"
|
||||
DEFAULT_OPENCV_PROFILE="opencv320"
|
||||
OPENCV320_LIB_GLOB="SDK/OpenCV320/Arm/aarch64/libopencv_*.so*"
|
||||
OPENCV480_LIB_GLOB="SDK/OpenCV480/Arm/aarch64/lib/libopencv_*.so*"
|
||||
OPENCV450_LIB_DIR_CANDIDATES=(
|
||||
"AppAlgo/stereo_bolt_delivery/lib/opencv"
|
||||
"AppAlgo/stereo_bolt_delivery/lib"
|
||||
"SDK/OpenCV450/Arm/aarch64/lib"
|
||||
"SDK/OpenCV45D/Arm/aarch64/lib"
|
||||
"SDK/OpenCV454/Arm/aarch64/lib"
|
||||
"SDK/OpenCV45/Arm/aarch64/lib"
|
||||
)
|
||||
OPENCV450_REQUIRED_LIBS=(
|
||||
"libopencv_calib3d.so.4.5"
|
||||
"libopencv_imgcodecs.so.4.5"
|
||||
"libopencv_dnn.so.4.5"
|
||||
"libopencv_imgproc.so.4.5"
|
||||
"libopencv_core.so.4.5"
|
||||
)
|
||||
FIXED_PACKAGE_MAINTAINER="visionrobot"
|
||||
FIXED_QT_REMOVE_WEBENGINE=true
|
||||
FIXED_QT_REMOVE_NFC=true
|
||||
@ -195,6 +210,16 @@ process_specs() {
|
||||
done
|
||||
}
|
||||
|
||||
resolve_package_source_path() {
|
||||
local src=$1
|
||||
|
||||
if [[ "${src}" = /* ]]; then
|
||||
printf '%s\n' "${src}"
|
||||
else
|
||||
printf '%s/%s\n' "${REPO_DIR}" "${src}"
|
||||
fi
|
||||
}
|
||||
|
||||
apply_package_defaults() {
|
||||
PACKAGE_SLUG=$(printf '%s' "${PACKAGE_NAME}" | tr '[:upper:]' '[:lower:]')
|
||||
PACKAGE_SECTION="${PACKAGE_SLUG}app"
|
||||
@ -258,6 +283,10 @@ copy_opencv_runtime() {
|
||||
opencv480)
|
||||
src_pattern="${OPENCV480_LIB_GLOB}"
|
||||
;;
|
||||
opencv450|opencv45d)
|
||||
copy_opencv450_runtime
|
||||
return 0
|
||||
;;
|
||||
none)
|
||||
return 0
|
||||
;;
|
||||
@ -271,6 +300,60 @@ copy_opencv_runtime() {
|
||||
copy_glob_items "${src_pattern}" "usr/lib" "globa" true
|
||||
}
|
||||
|
||||
copy_opencv450_runtime() {
|
||||
local candidates=()
|
||||
local candidate=""
|
||||
local candidate_abs=""
|
||||
local selected_dir=""
|
||||
local lib=""
|
||||
local missing=false
|
||||
|
||||
if [ -n "${DRONESCREW_OPENCV450_LIB_DIR:-}" ]; then
|
||||
candidates+=("${DRONESCREW_OPENCV450_LIB_DIR}")
|
||||
fi
|
||||
candidates+=("${OPENCV450_LIB_DIR_CANDIDATES[@]}")
|
||||
|
||||
for candidate in "${candidates[@]}"; do
|
||||
candidate_abs="$(resolve_package_source_path "${candidate}")"
|
||||
[ -d "${candidate_abs}" ] || continue
|
||||
|
||||
missing=false
|
||||
for lib in "${OPENCV450_REQUIRED_LIBS[@]}"; do
|
||||
if [ ! -e "${candidate_abs}/${lib}" ]; then
|
||||
missing=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "${missing}" = false ]; then
|
||||
selected_dir="${candidate_abs}"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "${selected_dir}" ]; then
|
||||
{
|
||||
echo "Missing OpenCV 4.5.0 runtime for ${PACKAGE_NAME}."
|
||||
echo "libstereo_bolt.so requires these exact sonames:"
|
||||
printf ' %s\n' "${OPENCV450_REQUIRED_LIBS[@]}"
|
||||
echo "Put them in AppAlgo/stereo_bolt_delivery/lib/opencv,"
|
||||
echo "or set DRONESCREW_OPENCV450_LIB_DIR to the directory that contains them."
|
||||
} >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "Copy OpenCV runtime (opencv450) from ${selected_dir}"
|
||||
mkdir -p "${PKG_PATH}/usr/lib"
|
||||
|
||||
shopt -s nullglob
|
||||
local opencv_libs=( "${selected_dir}"/libopencv_*.so.4.5* )
|
||||
shopt -u nullglob
|
||||
|
||||
for lib in "${opencv_libs[@]}"; do
|
||||
cp -a "${lib}" "${PKG_PATH}/usr/lib/"
|
||||
done
|
||||
}
|
||||
|
||||
copy_required_files() {
|
||||
copy_opencv_runtime
|
||||
process_specs true "${PACKAGE_COPY_SPECS[@]}"
|
||||
@ -297,6 +380,10 @@ Priority: optional
|
||||
Maintainer: ${PACKAGE_MAINTAINER}
|
||||
Description: ${PACKAGE_DESCRIPTION}
|
||||
EOF
|
||||
|
||||
if [ -n "${PACKAGE_DEPENDS:-}" ]; then
|
||||
printf 'Depends: %s\n' "${PACKAGE_DEPENDS}" >> "${PKG_PATH}/DEBIAN/control"
|
||||
fi
|
||||
}
|
||||
|
||||
set_permissions() {
|
||||
@ -366,7 +453,11 @@ load_package_context() {
|
||||
DEB_FILENAME="${RELEASE_PATH}/${PACKAGE_NAME}_${PKG_VERSION}_${BUILD_NUMBER}_${PKG_ARCH}_${TIMESTAMP}.deb"
|
||||
DESKTOP_FILE_NAME="${PACKAGE_SLUG}.desktop"
|
||||
EXEC_INSTALL_PATH="/${PACKAGE_BINARY_DEST_REL}"
|
||||
ICON_INSTALL_PATH="/${PACKAGE_ICON_DEST_REL}"
|
||||
if [ -n "${PACKAGE_ICON_DEST_REL}" ]; then
|
||||
ICON_INSTALL_PATH="/${PACKAGE_ICON_DEST_REL}"
|
||||
else
|
||||
ICON_INSTALL_PATH="application-x-executable"
|
||||
fi
|
||||
SERVICE_INSTALL_DIR="/opt/${PACKAGE_SLUG}"
|
||||
echo "加载项目 ${PACKAGE_TARGET} 上下文完成"
|
||||
|
||||
@ -401,7 +492,11 @@ finalize_package() {
|
||||
log_info "设置文件权限"
|
||||
set_permissions
|
||||
log_info "生成 DEB 安装包"
|
||||
fakeroot dpkg -b "${PKG_PATH}" "${DEB_FILENAME}" >/dev/null
|
||||
|
||||
# 使用低压缩等级避免 OOM(大包 + xz 默认等级 6 会吃几百 MB 内存)
|
||||
# 可通过 DEB_COMPRESS_OPTS 环境变量覆盖,例如 DEB_COMPRESS_OPTS="-Zgzip" 或 "-Zxz -z9"
|
||||
local compress_opts="${DEB_COMPRESS_OPTS:--Zxz -z1}"
|
||||
fakeroot dpkg-deb ${compress_opts} --build "${PKG_PATH}" "${DEB_FILENAME}" >/dev/null
|
||||
|
||||
log_info "安装包已生成: ${DEB_FILENAME}"
|
||||
log_info "安装包大小: $(ls -lh "${DEB_FILENAME}" | awk '{print $5}')"
|
||||
|
||||
@ -13,27 +13,114 @@ set -e
|
||||
SERVICE_SOURCE="${SERVICE_INSTALL_PATH}"
|
||||
SERVICE_UNIT="${SERVICE_UNIT_NAME}"
|
||||
SERVICE_TARGET="/etc/systemd/system/\${SERVICE_UNIT}"
|
||||
LD_CONF_FILE="/etc/ld.so.conf.d/${PACKAGE_SLUG}.conf"
|
||||
PKG_NAME="${PACKAGE_NAME}"
|
||||
PKG_VERSION="${PKG_RELEASE_VERSION}"
|
||||
INSTALL_DIR="${SERVICE_INSTALL_DIR}"
|
||||
|
||||
log() { echo "[\${PKG_NAME}] \$*"; }
|
||||
fail() { echo "[\${PKG_NAME}] [FAIL] \$*" >&2; exit 1; }
|
||||
|
||||
log "=========================================="
|
||||
log "开始安装 \${PKG_NAME} \${PKG_VERSION}"
|
||||
log " 安装目录: \${INSTALL_DIR}"
|
||||
log " 服务单元: \${SERVICE_UNIT}"
|
||||
log "=========================================="
|
||||
|
||||
# 1. 配置动态库搜索路径
|
||||
log "[1/6] 配置动态库搜索路径 \${LD_CONF_FILE}"
|
||||
cat > "\${LD_CONF_FILE}" <<'LDEOF'
|
||||
/usr/lib
|
||||
/opt/sysroot/lib/
|
||||
LDEOF
|
||||
ldconfig
|
||||
log " ldconfig 完成"
|
||||
|
||||
if [ ! -f "\${SERVICE_SOURCE}" ]; then
|
||||
echo "缺少服务文件: \${SERVICE_SOURCE}" >&2
|
||||
exit 1
|
||||
# 2. DroneScrewServer 专用:启动 mediamtx
|
||||
if [ "\${PKG_NAME}" = "DroneScrewServer" ]; then
|
||||
mkdir -p "\${INSTALL_DIR}/logs"
|
||||
log "[2/6] 检测并启动 mediamtx RTSP 服务器"
|
||||
MEDIAMTX_BIN="\${INSTALL_DIR}/bin/mediamtx"
|
||||
MEDIAMTX_CFG="\${INSTALL_DIR}/etc/mediamtx_dronescrew.yml"
|
||||
MEDIAMTX_SERVICE="\${INSTALL_DIR}/etc/mediamtx-dronescrew.service"
|
||||
|
||||
if [ -f "\${MEDIAMTX_BIN}" ] && [ -f "\${MEDIAMTX_CFG}" ] && [ -f "\${MEDIAMTX_SERVICE}" ]; then
|
||||
chmod +x "\${MEDIAMTX_BIN}"
|
||||
|
||||
# 复制 service 文件到 systemd 目录
|
||||
cp "\${MEDIAMTX_SERVICE}" /etc/systemd/system/mediamtx-dronescrew.service
|
||||
chmod 644 /etc/systemd/system/mediamtx-dronescrew.service
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable mediamtx-dronescrew.service >/dev/null 2>&1 || true
|
||||
systemctl restart mediamtx-dronescrew.service
|
||||
sleep 1
|
||||
|
||||
if systemctl is-active --quiet mediamtx-dronescrew.service; then
|
||||
log " [OK] mediamtx 已启动 (端口 8554)"
|
||||
else
|
||||
log " [WARN] mediamtx 启动失败,DroneScrewServer RTSP 推流可能不可用"
|
||||
log " 手动检查: journalctl -u mediamtx-dronescrew.service -n 20"
|
||||
fi
|
||||
else
|
||||
log " [WARN] mediamtx 文件缺失,跳过启动"
|
||||
fi
|
||||
else
|
||||
log "[2/6] 跳过 mediamtx(非 DroneScrewServer)"
|
||||
fi
|
||||
|
||||
# 3. 校验服务文件
|
||||
log "[3/6] 校验服务文件"
|
||||
[ -f "\${SERVICE_SOURCE}" ] || fail "缺少服务文件: \${SERVICE_SOURCE}"
|
||||
log " OK: \${SERVICE_SOURCE}"
|
||||
|
||||
# 4. 停止/禁用旧服务
|
||||
log "[4/6] 停止旧服务(如在运行)"
|
||||
if systemctl is-active --quiet "\${SERVICE_UNIT}" 2>/dev/null; then
|
||||
systemctl stop "\${SERVICE_UNIT}"
|
||||
log " 已停止 \${SERVICE_UNIT}"
|
||||
else
|
||||
log " 旧服务未运行,跳过"
|
||||
fi
|
||||
|
||||
if systemctl is-enabled --quiet "\${SERVICE_UNIT}" 2>/dev/null; then
|
||||
systemctl disable "\${SERVICE_UNIT}"
|
||||
systemctl disable "\${SERVICE_UNIT}" >/dev/null 2>&1 || true
|
||||
log " 已禁用旧 unit"
|
||||
fi
|
||||
|
||||
# 5. 注册新 unit
|
||||
log "[5/6] 注册并启用新 unit"
|
||||
cp "\${SERVICE_SOURCE}" "\${SERVICE_TARGET}"
|
||||
chmod 644 "\${SERVICE_TARGET}"
|
||||
systemctl daemon-reload
|
||||
systemctl enable "\${SERVICE_UNIT}"
|
||||
systemctl enable "\${SERVICE_UNIT}" >/dev/null 2>&1
|
||||
log " 已 enable \${SERVICE_UNIT}"
|
||||
|
||||
# 6. 启动并验证
|
||||
log "[6/6] 启动服务"
|
||||
set +e
|
||||
systemctl start "\${SERVICE_UNIT}"
|
||||
START_RC=\$?
|
||||
set -e
|
||||
sleep 1
|
||||
|
||||
if systemctl is-active --quiet "\${SERVICE_UNIT}"; then
|
||||
log " [OK] \${SERVICE_UNIT} 已在运行"
|
||||
systemctl status "\${SERVICE_UNIT}" --no-pager --lines=3 || true
|
||||
log "=========================================="
|
||||
log "\${PKG_NAME} 安装成功"
|
||||
log " 查看实时日志: journalctl -u \${SERVICE_UNIT} -f"
|
||||
log " 停止服务: systemctl stop \${SERVICE_UNIT}"
|
||||
if [ "\${PKG_NAME}" = "DroneScrewServer" ]; then
|
||||
log " mediamtx 日志: journalctl -u mediamtx-dronescrew.service -f"
|
||||
log " RTSP 拉流地址: rtsp://<板子IP>:8554/live/dronescrew"
|
||||
fi
|
||||
log "=========================================="
|
||||
else
|
||||
log " [FAIL] \${SERVICE_UNIT} 未能启动 (start rc=\${START_RC})"
|
||||
log " 最近 20 行日志:"
|
||||
journalctl -u "\${SERVICE_UNIT}" --no-pager --lines=20 || true
|
||||
fail "服务启动失败,请检查上面的日志"
|
||||
fi
|
||||
EOF
|
||||
|
||||
chmod +x "${PKG_PATH}/DEBIAN/postinst"
|
||||
@ -45,14 +132,50 @@ write_service_postrm() {
|
||||
set -e
|
||||
|
||||
SERVICE_UNIT="${SERVICE_UNIT_NAME}"
|
||||
LD_CONF_FILE="/etc/ld.so.conf.d/${PACKAGE_SLUG}.conf"
|
||||
PKG_NAME="${PACKAGE_NAME}"
|
||||
INSTALL_DIR="${SERVICE_INSTALL_DIR}"
|
||||
ACTION="\${1:-}"
|
||||
|
||||
systemctl stop "\${SERVICE_UNIT}" 2>/dev/null || true
|
||||
systemctl disable "\${SERVICE_UNIT}" 2>/dev/null || true
|
||||
log() { echo "[\${PKG_NAME}] \$*"; }
|
||||
|
||||
log "=========================================="
|
||||
log "卸载 \${PKG_NAME} (动作: \${ACTION})"
|
||||
log "=========================================="
|
||||
|
||||
log "[1/4] 停止并禁用 \${SERVICE_UNIT}"
|
||||
systemctl stop "\${SERVICE_UNIT}" 2>/dev/null && log " 已停止" || log " 未在运行"
|
||||
systemctl disable "\${SERVICE_UNIT}" >/dev/null 2>&1 && log " 已禁用" || true
|
||||
rm -f "/etc/systemd/system/\${SERVICE_UNIT}"
|
||||
systemctl daemon-reload
|
||||
|
||||
if [ "\${1:-}" = "remove" ]; then
|
||||
rm -rf "${SERVICE_INSTALL_DIR}"
|
||||
# DroneScrewServer 专用:停止 mediamtx
|
||||
if [ "\${PKG_NAME}" = "DroneScrewServer" ]; then
|
||||
log "[2/4] 停止并删除 mediamtx 服务"
|
||||
if systemctl is-active --quiet mediamtx-dronescrew.service 2>/dev/null; then
|
||||
systemctl stop mediamtx-dronescrew.service
|
||||
log " 已停止 mediamtx"
|
||||
fi
|
||||
systemctl disable mediamtx-dronescrew.service >/dev/null 2>&1 || true
|
||||
rm -f /etc/systemd/system/mediamtx-dronescrew.service
|
||||
systemctl daemon-reload
|
||||
log " mediamtx 服务已移除"
|
||||
else
|
||||
log "[2/4] 跳过 mediamtx(非 DroneScrewServer)"
|
||||
fi
|
||||
|
||||
if [ "\${ACTION}" = "remove" ] || [ "\${ACTION}" = "purge" ]; then
|
||||
log "[3/4] 清理安装目录 \${INSTALL_DIR}"
|
||||
rm -rf "\${INSTALL_DIR}"
|
||||
log "[4/4] 清理 ld.so.conf 并刷新缓存"
|
||||
rm -f "\${LD_CONF_FILE}"
|
||||
ldconfig
|
||||
log "=========================================="
|
||||
log "\${PKG_NAME} 卸载完成"
|
||||
log "=========================================="
|
||||
else
|
||||
log "[3/4] 跳过目录清理(仅升级阶段,保留安装目录)"
|
||||
log "[4/4] 跳过 ld.so.conf 清理"
|
||||
fi
|
||||
EOF
|
||||
|
||||
|
||||
@ -25,8 +25,12 @@
|
||||
#
|
||||
# 可选覆盖 (key=value):
|
||||
# 通用: app_dir version_macro opencv device_copy(none=不复制) description comment
|
||||
# package_depends(DEB Depends 字段,逗号分隔)
|
||||
# 桌面: algo_copy extra_copy optional_copy ld_conf
|
||||
# 服务: app_suffix(默认 Server)
|
||||
# 服务: app_dir app_suffix(默认 Server) extra_copy optional_copy
|
||||
#
|
||||
# 多条 spec: extra_copy 与 optional_copy 支持以英文逗号分隔多条复制规则
|
||||
# 例: extra_copy=glob|A/*.so|usr/lib,glob|B/*.so|usr/lib
|
||||
# =============================================================================
|
||||
|
||||
SCRIPT_DIR="${SCRIPT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)}"
|
||||
@ -34,51 +38,105 @@ source "${SCRIPT_DIR}/project_meta.sh"
|
||||
|
||||
COPY_VZNLSDK="glob|SDK/Device/VzNLSDK/Arm/aarch64/*.so|usr/lib"
|
||||
|
||||
# 复制编译生成的动态库
|
||||
# 说明:项目内部库绝大多数是静态库(CONFIG += staticlib),已在链接阶段
|
||||
# 打进可执行文件,运行时不需要 .so。实测各目录产出:
|
||||
# Device / Utils / VrNets —— 全为静态库,不产出 .so
|
||||
# Module —— 含 3 个动态库(BinocularMarkReceiver /
|
||||
# FFMediaStream / WDRemoteReceiver),需随包部署
|
||||
COPY_DEVICE_LIBS="globa|GrabBagPrj/buildarm/Device/*/*.so*|usr/lib"
|
||||
COPY_MODULE_LIBS="globa|GrabBagPrj/buildarm/Module/*/*.so*|usr/lib"
|
||||
COPY_UTILS_LIBS="globa|GrabBagPrj/buildarm/Utils/*/*.so*|usr/lib"
|
||||
COPY_VRNETS_LIBS="globa|GrabBagPrj/buildarm/VrNets/*.so*|usr/lib"
|
||||
|
||||
# 合并编译生成的动态库:当前仅 Module 实际产出 .so。
|
||||
# 这些规则走 required(extra_copy),空匹配会导致打包失败,
|
||||
# 因此只能收集确实存在 .so 的目录。若将来 Device/Utils/VrNets
|
||||
# 某目录新增动态库(CONFIG += shared),把对应 COPY_*_LIBS 追加到下方即可。
|
||||
COPY_ALL_BUILD_LIBS="${COPY_MODULE_LIBS}"
|
||||
|
||||
# ===== 桌面项目 =====
|
||||
DESKTOP_PROJECTS=(
|
||||
"GrabBag;bagPosition"
|
||||
"LapWeld;lapWeldDetection"
|
||||
"Workpiece;workpieceCornerExtraction"
|
||||
"ParticleSize;particleSizeMeasure"
|
||||
"ScrewPosition;rodAndBarDetection"
|
||||
"WorkpieceHole;workpieceHolePositioning"
|
||||
"StatorPosition;motorStatorPosition"
|
||||
"RodAndBarPosition;rodAndBarDetection"
|
||||
"RodWeldSeam;rodAndBarDetection"
|
||||
"HolePitPosition;workpieceHolePositioning"
|
||||
"WheelMeasure;wheelArchHeigthMeasure"
|
||||
"GrabBag;bagPosition;\
|
||||
extra_copy=${COPY_ALL_BUILD_LIBS}"
|
||||
"LapWeld;lapWeldDetection;\
|
||||
extra_copy=${COPY_ALL_BUILD_LIBS}"
|
||||
"Workpiece;workpieceCornerExtraction;\
|
||||
extra_copy=${COPY_ALL_BUILD_LIBS}"
|
||||
"ParticleSize;particleSizeMeasure;\
|
||||
extra_copy=${COPY_ALL_BUILD_LIBS}"
|
||||
"ScrewPosition;rodAndBarDetection;\
|
||||
extra_copy=${COPY_ALL_BUILD_LIBS}"
|
||||
"WorkpieceHole;workpieceHolePositioning;\
|
||||
extra_copy=${COPY_ALL_BUILD_LIBS}"
|
||||
"DiscHolePose;workpieceHolePositioning;\
|
||||
extra_copy=${COPY_ALL_BUILD_LIBS}"
|
||||
"TireHolePose;workpieceHolePositioning;\
|
||||
extra_copy=${COPY_ALL_BUILD_LIBS}"
|
||||
"StatorPosition;motorStatorPosition;\
|
||||
extra_copy=${COPY_ALL_BUILD_LIBS}"
|
||||
"RodAndBarPosition;rodAndBarDetection;\
|
||||
extra_copy=${COPY_ALL_BUILD_LIBS}"
|
||||
"RodWeldSeam;rodAndBarDetection;\
|
||||
extra_copy=${COPY_ALL_BUILD_LIBS}"
|
||||
"HolePitPosition;workpieceHolePositioning;\
|
||||
extra_copy=${COPY_ALL_BUILD_LIBS}"
|
||||
"WheelMeasure;wheelArchHeigthMeasure;\
|
||||
extra_copy=${COPY_ALL_BUILD_LIBS}"
|
||||
|
||||
"BagThreadPosition;bagThreadPositioning;\
|
||||
extra_copy=glob|SDK/Device/gl_linelaser_sdk/aarch64_linux/*.so|usr/lib"
|
||||
extra_copy=${COPY_ALL_BUILD_LIBS},glob|SDK/Device/gl_linelaser_sdk/aarch64_linux/*.so|usr/lib"
|
||||
|
||||
"HoleDetection;holeDetection;\
|
||||
algo_copy=globa|AppAlgo/holeDetection/arm/release/*.so*|usr/lib"
|
||||
algo_copy=globa|AppAlgo/holeDetection/arm/release/*.so*|usr/lib;\
|
||||
extra_copy=${COPY_ALL_BUILD_LIBS}"
|
||||
|
||||
"TunnelChannel;channelSpaceMeasure;\
|
||||
ld_conf=/usr/lib/HCNetSDKCom;\
|
||||
extra_copy=globa|SDK/Device/HCNetSDK/Arm64/lib/*.so*|usr/lib;\
|
||||
extra_copy=${COPY_ALL_BUILD_LIBS},globa|SDK/Device/HCNetSDK/Arm64/lib/*.so*|usr/lib;\
|
||||
optional_copy=globa|SDK/Device/HCNetSDK/Arm64/lib/HCNetSDKCom/*.so*|usr/lib/HCNetSDKCom"
|
||||
|
||||
"WorkpiecePosition;binocularMark;\
|
||||
app_dir=App/WorkpieceProject/WorkpiecePositionApp;\
|
||||
version_macro=WORKPIECEPOSITION;\
|
||||
opencv=opencv480;\
|
||||
device_copy=globa|SDK/Device/EpicEye/lib/arm/*.so*|usr/lib"
|
||||
device_copy=globa|SDK/Device/EpicEye/lib/arm/*.so*|usr/lib;\
|
||||
extra_copy=${COPY_ALL_BUILD_LIBS}"
|
||||
|
||||
"WorkpieceSplice;binocularMark;\
|
||||
app_dir=App/WorkpieceProject/WorkpieceSpliceApp;\
|
||||
version_macro=WORKPIECESPLICE;\
|
||||
opencv=opencv480;\
|
||||
device_copy=none"
|
||||
device_copy=none;\
|
||||
extra_copy=${COPY_ALL_BUILD_LIBS}"
|
||||
|
||||
"DroneScrewCtrlApp;none;\
|
||||
app_dir=App/DroneScrewbolt/DroneScrewCtrlApp;\
|
||||
version_macro=DRONESCREWCTRL;\
|
||||
device_copy=none;\
|
||||
extra_copy=${COPY_ALL_BUILD_LIBS},globa|SDK/ZeroMQ/Arm/aarch64/libzmq.so*|usr/lib;\
|
||||
description=DroneScrew Ctrl (board 2)"
|
||||
)
|
||||
|
||||
# ===== 服务项目 =====
|
||||
SERVICE_PROJECTS=(
|
||||
"BeltTearing;beltTearing"
|
||||
"BeltTearing;beltTearing;\
|
||||
extra_copy=${COPY_ALL_BUILD_LIBS}"
|
||||
|
||||
"BinocularMark;binocularMark;\
|
||||
opencv=opencv480;\
|
||||
device_copy=glob|SDK/Device/Galaxy/aarch64/lib/armv8/*|usr/lib"
|
||||
device_copy=glob|SDK/Device/Galaxy/aarch64/lib/armv8/*|usr/lib;\
|
||||
extra_copy=${COPY_ALL_BUILD_LIBS}"
|
||||
|
||||
"DroneScrewServer;none;\
|
||||
app_dir=App/DroneScrewbolt/DroneScrewServer;\
|
||||
version_macro=DRONESCREWSERVER;\
|
||||
app_suffix=Server;\
|
||||
opencv=opencv450;\
|
||||
device_copy=globa|SDK/Device/MVS/MVS_STD_V3.0.1_251113/MVS-3.0.1_aarch64_20251113/lib/aarch64/*.so*|usr/lib;\
|
||||
extra_copy=${COPY_ALL_BUILD_LIBS},globa|SDK/ZeroMQ/Arm/aarch64/libzmq.so*|usr/lib,file|SDK/mediamtx/arm64/mediamtx|opt/dronescrewserver/bin/mediamtx,file|App/DroneScrewbolt/DroneScrewServer/service/mediamtx_dronescrew.yml|opt/dronescrewserver/etc/mediamtx_dronescrew.yml,file|App/DroneScrewbolt/DroneScrewServer/service/mediamtx-dronescrew.service|opt/dronescrewserver/etc/mediamtx-dronescrew.service,globa|AppAlgo/stereo_bolt_delivery/lib/*.so*|usr/lib,dir|AppAlgo/stereo_bolt_delivery/config|opt/dronescrewserver/config,dir|AppAlgo/stereo_bolt_delivery/calib|opt/dronescrewserver/calib,dir|AppAlgo/stereo_bolt_delivery/weights|opt/dronescrewserver/weights;\
|
||||
optional_copy=globa|SDK/Protobuf_3_20/Arm/aarch64/libprotobuf.so*|usr/lib,globa|SDK/Device/MVS/MVS_STD_V3.0.1_251113/MVS-3.0.1_aarch64_20251113/lib/aarch64/ThirdParty/*.so*|usr/lib;\
|
||||
description=DroneScrew Server (board 1)"
|
||||
)
|
||||
|
||||
# ===== 执行注册 =====
|
||||
|
||||
@ -58,8 +58,27 @@ echo "版本文件: ${VERSION_FILE}"
|
||||
echo "完整版本号: ${FULL_VERSION}"
|
||||
|
||||
if ! grep -qE "\\|[[:space:]]*${PUBLISH_NAME}[[:space:]]*\\|" "${APP_LIST_FILE}"; then
|
||||
echo "错误: AppList.md 中未找到 ${PUBLISH_NAME}" >&2
|
||||
exit 1
|
||||
echo "警告: AppList.md 中未找到 ${PUBLISH_NAME},自动添加..."
|
||||
|
||||
# 获取当前最大序号
|
||||
max_num=$(awk -F'|' 'NF >= 6 && $2 ~ /^[[:space:]]*[0-9]+[[:space:]]*$/ {
|
||||
num = $2
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", num)
|
||||
if (num > max) max = num
|
||||
} END { print max+0 }' "${APP_LIST_FILE}")
|
||||
|
||||
next_num=$((max_num + 1))
|
||||
|
||||
# 获取项目描述(如果有)
|
||||
project_desc=$(get_project_meta "${VERSION_PROJECT}" package_description)
|
||||
if [ -z "$project_desc" ]; then
|
||||
project_desc="${VERSION_PROJECT}"
|
||||
fi
|
||||
|
||||
# 在表格末尾(最后一个空行之前)添加新行
|
||||
sed -i "/^$/i | ${next_num} | ${project_desc} | ${PUBLISH_NAME} | ${FULL_VERSION} |" "${APP_LIST_FILE}"
|
||||
|
||||
echo "已添加 ${PUBLISH_NAME} 到 AppList.md (序号: ${next_num})"
|
||||
fi
|
||||
|
||||
tmp_file="$(mktemp)"
|
||||
|
||||
@ -5,6 +5,53 @@
|
||||
#include <Eigen/SVD>
|
||||
#include <cmath>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
double NormalizeAnglePi(double angle)
|
||||
{
|
||||
double normalized = std::fmod(angle + M_PI, 2.0 * M_PI);
|
||||
if (normalized < 0.0) {
|
||||
normalized += 2.0 * M_PI;
|
||||
}
|
||||
return normalized - M_PI;
|
||||
}
|
||||
|
||||
void GetEulerOrderAxes(HECEulerOrder order, int axes[3])
|
||||
{
|
||||
switch (order) {
|
||||
case HECEulerOrder::XYZ: axes[0] = 0; axes[1] = 1; axes[2] = 2; break;
|
||||
case HECEulerOrder::XZY: axes[0] = 0; axes[1] = 2; axes[2] = 1; break;
|
||||
case HECEulerOrder::YXZ: axes[0] = 1; axes[1] = 0; axes[2] = 2; break;
|
||||
case HECEulerOrder::YZX: axes[0] = 1; axes[1] = 2; axes[2] = 0; break;
|
||||
case HECEulerOrder::ZXY: axes[0] = 2; axes[1] = 0; axes[2] = 1; break;
|
||||
case HECEulerOrder::ZYX: axes[0] = 2; axes[1] = 1; axes[2] = 0; break;
|
||||
}
|
||||
}
|
||||
|
||||
void NormalizeEulerBranch(HECEulerOrder order, HECEulerAngles& angles)
|
||||
{
|
||||
double values[3] = { angles.roll, angles.pitch, angles.yaw };
|
||||
int axes[3] = { 0, 1, 2 };
|
||||
GetEulerOrderAxes(order, axes);
|
||||
|
||||
if (std::abs(values[axes[1]]) > M_PI / 2.0) {
|
||||
values[axes[0]] = NormalizeAnglePi(values[axes[0]] + M_PI);
|
||||
values[axes[1]] = NormalizeAnglePi(M_PI - values[axes[1]]);
|
||||
values[axes[2]] = NormalizeAnglePi(values[axes[2]] + M_PI);
|
||||
} else {
|
||||
values[0] = NormalizeAnglePi(values[0]);
|
||||
values[1] = NormalizeAnglePi(values[1]);
|
||||
values[2] = NormalizeAnglePi(values[2]);
|
||||
}
|
||||
|
||||
angles.roll = values[0];
|
||||
angles.pitch = values[1];
|
||||
angles.yaw = values[2];
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
HandEyeCalib::HandEyeCalib()
|
||||
{
|
||||
}
|
||||
@ -195,6 +242,8 @@ void HandEyeCalib::RotationMatrixToEuler(
|
||||
angles.roll = ea[0]; angles.pitch = ea[1]; angles.yaw = ea[2];
|
||||
break;
|
||||
}
|
||||
|
||||
NormalizeEulerBranch(order, angles);
|
||||
}
|
||||
|
||||
void HandEyeCalib::EulerToRotationMatrix(
|
||||
|
||||
118
Module/WDRemoteReceiver/Inc/IWDRemoteReceiver.h
Normal file
118
Module/WDRemoteReceiver/Inc/IWDRemoteReceiver.h
Normal file
@ -0,0 +1,118 @@
|
||||
#ifndef IWDREMOTERECEIVER_H
|
||||
#define IWDREMOTERECEIVER_H
|
||||
|
||||
#include "WDRemoteDataTypes.h"
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// 导出宏定义
|
||||
#ifdef _WIN32
|
||||
# ifdef WDREMOTERECEIVER_LIBRARY
|
||||
# define WDREMOTERECEIVER_EXPORT __declspec(dllexport)
|
||||
# else
|
||||
# define WDREMOTERECEIVER_EXPORT __declspec(dllimport)
|
||||
# endif
|
||||
#else
|
||||
# define WDREMOTERECEIVER_EXPORT __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief 远程接收器接口
|
||||
*
|
||||
* 把对 ZMQ 控制通道 + 结果订阅 + 原始图像订阅的访问封装为一个动态库,
|
||||
* 不绑定到具体业务,下游项目可以直接复用。
|
||||
*
|
||||
* 内部使用:
|
||||
* - IVrZeroMQClient (控制 REQ-REP)
|
||||
* - IVrZeroMQSubscriber (结果 PUB-SUB,topic="result")
|
||||
* - IVrZeroMQSubscriber (原始图像 PUB-SUB,topic="raw")
|
||||
*
|
||||
* 典型用法:
|
||||
* IWDRemoteReceiver* p = nullptr;
|
||||
* IWDRemoteReceiver::CreateInstance(&p);
|
||||
* p->SetEventCallback(...);
|
||||
* p->SetDetectionCallback(...);
|
||||
* p->SetRawImageCallback(...); // 双目图像
|
||||
* std::vector<WDRemoteDeviceInfo> devices;
|
||||
* p->SearchDevices(devices); // UDP 广播发现设备
|
||||
* p->Open(devices[0].machineCode); // 按机器码打开指定设备
|
||||
* p->StartWork(WDRemoteWorkMode::Detection);
|
||||
* ...
|
||||
* p->StopWork(); // 发送停止指令
|
||||
* p->Disconnect(); // 断开所有通道
|
||||
*/
|
||||
class WDREMOTERECEIVER_EXPORT IWDRemoteReceiver
|
||||
{
|
||||
public:
|
||||
virtual ~IWDRemoteReceiver() = default;
|
||||
|
||||
/**
|
||||
* @brief 连接参数
|
||||
*/
|
||||
struct ConnectConfig
|
||||
{
|
||||
std::string serverIp{"127.0.0.1"};
|
||||
int controlPort{5555}; // ZMQ REQ-REP 控制
|
||||
int resultPort{5556}; // ZMQ PUB 检测结果
|
||||
int rawImagePort{5557}; // ZMQ PUB 原始图像,0 表示不启用
|
||||
};
|
||||
|
||||
// 回调类型
|
||||
using DetectionCallback = std::function<void(const WDRemoteDetectionFrame& frame)>;
|
||||
using RawImageCallback = std::function<void(const WDRemoteBinocularRawImage& image)>;
|
||||
using EventCallback = std::function<void(WDRemoteEventType ev,
|
||||
const std::string& msg)>;
|
||||
|
||||
// 连接 / 断开。推荐使用 SearchDevices + Open;Connect 保留给兼容旧调用。
|
||||
virtual int Connect(const ConnectConfig& cfg) = 0;
|
||||
virtual int SearchDevices(std::vector<WDRemoteDeviceInfo>& devices,
|
||||
int timeoutMs = 150) = 0;
|
||||
virtual int Open(const std::string& machineCode,
|
||||
int timeoutMs = 3000) = 0;
|
||||
virtual int Disconnect() = 0;
|
||||
virtual bool IsConnected() const = 0;
|
||||
|
||||
// 控制接口(内部同步走 ZMQ REQ-REP)
|
||||
virtual int StartWork(int timeoutMs = 3000) = 0;
|
||||
virtual int StartWork(WDRemoteWorkMode mode, int timeoutMs = 3000) = 0;
|
||||
|
||||
virtual int StopWork (int timeoutMs = 3000) = 0;
|
||||
virtual int StopWork (WDRemoteWorkMode mode, int timeoutMs = 3000) = 0;
|
||||
virtual int RequestSingleDetection(int timeoutMs = 15000) = 0;
|
||||
virtual int SetExposure(double exposureTime, int timeoutMs = 3000) = 0;
|
||||
virtual int SetGain (double gain, int timeoutMs = 3000) = 0;
|
||||
virtual int SetAlgoParams(const WDRemoteAlgoParams& params, int timeoutMs = 3000) = 0;
|
||||
virtual WDRemoteServerInfo GetServerInfo(int timeoutMs = 3000) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置检测模式下的图像传输模式
|
||||
* @param mode "binocular"(默认,发送左右目)或 "mono"(仅发送左目)
|
||||
*
|
||||
* 在调用 StartWork(Detection) 之前设置;
|
||||
* 仅在检测模式下生效,实时传图模式不受影响。
|
||||
*/
|
||||
virtual void SetDetectMode(const std::string& mode) = 0;
|
||||
|
||||
/**
|
||||
* @brief 自定义控制命令(透传 JSON 给服务端 ZMQ REP socket)
|
||||
*
|
||||
* 服务端目前不识别的命令会回 "unknown cmd"。
|
||||
* 上层可借此扩展,无需修改库本身。
|
||||
*/
|
||||
virtual int SendCustomCommand(const std::string& jsonReq,
|
||||
std::string& jsonResp,
|
||||
int timeoutMs = 3000) = 0;
|
||||
|
||||
// 回调注入
|
||||
virtual void SetDetectionCallback(DetectionCallback cb) = 0;
|
||||
virtual void SetRawImageCallback (RawImageCallback cb) = 0; // 双目图像
|
||||
virtual void SetEventCallback (EventCallback cb) = 0;
|
||||
|
||||
/**
|
||||
* @brief 创建实例
|
||||
*/
|
||||
static int CreateInstance(IWDRemoteReceiver** ppReceiver);
|
||||
};
|
||||
|
||||
#endif // IWDREMOTERECEIVER_H
|
||||
357
Module/WDRemoteReceiver/Inc/WDRemoteDataTypes.h
Normal file
357
Module/WDRemoteReceiver/Inc/WDRemoteDataTypes.h
Normal file
@ -0,0 +1,357 @@
|
||||
#ifndef WDREMOTEDATATYPES_H
|
||||
#define WDREMOTEDATATYPES_H
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
/**
|
||||
* @file WDRemoteDataTypes.h
|
||||
* @brief WDRemoteReceiver 数据类型定义
|
||||
*
|
||||
* 本文件包含所有 WDRemoteReceiver 使用的数据结构、枚举和常量定义。
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief 远程图像像素格式
|
||||
*
|
||||
* 取值与服务端 ZMQ 原始图像 PUB 的 pixelFormat 字段保持一致。
|
||||
*/
|
||||
enum class WDRemotePixelFormat : uint32_t
|
||||
{
|
||||
UNKNOWN = 0xFFFFFFFFu,
|
||||
MONO8 = 0,
|
||||
BGR8 = 1,
|
||||
RGB8 = 2,
|
||||
BAYERRG8 = 3,
|
||||
NV12 = 4,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 事件类型
|
||||
*/
|
||||
enum class WDRemoteEventType
|
||||
{
|
||||
UNKNOWN,
|
||||
CONNECTED, // 控制通道已建立
|
||||
DISCONNECTED, // 控制通道已断开
|
||||
CONNECTION_ERROR, // 连接错误
|
||||
REQUEST_TIMEOUT, // 同步请求超时
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 远程服务端的检测目标矩形框(图像像素坐标)
|
||||
*
|
||||
* 字段与服务端 DroneScrewBox 一一对应,但作为对外通用结构使用,
|
||||
* 不绑定到任何具体业务结构体。
|
||||
*/
|
||||
struct WDRemoteDetectionBox
|
||||
{
|
||||
int classId{0};
|
||||
float score{0.0f};
|
||||
int x{0};
|
||||
int y{0};
|
||||
int width{0};
|
||||
int height{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 远程单帧检测结果
|
||||
*/
|
||||
// Stereo distance result in millimeters.
|
||||
struct WDRemoteDetectionDistance
|
||||
{
|
||||
int fromId{0};
|
||||
int toId{0};
|
||||
float distanceMm{0.0f};
|
||||
};
|
||||
|
||||
struct WDRemoteDetectionFrame
|
||||
{
|
||||
uint64_t frameId{0};
|
||||
int64_t timestampUs{0};
|
||||
int imageWidth{0};
|
||||
int imageHeight{0};
|
||||
bool success{true};
|
||||
int errorCode{0};
|
||||
std::string message;
|
||||
std::vector<WDRemoteDetectionBox> boxes;
|
||||
std::vector<WDRemoteDetectionDistance> distances;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 远程算法参数(透传给服务端)
|
||||
*/
|
||||
struct WDRemoteAlgoParams
|
||||
{
|
||||
float scoreThreshold{0.5f};
|
||||
float nmsThreshold{0.45f};
|
||||
int inputWidth{640};
|
||||
int inputHeight{640};
|
||||
std::string modelPath;
|
||||
int modelType{0}; // 0=YOLO, 1=自定义
|
||||
};
|
||||
|
||||
struct WDRemoteMatrix
|
||||
{
|
||||
int rows{0};
|
||||
int cols{0};
|
||||
std::vector<double> data;
|
||||
|
||||
bool empty() const { return rows <= 0 || cols <= 0 || data.empty(); }
|
||||
};
|
||||
|
||||
struct WDRemoteCameraCalibration
|
||||
{
|
||||
bool valid{false};
|
||||
int imageWidth{0};
|
||||
int imageHeight{0};
|
||||
double rms{0.0};
|
||||
WDRemoteMatrix cameraMatrix;
|
||||
WDRemoteMatrix distCoeffs;
|
||||
};
|
||||
|
||||
struct WDRemoteStereoCalibration
|
||||
{
|
||||
bool valid{false};
|
||||
double rms{0.0};
|
||||
double baselineMm{0.0};
|
||||
WDRemoteMatrix R;
|
||||
WDRemoteMatrix T;
|
||||
WDRemoteMatrix E;
|
||||
WDRemoteMatrix F;
|
||||
WDRemoteMatrix R1;
|
||||
WDRemoteMatrix R2;
|
||||
WDRemoteMatrix P1;
|
||||
WDRemoteMatrix P2;
|
||||
WDRemoteMatrix Q;
|
||||
};
|
||||
|
||||
struct WDRemoteCalibrationInfo
|
||||
{
|
||||
bool valid{false};
|
||||
std::string sourcePath;
|
||||
std::string algoConfigPath;
|
||||
std::string message;
|
||||
WDRemoteCameraCalibration left;
|
||||
WDRemoteCameraCalibration right;
|
||||
WDRemoteStereoCalibration stereo;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 服务端运行时信息(GetServerInfo 返回)
|
||||
*/
|
||||
struct WDRemoteServerInfo
|
||||
{
|
||||
bool ok{false};
|
||||
std::string rtspUrl;
|
||||
std::string version;
|
||||
int controlPort{0};
|
||||
int resultPort{0};
|
||||
int rawImagePort{0};
|
||||
double detectFps{0.0};
|
||||
double streamFps{0.0};
|
||||
double leftExposure{0.0};
|
||||
double rightExposure{0.0};
|
||||
double leftGain{0.0};
|
||||
double rightGain{0.0};
|
||||
int leftCameraIndex{0};
|
||||
int rightCameraIndex{1};
|
||||
std::string leftCameraSerial;
|
||||
std::string rightCameraSerial;
|
||||
int streamWidth{0};
|
||||
int streamHeight{0};
|
||||
std::string streamCamera;
|
||||
int binningHorizontal{0};
|
||||
int binningVertical{0};
|
||||
std::string mode;
|
||||
std::string detectMode;
|
||||
// 算法参数
|
||||
float algoScore{0.5f};
|
||||
float algoNms{0.45f};
|
||||
int algoWidth{640};
|
||||
int algoHeight{640};
|
||||
std::string algoModel;
|
||||
int algoModelType{0};
|
||||
WDRemoteCalibrationInfo calibration;
|
||||
std::string rawJson; // 原始 JSON 应答,方便扩展字段
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 远程服务设备信息(SearchDevices 返回)
|
||||
*/
|
||||
struct WDRemoteDeviceInfo
|
||||
{
|
||||
std::string machineCode;
|
||||
std::string clientMachineCode;
|
||||
std::string serverIp;
|
||||
std::string deviceName;
|
||||
int controlPort{5555};
|
||||
int resultPort{5556};
|
||||
int rawImagePort{5557};
|
||||
std::string rtspUrl;
|
||||
double detectFps{0.0};
|
||||
double streamFps{0.0};
|
||||
double leftExposure{0.0};
|
||||
double rightExposure{0.0};
|
||||
double leftGain{0.0};
|
||||
double rightGain{0.0};
|
||||
int leftCameraIndex{0};
|
||||
int rightCameraIndex{1};
|
||||
std::string leftCameraSerial;
|
||||
std::string rightCameraSerial;
|
||||
int streamWidth{0};
|
||||
int streamHeight{0};
|
||||
std::string streamCamera;
|
||||
int binningHorizontal{0};
|
||||
int binningVertical{0};
|
||||
std::string mode;
|
||||
std::string detectMode;
|
||||
std::string rawJson;
|
||||
};
|
||||
|
||||
constexpr uint16_t WD_REMOTE_DISCOVERY_PORT = 5554;
|
||||
|
||||
/**
|
||||
* @brief StartWork 工作模式
|
||||
*/
|
||||
enum class WDRemoteWorkMode
|
||||
{
|
||||
LiveStream = 0, // 实时左目传图
|
||||
Detection = 1, // 检测模式,订阅检测结果和 raw 图像
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 远程原始图像数据(单目)
|
||||
*
|
||||
* - data / dataSize 在回调内有效,回调返回后被库回收
|
||||
* - 上层若需要保存,请深拷贝
|
||||
*/
|
||||
struct WDRemoteRawImage
|
||||
{
|
||||
uint64_t frameId{0};
|
||||
int64_t timestampUs{0};
|
||||
int width{0};
|
||||
int height{0};
|
||||
int stride{0};
|
||||
WDRemotePixelFormat pixelFormat{WDRemotePixelFormat::UNKNOWN};
|
||||
const uint8_t* data{nullptr};
|
||||
size_t dataSize{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 远程双目原始图像数据
|
||||
*
|
||||
* 用于双目相机系统,同时包含左右目图像。
|
||||
* - leftData / rightData / dataSize 在回调内有效,回调返回后被库回收
|
||||
* - 上层若需要保存,请深拷贝
|
||||
*/
|
||||
struct WDRemoteBinocularRawImage
|
||||
{
|
||||
uint64_t frameId{0};
|
||||
int64_t timestampUs{0};
|
||||
|
||||
// 左目图像
|
||||
int leftWidth{0};
|
||||
int leftHeight{0};
|
||||
int leftStride{0};
|
||||
WDRemotePixelFormat leftPixelFormat{WDRemotePixelFormat::UNKNOWN};
|
||||
const uint8_t* leftData{nullptr};
|
||||
size_t leftDataSize{0};
|
||||
|
||||
// 右目图像
|
||||
int rightWidth{0};
|
||||
int rightHeight{0};
|
||||
int rightStride{0};
|
||||
WDRemotePixelFormat rightPixelFormat{WDRemotePixelFormat::UNKNOWN};
|
||||
const uint8_t* rightData{nullptr};
|
||||
size_t rightDataSize{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 原始图像 ZMQ PUB 协议头(48 字节,紧凑布局)
|
||||
*
|
||||
* 服务端 PUB 数据 = topic(独立 ZMQ 分帧) + payload
|
||||
* payload = WDRemoteRawImageHeader (48B) + raw bytes
|
||||
*
|
||||
* 这里同时给到客户端用于序列化/反序列化,便于第三方接入。
|
||||
*/
|
||||
#pragma pack(push, 1)
|
||||
struct WDRemoteRawImageHeader
|
||||
{
|
||||
uint32_t magic; // 0x52415749 ('RAWI')
|
||||
uint32_t version; // 1
|
||||
uint64_t frameId;
|
||||
int64_t timestampUs;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t stride;
|
||||
uint32_t pixelFormat; // WDRemotePixelFormat
|
||||
uint32_t dataSize;
|
||||
uint32_t reserved;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
static_assert(sizeof(WDRemoteRawImageHeader) == 48,
|
||||
"WDRemoteRawImageHeader must be 48 bytes");
|
||||
|
||||
constexpr uint32_t WD_REMOTE_RAW_IMAGE_MAGIC = 0x52415749u; // 'RAWI'
|
||||
constexpr uint32_t WD_REMOTE_RAW_IMAGE_VERSION = 1u;
|
||||
|
||||
/**
|
||||
* @brief 双目原始图像 ZMQ PUB 协议头(88 字节,紧凑布局)
|
||||
*
|
||||
* 服务端 PUB 数据 = topic(独立 ZMQ 分帧) + payload
|
||||
* payload = WDRemoteBinocularRawImageHeader (88B) + left raw bytes + right raw bytes
|
||||
*/
|
||||
#pragma pack(push, 1)
|
||||
struct WDRemoteBinocularRawImageHeader
|
||||
{
|
||||
uint32_t magic; // 0x42495749 ('BIWI' - Binocular Image) 4B
|
||||
uint32_t version; // 1 4B
|
||||
uint64_t frameId; // 8B
|
||||
int64_t timestampUs; // 8B
|
||||
|
||||
// 左目图像信息 (20B)
|
||||
uint32_t leftWidth;
|
||||
uint32_t leftHeight;
|
||||
uint32_t leftStride;
|
||||
uint32_t leftPixelFormat;
|
||||
uint32_t leftDataSize;
|
||||
|
||||
// 右目图像信息 (20B)
|
||||
uint32_t rightWidth;
|
||||
uint32_t rightHeight;
|
||||
uint32_t rightStride;
|
||||
uint32_t rightPixelFormat;
|
||||
uint32_t rightDataSize;
|
||||
|
||||
uint32_t reserved[6]; // 24B padding (total: 4+4+8+8+20+20+24=88)
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
static_assert(sizeof(WDRemoteBinocularRawImageHeader) == 88,
|
||||
"WDRemoteBinocularRawImageHeader must be 88 bytes");
|
||||
|
||||
constexpr uint32_t WD_REMOTE_BINOCULAR_RAW_IMAGE_MAGIC = 0x42495749u; // 'BIWI'
|
||||
constexpr uint32_t WD_REMOTE_BINOCULAR_RAW_IMAGE_VERSION = 1u;
|
||||
|
||||
/**
|
||||
* @brief 双目原始图像 payload 压缩方式
|
||||
*
|
||||
* WDRemoteBinocularRawImageHeader::reserved[0] 记录该值:
|
||||
* - 0: payload 中 left/right 数据就是原始图像字节
|
||||
* - 1: payload 中 left/right 数据是 PNG 压缩后的图像字节
|
||||
* - 2: payload 中 left/right 数据是 MJPEG/JPEG 压缩后的图像字节(MPP 硬件编码,有损)
|
||||
* - 3: payload 中 left/right 数据是 zlib/deflate 压缩后的原始字节(真·无损,bit-exact)
|
||||
*
|
||||
* reserved[1] / reserved[2] 在压缩模式下分别记录左右目压缩前的原始字节数。
|
||||
*/
|
||||
constexpr uint32_t WD_REMOTE_IMAGE_COMPRESSION_RAW = 0u;
|
||||
constexpr uint32_t WD_REMOTE_IMAGE_COMPRESSION_PNG = 1u;
|
||||
constexpr uint32_t WD_REMOTE_IMAGE_COMPRESSION_MJPEG = 2u;
|
||||
constexpr uint32_t WD_REMOTE_IMAGE_COMPRESSION_ZLIB = 3u;
|
||||
|
||||
#endif // WDREMOTEDATATYPES_H
|
||||
1241
Module/WDRemoteReceiver/Src/WDRemoteReceiver.cpp
Normal file
1241
Module/WDRemoteReceiver/Src/WDRemoteReceiver.cpp
Normal file
File diff suppressed because it is too large
Load Diff
104
Module/WDRemoteReceiver/WDRemoteReceiver.pro
Normal file
104
Module/WDRemoteReceiver/WDRemoteReceiver.pro
Normal file
@ -0,0 +1,104 @@
|
||||
# WDRemoteReceiver.pro
|
||||
# 远程设备控制 + 结果订阅 + 原始图像订阅 封装库
|
||||
# 对外提供 IWDRemoteReceiver 接口;下游项目可直接复用,不与 DroneScrew 业务绑定
|
||||
|
||||
QT += core network
|
||||
QT -= gui
|
||||
|
||||
TARGET = WDRemoteReceiver
|
||||
TEMPLATE = lib
|
||||
CONFIG += c++17
|
||||
|
||||
# 全平台使用动态库
|
||||
CONFIG += shared
|
||||
|
||||
# 宏定义
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
DEFINES += WDREMOTERECEIVER_LIBRARY
|
||||
|
||||
# 让本库内部能使用 IVrZeroMQClient / IVrZeroMQSubscriber 的导入宏
|
||||
win32 {
|
||||
DEFINES += VR_MQCLIENT_EXPORTS=0
|
||||
DEFINES += VR_MQPUBSUB_EXPORTS=0
|
||||
}
|
||||
|
||||
unix {
|
||||
DEFINES += VR_MQCLIENT_EXPORTS=0
|
||||
DEFINES += VR_MQPUBSUB_EXPORTS=0
|
||||
}
|
||||
|
||||
# UTF-8 编码
|
||||
win32-msvc {
|
||||
QMAKE_CXXFLAGS += /utf-8
|
||||
QMAKE_CFLAGS += /utf-8
|
||||
}
|
||||
|
||||
# 头文件路径
|
||||
INCLUDEPATH += $$PWD/Inc
|
||||
INCLUDEPATH += $$PWD/_Inc
|
||||
INCLUDEPATH += $$PWD/../../Utils/VrUtils/Inc
|
||||
INCLUDEPATH += $$PWD/../../Utils/VrUtils/jsoncpp
|
||||
INCLUDEPATH += $$PWD/../../Utils/VrCommon/Inc
|
||||
INCLUDEPATH += $$PWD/../AuthModule/Inc
|
||||
|
||||
# 依赖模块(ZeroMQ 控制 + 结果订阅 + 原始图像订阅)
|
||||
INCLUDEPATH += $$PWD/../ZeroMQClient/Inc
|
||||
INCLUDEPATH += $$PWD/../ZeroMQPubSub/Inc
|
||||
|
||||
# SDK 头(ZeroMQ)
|
||||
INCLUDEPATH += $$PWD/../../SDK/ZeroMQ/include
|
||||
INCLUDEPATH += $$PWD/../../SDK/OpenCV320/include
|
||||
|
||||
HEADERS += \
|
||||
Inc/IWDRemoteReceiver.h \
|
||||
_Inc/WDRemoteReceiver.h
|
||||
|
||||
SOURCES += \
|
||||
Src/WDRemoteReceiver.cpp
|
||||
|
||||
# 库链接
|
||||
win32 {
|
||||
CONFIG(debug, debug|release) {
|
||||
LIBS += -L../ZeroMQClient/debug -lZeroMQClient
|
||||
LIBS += -L../ZeroMQPubSub/debug -lZeroMQPubSub
|
||||
LIBS += -L../AuthModule/debug -lAuthModule
|
||||
LIBS += -L../../Utils/VrUtils/debug -lVrUtils
|
||||
} else {
|
||||
LIBS += -L../ZeroMQClient/release -lZeroMQClient
|
||||
LIBS += -L../ZeroMQPubSub/release -lZeroMQPubSub
|
||||
LIBS += -L../AuthModule/release -lAuthModule
|
||||
LIBS += -L../../Utils/VrUtils/release -lVrUtils
|
||||
}
|
||||
|
||||
# ZeroMQ SDK
|
||||
CONFIG(debug, debug|release) {
|
||||
LIBS += -L$$PWD/../../SDK/ZeroMQ/Windows/x64/Debug -llibzmq-v142-mt-gd-4_3_5
|
||||
} else {
|
||||
LIBS += -L$$PWD/../../SDK/ZeroMQ/Windows/x64/Release -llibzmq-v142-mt-4_3_5
|
||||
}
|
||||
|
||||
LIBS += -lws2_32
|
||||
|
||||
CONFIG(debug, debug|release) {
|
||||
LIBS += -L$$PWD/../../SDK/OpenCV320/Windows/vc14/Release -lopencv_world320d
|
||||
} else {
|
||||
LIBS += -L$$PWD/../../SDK/OpenCV320/Windows/vc14/Release -lopencv_world320
|
||||
}
|
||||
}
|
||||
|
||||
unix:!macx {
|
||||
LIBS += -L../ZeroMQClient -lZeroMQClient
|
||||
LIBS += -L../ZeroMQPubSub -lZeroMQPubSub
|
||||
LIBS += -L../AuthModule -lAuthModule
|
||||
LIBS += -L../../Utils/VrUtils -lVrUtils
|
||||
|
||||
LIBS += -L$$PWD/../../SDK/ZeroMQ/Arm/aarch64 -lzmq
|
||||
LIBS += -L$$PWD/../../SDK/OpenCV320/Arm/aarch64 -lopencv_core -lopencv_imgcodecs
|
||||
LIBS += -lpthread
|
||||
|
||||
QMAKE_CXXFLAGS += -fPIC
|
||||
|
||||
target.path = /usr/lib
|
||||
}
|
||||
|
||||
!isEmpty(target.path): INSTALLS += target
|
||||
96
Module/WDRemoteReceiver/_Inc/WDRemoteReceiver.h
Normal file
96
Module/WDRemoteReceiver/_Inc/WDRemoteReceiver.h
Normal file
@ -0,0 +1,96 @@
|
||||
#ifndef WDREMOTERECEIVER_H
|
||||
#define WDREMOTERECEIVER_H
|
||||
|
||||
#include "IWDRemoteReceiver.h"
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class IVrZeroMQClient;
|
||||
class IVrZeroMQSubscriber;
|
||||
|
||||
/**
|
||||
* @brief IWDRemoteReceiver 实现
|
||||
*
|
||||
* - 控制:IVrZeroMQClient (REQ-REP)
|
||||
* - 结果订阅:IVrZeroMQSubscriber on topic="result"
|
||||
* - 原始图像订阅:IVrZeroMQSubscriber on topic="raw"
|
||||
*/
|
||||
class WDRemoteReceiver : public IWDRemoteReceiver
|
||||
{
|
||||
public:
|
||||
WDRemoteReceiver();
|
||||
~WDRemoteReceiver() override;
|
||||
|
||||
// IWDRemoteReceiver
|
||||
int Connect(const ConnectConfig& cfg) override;
|
||||
int SearchDevices(std::vector<WDRemoteDeviceInfo>& devices,
|
||||
int timeoutMs) override;
|
||||
int Open(const std::string& machineCode,
|
||||
int timeoutMs) override;
|
||||
int Disconnect() override;
|
||||
bool IsConnected() const override;
|
||||
|
||||
int StartWork(int timeoutMs) override;
|
||||
int StartWork(WDRemoteWorkMode mode, int timeoutMs) override;
|
||||
int StopWork (int timeoutMs) override;
|
||||
int StopWork (WDRemoteWorkMode mode, int timeoutMs) override;
|
||||
int RequestSingleDetection(int timeoutMs) override;
|
||||
int SetExposure(double exposureTime, int timeoutMs) override;
|
||||
int SetGain (double gain, int timeoutMs) override;
|
||||
int SetAlgoParams(const WDRemoteAlgoParams& params, int timeoutMs) override;
|
||||
WDRemoteServerInfo GetServerInfo(int timeoutMs) override;
|
||||
int SendCustomCommand(const std::string& jsonReq,
|
||||
std::string& jsonResp,
|
||||
int timeoutMs) override;
|
||||
void SetDetectMode(const std::string& mode) override;
|
||||
|
||||
void SetDetectionCallback(DetectionCallback cb) override;
|
||||
void SetRawImageCallback (RawImageCallback cb) override; // 双目图像
|
||||
void SetEventCallback (EventCallback cb) override;
|
||||
|
||||
private:
|
||||
int sendCommandAndParse(const std::string& jsonReq,
|
||||
std::string& jsonResp,
|
||||
int timeoutMs);
|
||||
|
||||
// 内部订阅管理(StartWork/StopWork 自动调用)
|
||||
int StartSubscribeDetection();
|
||||
int StopSubscribeDetection();
|
||||
int StartSubscribeRawImage();
|
||||
int StopSubscribeRawImage();
|
||||
|
||||
void onResultMessage (const char* topic, size_t topicLen,
|
||||
const char* data, size_t dataLen);
|
||||
void onRawImageMessage(const char* topic, size_t topicLen,
|
||||
const char* data, size_t dataLen);
|
||||
|
||||
void emitEvent(WDRemoteEventType ev, const std::string& msg);
|
||||
|
||||
static bool parseDetectionJson(const std::string& jsonStr,
|
||||
WDRemoteDetectionFrame& outFrame);
|
||||
static bool parseDeviceInfoJson(const std::string& jsonStr,
|
||||
const std::string& serverIp,
|
||||
WDRemoteDeviceInfo& outDevice);
|
||||
|
||||
private:
|
||||
ConnectConfig m_cfg;
|
||||
std::atomic<bool> m_bConnected{false};
|
||||
|
||||
IVrZeroMQClient* m_pCtrlClient{nullptr};
|
||||
IVrZeroMQSubscriber* m_pSubResult{nullptr};
|
||||
IVrZeroMQSubscriber* m_pSubRaw{nullptr};
|
||||
|
||||
std::mutex m_mtxCtrl; // 串行化 REQ-REP
|
||||
mutable std::mutex m_mtxCallback;
|
||||
mutable std::mutex m_mtxDetectMode;
|
||||
|
||||
DetectionCallback m_cbDetection;
|
||||
RawImageCallback m_cbRawImage; // 双目图像回调
|
||||
EventCallback m_cbEvent;
|
||||
std::vector<WDRemoteDeviceInfo> m_lastDevices;
|
||||
std::string m_detectMode{"binocular"}; // "binocular" | "mono"
|
||||
};
|
||||
|
||||
#endif // WDREMOTERECEIVER_H
|
||||
@ -175,7 +175,7 @@ void CVrZeroMQServer::_WorkTask(zmq::socket_t *socket)
|
||||
std::string strResponse = m_funRecvAck(static_cast<char*>(msg.data()), msg.size());
|
||||
zmq::message_t response(strResponse.c_str(), strResponse.size());
|
||||
socket->send(response, zmq::send_flags::none);
|
||||
LOG_DEBUG("[ZMQ-REP] _WorkTask send resp: %s\n", strResponse.c_str());
|
||||
LOG_DEBUG("[ZMQ-REP] _WorkTask send resp bytes=%zu\n", strResponse.size());
|
||||
} else {
|
||||
socket->send(msg, zmq::send_flags::none);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user