This commit is contained in:
2026-08-03 14:03:00 +09:00
parent 5e7cffa901
commit b7e910dcf7
24 changed files with 1081 additions and 111 deletions
@@ -0,0 +1,4 @@
ALTER TABLE "course_week_modules"
ADD COLUMN IF NOT EXISTS "default_parameters" JSONB NOT NULL DEFAULT '[]';
COMMENT ON COLUMN "course_week_modules"."default_parameters" IS '주차 모듈 실행 시 적용하는 기본 ROS 파라미터 배열.';
@@ -0,0 +1,45 @@
CREATE TABLE "course_maps" (
"id" UUID NOT NULL,
"course_id" UUID NOT NULL,
"key" TEXT NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT,
"platform_key" TEXT NOT NULL DEFAULT 'NAV2',
"world_file" TEXT NOT NULL DEFAULT 'empty_world.world',
"viewer_model" TEXT NOT NULL DEFAULT 'turtlebot3_world',
"layout" JSONB NOT NULL DEFAULT '{"version":1,"objects":[]}',
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "course_maps_pkey" PRIMARY KEY ("id")
);
CREATE TABLE "course_week_maps" (
"id" UUID NOT NULL,
"course_id" UUID NOT NULL,
"week" INTEGER NOT NULL,
"map_key" TEXT NOT NULL,
"sort_order" INTEGER NOT NULL DEFAULT 0,
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "course_week_maps_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "course_maps_course_id_key_key" ON "course_maps"("course_id", "key");
CREATE INDEX "course_maps_course_id_platform_key_idx" ON "course_maps"("course_id", "platform_key");
CREATE UNIQUE INDEX "course_week_maps_course_id_week_map_key_key" ON "course_week_maps"("course_id", "week", "map_key");
CREATE INDEX "course_week_maps_course_id_week_idx" ON "course_week_maps"("course_id", "week");
ALTER TABLE "course_maps" ADD CONSTRAINT "course_maps_course_id_fkey" FOREIGN KEY ("course_id") REFERENCES "courses"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "course_week_maps" ADD CONSTRAINT "course_week_maps_course_id_fkey" FOREIGN KEY ("course_id") REFERENCES "courses"("id") ON DELETE CASCADE ON UPDATE CASCADE;
INSERT INTO "course_week_maps" ("id", "course_id", "week", "map_key", "sort_order", "updated_at")
SELECT gen_random_uuid(), "course_id", "week", 'nav2-turtlebot3-world', 0, CURRENT_TIMESTAMP
FROM "course_week_visualization_platforms"
WHERE "platform_key" = 'NAV2' AND "enabled" = true
ON CONFLICT ("course_id", "week", "map_key") DO NOTHING;
INSERT INTO "course_week_maps" ("id", "course_id", "week", "map_key", "sort_order", "updated_at")
SELECT gen_random_uuid(), "course_id", "week", 'nav2-turtlebot3-house', 1, CURRENT_TIMESTAMP
FROM "course_week_visualization_platforms"
WHERE "platform_key" = 'NAV2' AND "enabled" = true
ON CONFLICT ("course_id", "week", "map_key") DO NOTHING;
@@ -0,0 +1,27 @@
CREATE TABLE "student_saved_maps" (
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
"user_id" UUID NOT NULL,
"course_id" UUID NOT NULL,
"week" INTEGER NOT NULL,
"name" TEXT NOT NULL,
"source_map_key" TEXT NOT NULL,
"world_file" TEXT NOT NULL,
"viewer_model" TEXT NOT NULL,
"yaml_path" TEXT NOT NULL,
"pgm_path" TEXT NOT NULL,
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "student_saved_maps_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "student_saved_maps_user_id_course_id_week_yaml_path_key"
ON "student_saved_maps"("user_id", "course_id", "week", "yaml_path");
CREATE INDEX "student_saved_maps_user_id_course_id_week_created_at_idx"
ON "student_saved_maps"("user_id", "course_id", "week", "created_at");
ALTER TABLE "student_saved_maps"
ADD CONSTRAINT "student_saved_maps_user_id_fkey"
FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "student_saved_maps"
ADD CONSTRAINT "student_saved_maps_course_id_fkey"
FOREIGN KEY ("course_id") REFERENCES "courses"("id") ON DELETE CASCADE ON UPDATE CASCADE;
@@ -0,0 +1,27 @@
INSERT INTO "visualization_platforms" (
"platform_key", "type", "display_name", "description", "default_config", "sort_order", "enabled", "created_at", "updated_at"
) VALUES
(
'REAL_SLAM',
'REAL_SLAM',
'실차 SLAM',
'원격 플랫폼의 실제 센서로 지도를 생성합니다.',
'{"type":"REAL_SLAM","target":"remote-jetson","runtimeKind":"real","viewer":{"stack":"nav2","mode":"slam"},"bridge":{"name":"nav2-ros-bridge","port":8765,"image":"slam-ros-bridge:latest","env":{"ROBOT_STACK":"nav2"}},"runtimeImages":[{"url":"https://pub-ca72c40797144462bdb7d200c215d361.r2.dev/studio-artifacts/runtime-images/slam-ros-bridge/latest/slam-ros-bridge-latest.tar.zst","image":"slam-ros-bridge:latest","fileName":"slam-ros-bridge-latest.tar.zst","required":true}]}'::jsonb,
30, true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
),
(
'REAL_NAVIGATION',
'REAL_NAVIGATION',
'실차 자율주행',
'실차 SLAM 결과 맵에서 초기 위치와 목표점을 지정해 주행합니다.',
'{"type":"REAL_NAVIGATION","target":"remote-jetson","runtimeKind":"real","viewer":{"stack":"nav2","mode":"navigation"},"bridge":{"name":"nav2-ros-bridge","port":8765,"image":"slam-ros-bridge:latest","env":{"ROBOT_STACK":"nav2"}},"runtimeImages":[{"url":"https://pub-ca72c40797144462bdb7d200c215d361.r2.dev/studio-artifacts/runtime-images/slam-ros-bridge/latest/slam-ros-bridge-latest.tar.zst","image":"slam-ros-bridge:latest","fileName":"slam-ros-bridge-latest.tar.zst","required":true}]}'::jsonb,
31, true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
)
ON CONFLICT ("platform_key") DO UPDATE SET
"type" = EXCLUDED."type",
"display_name" = EXCLUDED."display_name",
"description" = EXCLUDED."description",
"default_config" = EXCLUDED."default_config",
"sort_order" = EXCLUDED."sort_order",
"enabled" = EXCLUDED."enabled",
"updated_at" = CURRENT_TIMESTAMP;
@@ -0,0 +1,12 @@
UPDATE "visualization_platforms"
SET
"display_name" = CASE "platform_key"
WHEN 'REAL_SLAM' THEN '플랫폼 SLAM'
WHEN 'REAL_NAVIGATION' THEN '플랫폼 자율주행'
END,
"description" = CASE "platform_key"
WHEN 'REAL_SLAM' THEN '원격 플랫폼의 센서로 지도를 생성합니다.'
WHEN 'REAL_NAVIGATION' THEN '플랫폼 SLAM 결과 맵에서 초기 위치와 목표점을 지정해 주행합니다.'
END,
"updated_at" = CURRENT_TIMESTAMP
WHERE "platform_key" IN ('REAL_SLAM', 'REAL_NAVIGATION');
@@ -0,0 +1,2 @@
ALTER TABLE "platform_control_sessions"
ADD COLUMN "extension_count" INTEGER NOT NULL DEFAULT 0;
@@ -0,0 +1,18 @@
INSERT INTO "visualization_platforms" (
"platform_key", "type", "display_name", "description", "default_config", "sort_order", "enabled", "created_at", "updated_at"
) VALUES (
'REAL_SLAM_3D',
'REAL_SLAM_3D',
'플랫폼 3D SLAM',
'플랫폼의 3D LiDAR 포인트클라우드와 ICP 위치를 읽기 전용으로 표시합니다.',
'{"type":"REAL_SLAM_3D","target":"remote-jetson","runtimeKind":"real","viewer":{"stack":"nav2","mode":"slam3d"},"bridge":{"name":"nav2-ros-bridge","port":8765,"image":"slam-ros-bridge:jetson_orin-3d","env":{"ROBOT_STACK":"nav2","READ_ONLY":"true","ODOM_TOPIC":"/odom","ICP_ODOM_TOPIC":"/icp_odom","SCAN_TOPIC":"/scan","POINT_CLOUD_TOPIC":"/cloud_map","FALLBACK_POINT_CLOUD_TOPIC":"/lidar_filtered"}},"runtimeImages":[{"url":"https://pub-ca72c40797144462bdb7d200c215d361.r2.dev/studio-artifacts/runtime-images/slam-ros-bridge/jetson-orin-3d/slam-ros-bridge-jetson-orin-3d.tar.zst","image":"slam-ros-bridge:jetson_orin-3d","fileName":"slam-ros-bridge-jetson-orin-3d.tar.zst","required":true}]}'::jsonb,
32, true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
)
ON CONFLICT ("platform_key") DO UPDATE SET
"type" = EXCLUDED."type",
"display_name" = EXCLUDED."display_name",
"description" = EXCLUDED."description",
"default_config" = EXCLUDED."default_config",
"sort_order" = EXCLUDED."sort_order",
"enabled" = EXCLUDED."enabled",
"updated_at" = CURRENT_TIMESTAMP;
@@ -0,0 +1,2 @@
ALTER TABLE "student_saved_maps"
ADD COLUMN "database_path" TEXT;
@@ -0,0 +1,2 @@
ALTER TABLE "student_saved_maps"
ADD COLUMN "preview_pgm" TEXT;