patch..
This commit is contained in:
+55
-14
@@ -1,6 +1,6 @@
|
||||
import { app, BrowserWindow, Menu, globalShortcut, ipcMain, screen } from 'electron'
|
||||
import type { WebContents } from 'electron'
|
||||
import { spawn, type ChildProcessWithoutNullStreams } from 'child_process'
|
||||
import { spawn } from 'child_process'
|
||||
import * as pty from 'node-pty'
|
||||
import type { IPty } from 'node-pty'
|
||||
import { appendFileSync, createWriteStream, existsSync, mkdirSync } from 'fs'
|
||||
@@ -245,7 +245,7 @@ type RosNetworkConfig = {
|
||||
peer?: RemoteWorkspaceTarget | null
|
||||
}
|
||||
|
||||
type VisualizationStack = 'autoware' | 'nav2' | 'slam'
|
||||
type VisualizationStack = 'autoware' | 'nav2' | 'slam' | 'slam3d'
|
||||
|
||||
type VisualizationBridgeStatus = {
|
||||
ok: boolean
|
||||
@@ -1212,8 +1212,9 @@ async function resolveRemoteCustomModuleRuntimeImage(
|
||||
folderName: string,
|
||||
fallbackImage: string,
|
||||
): Promise<string> {
|
||||
const output = await runSshOutput(target, [
|
||||
`ROOT=${quoteShellSingle(target.workspaceRoot)}`,
|
||||
const normalizedTarget = normalizeRemoteTarget(target)
|
||||
const output = await runSshOutput(normalizedTarget, [
|
||||
`ROOT=${quoteShellSingle(normalizedTarget.workspaceRoot)}`,
|
||||
...expandRemotePathVar('ROOT'),
|
||||
`CONFIG="$ROOT/${folderName}/.autonomy-studio-module.json"`,
|
||||
'if [ -f "$CONFIG" ]; then python3 - "$CONFIG" <<\'PY\'',
|
||||
@@ -1524,6 +1525,31 @@ async function runCommand(command: string, args: string[], cwd?: string, env?: N
|
||||
await runCommandOutput(command, args, cwd, env, timeoutMs)
|
||||
}
|
||||
|
||||
async function listLocalDockerImages(): Promise<{ images: string[]; message?: string }> {
|
||||
try {
|
||||
const output = await runCommandOutput('docker', ['image', 'ls', '--format', '{{json .}}'], undefined, undefined, 10_000)
|
||||
const images = Array.from(new Set(output.split('\n').flatMap((line) => {
|
||||
if (!line.trim()) return []
|
||||
try {
|
||||
const image = JSON.parse(line) as { Repository?: unknown; Tag?: unknown }
|
||||
const repository = typeof image.Repository === 'string' ? image.Repository.trim() : ''
|
||||
const tag = typeof image.Tag === 'string' ? image.Tag.trim() : ''
|
||||
if (!repository || !tag || repository === '<none>' || tag === '<none>') return []
|
||||
return [`${repository}:${tag}`]
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}))).sort((left, right) => left.localeCompare(right))
|
||||
|
||||
return { images }
|
||||
} catch (error) {
|
||||
return {
|
||||
images: [],
|
||||
message: error instanceof Error ? error.message : '로컬 Docker 이미지를 불러오지 못했습니다.'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const rosGraphMarker = '__AUTONOMY_STUDIO_ROS_GRAPH__'
|
||||
const rosGraphProbeScript = [
|
||||
'import json, rclpy',
|
||||
@@ -1799,7 +1825,7 @@ function remoteSshDestination(target: RemoteWorkspaceTarget): string {
|
||||
async function queueRemoteSsh<T>(target: ReturnType<typeof normalizeRemoteTarget>, task: () => Promise<T>): Promise<T> {
|
||||
const key = `${target.user}@${target.host}:${target.port}`
|
||||
const previous = remoteSshQueues.get(key) ?? Promise.resolve()
|
||||
let release: (() => void) | null = null
|
||||
let release: () => void = () => undefined
|
||||
const current = new Promise<void>((resolvePromise) => {
|
||||
release = resolvePromise
|
||||
})
|
||||
@@ -1810,7 +1836,7 @@ async function queueRemoteSsh<T>(target: ReturnType<typeof normalizeRemoteTarget
|
||||
try {
|
||||
return await task()
|
||||
} finally {
|
||||
release?.()
|
||||
release()
|
||||
if (remoteSshQueues.get(key) === queued) remoteSshQueues.delete(key)
|
||||
}
|
||||
}
|
||||
@@ -3219,7 +3245,7 @@ async function readVisualizationBridgeStatus(): Promise<VisualizationBridgeStatu
|
||||
}
|
||||
|
||||
async function waitForVisualizationBridgeReady(stack: VisualizationStack, bridgePort: number, timeoutMs = 20000): Promise<void> {
|
||||
const endpoint = stack === 'slam' || stack === 'nav2' ? 'health' : 'stack'
|
||||
const endpoint = stack === 'slam' || stack === 'slam3d' || stack === 'nav2' ? 'health' : 'stack'
|
||||
const startedAt = Date.now()
|
||||
let lastMessage = ''
|
||||
|
||||
@@ -4993,6 +5019,26 @@ async function stopRemoteWorkspaceModules(
|
||||
}
|
||||
}
|
||||
|
||||
async function resetRemoteRosSession(
|
||||
targetInput: RemoteWorkspaceTarget,
|
||||
): Promise<{ ok: boolean; message: string }> {
|
||||
try {
|
||||
const target = normalizeRemoteTarget(targetInput)
|
||||
await runSsh(target, [
|
||||
'set -e',
|
||||
// Module containers are always named from their LMS module folder. Remove every
|
||||
// one so a prior control session cannot keep publishing on the shared domain.
|
||||
'MODULE_CONTAINERS="$(docker ps -a --format \"{{.ID}} {{.Names}}\" 2>/dev/null | awk \'$2 ~ /^lms-/ { print $1 }\')"',
|
||||
'if [ -n "$MODULE_CONTAINERS" ]; then docker rm -f $MODULE_CONTAINERS >/dev/null 2>&1 || true; fi',
|
||||
'docker rm -f nav2-ros-bridge autoware-ros-bridge slam-ros-bridge >/dev/null 2>&1 || true',
|
||||
'find "$HOME/autonomy-studio/runtime" "$HOME/.autonomy-studio/runtime" -type f -path "*/docker/fastdds-peer-*.xml" -delete 2>/dev/null || true',
|
||||
].join('\n'), 45_000)
|
||||
return { ok: true, message: '원격 ROS 연결을 초기화했습니다.' }
|
||||
} catch (error) {
|
||||
return { ok: false, message: error instanceof Error ? error.message : String(error) }
|
||||
}
|
||||
}
|
||||
|
||||
async function resetRemoteWorkspaceSession(
|
||||
targetInput: RemoteWorkspaceTarget,
|
||||
modules: Array<{ moduleName: string; folderName?: string | null; templateRepositoryUrl?: string | null }>,
|
||||
@@ -5328,13 +5374,6 @@ function stopTerminalsForWebContents(webContentsId: number): void {
|
||||
}
|
||||
}
|
||||
|
||||
function scheduleStopTerminal(terminalKey: string): void {
|
||||
const currentTimer = terminalStopTimers.get(terminalKey)
|
||||
if (currentTimer) clearTimeout(currentTimer)
|
||||
const timer = setTimeout(() => stopTerminal(terminalKey), 300)
|
||||
terminalStopTimers.set(terminalKey, timer)
|
||||
}
|
||||
|
||||
function cancelStopTerminal(terminalKey: string): void {
|
||||
const timer = terminalStopTimers.get(terminalKey)
|
||||
if (!timer) return
|
||||
@@ -5743,6 +5782,7 @@ function registerLocalWorkspaceHandlers(): void {
|
||||
closeSplashAndShowMain()
|
||||
})
|
||||
ipcMain.handle('local-workspace:modules:list', (_event, workspaceRoot?: string | null, courseModules?: CourseModuleInput[] | null) => collectAvailableModules(workspaceRoot, courseModules))
|
||||
ipcMain.handle('local-workspace:modules:docker-images', () => listLocalDockerImages())
|
||||
ipcMain.handle('local-workspace:ros-graph', () => collectLocalRosGraph())
|
||||
ipcMain.handle('local-workspace:modules:install', (_event, moduleName: string, workspaceRoot?: string | null, folderName?: string | null, templateRepositoryUrl?: string | null, studentRepositoryUrl?: string | null, options?: ModuleInstallOptions | null) => installModuleFromCache(moduleName, workspaceRoot, folderName, templateRepositoryUrl, studentRepositoryUrl, options))
|
||||
ipcMain.handle('local-workspace:modules:create-custom', (_event, workspaceRoot?: string | null, input?: CustomModuleScaffoldInput | null) => {
|
||||
@@ -5815,6 +5855,7 @@ function registerLocalWorkspaceHandlers(): void {
|
||||
ipcMain.handle('remote-workspace:modules:stop', (_event, target: RemoteWorkspaceTarget, moduleName: string, folderName?: string | null) => stopRemoteWorkspaceModule(target, moduleName, folderName))
|
||||
ipcMain.handle('remote-workspace:modules:stop-many', (_event, target: RemoteWorkspaceTarget, modules: Array<{ moduleName: string; folderName?: string | null }>) => stopRemoteWorkspaceModules(target, modules))
|
||||
ipcMain.handle('remote-workspace:modules:reset-session', (_event, target: RemoteWorkspaceTarget, modules: Array<{ moduleName: string; folderName?: string | null; templateRepositoryUrl?: string | null }>) => resetRemoteWorkspaceSession(target, modules))
|
||||
ipcMain.handle('remote-workspace:ros:reset-session', (_event, target: RemoteWorkspaceTarget) => resetRemoteRosSession(target))
|
||||
ipcMain.handle('remote-workspace:modules:restart', (_event, target: RemoteWorkspaceTarget, moduleName: string, folderName?: string | null, rosParams?: string | null, gpuEnabled?: boolean | null, rosDomainId?: string | null, runtimeImages?: RuntimeImageArtifact[] | null, rosNetwork?: RosNetworkConfig | null) => restartRemoteWorkspaceModule(target, moduleName, folderName, rosParams, gpuEnabled, rosDomainId, runtimeImages, rosNetwork))
|
||||
ipcMain.handle('remote-workspace:modules:logs', (_event, target: RemoteWorkspaceTarget, moduleName: string, folderName?: string | null) => readRemoteWorkspaceModuleLogs(target, moduleName, folderName))
|
||||
ipcMain.handle('remote-workspace:visualization:status', (_event, target: RemoteWorkspaceTarget) => readRemoteVisualizationBridgeStatus(target))
|
||||
|
||||
Reference in New Issue
Block a user