|
|
|
@@ -1,6 +1,8 @@
|
|
|
|
|
import { app, BrowserWindow, dialog, Menu, globalShortcut, ipcMain, screen, shell } from 'electron'
|
|
|
|
|
import { checkDockerStatus } from './dockerStatus'
|
|
|
|
|
import { yahboomDevicePreflight, yahboomModuleCases, yahboomRuntimeImage, yahboomRuntimeArtifacts, yahboomSimulation } from './yahboomRuntime'
|
|
|
|
|
import { visualizationProgress, reportVisualizationProgress } from './visualizationProgress'
|
|
|
|
|
import { moduleProgress, reportModuleProgress, createModuleProgressParser } from './moduleProgress'
|
|
|
|
|
import { bridgeArtifact, jetsonBridgeImage, localBridgeImage } from './bridgeArtifacts'
|
|
|
|
|
import type { WebContents } from 'electron'
|
|
|
|
|
import { spawn } from 'child_process'
|
|
|
|
@@ -1359,6 +1361,8 @@ function resolveNav2RuntimeImage(
|
|
|
|
|
folderName?: string | null,
|
|
|
|
|
rosParams = '',
|
|
|
|
|
): string {
|
|
|
|
|
const yahboomImage = yahboomRuntimeImage(folderName)
|
|
|
|
|
if (yahboomImage) return yahboomImage
|
|
|
|
|
// NAV2-MKMini normally targets the real platform image. Gazebo is an
|
|
|
|
|
// explicit local opt-in, so existing platform launches keep their image.
|
|
|
|
|
const mkminiGazebo = /lms-nav2-mkmini/i.test(folderName ?? '')
|
|
|
|
@@ -1699,7 +1703,24 @@ function remoteRepositoryUrl(repositoryUrl: string): string {
|
|
|
|
|
return repositoryUrl
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleModuleOperation(channel: string, handler: Parameters<typeof ipcMain.handle>[1]): void {
|
|
|
|
|
ipcMain.handle(channel, (event, ...args) => {
|
|
|
|
|
const remote = channel.startsWith('remote-')
|
|
|
|
|
const folderName = String(args[2] || args[remote ? 1 : 0])
|
|
|
|
|
const send = (message: string): void => {
|
|
|
|
|
if (!event.sender.isDestroyed()) event.sender.send('module:progress', folderName, message)
|
|
|
|
|
}
|
|
|
|
|
return moduleProgress.run(send, () => visualizationProgress.run((message) => {
|
|
|
|
|
if (!message.startsWith('시각화')) send(message)
|
|
|
|
|
}, async () => {
|
|
|
|
|
send('모듈 작업 준비 중...')
|
|
|
|
|
try { return await handler(event, ...args) } finally { send('') }
|
|
|
|
|
}))
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function runCommandOutput(command: string, args: string[], cwd?: string, env?: NodeJS.ProcessEnv, timeoutMs = 120_000): Promise<string> {
|
|
|
|
|
const readProgress = createModuleProgressParser()
|
|
|
|
|
return new Promise<string>((resolvePromise, rejectPromise) => {
|
|
|
|
|
let output = ''
|
|
|
|
|
let terminated = false
|
|
|
|
@@ -1729,8 +1750,10 @@ async function runCommandOutput(command: string, args: string[], cwd?: string, e
|
|
|
|
|
rejectPromise(new Error(`${command} ${args.join(' ')} timed out`))
|
|
|
|
|
}, timeoutMs)
|
|
|
|
|
|
|
|
|
|
child.stdout.on('data', (data: Buffer) => {
|
|
|
|
|
child.stdout.setEncoding('utf8')
|
|
|
|
|
child.stdout.on('data', (data: string) => {
|
|
|
|
|
output += data.toString()
|
|
|
|
|
readProgress(data.toString())
|
|
|
|
|
})
|
|
|
|
|
child.stderr.on('data', (data: Buffer) => {
|
|
|
|
|
output += data.toString()
|
|
|
|
@@ -2331,18 +2354,18 @@ async function syncRemoteSavedMapArtifacts(
|
|
|
|
|
'set -e',
|
|
|
|
|
`ROOT=${quoteShellSingle(target.workspaceRoot)}`,
|
|
|
|
|
`MAPS_DIRECTORY=${quoteShellSingle(mapsDirectory)}`,
|
|
|
|
|
'if [ ! -d "$MAPS_DIRECTORY" ] || [ ! -w "$MAPS_DIRECTORY" ]; then',
|
|
|
|
|
' if docker image inspect future-nav2:humble-jetson_orin >/dev/null 2>&1; then',
|
|
|
|
|
' IMAGE="future-nav2:humble-jetson_orin"',
|
|
|
|
|
' elif docker image inspect future-nav2:humble >/dev/null 2>&1; then',
|
|
|
|
|
' IMAGE="future-nav2:humble"',
|
|
|
|
|
' else',
|
|
|
|
|
' IMAGE=""',
|
|
|
|
|
' fi',
|
|
|
|
|
// A missing directory is normal on first install, not a permission failure.
|
|
|
|
|
'mkdir -p "$MAPS_DIRECTORY" 2>/dev/null || true',
|
|
|
|
|
'if [ ! -d "$MAPS_DIRECTORY" ] || [ ! -w "$MAPS_DIRECTORY" ] || [ ! -x "$MAPS_DIRECTORY" ]; then',
|
|
|
|
|
' IMAGE=""',
|
|
|
|
|
' for CANDIDATE in future-nav2:humble-yahboom future-nav2:humble-yahboom-agent future-nav2:humble-jetson_orin future-nav2:humble; do',
|
|
|
|
|
' if docker image inspect "$CANDIDATE" >/dev/null 2>&1; then IMAGE="$CANDIDATE"; break; fi',
|
|
|
|
|
' done',
|
|
|
|
|
' if [ -z "$IMAGE" ]; then echo "저장된 맵 디렉터리에 쓸 권한이 없고 권한 복구용 NAV2 이미지를 찾을 수 없습니다." >&2; exit 1; fi',
|
|
|
|
|
' docker run --rm --user 0:0 -v "$ROOT:/workspace" --entrypoint /bin/sh "$IMAGE" -lc "mkdir -p /workspace/maps && chown -R $(id -u):$(id -g) /workspace/maps"',
|
|
|
|
|
'fi',
|
|
|
|
|
'mkdir -p "$MAPS_DIRECTORY"',
|
|
|
|
|
'test -w "$MAPS_DIRECTORY" && test -x "$MAPS_DIRECTORY" || { echo "저장된 맵 디렉터리 권한 복구에 실패했습니다: $MAPS_DIRECTORY" >&2; exit 1; }',
|
|
|
|
|
...downloads.map((artifact) => (
|
|
|
|
|
`curl -fsSL --retry 2 ${quoteShellSingle(artifact.url)} -o ${quoteShellSingle(`${mapsDirectory}/${artifact.fileName}`)}`
|
|
|
|
|
)),
|
|
|
|
@@ -4573,6 +4596,7 @@ function buildNav2DockerRunScript(): string {
|
|
|
|
|
' )',
|
|
|
|
|
'else',
|
|
|
|
|
'case "$MODULE_FOLDER" in',
|
|
|
|
|
...yahboomModuleCases(),
|
|
|
|
|
' *lms-nav2-mkmini*) PACKAGE=vehicle_pkg; LAUNCH=vehicle.launch.py ;;',
|
|
|
|
|
' *lms-nav2-gazebo-simulator*) PACKAGE=nav2_turtlebot_simulator; LAUNCH=simulator.launch.py ;;',
|
|
|
|
|
' *lms-nav2-gazebo-vehicle*) PACKAGE=vehicle_pkg; LAUNCH=vehicle.launch.py ;;',
|
|
|
|
@@ -4609,6 +4633,7 @@ function buildNav2DockerRunScript(): string {
|
|
|
|
|
'fi',
|
|
|
|
|
'',
|
|
|
|
|
'test -f "$MODULE_ROOT/$MODULE_FOLDER/install/setup.bash" || { echo "Module is not built: $MODULE_FOLDER" >&2; exit 1; }',
|
|
|
|
|
...yahboomDevicePreflight(),
|
|
|
|
|
'docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true',
|
|
|
|
|
'',
|
|
|
|
|
'args=(',
|
|
|
|
@@ -4625,6 +4650,7 @@ function buildNav2DockerRunScript(): string {
|
|
|
|
|
' -v "$(dirname "$0"):/runtime/nav2/docker:ro"',
|
|
|
|
|
' -v /etc/localtime:/etc/localtime:ro',
|
|
|
|
|
')',
|
|
|
|
|
'if [ -n "$YAHBOOM_DEVICE" ]; then args+=(--device "$YAHBOOM_DEVICE:$YAHBOOM_DEVICE"); fi',
|
|
|
|
|
'if [[ "$GPU_ENABLED" == "true" || "$GPU_ENABLED" == "1" ]]; then',
|
|
|
|
|
' args+=(--gpus all -e NVIDIA_VISIBLE_DEVICES=all -e NVIDIA_DRIVER_CAPABILITIES=compute,utility,graphics)',
|
|
|
|
|
'fi',
|
|
|
|
@@ -4667,6 +4693,7 @@ function buildNav2DockerBuildScript(): string {
|
|
|
|
|
' )',
|
|
|
|
|
'else',
|
|
|
|
|
'case "$MODULE_FOLDER" in',
|
|
|
|
|
...yahboomModuleCases(true),
|
|
|
|
|
' *lms-nav2-mkmini*) TARGET=vehicle_pkg ;;',
|
|
|
|
|
' *lms-nav2-gazebo-simulator*) TARGET=nav2_turtlebot_simulator ;;',
|
|
|
|
|
' *lms-nav2-gazebo-vehicle*) TARGET=vehicle_pkg ;;',
|
|
|
|
@@ -4897,7 +4924,7 @@ async function findNav2SimulatorRobotModel(containerName: string): Promise<strin
|
|
|
|
|
// Query Gazebo directly first. The ROS GetModelList service can be slow or
|
|
|
|
|
// unavailable while DDS participants are still discovering each other, even
|
|
|
|
|
// though the simulator itself is already able to accept a pose update.
|
|
|
|
|
const knownModels = ['mkmini_gazebo', 'turtlebot3_burger_3d_lidar', 'turtlebot3_burger']
|
|
|
|
|
const knownModels = ['mkmini_gazebo', 'turtlebot3_burger_3d_lidar', 'turtlebot3_burger', 'yahboom_gazebo']
|
|
|
|
|
for (const modelName of knownModels) {
|
|
|
|
|
const pose = await runCommandOutput(
|
|
|
|
|
'docker',
|
|
|
|
@@ -4923,7 +4950,7 @@ async function findNav2SimulatorRobotModel(containerName: string): Promise<strin
|
|
|
|
|
const modelList = output.match(/model_names=\[([^\]]*)\]/)?.[1] ?? ''
|
|
|
|
|
const modelNames = Array.from(modelList.matchAll(/'([^']+)'/g), (match) => match[1])
|
|
|
|
|
const robotModel = modelNames.find((modelName) => (
|
|
|
|
|
/turtlebot|burger|mkmini|mk-mini|robot/i.test(modelName) && !/world/i.test(modelName)
|
|
|
|
|
/turtlebot|burger|mkmini|mk-mini|yahboom|robot/i.test(modelName) && !/world/i.test(modelName)
|
|
|
|
|
))
|
|
|
|
|
if (!robotModel) throw new Error('Gazebo 로봇 모델을 찾을 수 없습니다.')
|
|
|
|
|
return robotModel
|
|
|
|
@@ -5017,6 +5044,29 @@ async function clearNav2RuntimeObstacles(): Promise<{ ok: boolean; message: stri
|
|
|
|
|
return { ok: true, message: '장애물을 제거했습니다.' }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function resolveLocalModuleRuntime(
|
|
|
|
|
moduleRoot: string,
|
|
|
|
|
folderName: string,
|
|
|
|
|
rosParams: string,
|
|
|
|
|
runtimeImages?: RuntimeImageArtifact[] | null,
|
|
|
|
|
): Promise<{ image: string; artifacts: RuntimeImageArtifact[] | null | undefined }> {
|
|
|
|
|
if (yahboomSimulation(folderName, rosParams)) {
|
|
|
|
|
const artifact = runtimeImages?.find((item) =>
|
|
|
|
|
item.image?.trim() === defaultNav2GazeboRuntimeImage.image && item.url?.trim())
|
|
|
|
|
?? defaultNav2GazeboRuntimeImage
|
|
|
|
|
return { image: 'future-nav2-gazebo:humble', artifacts: [artifact] }
|
|
|
|
|
}
|
|
|
|
|
const image = await resolveCustomModuleRuntimeImage(
|
|
|
|
|
moduleRoot, resolveNav2RuntimeImage(runtimeImages, folderName, rosParams),
|
|
|
|
|
)
|
|
|
|
|
return {
|
|
|
|
|
image,
|
|
|
|
|
artifacts: yahboomRuntimeImage(folderName)
|
|
|
|
|
? yahboomRuntimeArtifacts(folderName, image, runtimeImages)
|
|
|
|
|
: requiredRuntimeImages(runtimeImages, image),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function startWorkspaceModule(
|
|
|
|
|
moduleName: string,
|
|
|
|
|
workspaceRoot?: string | null,
|
|
|
|
@@ -5044,11 +5094,10 @@ async function startWorkspaceModule(
|
|
|
|
|
const fastDdsConfigPath = await prepareLocalRosNetworkConfig(rosNetwork, rosDomainId)
|
|
|
|
|
const rosParams = typeof rosParamsOverride === 'string' ? rosParamsOverride.trim() : await readModuleRosParams(moduleRoot)
|
|
|
|
|
const scriptPath = resolveNav2DockerRunScript()
|
|
|
|
|
const runtimeImage = await resolveCustomModuleRuntimeImage(
|
|
|
|
|
moduleRoot,
|
|
|
|
|
resolveNav2RuntimeImage(runtimeImages, targetFolderName, rosParams),
|
|
|
|
|
)
|
|
|
|
|
await prepareRuntimeImages(requiredRuntimeImages(runtimeImages, runtimeImage))
|
|
|
|
|
const runtime = await resolveLocalModuleRuntime(moduleRoot, targetFolderName, rosParams, runtimeImages)
|
|
|
|
|
const runtimeImage = runtime.image
|
|
|
|
|
await prepareRuntimeImages(runtime.artifacts)
|
|
|
|
|
reportModuleProgress("모듈 실행 중...")
|
|
|
|
|
await runCommand(
|
|
|
|
|
'bash',
|
|
|
|
|
[
|
|
|
|
@@ -5072,6 +5121,7 @@ async function startWorkspaceModule(
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await prepareRuntimeImages(runtimeImages)
|
|
|
|
|
reportModuleProgress("모듈 실행 중...")
|
|
|
|
|
await ensureStudioRuntimeAssets()
|
|
|
|
|
const scriptPath = resolveAutowareDockerRunScript()
|
|
|
|
|
if (!existsSync(scriptPath)) throw new Error(`${scriptPath} 파일을 찾을 수 없습니다.`)
|
|
|
|
@@ -5129,11 +5179,10 @@ async function buildWorkspaceModule(
|
|
|
|
|
if (isNav2ModuleFolder(targetFolderName)) {
|
|
|
|
|
await ensureNav2RuntimeAssets()
|
|
|
|
|
const rosParams = typeof rosParamsOverride === 'string' ? rosParamsOverride.trim() : await readModuleRosParams(moduleRoot)
|
|
|
|
|
const runtimeImage = await resolveCustomModuleRuntimeImage(
|
|
|
|
|
moduleRoot,
|
|
|
|
|
resolveNav2RuntimeImage(runtimeImages, targetFolderName, rosParams),
|
|
|
|
|
)
|
|
|
|
|
await prepareRuntimeImages(requiredRuntimeImages(runtimeImages, runtimeImage))
|
|
|
|
|
const runtime = await resolveLocalModuleRuntime(moduleRoot, targetFolderName, rosParams, runtimeImages)
|
|
|
|
|
const runtimeImage = runtime.image
|
|
|
|
|
await prepareRuntimeImages(runtime.artifacts)
|
|
|
|
|
reportModuleProgress("모듈 빌드 중...")
|
|
|
|
|
await runCommand(
|
|
|
|
|
'bash',
|
|
|
|
|
[
|
|
|
|
@@ -5151,6 +5200,7 @@ async function buildWorkspaceModule(
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await prepareRuntimeImages(runtimeImages)
|
|
|
|
|
reportModuleProgress("모듈 빌드 중...")
|
|
|
|
|
await runCommand(
|
|
|
|
|
'docker',
|
|
|
|
|
[
|
|
|
|
@@ -5173,6 +5223,7 @@ async function prepareRemoteRuntimeImages(
|
|
|
|
|
target: RemoteWorkspaceTarget,
|
|
|
|
|
runtimeImages?: RuntimeImageArtifact[] | null,
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
reportModuleProgress('이미지 준비 대기 중...')
|
|
|
|
|
const artifacts = (runtimeImages ?? [])
|
|
|
|
|
.filter((image) => image?.image?.trim())
|
|
|
|
|
.map((image) => ({
|
|
|
|
@@ -5208,12 +5259,18 @@ async function prepareRemoteRuntimeImages(
|
|
|
|
|
' name = item.get("fileName") or os.path.basename(url.split("?", 1)[0]) or "runtime-image.tar"',
|
|
|
|
|
' path = os.path.join(cache, name)',
|
|
|
|
|
' if os.path.exists(path): os.unlink(path)',
|
|
|
|
|
' print("LMS_MODULE_PROGRESS:이미지 다운로드 중...", flush=True)',
|
|
|
|
|
' subprocess.run(["curl", "-fL", url, "-o", path], check=True)',
|
|
|
|
|
' print("LMS_MODULE_PROGRESS:이미지 파일 확인 중...", flush=True)',
|
|
|
|
|
' expected = (item.get("checksumSha256") or "").strip().lower()',
|
|
|
|
|
' if expected:',
|
|
|
|
|
' import hashlib',
|
|
|
|
|
' with open(path, "rb") as fp: actual = hashlib.sha256(fp.read()).hexdigest().lower()',
|
|
|
|
|
' digest = hashlib.sha256()',
|
|
|
|
|
' with open(path, "rb") as fp:',
|
|
|
|
|
' for chunk in iter(lambda: fp.read(1024 * 1024), b""): digest.update(chunk)',
|
|
|
|
|
' actual = digest.hexdigest().lower()',
|
|
|
|
|
' if actual != expected: raise SystemExit(f"runtime image checksum mismatch: {name}")',
|
|
|
|
|
' print("LMS_MODULE_PROGRESS:이미지 불러오는 중...", flush=True)',
|
|
|
|
|
' if path.lower().endswith(".tar.zst") or path.lower().endswith(".tzst"):',
|
|
|
|
|
' import shlex',
|
|
|
|
|
' subprocess.run(f"zstd -dc {shlex.quote(path)} | docker load", shell=True, check=True)',
|
|
|
|
@@ -5255,14 +5312,15 @@ async function startRemoteWorkspaceModule(
|
|
|
|
|
? await resolveRemoteCustomModuleRuntimeImage(
|
|
|
|
|
target,
|
|
|
|
|
targetFolderName,
|
|
|
|
|
await resolveRemoteNav2RuntimeImage(runtimeImages),
|
|
|
|
|
yahboomRuntimeImage(targetFolderName) ?? await resolveRemoteNav2RuntimeImage(runtimeImages),
|
|
|
|
|
)
|
|
|
|
|
: null
|
|
|
|
|
const runtimeRoot = nav2Module
|
|
|
|
|
? await ensureRemoteNav2RuntimeAssets(target)
|
|
|
|
|
: await ensureRemoteStudioRuntimeAssets(target)
|
|
|
|
|
const fastDdsConfigPath = await prepareRemoteRosNetworkConfig(target, runtimeRoot, rosNetwork, rosDomainId)
|
|
|
|
|
await prepareRemoteRuntimeImages(target, runtimeImages)
|
|
|
|
|
await prepareRemoteRuntimeImages(target, yahboomRuntimeArtifacts(targetFolderName, runtimeImage, runtimeImages))
|
|
|
|
|
reportModuleProgress("모듈 실행 중...")
|
|
|
|
|
const currentStatus = await runSshOutput(target, `timeout 12s docker ps -a --filter ${quoteShellSingle(`name=^/${dockerInfo.containerKey}$`)} --format "{{.Status}}" 2>/dev/null || true`, 20000)
|
|
|
|
|
if (/^Up\b/i.test(currentStatus.trim())) return { ok: true, message: '이미 실행 중입니다.' }
|
|
|
|
|
if (currentStatus.trim()) {
|
|
|
|
@@ -5352,9 +5410,10 @@ async function buildRemoteWorkspaceModule(
|
|
|
|
|
const runtimeImage = await resolveRemoteCustomModuleRuntimeImage(
|
|
|
|
|
target,
|
|
|
|
|
targetFolderName,
|
|
|
|
|
await resolveRemoteNav2RuntimeImage(runtimeImages),
|
|
|
|
|
yahboomRuntimeImage(targetFolderName) ?? await resolveRemoteNav2RuntimeImage(runtimeImages),
|
|
|
|
|
)
|
|
|
|
|
await prepareRemoteRuntimeImages(target, runtimeImages)
|
|
|
|
|
await prepareRemoteRuntimeImages(target, yahboomRuntimeArtifacts(targetFolderName, runtimeImage, runtimeImages))
|
|
|
|
|
reportModuleProgress("모듈 빌드 중...")
|
|
|
|
|
await runSsh(target, [
|
|
|
|
|
'set -e',
|
|
|
|
|
`RUNTIME_ROOT=${quoteShellSingle(runtimeRoot)}`,
|
|
|
|
@@ -6286,8 +6345,8 @@ function registerLocalWorkspaceHandlers(): void {
|
|
|
|
|
return importCustomWorkspaceModule(workspaceRoot, input)
|
|
|
|
|
})
|
|
|
|
|
ipcMain.handle('local-workspace:modules:delete', (_event, moduleName: string, workspaceRoot?: string | null, folderName?: string | null) => deleteWorkspaceModule(moduleName, workspaceRoot, folderName))
|
|
|
|
|
ipcMain.handle('local-workspace:modules:start', (_event, moduleName: string, workspaceRoot?: string | null, folderName?: string | null, rosParams?: string | null, gpuEnabled?: boolean | null, rosDomainId?: string | null, runtimeImages?: RuntimeImageArtifact[] | null, rosNetwork?: RosNetworkConfig | null) => startWorkspaceModule(moduleName, workspaceRoot, folderName, rosParams, gpuEnabled, rosDomainId, runtimeImages, rosNetwork))
|
|
|
|
|
ipcMain.handle('local-workspace:modules:build', (_event, moduleName: string, workspaceRoot?: string | null, folderName?: string | null, gpuEnabled?: boolean | null, rosDomainId?: string | null, runtimeImages?: RuntimeImageArtifact[] | null, rosParams?: string | null) => buildWorkspaceModule(moduleName, workspaceRoot, folderName, gpuEnabled, rosDomainId, runtimeImages, rosParams))
|
|
|
|
|
handleModuleOperation('local-workspace:modules:start', (_event, moduleName: string, workspaceRoot?: string | null, folderName?: string | null, rosParams?: string | null, gpuEnabled?: boolean | null, rosDomainId?: string | null, runtimeImages?: RuntimeImageArtifact[] | null, rosNetwork?: RosNetworkConfig | null) => startWorkspaceModule(moduleName, workspaceRoot, folderName, rosParams, gpuEnabled, rosDomainId, runtimeImages, rosNetwork))
|
|
|
|
|
handleModuleOperation('local-workspace:modules:build', (_event, moduleName: string, workspaceRoot?: string | null, folderName?: string | null, gpuEnabled?: boolean | null, rosDomainId?: string | null, runtimeImages?: RuntimeImageArtifact[] | null, rosParams?: string | null) => buildWorkspaceModule(moduleName, workspaceRoot, folderName, gpuEnabled, rosDomainId, runtimeImages, rosParams))
|
|
|
|
|
ipcMain.handle('local-workspace:modules:stop', (_event, moduleName: string, workspaceRoot?: string | null, folderName?: string | null) => stopWorkspaceModule(moduleName, workspaceRoot, folderName))
|
|
|
|
|
ipcMain.handle('local-workspace:modules:stop-many', (_event, modules: Array<{ moduleName: string; folderName?: string | null }>, workspaceRoot?: string | null) => stopWorkspaceModules(modules, workspaceRoot))
|
|
|
|
|
ipcMain.handle('local-workspace:modules:stop-running', () => stopRunningWorkspaceContainers())
|
|
|
|
@@ -6349,8 +6408,8 @@ function registerLocalWorkspaceHandlers(): void {
|
|
|
|
|
if (!moduleName?.trim()) throw new Error('전송할 모듈명이 없습니다.')
|
|
|
|
|
return transferLocalWorkspaceModuleToRemote(target, sourceWorkspaceRoot, moduleName, folderName)
|
|
|
|
|
})
|
|
|
|
|
ipcMain.handle('remote-workspace:modules:start', (_event, target: RemoteWorkspaceTarget, moduleName: string, folderName?: string | null, rosParams?: string | null, gpuEnabled?: boolean | null, rosDomainId?: string | null, runtimeImages?: RuntimeImageArtifact[] | null, rosNetwork?: RosNetworkConfig | null) => startRemoteWorkspaceModule(target, moduleName, folderName, rosParams, gpuEnabled, rosDomainId, runtimeImages, rosNetwork))
|
|
|
|
|
ipcMain.handle('remote-workspace:modules:build', (_event, target: RemoteWorkspaceTarget, moduleName: string, folderName?: string | null, gpuEnabled?: boolean | null, rosDomainId?: string | null, runtimeImages?: RuntimeImageArtifact[] | null) => buildRemoteWorkspaceModule(target, moduleName, folderName, gpuEnabled, rosDomainId, runtimeImages))
|
|
|
|
|
handleModuleOperation('remote-workspace:modules:start', (_event, target: RemoteWorkspaceTarget, moduleName: string, folderName?: string | null, rosParams?: string | null, gpuEnabled?: boolean | null, rosDomainId?: string | null, runtimeImages?: RuntimeImageArtifact[] | null, rosNetwork?: RosNetworkConfig | null) => startRemoteWorkspaceModule(target, moduleName, folderName, rosParams, gpuEnabled, rosDomainId, runtimeImages, rosNetwork))
|
|
|
|
|
handleModuleOperation('remote-workspace:modules:build', (_event, target: RemoteWorkspaceTarget, moduleName: string, folderName?: string | null, gpuEnabled?: boolean | null, rosDomainId?: string | null, runtimeImages?: RuntimeImageArtifact[] | null) => buildRemoteWorkspaceModule(target, moduleName, folderName, gpuEnabled, rosDomainId, runtimeImages))
|
|
|
|
|
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))
|
|
|
|
@@ -6535,8 +6594,8 @@ app.whenReady().then(() => {
|
|
|
|
|
ipcMain.handle('modules:list', (_event, workspaceRoot?: string | null, courseModules?: CourseModuleInput[] | null) => collectAvailableModules(workspaceRoot, courseModules))
|
|
|
|
|
ipcMain.handle('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('modules:delete', (_event, moduleName: string, workspaceRoot?: string | null, folderName?: string | null) => deleteWorkspaceModule(moduleName, workspaceRoot, folderName))
|
|
|
|
|
ipcMain.handle('modules:start', (_event, moduleName: string, workspaceRoot?: string | null, folderName?: string | null, rosParams?: string | null, gpuEnabled?: boolean | null, rosDomainId?: string | null, runtimeImages?: RuntimeImageArtifact[] | null, rosNetwork?: RosNetworkConfig | null) => startWorkspaceModule(moduleName, workspaceRoot, folderName, rosParams, gpuEnabled, rosDomainId, runtimeImages, rosNetwork))
|
|
|
|
|
ipcMain.handle('modules:build', (_event, moduleName: string, workspaceRoot?: string | null, folderName?: string | null, gpuEnabled?: boolean | null, rosDomainId?: string | null, runtimeImages?: RuntimeImageArtifact[] | null, rosParams?: string | null) => buildWorkspaceModule(moduleName, workspaceRoot, folderName, gpuEnabled, rosDomainId, runtimeImages, rosParams))
|
|
|
|
|
handleModuleOperation('modules:start', (_event, moduleName: string, workspaceRoot?: string | null, folderName?: string | null, rosParams?: string | null, gpuEnabled?: boolean | null, rosDomainId?: string | null, runtimeImages?: RuntimeImageArtifact[] | null, rosNetwork?: RosNetworkConfig | null) => startWorkspaceModule(moduleName, workspaceRoot, folderName, rosParams, gpuEnabled, rosDomainId, runtimeImages, rosNetwork))
|
|
|
|
|
handleModuleOperation('modules:build', (_event, moduleName: string, workspaceRoot?: string | null, folderName?: string | null, gpuEnabled?: boolean | null, rosDomainId?: string | null, runtimeImages?: RuntimeImageArtifact[] | null, rosParams?: string | null) => buildWorkspaceModule(moduleName, workspaceRoot, folderName, gpuEnabled, rosDomainId, runtimeImages, rosParams))
|
|
|
|
|
ipcMain.handle('modules:stop', (_event, moduleName: string, workspaceRoot?: string | null, folderName?: string | null) => stopWorkspaceModule(moduleName, workspaceRoot, folderName))
|
|
|
|
|
ipcMain.handle('modules:stop-many', (_event, modules: Array<{ moduleName: string; folderName?: string | null }>, workspaceRoot?: string | null) => stopWorkspaceModules(modules, workspaceRoot))
|
|
|
|
|
ipcMain.handle('modules:stop-running', () => stopRunningWorkspaceContainers())
|
|
|
|
|