RobotNet/RobotNet.MapManager/Controllers/MapsManagerController.cs
2025-10-15 15:15:53 +07:00

572 lines
24 KiB
C#

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
using RobotNet.MapManager.Data;
using RobotNet.MapManager.Hubs;
using RobotNet.MapManager.Services;
using RobotNet.MapShares.Dtos;
using RobotNet.MapShares.Enums;
using RobotNet.Shares;
namespace RobotNet.MapManager.Controllers;
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class MapsManagerController(MapEditorDbContext MapDb, MapEditorStorageRepository StorageRepo, IHubContext<MapHub> MapHub, LoggerController<MapsManagerController> Logger) : ControllerBase
{
[HttpGet]
public async Task<IEnumerable<MapInfoDto>> GetMapInfos([FromQuery(Name = "txtSearch")] string? txtSearch)
{
try
{
if (string.IsNullOrWhiteSpace(txtSearch))
{
return await (from map in MapDb.Maps
select new MapInfoDto()
{
Id = map.Id,
VersionId = map.VersionId,
Name = map.Name,
Description = map.Description,
Active = map.Active,
OriginX = map.OriginX,
OriginY = map.OriginY,
Width = Math.Round(map.ImageWidth * map.Resolution, 2),
Height = Math.Round(map.ImageHeight * map.Resolution, 2),
Resolution = map.Resolution,
VDA5050 = map.VDA5050,
}).ToListAsync();
}
else
{
return await (from map in MapDb.Maps
where !string.IsNullOrEmpty(map.Name) && map.Name.Contains(txtSearch)
select new MapInfoDto()
{
Id = map.Id,
VersionId = map.VersionId,
Name = map.Name,
Description = map.Description,
Active = map.Active,
OriginX = map.OriginX,
OriginY = map.OriginY,
Width = Math.Round(map.ImageWidth * map.Resolution, 2),
Height = Math.Round(map.ImageHeight * map.Resolution, 2),
Resolution = map.Resolution,
VDA5050 = map.VDA5050,
}).ToListAsync();
}
}
catch (Exception ex)
{
Logger.Warning($"GetMapInfos: Hệ thống có lỗi xảy ra - {ex.Message}");
return [];
}
}
[HttpGet]
[Route("{id}")]
public async Task<MessageResult<MapInfoDto>> GetMapInfoId(Guid id)
{
var map = await MapDb.Maps.FirstOrDefaultAsync(m => m.Id == id);
if (map == null) return new MessageResult<MapInfoDto>(false, $"Không tìm thấy map {id}");
return new(true)
{
Data = new MapInfoDto()
{
Id = map.Id,
Name = map.Name,
Active = map.Active,
OriginX = map.OriginX,
OriginY = map.OriginY,
Width = Math.Round(map.ImageWidth * map.Resolution, 2),
Height = Math.Round(map.ImageHeight * map.Resolution, 2),
Resolution = map.Resolution,
},
};
}
[HttpGet]
[Route("info")]
public async Task<MessageResult<MapInfoDto>> GetMapInfoName([FromQuery(Name = "name")] string name)
{
var map = await MapDb.Maps.FirstOrDefaultAsync(m => m.Name == name);
if (map == null) return new MessageResult<MapInfoDto>(false, $"Không tìm thấy map {name}");
return new(true)
{
Data = new MapInfoDto()
{
Id = map.Id,
Name = map.Name,
Active = map.Active,
OriginX = map.OriginX,
OriginY = map.OriginY,
Width = Math.Round(map.ImageWidth * map.Resolution, 2),
Height = Math.Round(map.ImageHeight * map.Resolution, 2),
Resolution = map.Resolution,
},
};
}
[HttpPost]
[Route("")]
public async Task<MessageResult<MapInfoDto>> CreateMap([FromForm] MapCreateModel model, [FromForm(Name = "Image")] IFormFile imageUpload)
{
try
{
if (imageUpload is null) return new(false, "Dữ liệu không hợp lệ");
if (imageUpload.ContentType != "image/png") return new(false, "Ảnh map chỉ hỗ trợ định dạng image/png");
if (await MapDb.Maps.AnyAsync(map => map.Name == model.Name)) return new(false, "Tên của map đã tồn tại. Hãy đặt tên khác!");
var image = SixLabors.ImageSharp.Image.Load(imageUpload.OpenReadStream());
var entityMap = await MapDb.Maps.AddAsync(new Map()
{
Name = model.Name,
OriginX = model.OriginX,
OriginY = model.OriginY,
Resolution = model.Resolution,
ImageHeight = (ushort)image.Height,
ImageWidth = (ushort)image.Width,
Active = false,
NodeCount = 0,
NodeNameAutoGenerate = true,
NodeNameTemplateDefault = "Node",
NodeAllowedDeviationXyDefault = 0.1,
NodeAllowedDeviationThetaDefault = 0,
EdgeMinLengthDefault = 1,
EdgeMaxHeightDefault = 1,
EdgeMinHeightDefault = 0.1,
EdgeStraightMaxSpeedDefault = 1,
EdgeCurveMaxSpeedDefault = 0.3,
EdgeMaxRotationSpeedDefault = 0.5,
EdgeAllowedDeviationXyDefault = 0.1,
EdgeAllowedDeviationThetaDefault = 0,
EdgeRotationAllowedDefault = true,
EdgeDirectionAllowedDefault = DirectionAllowed.Both,
ZoneMinSquareDefault = 0.25,
});
await MapDb.SaveChangesAsync();
var (isSuccess, message) = await StorageRepo.UploadAsync("MapImages", $"{entityMap.Entity.Id}", imageUpload.OpenReadStream(), imageUpload.Length, imageUpload.ContentType, CancellationToken.None);
if (!isSuccess)
{
MapDb.Maps.Remove(entityMap.Entity);
await MapDb.SaveChangesAsync();
return new(false, message);
}
await MapDb.SaveChangesAsync();
Logger.Info($"User {HttpContext.User.Identity?.Name} đã tạo bản đồ mới với tên: {model.Name}");
return new(true)
{
Data = new MapInfoDto()
{
Id = entityMap.Entity.Id,
Name = entityMap.Entity.Name,
Active = entityMap.Entity.Active,
OriginX = entityMap.Entity.OriginX,
OriginY = entityMap.Entity.OriginY,
Width = entityMap.Entity.ImageWidth * entityMap.Entity.Resolution,
Height = entityMap.Entity.ImageHeight * entityMap.Entity.Resolution,
Resolution = entityMap.Entity.Resolution,
},
};
}
catch (Exception ex)
{
Logger.Warning($"CreateMap: Hệ thống có lỗi xảy ra - {ex.Message}");
return new(false, $"CreateMap: Hệ thống có lỗi xảy ra - {ex.Message}");
}
}
[HttpPut]
[Route("{id}")]
public async Task<MessageResult<MapInfoDto>> UpdateMap(Guid id, [FromBody] MapUpdateModel model)
{
try
{
if (id != model.Id) return new(false, "Dữ liệu gửi không chính xác");
var map = await MapDb.Maps.FindAsync(id);
if (map == null) return new(false, $"Không tồn tại map id = {id}");
if (map.Name != model.Name && await MapDb.Maps.AnyAsync(m => m.Name == model.Name))
{
return new(false, "Tên của map đã tồn tại, Hãy đặt tên khác!");
}
if (model.Resolution <= 0)
{
return new(false, "Độ phân giải của bản đồ phải lớn hơn 0");
}
map.Name = model.Name;
bool originChange = map.OriginX != model.OriginX || map.OriginY != model.OriginY;
if (originChange)
{
map.OriginX = model.OriginX;
map.OriginY = model.OriginY;
}
if (map.Resolution != model.Resolution)
{
var scale = model.Resolution / map.Resolution;
map.Resolution = model.Resolution;
if (originChange)
{
map.OriginX *= scale;
map.OriginY *= scale;
}
var nodes = await MapDb.Nodes.Where(n => n.MapId == map.Id).ToListAsync();
foreach (var node in nodes)
{
node.X *= scale;
node.Y *= scale;
}
var edges = await MapDb.Edges.Where(e => e.MapId == map.Id).ToListAsync();
foreach (var edge in edges)
{
edge.ControlPoint1X *= scale;
edge.ControlPoint1Y *= scale;
edge.ControlPoint2X *= scale;
edge.ControlPoint2Y *= scale;
}
}
await MapDb.SaveChangesAsync();
Logger.Info($"User {HttpContext.User.Identity?.Name} đã cập nhật thông tin bản đồ : {model.Id} - {map.Name}");
return new(true)
{
Data = new()
{
Id = id,
Name = map.Name,
OriginX = map.OriginX,
OriginY = map.OriginY,
Resolution = model.Resolution,
Width = Math.Round(map.ImageWidth * map.Resolution, 2),
Height = Math.Round(map.ImageHeight * map.Resolution, 2),
},
};
}
catch (Exception ex)
{
Logger.Warning($"UpdateMap {id}: Hệ thống có lỗi xảy ra - {ex.Message}");
return new(false, $"UpdateMap {id}: Hệ thống có lỗi xảy ra - {ex.Message}");
}
}
[HttpDelete]
[Route("{id}")]
public async Task<MessageResult> DeleteMap(Guid id)
{
try
{
var map = await MapDb.Maps.FindAsync(id);
if (map == null) return new(false, $"Không tồn tại map id = {id}");
MapDb.Elements.RemoveRange(MapDb.Elements.Where(e => e.MapId == map.Id));
MapDb.ElementModels.RemoveRange(MapDb.ElementModels.Where(em => em.MapId == map.Id));
MapDb.Edges.RemoveRange(MapDb.Edges.Where(edge => edge.MapId == map.Id));
MapDb.Nodes.RemoveRange(MapDb.Nodes.Where(node => node.MapId == map.Id));
MapDb.Zones.RemoveRange(MapDb.Zones.Where(zone => zone.MapId == map.Id));
MapDb.Actions.RemoveRange(MapDb.Actions.Where(action => action.MapId == map.Id));
MapDb.Maps.Remove(map);
await MapDb.SaveChangesAsync();
await StorageRepo.DeleteAsync("MapImages", $"{id}", CancellationToken.None);
Logger.Info($"User {HttpContext.User.Identity?.Name} đã xóa bản đồ {map.Name}");
return new(true);
}
catch (Exception ex)
{
Logger.Warning($"DeleteMap {id}: Hệ thống có lỗi xảy ra - {ex.Message}");
return new(false, $"DeleteMap {id}: Hệ thống có lỗi xảy ra - {ex.Message}");
}
}
[HttpPut]
[Route("active")]
public async Task<MessageResult> ActiveToggle([FromBody] MapActiveModel model)
{
var map = await MapDb.Maps.FindAsync(model.Id);
if (map == null) return new(false, $"Không tồn tại map id = {model.Id}");
map.Active = model.Active;
await MapDb.SaveChangesAsync();
await MapHub.Clients.All.SendAsync("MapUpdated", model.Id);
Logger.Info($"User {HttpContext.User.Identity?.Name} đã thay đổi trạng thái active bản đồ {map.Name}: {model.Active}");
return new(true);
}
[HttpPut]
[Route("image/{id}")]
public async Task<MessageResult> UpdateImage(Guid id, [FromForm(Name = "image")] IFormFile image)
{
try
{
var map = await MapDb.Maps.FindAsync(id);
if (map == null) return new(false, $"Không tồn tại map id = {id}");
var imageStream = image.OpenReadStream();
var imageUpdate = SixLabors.ImageSharp.Image.Load(imageStream);
map.ImageWidth = (ushort)imageUpdate.Width;
map.ImageHeight = (ushort)imageUpdate.Height;
await MapDb.SaveChangesAsync();
var (isSuccess, message) = await StorageRepo.UploadAsync("MapImages", $"{id}", image.OpenReadStream(), image.Length, image.ContentType, CancellationToken.None);
Logger.Info($"User {HttpContext.User.Identity?.Name} đã thay đổi ảnh của bản đồ {map.Name}");
return new(true);
}
catch (Exception ex)
{
Logger.Warning($"UpdateImage {id}: Hệ thống có lỗi xảy ra - {ex.Message}");
return new(false, $"UpdateImage {id}: Hệ thống có lỗi xảy ra - {ex.Message}");
}
}
[HttpPost]
[Route("import")]
public async Task<MessageResult<MapInfoDto>> ImportNewMap([FromBody] MapExportDto model)
{
if (model == null || model.Data == null || model.Data.ImageData == null || model.Data.ImageData.Length == 0)
{
return new(false, "Dữ liệu đầu vào không hợp lệ");
}
using var transaction = await MapDb.Database.BeginTransactionAsync();
try
{
if (await MapDb.Maps.AnyAsync(map => map.Name == model.Name)) return new(false, "Tên của map đã tồn tại, Hãy đặt tên khác!");
using var stream = new MemoryStream(model.Data.ImageData);
var imageFile = new FormFile(stream, 0, model.Data.ImageData.Length, "", $"{model.Name}.png")
{
Headers = new HeaderDictionary(),
ContentType = "image/png",
};
var image = SixLabors.ImageSharp.Image.Load(imageFile.OpenReadStream());
var entityMap = await MapDb.Maps.AddAsync(new Map()
{
Name = model.Name,
Description = model.Description,
OriginX = model.Info.OriginX,
OriginY = model.Info.OriginY,
Resolution = model.Info.Resolution,
ImageHeight = (ushort)image.Height,
ImageWidth = (ushort)image.Width,
Active = false,
ViewX = model.Info.ViewX,
ViewY = model.Info.ViewY,
ViewWidth = model.Info.ViewWidth,
ViewHeight = model.Info.ViewHeight,
VDA5050 = model.Info.VDA5050,
NodeCount = model.Data.NodeCount,
NodeNameAutoGenerate = model.Setting.NodeNameAutoGenerate,
NodeNameTemplateDefault = model.Setting.NodeNameTemplate,
NodeAllowedDeviationXyDefault = model.Setting.NodeAllowedDeviationXy,
NodeAllowedDeviationThetaDefault = model.Setting.NodeAllowedDeviationTheta,
EdgeMinLengthDefault = model.Setting.EdgeMinLength,
EdgeMaxHeightDefault = model.Setting.EdgeMaxHeight,
EdgeMinHeightDefault = model.Setting.EdgeMinHeight,
EdgeStraightMaxSpeedDefault = model.Setting.EdgeStraightMaxSpeed,
EdgeCurveMaxSpeedDefault = model.Setting.EdgeCurveMaxSpeed,
EdgeMaxRotationSpeedDefault = model.Setting.EdgeMaxRotationSpeed,
EdgeAllowedDeviationXyDefault = model.Setting.EdgeAllowedDeviationXy,
EdgeAllowedDeviationThetaDefault = model.Setting.EdgeAllowedDeviationTheta,
EdgeRotationAllowedDefault = model.Setting.EdgeRotationAllowed,
EdgeDirectionAllowedDefault = model.Setting.EdgeDirectionAllowed,
ZoneMinSquareDefault = model.Setting.ZoneMinSquare,
});
var (isSuccess, message) = await StorageRepo.UploadAsync("MapImages", $"{entityMap.Entity.Id}", imageFile.OpenReadStream(), imageFile.Length, imageFile.ContentType, CancellationToken.None);
if (!isSuccess)
{
await transaction.RollbackAsync();
return new(false, message);
}
Dictionary<Guid, Guid> actionSwap = [];
foreach (var action in model.Data.Actions)
{
var actionDb = await MapDb.Actions.AddAsync(new Data.Action()
{
MapId = entityMap.Entity.Id,
Name = action.Name,
Content = action.Content,
});
actionSwap.Add(action.Id, actionDb.Entity.Id);
}
Dictionary<Guid, Guid> nodeSwap = [];
foreach (var node in model.Data.Nodes)
{
var actions = System.Text.Json.JsonSerializer.Deserialize<Guid[]>(node.Actions ?? "");
List<Guid> newActions = [];
if (actions is not null && actions.Length > 0)
{
foreach (var actionId in actions)
{
if (actionSwap.TryGetValue(actionId, out Guid newActionId) && newActionId != Guid.Empty) newActions.Add(newActionId);
}
}
var nodeDb = await MapDb.Nodes.AddAsync(new Node()
{
Name = node.Name,
MapId = entityMap.Entity.Id,
X = node.X,
Y = node.Y,
Theta = node.Theta,
AllowedDeviationXy = node.AllowedDeviationXy,
AllowedDeviationTheta = node.AllowedDeviationTheta,
Actions = System.Text.Json.JsonSerializer.Serialize(newActions),
});
nodeSwap.Add(node.Id, nodeDb.Entity.Id);
}
var Edges = model.Data.Edges.Select(e => new Edge()
{
MapId = entityMap.Entity.Id,
StartNodeId = nodeSwap[e.StartNodeId],
EndNodeId = nodeSwap[e.EndNodeId],
ControlPoint1X = e.ControlPoint1X,
ControlPoint1Y = e.ControlPoint1Y,
ControlPoint2X = e.ControlPoint2X,
ControlPoint2Y = e.ControlPoint2Y,
TrajectoryDegree = e.TrajectoryDegree,
MaxHeight = e.MaxHeight,
MinHeight = e.MinHeight,
MaxSpeed = e.MaxSpeed,
MaxRotationSpeed = e.MaxRotationSpeed,
AllowedDeviationXy = e.AllowedDeviationXy,
AllowedDeviationTheta = e.AllowedDeviationTheta,
DirectionAllowed = e.DirectionAllowed,
RotationAllowed = e.RotationAllowed,
Actions = e.Actions,
}).ToList();
var Zones = model.Data.Zones.Select(z => new Zone()
{
MapId = entityMap.Entity.Id,
Type = z.Type,
X1 = z.X1,
X2 = z.X2,
Y1 = z.Y1,
Y2 = z.Y2,
X3 = z.X3,
X4 = z.X4,
Y3 = z.Y3,
Y4 = z.Y4,
}).ToList();
Dictionary<Guid, Guid> elementModelSwap = [];
foreach (var elementModel in model.Data.ElementModels)
{
var elementModelDb = await MapDb.ElementModels.AddAsync(new ElementModel()
{
MapId = entityMap.Entity.Id,
Name = elementModel.Name,
Height = elementModel.Height,
Width = elementModel.Width,
Image1Height = elementModel.Image1Height,
Image1Width = elementModel.Image1Width,
Image2Height = elementModel.Image2Height,
Image2Width = elementModel.Image2Width,
Content = elementModel.Content,
});
elementModelSwap.Add(elementModel.Id, elementModelDb.Entity.Id);
using var openStream = new MemoryStream(elementModel.ImageOpenData);
var imageOpenFile = new FormFile(openStream, 0, elementModel.ImageOpenData.Length, "", $"{elementModel.Name}O.png")
{
Headers = new HeaderDictionary(),
ContentType = "image/png",
};
await StorageRepo.UploadAsync("ElementOpenModels", $"{elementModelDb.Entity.Id}", imageOpenFile.OpenReadStream(), imageOpenFile.Length, imageOpenFile.ContentType, CancellationToken.None);
using var closeStream = new MemoryStream(elementModel.ImageCloseData);
var imageCloseFile = new FormFile(closeStream, 0, elementModel.ImageCloseData.Length, "", $"{elementModel.Name}C.png")
{
Headers = new HeaderDictionary(),
ContentType = "image/png",
};
await StorageRepo.UploadAsync("ElementCloseModels", $"{elementModelDb.Entity.Id}", imageCloseFile.OpenReadStream(), imageCloseFile.Length, imageCloseFile.ContentType, CancellationToken.None);
}
var Elements = model.Data.Elements.Select(e => new Data.Element()
{
MapId = entityMap.Entity.Id,
Name = e.Name,
IsOpen = e.IsOpen,
ModelId = elementModelSwap[e.ModelId],
Content = e.Content,
NodeId = nodeSwap[e.NodeId],
OffsetX = e.OffsetX,
OffsetY = e.OffsetY,
}).ToList();
if (Edges.Count > 0) await MapDb.Edges.AddRangeAsync(Edges);
if (Zones.Count > 0) await MapDb.Zones.AddRangeAsync(Zones);
if (Elements.Count > 0) await MapDb.Elements.AddRangeAsync(Elements);
await MapDb.SaveChangesAsync();
await transaction.CommitAsync();
return new(true)
{
Data = new MapInfoDto()
{
Id = entityMap.Entity.Id,
Name = entityMap.Entity.Name,
Active = entityMap.Entity.Active,
OriginX = entityMap.Entity.OriginX,
OriginY = entityMap.Entity.OriginY,
Width = entityMap.Entity.ImageWidth * entityMap.Entity.Resolution,
Height = entityMap.Entity.ImageHeight * entityMap.Entity.Resolution,
Resolution = entityMap.Entity.Resolution,
}
};
}
catch (IOException ex)
{
await transaction.RollbackAsync();
Logger.Warning($"ImportNewMap: Lỗi khi xử lý hình ảnh: {ex.Message}");
return new(false, $"ImportNewMap: Lỗi khi xử lý hình ảnh: {ex.Message}");
}
catch (DbUpdateException ex)
{
await transaction.RollbackAsync();
Logger.Warning($"ImportNewMap: Lỗi khi lưu vào database: {ex.Message}");
return new(false, $"ImportNewMap: Lỗi khi lưu vào database: {ex.Message}");
}
catch (Exception ex)
{
await transaction.RollbackAsync();
Logger.Warning($"ImportNewMap: Hệ thống có lỗi xảy ra - {ex.Message}");
return new(false, $"ImportNewMap: Hệ thống có lỗi xảy ra - {ex.Message}");
}
}
}