fix(launcher): run NAV2 modules through host runtime
This commit is contained in:
+19
@@ -5,3 +5,22 @@ out/
|
||||
.env.*
|
||||
*.log
|
||||
.DS_Store
|
||||
|
||||
# Direct `tsc -p` output. Electron Vite owns application compilation.
|
||||
/electron.vite.config.js
|
||||
/electron.vite.config.d.ts
|
||||
/src/main/*.js
|
||||
/src/main/*.d.ts
|
||||
/src/main/**/*.js
|
||||
/src/main/**/*.d.ts
|
||||
/src/preload/*.js
|
||||
/src/preload/*.d.ts
|
||||
/src/preload/**/*.js
|
||||
/src/preload/**/*.d.ts
|
||||
/src/renderer/src/*.js
|
||||
/src/renderer/src/*.jsx
|
||||
/src/renderer/src/*.d.ts
|
||||
/src/renderer/src/**/*.js
|
||||
/src/renderer/src/**/*.jsx
|
||||
/src/renderer/src/**/*.d.ts
|
||||
*.tsbuildinfo
|
||||
|
||||
@@ -176,6 +176,28 @@ function containerName(folderName: string): string | null {
|
||||
return /^[A-Za-z0-9_.-]+-[A-Za-z0-9_.-]+_v[A-Za-z0-9_.-]+$/.test(name) ? name : null
|
||||
}
|
||||
|
||||
function isNav2ModuleFolder(folderName: string): boolean {
|
||||
return /^\[lms-nav2-[^\]]+\].+_v[^/]+_ws$/.test(folderName)
|
||||
}
|
||||
|
||||
function resolveLocalNav2RuntimeImage(module: CourseModule, body: Record<string, unknown>, rosParams: string): string {
|
||||
const configured = module.dockerImage?.trim() ?? ''
|
||||
if (configured && /^[A-Za-z0-9._/:@-]+$/.test(configured)) return configured
|
||||
|
||||
const gazeboModule = /^\[lms-nav2-gazebo-/i.test(module.folderName)
|
||||
|| (/lms-nav2-mkmini/i.test(module.folderName) && /(?:^|\s)simulator:=gazebo(?:\s|$)/.test(rosParams))
|
||||
const runtimeImages = Array.isArray(body.runtimeImages) ? body.runtimeImages : []
|
||||
const matched = runtimeImages.find((value) => {
|
||||
if (!value || typeof value !== 'object' || Array.isArray(value)) return false
|
||||
const image = (value as RuntimeImageArtifact).image?.trim() ?? ''
|
||||
return gazeboModule
|
||||
? /nav2.*gazebo|gazebo.*nav2/i.test(image)
|
||||
: /nav2/i.test(image) && !/gazebo|ros-bridge/i.test(image)
|
||||
}) as RuntimeImageArtifact | undefined
|
||||
|
||||
return matched?.image?.trim() || (gazeboModule ? 'future-nav2-gazebo:humble' : 'future-nav2:humble')
|
||||
}
|
||||
|
||||
function nav2LaunchSpec(folderName: string): { packageName: string; launchFile: string } | null {
|
||||
const module = folderName.match(/^\[lms-nav2-(?:gazebo-)?([a-z]+)\]/)?.[1]
|
||||
const specs: Record<string, { packageName: string; launchFile: string }> = {
|
||||
@@ -930,7 +952,6 @@ export class LmsWebBridge {
|
||||
const module = this.moduleFromBody(body)
|
||||
const root = expandWorkspaceRoot(typeof body.workspaceRoot === 'string' ? body.workspaceRoot : undefined)
|
||||
const target = resolve(root, module.folderName)
|
||||
const runtimeConfigRoot = resolve(homedir(), 'autonomy-studio', 'runtime', 'config')
|
||||
if (!existsSync(target)) throw new Error('설치된 모듈을 찾을 수 없습니다.')
|
||||
const container = containerName(module.folderName)
|
||||
if (!container) throw new Error('모듈 컨테이너 이름이 올바르지 않습니다.')
|
||||
@@ -940,6 +961,23 @@ export class LmsWebBridge {
|
||||
const rosParams = typeof body.rosParams === 'string' ? body.rosParams.trim() : ''
|
||||
const rosDomainId = typeof body.rosDomainId === 'number' || typeof body.rosDomainId === 'string' ? String(body.rosDomainId).replace(/[^0-9]/g, '') || '0' : '0'
|
||||
const gpuEnabled = body.gpuEnabled === true
|
||||
if (isNav2ModuleFolder(module.folderName)) {
|
||||
const runtimeScript = resolve(homedir(), 'autonomy-studio', 'runtime', 'nav2', 'docker', 'docker_run_nav2.sh')
|
||||
if (!existsSync(runtimeScript)) throw new Error(`NAV2 런타임 스크립트를 찾을 수 없습니다: ${runtimeScript}`)
|
||||
await command('bash', [
|
||||
runtimeScript,
|
||||
container,
|
||||
root,
|
||||
module.folderName,
|
||||
resolveLocalNav2RuntimeImage(module, body, rosParams),
|
||||
rosDomainId,
|
||||
gpuEnabled ? 'true' : 'false',
|
||||
...rosParams.split(/\s+/).filter(Boolean),
|
||||
], undefined, 45_000)
|
||||
return { ok: true, message: '실행 요청을 보냈습니다.' }
|
||||
}
|
||||
|
||||
const runtimeConfigRoot = resolve(homedir(), 'autonomy-studio', 'runtime', 'config')
|
||||
await mkdir(runtimeConfigRoot, { recursive: true })
|
||||
const args = ['run', '-d', '--name', container, '--network', 'host', '-v', `${target}:/autoware:rw`, '-v', `${runtimeConfigRoot}:/autoware/config:rw`, '-v', '/etc/localtime:/etc/localtime:ro', '-e', `ROS_DOMAIN_ID=${rosDomainId}`, '-e', 'RCUTILS_COLORIZED_OUTPUT=1', '-e', 'ROS_LOCALHOST_ONLY=1']
|
||||
if (process.env.DISPLAY) args.push('-e', `DISPLAY=${process.env.DISPLAY}`, '-v', '/tmp/.X11-unix:/tmp/.X11-unix')
|
||||
@@ -957,6 +995,19 @@ export class LmsWebBridge {
|
||||
const container = containerName(module.folderName)
|
||||
if (!container) throw new Error('모듈 컨테이너 이름이 올바르지 않습니다.')
|
||||
const rosDomainId = typeof body.rosDomainId === 'number' || typeof body.rosDomainId === 'string' ? String(body.rosDomainId).replace(/[^0-9]/g, '') || '0' : '0'
|
||||
if (isNav2ModuleFolder(module.folderName)) {
|
||||
const runtimeScript = resolve(homedir(), 'autonomy-studio', 'runtime', 'nav2', 'docker', 'docker_build_nav2.sh')
|
||||
if (!existsSync(runtimeScript)) throw new Error(`NAV2 빌드 스크립트를 찾을 수 없습니다: ${runtimeScript}`)
|
||||
const rosParams = typeof body.rosParams === 'string' ? body.rosParams.trim() : ''
|
||||
await command('bash', [
|
||||
runtimeScript,
|
||||
root,
|
||||
module.folderName,
|
||||
resolveLocalNav2RuntimeImage(module, body, rosParams),
|
||||
rosDomainId,
|
||||
], undefined, 900_000)
|
||||
return { ok: true, message: '빌드가 완료되었습니다.' }
|
||||
}
|
||||
await command('docker', [
|
||||
'run', '--rm', '--name', `${container}-build`, '--volume', `${target}:/autoware:rw`, '-u', 'root', '--net=host',
|
||||
'-e', `ROS_DOMAIN_ID=${rosDomainId}`, '-v', '/etc/localtime:/etc/localtime:ro', '--entrypoint', '/bin/bash', AUTOWARE_IMAGE,
|
||||
|
||||
+3
-1
@@ -2,7 +2,9 @@
|
||||
"extends": "@electron-toolkit/tsconfig/tsconfig.node.json",
|
||||
"include": ["electron.vite.config.*", "src/main/**/*", "src/preload/**/*"],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"composite": false,
|
||||
"incremental": false,
|
||||
"noEmit": true,
|
||||
"moduleResolution": "bundler",
|
||||
"types": ["electron-vite/node"]
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
+3
-1
@@ -2,7 +2,9 @@
|
||||
"extends": "@electron-toolkit/tsconfig/tsconfig.web.json",
|
||||
"include": ["src/renderer/src/**/*"],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"composite": false,
|
||||
"incremental": false,
|
||||
"noEmit": true,
|
||||
"resolveJsonModule": true,
|
||||
"moduleResolution": "bundler",
|
||||
"paths": {
|
||||
|
||||
Reference in New Issue
Block a user