diff --git a/RobotApp.Client/Pages/Components/Mapping/MapView.razor b/RobotApp.Client/Pages/Components/Mapping/MapView.razor index df2d737..86aaa64 100644 --- a/RobotApp.Client/Pages/Components/Mapping/MapView.razor +++ b/RobotApp.Client/Pages/Components/Mapping/MapView.razor @@ -1,8 +1,25 @@ @inject IJSRuntime JS +@inject HttpClient Http @using Excubo.Blazor.Canvas.Contexts
+ + + + + + + + + + - - + + + + + + +
@@ -56,6 +84,20 @@ private const double RulerHeight = 20; + private const double RobotWidth = 0.606; + private const double RobotLength = 1.106; + + private bool RobotImageLoaded = false; + private bool MapImageLoaded = false; + + private const double ImageX = -10; + private const double ImageY = -10; + private const double ImageResolution = 0.05; + + private double MapImageWidth = 0; + private double MapImageHeight = 0; + private const string MAP_CACHE_KEY = "map_image"; + protected override async Task OnAfterRenderAsync(bool first_render) { await base.OnAfterRenderAsync(first_render); @@ -71,9 +113,45 @@ CanvasTranslateX = CanvasWidth / 2; CanvasTranslateY = CanvasHeight / 2; + await LoadRobotImage(); + await LoadMapImage(); + await DrawCanvas(); } + private async Task LoadRobotImage() + { + try + { + await JS.InvokeVoidAsync("preloadImage", "images/AMR-250.png"); + RobotImageLoaded = true; + } + catch + { + RobotImageLoaded = false; + } + } + + private async Task LoadMapImage() + { + try + { + MapImageLoaded = false; + string baseUrl = Http.BaseAddress?.ToString() ?? ""; + string apiUrl = $"{baseUrl}api/images/map"; + await JS.InvokeVoidAsync("preloadImageFromUrl", apiUrl, MAP_CACHE_KEY); + + var imageDimensions = await JS.InvokeAsync("getImageDimensions", MAP_CACHE_KEY); + MapImageWidth = imageDimensions.Width * ImageResolution; + MapImageHeight = imageDimensions.Height * ImageResolution; + if (MapImageWidth > 0 && MapImageHeight > 0) MapImageLoaded = true; + } + catch + { + MapImageLoaded = false; + } + } + private async Task DrawCanvas() { await using var ctx = await JS.GetContext2DAsync(CanvasRef); @@ -86,25 +164,202 @@ await ctx.TranslateAsync(CanvasTranslateX, CanvasTranslateY); await ctx.ScaleAsync(ZoomScale, ZoomScale); + + await DrawMapImage(ctx); await DrawGrid(ctx); await DrawAxes(ctx); - + await DrawLaserScannerPoints(ctx); await ctx.RestoreAsync(); - // Draw mouse indicator (outside transform) if (IsMouseInCanvas) { await DrawMouseIndicator(ctx); } } + private async Task DrawMapImage(Context2D ctx) + { + if (!MapImageLoaded) + { + return; + } + + await ctx.SaveAsync(); + + try + { + + double imageWidthCanvas = MapImageWidth * BASE_PIXELS_PER_METER; + double imageHeightCanvas = MapImageHeight * BASE_PIXELS_PER_METER; + + double mapCanvasX = ImageX * BASE_PIXELS_PER_METER; + double mapCanvasY = (ImageY + MapImageHeight) * BASE_PIXELS_PER_METER; + + bool success = await JS.InvokeAsync("drawCachedImageOnCanvas", + CanvasRef, + MAP_CACHE_KEY, + mapCanvasX, + mapCanvasY - imageHeightCanvas, + imageWidthCanvas, + imageHeightCanvas); + } + catch + { + } + + await ctx.RestoreAsync(); + } + + private async Task DrawLaserScannerPoints(Context2D ctx) + { + var scanData = GenerateLaserScanData(); + + double robotCanvasX = scanData.RobotX * BASE_PIXELS_PER_METER; + double robotCanvasY = scanData.RobotY * BASE_PIXELS_PER_METER; + + await ctx.SaveAsync(); + + if (scanData.Points.Count > 0) + { + await ctx.BeginPathAsync(); + + for (int i = 0; i < scanData.Points.Count; i++) + { + var point = scanData.Points[i]; + double pointCanvasX = point.X * BASE_PIXELS_PER_METER; + double pointCanvasY = point.Y * BASE_PIXELS_PER_METER; + + if (i == 0) + { + await ctx.MoveToAsync(pointCanvasX, pointCanvasY); + } + else + { + await ctx.LineToAsync(pointCanvasX, pointCanvasY); + } + } + + await ctx.StrokeStyleAsync("rgba(255, 100, 100, 0.8)"); + await ctx.LineWidthAsync(2 / ZoomScale); + await ctx.StrokeAsync(); + + await ctx.LineToAsync(robotCanvasX, robotCanvasY); + await ctx.ClosePathAsync(); + await ctx.FillStyleAsync("rgba(255, 100, 100, 0.1)"); + await ctx.FillAsync(FillRule.NonZero); + } + + await DrawRobotImage(ctx, robotCanvasX, robotCanvasY, scanData.RobotOrientation); + await DrawRobotOrientationArrows(ctx, robotCanvasX, robotCanvasY, scanData.RobotOrientation); + + await ctx.RestoreAsync(); + } + + private async Task DrawRobotImage(Context2D ctx, double robotCanvasX, double robotCanvasY, double robotOrientation) + { + if (!RobotImageLoaded) + { + // Fallback to circle if image not loaded + await ctx.FillStyleAsync("rgba(0, 255, 0, 0.8)"); + await ctx.BeginPathAsync(); + await ctx.ArcAsync(robotCanvasX, robotCanvasY, 8 / ZoomScale, 0, Math.PI * 2); + await ctx.FillAsync(FillRule.NonZero); + return; + } + + await ctx.SaveAsync(); + + double robotWidthPixels = RobotWidth * BASE_PIXELS_PER_METER; + double robotLengthPixels = RobotLength * BASE_PIXELS_PER_METER; + + double scaledWidth = ZoomScale < 1 ? robotWidthPixels / ZoomScale : robotWidthPixels; + double scaledLength = ZoomScale < 1 ? robotLengthPixels / ZoomScale : robotLengthPixels; + + await ctx.TranslateAsync(robotCanvasX, robotCanvasY); + await ctx.RotateAsync(robotOrientation); + + try + { + bool success = await JS.InvokeAsync("drawImageOnCanvas", + CanvasRef, + "images/AMR-250.png", + -scaledLength / 2, + -scaledWidth / 2, + scaledLength, + scaledWidth); + + if (!success) + { + await ctx.FillStyleAsync("rgba(0, 255, 0, 0.8)"); + await ctx.FillRectAsync(-scaledLength / 2, -scaledWidth / 2, scaledLength, scaledWidth); + } + } + catch + { + await ctx.FillStyleAsync("rgba(0, 255, 0, 0.8)"); + await ctx.FillRectAsync(-scaledLength / 2, -scaledWidth / 2, scaledLength, scaledWidth); + } + + await ctx.RestoreAsync(); + } + + private async Task DrawRobotOrientationArrows(Context2D ctx, double robotCanvasX, double robotCanvasY, double robotOrientation) + { + double arrowLength = 30 / ZoomScale; + double arrowHeadSize = 10 / ZoomScale; + + await ctx.StrokeStyleAsync("rgba(0, 100, 255, 1.0)"); + await ctx.FillStyleAsync("rgba(0, 100, 255, 1.0)"); + await ctx.LineWidthAsync(3 / ZoomScale); + + await ctx.BeginPathAsync(); + await ctx.MoveToAsync(robotCanvasX, robotCanvasY); + double xAxisEndX = robotCanvasX + Math.Cos(robotOrientation) * (arrowLength - arrowHeadSize + 1); + double xAxisEndY = robotCanvasY + Math.Sin(robotOrientation) * (arrowLength - arrowHeadSize + 1); + await ctx.LineToAsync(xAxisEndX, xAxisEndY); + await ctx.StrokeAsync(); + + await ctx.BeginPathAsync(); + double xArrowTipX = robotCanvasX + Math.Cos(robotOrientation) * arrowLength; + double xArrowTipY = robotCanvasY + Math.Sin(robotOrientation) * arrowLength; + await ctx.MoveToAsync(xArrowTipX, xArrowTipY); + double xArrowAngle = robotOrientation + Math.PI; + await ctx.LineToAsync(xArrowTipX + Math.Cos(xArrowAngle + Math.PI / 6) * arrowHeadSize, xArrowTipY + Math.Sin(xArrowAngle + Math.PI / 6) * arrowHeadSize); + await ctx.LineToAsync(xArrowTipX + Math.Cos(xArrowAngle - Math.PI / 6) * arrowHeadSize, xArrowTipY + Math.Sin(xArrowAngle - Math.PI / 6) * arrowHeadSize); + await ctx.ClosePathAsync(); + await ctx.FillAsync(FillRule.NonZero); + + await ctx.StrokeStyleAsync("rgba(255, 50, 50, 1.0)"); + await ctx.FillStyleAsync("rgba(255, 50, 50, 1.0)"); + await ctx.LineWidthAsync(3 / ZoomScale); + + double yAxisAngle = robotOrientation + Math.PI / 2; + + await ctx.BeginPathAsync(); + await ctx.MoveToAsync(robotCanvasX, robotCanvasY); + double yAxisEndX = robotCanvasX + Math.Cos(yAxisAngle) * (arrowLength - arrowHeadSize + 1); + double yAxisEndY = robotCanvasY + Math.Sin(yAxisAngle) * (arrowLength - arrowHeadSize + 1); + await ctx.LineToAsync(yAxisEndX, yAxisEndY); + await ctx.StrokeAsync(); + + await ctx.BeginPathAsync(); + double yArrowTipX = robotCanvasX + Math.Cos(yAxisAngle) * arrowLength; + double yArrowTipY = robotCanvasY + Math.Sin(yAxisAngle) * arrowLength; + await ctx.MoveToAsync(yArrowTipX, yArrowTipY); + double yArrowAngle = yAxisAngle + Math.PI; + await ctx.LineToAsync(yArrowTipX + Math.Cos(yArrowAngle + Math.PI / 6) * arrowHeadSize, yArrowTipY + Math.Sin(yArrowAngle + Math.PI / 6) * arrowHeadSize); + await ctx.LineToAsync(yArrowTipX + Math.Cos(yArrowAngle - Math.PI / 6) * arrowHeadSize, yArrowTipY + Math.Sin(yArrowAngle - Math.PI / 6) * arrowHeadSize); + await ctx.ClosePathAsync(); + await ctx.FillAsync(FillRule.NonZero); + } + private async Task DrawMouseIndicator(Context2D ctx) { await ctx.SaveAsync(); - await ctx.StrokeStyleAsync("rgba(255, 50, 50, 0.8)"); + await ctx.StrokeStyleAsync("rgba(255, 50, 50, 0.8)"); await ctx.LineWidthAsync(1); - await ctx.SetLineDashAsync(new double[] { 3, 3 }); + await ctx.SetLineDashAsync(new double[] { 3, 3 }); await ctx.BeginPathAsync(); await ctx.MoveToAsync(MouseX, RulerHeight); @@ -112,7 +367,7 @@ await ctx.StrokeAsync(); await ctx.BeginPathAsync(); - await ctx.MoveToAsync(RulerHeight, MouseY); + await ctx.MoveToAsync(RulerHeight, MouseY); await ctx.LineToAsync(CanvasWidth, MouseY); await ctx.StrokeAsync(); @@ -120,13 +375,13 @@ const double labelPadding = 7; const double labelMargin = 8; - + string coordinateText = $"({WorldMouseX:F2}m, {WorldMouseY:F2}m)"; await ctx.FontAsync("bold 12px Arial"); var textMetrics = await ctx.MeasureTextAsync(coordinateText); double textWidth = textMetrics.Width; - double textHeight = 16; + double textHeight = 16; double labelX = MouseX + labelMargin; double labelY = MouseY - textHeight - labelPadding * 2 - labelMargin; @@ -140,10 +395,10 @@ labelY = MouseY + labelMargin; } - await ctx.FillStyleAsync("rgba(0, 0, 0, 0.8)"); + await ctx.FillStyleAsync("rgba(0, 0, 0, 0.8)"); await ctx.FillRectAsync(labelX, labelY, textWidth + labelPadding * 2, textHeight + labelPadding * 2); - await ctx.StrokeStyleAsync("rgba(255, 255, 255, 0.6)"); + await ctx.StrokeStyleAsync("rgba(255,255,255,0.6)"); await ctx.LineWidthAsync(1); await ctx.StrokeRectAsync(labelX, labelY, textWidth + labelPadding * 2, textHeight + labelPadding * 2); @@ -159,16 +414,16 @@ await ctx.StrokeAsync(); await ctx.SaveAsync(); - + await ctx.TranslateAsync(labelX + labelPadding + textWidth / 2, labelY + textHeight / 2); await ctx.ScaleAsync(1, -1); - + await ctx.FillStyleAsync("white"); await ctx.FontAsync("bold 12px Arial"); await ctx.TextAlignAsync(TextAlign.Center); await ctx.TextBaseLineAsync(TextBaseLine.Bottom); await ctx.FillTextAsync(coordinateText, 0, 0); - + await ctx.RestoreAsync(); } @@ -326,23 +581,9 @@ await ctx.FillStyleAsync("red"); await ctx.BeginPathAsync(); - await ctx.ArcAsync(originCanvasX, originCanvasY, 10 / ZoomScale, 0, Math.PI * 2); + await ctx.ArcAsync(originCanvasX, originCanvasY, 8 / ZoomScale, 0, Math.PI * 2); await ctx.FillAsync(FillRule.NonZero); - await ctx.LineWidthAsync(2 / ZoomScale); - - await ctx.StrokeStyleAsync("blue"); - await ctx.BeginPathAsync(); - await ctx.MoveToAsync(-CanvasWidth / ZoomScale, originCanvasY); - await ctx.LineToAsync(CanvasWidth / ZoomScale, originCanvasY); - await ctx.StrokeAsync(); - - await ctx.StrokeStyleAsync("red"); - await ctx.BeginPathAsync(); - await ctx.MoveToAsync(originCanvasX, -CanvasHeight / ZoomScale); - await ctx.LineToAsync(originCanvasX, CanvasHeight / ZoomScale); - await ctx.StrokeAsync(); - double gridSpacingMeters = GetGridSpacingMeters(); double arrowLength = gridSpacingMeters * BASE_PIXELS_PER_METER; double arrowHeadSize = 16 / ZoomScale; @@ -353,13 +594,15 @@ await ctx.BeginPathAsync(); await ctx.MoveToAsync(originCanvasX, originCanvasY); - await ctx.LineToAsync(originCanvasX + arrowLength, originCanvasY); + await ctx.LineToAsync(originCanvasX + arrowLength - arrowHeadSize, originCanvasY); await ctx.StrokeAsync(); await ctx.BeginPathAsync(); - await ctx.MoveToAsync(originCanvasX + arrowLength, originCanvasY); - await ctx.LineToAsync(originCanvasX + arrowLength - arrowHeadSize, originCanvasY - arrowHeadSize / 2); - await ctx.LineToAsync(originCanvasX + arrowLength - arrowHeadSize, originCanvasY + arrowHeadSize / 2); + double xArrowTipX = originCanvasX + arrowLength; + double xArrowTipY = originCanvasY; + await ctx.MoveToAsync(xArrowTipX, xArrowTipY); + await ctx.LineToAsync(xArrowTipX - arrowHeadSize, xArrowTipY - arrowHeadSize / 2); + await ctx.LineToAsync(xArrowTipX - arrowHeadSize, xArrowTipY + arrowHeadSize / 2); await ctx.ClosePathAsync(); await ctx.FillAsync(FillRule.NonZero); @@ -369,13 +612,15 @@ await ctx.BeginPathAsync(); await ctx.MoveToAsync(originCanvasX, originCanvasY); - await ctx.LineToAsync(originCanvasX, originCanvasY + arrowLength); + await ctx.LineToAsync(originCanvasX, originCanvasY + arrowLength - arrowHeadSize); await ctx.StrokeAsync(); await ctx.BeginPathAsync(); - await ctx.MoveToAsync(originCanvasX, originCanvasY + arrowLength); - await ctx.LineToAsync(originCanvasX - arrowHeadSize / 2, originCanvasY + arrowLength - arrowHeadSize); - await ctx.LineToAsync(originCanvasX + arrowHeadSize / 2, originCanvasY + arrowLength - arrowHeadSize); + double yArrowTipX = originCanvasX; + double yArrowTipY = originCanvasY + arrowLength; + await ctx.MoveToAsync(yArrowTipX, yArrowTipY); + await ctx.LineToAsync(yArrowTipX - arrowHeadSize / 2, yArrowTipY - arrowHeadSize); + await ctx.LineToAsync(yArrowTipX + arrowHeadSize / 2, yArrowTipY - arrowHeadSize); await ctx.ClosePathAsync(); await ctx.FillAsync(FillRule.NonZero); } @@ -515,9 +760,100 @@ await DrawCanvas(); } + private async Task ReloadMapImage() + { + MapImageLoaded = false; + await LoadMapImage(); + await DrawCanvas(); + StateHasChanged(); + } + + private LaserScanData GenerateLaserScanData() + { + // Robot position (in world coordinates) + double robotX = 2; // Robot at origin for demo + double robotY = 2; + double robotOrientation = 0; // Robot facing right (0 degrees) + + Random random = new Random(42); // Fixed seed for consistent pattern + + // Laser scanner parameters + const double maxRange = 8.0; // meters + const double minRange = 0.2; // meters (fix: was 7.0, should be minimum) + const int numPoints = 270; // Number of laser points + const double startAngle = -Math.PI / 2 - Math.PI / 4; + const double endAngle = Math.PI / 2 + Math.PI / 4; + + double angleStep = (endAngle - startAngle) / (numPoints - 1); + + var scanData = new LaserScanData + { + RobotX = robotX, + RobotY = robotY, + RobotOrientation = robotOrientation, + }; + + // Generate laser points + for (int i = 0; i < numPoints; i++) + { + double angle = startAngle + i * angleStep; + + // Random range with some clustering around obstacles + double range; + if (random.NextDouble() < 0.3) // 30% chance of obstacles + { + range = random.NextDouble() * 3.0 + 1.0; // 1-4 meters (obstacles) + } + else if (random.NextDouble() < 0.1) // 10% chance of very close objects + { + range = random.NextDouble() * 0.8 + 0.2; // 0.2-1.0 meters + } + else + { + range = random.NextDouble() * maxRange * 0.7 + maxRange * 0.3; // Far points + } + + // Add some noise to make it realistic + range += (random.NextDouble() - 0.5) * 0.1; + range = Math.Max(minRange, Math.Min(maxRange, range)); + + // Calculate point position relative to robot + double pointX = robotX + Math.Cos(angle + robotOrientation) * range; + double pointY = robotY + Math.Sin(angle + robotOrientation) * range; + + scanData.Points.Add(new LaserScanPoint + { + X = pointX, + Y = pointY + }); + } + + return scanData; + } + + public class LaserScanPoint + { + public double X { get; set; } + public double Y { get; set; } + } + + public class LaserScanData + { + public double RobotX { get; set; } + public double RobotY { get; set; } + public double RobotOrientation { get; set; } + public List Points { get; set; } = new(); + } + public class DomRect { public double Width { get; set; } public double Height { get; set; } } + + public class ImageDimensions + { + public double Width { get; set; } + public double Height { get; set; } + } } diff --git a/RobotApp.Client/wwwroot/images/AMR-250.png b/RobotApp.Client/wwwroot/images/AMR-250.png new file mode 100644 index 0000000..b4ae207 Binary files /dev/null and b/RobotApp.Client/wwwroot/images/AMR-250.png differ diff --git a/RobotApp.Client/wwwroot/js/canvas.js b/RobotApp.Client/wwwroot/js/canvas.js index 5b34e27..e956683 100644 --- a/RobotApp.Client/wwwroot/js/canvas.js +++ b/RobotApp.Client/wwwroot/js/canvas.js @@ -8,4 +8,94 @@ window.setCanvasSize = (canvas, width, height) => { canvas.width = width; canvas.height = height; -} \ No newline at end of file +} + +// Image loading and caching functionality +window.imageCache = new Map(); + +window.preloadImage = (imagePath) => { + return new Promise((resolve, reject) => { + if (window.imageCache.has(imagePath)) { + resolve(window.imageCache.get(imagePath)); + return; + } + + const img = new Image(); + img.onload = () => { + window.imageCache.set(imagePath, img); + resolve(img); + }; + img.onerror = () => { + reject(new Error(`Failed to load image: ${imagePath}`)); + }; + img.src = imagePath; + }); +}; + +window.preloadImageFromUrl = (url, cacheKey) => { + return new Promise((resolve, reject) => { + if (window.imageCache.has(cacheKey)) { + resolve(window.imageCache.get(cacheKey)); + return; + } + + const img = new Image(); + img.onload = () => { + window.imageCache.set(cacheKey, img); + resolve(img); + }; + img.onerror = (error) => { + reject(new Error(`Failed to load image from URL: ${url}`)); + }; + + // Don't set crossOrigin for same-origin requests + // Only set it if you're loading from a different domain + // img.crossOrigin = 'anonymous'; + + img.src = url; + }); +}; + +window.getImageDimensions = (cacheKey) => { + const img = window.imageCache.get(cacheKey); + if (!img) { + return { width: 0, height: 0 }; + } + + return { + width: img.naturalWidth || img.width, + height: img.naturalHeight || img.height + }; +}; + +window.drawImageOnCanvas = (canvas, imagePath, x, y, width, height) => { + const ctx = canvas.getContext('2d'); + const img = window.imageCache.get(imagePath); + + if (!img) { + return false; + } + + try { + ctx.drawImage(img, x, y, width, height); + return true; + } catch (error) { + return false; + } +}; + +window.drawCachedImageOnCanvas = (canvas, cacheKey, x, y, width, height) => { + const ctx = canvas.getContext('2d'); + const img = window.imageCache.get(cacheKey); + + if (!img) { + return false; + } + + try { + ctx.drawImage(img, x, y, width, height); + return true; + } catch (error) { + return false; + } +}; \ No newline at end of file diff --git a/RobotApp/Controllers/ImagesController.cs b/RobotApp/Controllers/ImagesController.cs index 05c2a21..b1995f6 100644 --- a/RobotApp/Controllers/ImagesController.cs +++ b/RobotApp/Controllers/ImagesController.cs @@ -1,11 +1,62 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using RobotApp.Services; namespace RobotApp.Controllers; [Route("api/[controller]")] [ApiController] [AllowAnonymous] -public class ImagesController : ControllerBase +public class ImagesController(Services.Logger Logger) : ControllerBase { + [HttpGet] + [Route("map")] + public async Task GetMapImage() + { + try + { + await Task.Delay(1); + + string fileName = "gara20250309.png"; + string filePath = Path.Combine("maps", fileName); + + if (System.IO.File.Exists(filePath)) + { + byte[] imageBytes = await System.IO.File.ReadAllBytesAsync(filePath); + return File(imageBytes, "image/png"); + } + else + { + string mapsDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "maps"); + if (Directory.Exists(mapsDir)) + { + var pngFiles = Directory.GetFiles(mapsDir, "*.png"); + + return NotFound(new + { + error = "Map image not found", + searchPath = filePath, + mapsDirectory = mapsDir, + availableFiles = pngFiles.Select(Path.GetFileName).ToArray(), + baseDirectory = AppDomain.CurrentDomain.BaseDirectory + }); + } + else + { + Logger.Warning($"GetMapImage: Maps directory does not exist: {mapsDir}"); + return NotFound(new + { + error = "Maps directory not found", + searchPath = mapsDir, + baseDirectory = AppDomain.CurrentDomain.BaseDirectory + }); + } + } + } + catch (Exception ex) + { + Logger.Warning($"GetMapImage: Exception occurred - {ex.Message}"); + return StatusCode(500, new { error = "Internal server error", message = ex.Message, stackTrace = ex.StackTrace }); + } + } } diff --git a/RobotApp/Program.cs b/RobotApp/Program.cs index e81d55c..fa67d52 100644 --- a/RobotApp/Program.cs +++ b/RobotApp/Program.cs @@ -22,6 +22,9 @@ builder.Services.AddScoped appDbOptions = options => options.UseSqlite(connectionString, b => b.MigrationsAssembly("RobotApp")); @@ -68,6 +71,10 @@ app.UseAuthorization(); app.UseAntiforgery(); app.MapStaticAssets(); + +// Map API Controllers +app.MapControllers(); + app.MapRazorComponents() .AddInteractiveServerRenderMode() .AddInteractiveWebAssemblyRenderMode() diff --git a/RobotApp/Properties/launchSettings.json b/RobotApp/Properties/launchSettings.json index b4aecb3..19731fb 100644 --- a/RobotApp/Properties/launchSettings.json +++ b/RobotApp/Properties/launchSettings.json @@ -5,6 +5,7 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, + "workingDirectory": "$(TargetDir)", "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", "applicationUrl": "http://localhost:5229", "environmentVariables": { @@ -15,6 +16,7 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, + "workingDirectory": "$(TargetDir)", "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", "applicationUrl": "https://localhost:7150;http://localhost:5229", "environmentVariables": { diff --git a/RobotApp/maps/gara20250309 .png b/RobotApp/maps/gara20250309 .png new file mode 100644 index 0000000..bc4110d Binary files /dev/null and b/RobotApp/maps/gara20250309 .png differ diff --git a/RobotApp/robot.db b/RobotApp/robot.db index 6249b2f..56f2f75 100644 Binary files a/RobotApp/robot.db and b/RobotApp/robot.db differ