using System;
|
using System.Collections.Generic;
|
using System.Linq;
|
using System.Text;
|
using System.Threading.Tasks;
|
using AutoMapper;
|
using HslCommunication.WebSocket;
|
using WIDESEAWCS_BasicInfoRepository;
|
using WIDESEAWCS_BasicInfoService;
|
using WIDESEAWCS_Common;
|
using WIDESEAWCS_Core;
|
using WIDESEAWCS_Core.BaseRepository;
|
using WIDESEAWCS_Core.BaseServices;
|
using WIDESEAWCS_Core.Helper;
|
using WIDESEAWCS_DTO.BasicInfo;
|
using WIDESEAWCS_DTO.PlacedBlockDTO;
|
using WIDESEAWCS_DTO.TaskInfo;
|
using WIDESEAWCS_IBasicInfoRepository;
|
using WIDESEAWCS_IBasicInfoService;
|
using WIDESEAWCS_ITaskInfoRepository;
|
using WIDESEAWCS_Model.Models;
|
|
namespace WIDESEAWCS_BasicInfoService
|
{
|
/// <summary>
|
/// 容器服务类,提供容器相关的业务逻辑操作
|
/// </summary>
|
/// <remarks>
|
/// 主要功能包括: <br/>
|
/// 1. 获取物品在容器中的放置位置 <br/>
|
/// 2. 处理异常工位的位置分配 <br/>
|
/// 3. 根据订单或尺寸获取合适的位置 <br/>
|
/// 4. 释放容器资源 <br/>
|
/// 使用依赖注入方式获取仓储接口和工作单元管理
|
/// </remarks>
|
public class ContainerService : ServiceBase<Dt_Container, IContainerRepository>, IContainerService
|
{
|
/// <summary>
|
/// 用于对象映射的映射器实例
|
/// </summary>
|
private readonly IMapper _mapper;
|
/// <summary>
|
/// 工作单元管理接口实例,用于管理数据库事务和工作单元
|
/// </summary>
|
private readonly IUnitOfWorkManage _unitOfWorkManage;
|
/// <summary>
|
/// 容器物品仓储接口实例
|
/// </summary>
|
private readonly IContainerItemRepository _containerItemRepository;
|
/// <summary>
|
/// 订单容器仓储接口实例
|
/// </summary>
|
private readonly IOrderContainerRepository _orderContainerRepository;
|
/// <summary>
|
/// 任务仓储接口实例,用于操作任务数据
|
/// </summary>
|
private readonly ITaskRepository _taskRepository;
|
/// <summary>
|
/// WebSocket服务器实例,用于处理实时通信
|
/// </summary>
|
private readonly WebSocketServer _webSocketServer;
|
|
/// <summary>
|
/// 容器服务构造函数
|
/// </summary>
|
/// <param name="BaseDal">容器仓储接口</param>
|
/// <param name="mapper">对象映射器</param>
|
/// <param name="unitOfWorkManage">工作单元管理器</param>
|
/// <param name="containerItemRepository">容器项仓储接口</param>
|
/// <param name="orderContainerRepository">订单容器仓储接口</param>
|
/// <param name="taskRepository">任务仓储接口</param>
|
/// <param name="webSocketServer">WebSocket服务实例</param>
|
public ContainerService(IContainerRepository BaseDal, IMapper mapper, IUnitOfWorkManage unitOfWorkManage, IContainerItemRepository containerItemRepository, IOrderContainerRepository orderContainerRepository, ITaskRepository taskRepository, WebSocketServer webSocketServer) : base(BaseDal)
|
{
|
_mapper = mapper;
|
_unitOfWorkManage = unitOfWorkManage;
|
_containerItemRepository = containerItemRepository;
|
_orderContainerRepository = orderContainerRepository;
|
_taskRepository = taskRepository;
|
_webSocketServer = webSocketServer;
|
}
|
|
/// <summary>
|
/// 获取任务位置信息
|
/// </summary>
|
/// <param name="length">物品长度</param>
|
/// <param name="width">物品宽度</param>
|
/// <param name="height">物品高度</param>
|
/// <param name="containerSize">容器尺寸</param>
|
/// <param name="placedBlocks">已放置块列表</param>
|
/// <param name="edge">边缘参数</param>
|
/// <returns>返回任务位置信息,若放置失败则返回null</returns>
|
/// <exception cref="Exception">当放置过程中发生错误时抛出异常</exception>
|
public TaskPosition? GetTaskPosition(int length, int width, int height, ContainerSize containerSize, List<PlacedBlock> placedBlocks, int edge)
|
{
|
try
|
{
|
PlaceBlockService placeBlockService = new PlaceBlockService(containerSize, placedBlocks);
|
|
TaskPosition? taskPosition = placeBlockService.PlaceBlock(length, width, height, edge);
|
return taskPosition;
|
}
|
catch (Exception ex)
|
{
|
throw new Exception(ex.Message);
|
}
|
}
|
|
/// <summary>
|
/// 获取异常工位位置
|
/// </summary>
|
/// <param name="length">物品长度</param>
|
/// <param name="width">物品宽度</param>
|
/// <param name="height">物品高度</param>
|
/// <returns>元组包含三个值:是否成功获取、任务位置对象(失败时为null)、错误信息(成功时为空)</returns>
|
/// <remarks>
|
/// 当异常容器中物品数量≥5时,使用固定Z轴位置99;否则根据当前物品数量计算位置
|
/// </remarks>
|
public (bool, TaskPosition?, string) GetExceptionPosition(int length, int width, int height)
|
{
|
Dt_Container? container = Db.Queryable<Dt_Container>().Where(x => x.ContainerType == ContainerTypeEnum.ExceptionContainer.ObjToInt()).Includes(x => x.Items).First();
|
if (container == null)
|
{
|
return (false, null, "请配置异常工位");
|
}
|
|
if (container.Items.Count >= 5)
|
{
|
ContainerSize containerSize = new ContainerSize(container.ContainerLength, container.ContainerWidth, container.ContainerHeight);
|
ExceptionPlaceBlockService exceptionPlaceBlockService = new ExceptionPlaceBlockService(containerSize);
|
TaskPosition taskPosition = exceptionPlaceBlockService.ExceptionPlaceBlock(length, width, height, 0);
|
taskPosition.TakeCenterPositionZ = 99;
|
taskPosition.PutPosition = container.ContainerCode;
|
if (LightStatusStorage.LightStatusDic.ContainsKey(container.ContainerCode))
|
{
|
LightStatusStorage.LightStatusDic[container.ContainerCode] = LightStatusEnum.LightError;
|
}
|
return (true, taskPosition, "");
|
}
|
else
|
{
|
ContainerSize containerSize = new ContainerSize(container.ContainerLength, container.ContainerWidth, container.ContainerHeight);
|
ExceptionPlaceBlockService exceptionPlaceBlockService = new ExceptionPlaceBlockService(containerSize);
|
TaskPosition taskPosition = exceptionPlaceBlockService.ExceptionPlaceBlock(length, width, height, container.Items.Count);
|
taskPosition.PutPosition = container.ContainerCode;
|
return (true, taskPosition, "");
|
}
|
|
}
|
|
/// <summary>
|
/// 根据订单信息或尺寸获取任务位置
|
/// </summary>
|
/// <param name="orderId">订单ID</param>
|
/// <param name="orderNo">订单编号</param>
|
/// <param name="length">长度</param>
|
/// <param name="width">宽度</param>
|
/// <param name="height">高度</param>
|
/// <returns>
|
/// 返回元组包含三个值: <br/>
|
/// Item1 - 是否成功获取位置 <br/>
|
/// Item2 - 获取到的任务位置对象,可能为null <br/>
|
/// Item3 - 错误代码,成功时返回空字符串
|
/// </returns>
|
public (bool, TaskPosition?, string) GetPosition(int orderId, string orderNo, int length, int width, int height)
|
{
|
TaskPosition? taskPosition = GetPositionByOrder(orderId, orderNo, length, width, height);
|
if (taskPosition != null)
|
{
|
return (true, taskPosition, "");
|
}
|
taskPosition = GetPosition(length, width, height);
|
if (taskPosition != null)
|
{
|
return (true, taskPosition, "");
|
}
|
return (false, null, "10001");
|
}
|
|
/// <summary>
|
/// 根据物品尺寸获取合适的容器位置
|
/// </summary>
|
/// <param name="length">物品长度</param>
|
/// <param name="width">物品宽度</param>
|
/// <param name="height">物品高度</param>
|
/// <returns>返回找到的任务位置信息,包含容器编码;若未找到合适位置则返回null</returns>
|
/// <remarks>
|
/// 该方法会查询所有可用空容器,并根据物品尺寸和容器状态判断是否适合放置。 <br/>
|
/// 对于超大物品(长或宽超过1600)或有特殊要求的容器会进行特殊处理。
|
/// </remarks>
|
public TaskPosition? GetPosition(int length, int width, int height)
|
{
|
List<Dt_Container> containers = Db.Queryable<Dt_Container>().Where(x => x.ContainerType == ContainerTypeEnum.PutContainer.ObjToInt()).Includes(x => x.Items).ToList();
|
|
Dt_Container? container = containers.OrderBy(x => x.ContainerSort).FirstOrDefault(x => x.ContainerStatus == ContainerStatusEnum.Empty.ObjToInt() && x.ContainerEnable);
|
List<PlacedBlock> placedBlocks = new List<PlacedBlock>();
|
if (container != null)
|
{
|
int edge = 0;
|
if (container.ContainerNo == containers.Where(x => x.DeviceCode == container.DeviceCode && x.ContainerType == ContainerTypeEnum.PutContainer.ObjToInt()).Min(x => x.ContainerNo))
|
{
|
edge = 1;
|
}
|
int containerLength = container.ContainerLength;
|
int containerWidth = container.ContainerWidth;
|
int maxValue = Math.Max(length, width);
|
int minValue = Math.Min(length, width);
|
if (minValue > container.ContainerWidth)
|
{
|
containerLength = maxValue;
|
containerWidth = minValue;
|
}
|
ContainerSize containerSize = new ContainerSize(containerLength, containerWidth, container.ContainerHeight);
|
TaskPosition? taskPosition = GetTaskPosition(length, width, height, containerSize, placedBlocks, edge);
|
if (taskPosition != null)
|
{
|
taskPosition.PutPosition = container.ContainerCode;
|
return taskPosition;
|
}
|
}
|
|
return null;
|
}
|
|
/// <summary>
|
/// 获取指定容器中的可用位置
|
/// </summary>
|
/// <param name="container">目标容器对象</param>
|
/// <param name="length">待放置物品长度</param>
|
/// <param name="width">待放置物品宽度</param>
|
/// <param name="height">待放置物品高度</param>
|
/// <returns>元组包含: <br/>
|
/// - bool: 是否找到合适位置 <br/>
|
/// - TaskPosition?: 找到的任务位置信息(可为null) <br/>
|
/// - string: 错误代码(成功时返回空字符串)
|
/// </returns>
|
/// <remarks>
|
/// 方法会检查容器尺寸是否满足物品放置要求, <br/>
|
/// 并考虑容器边缘情况计算最佳放置位置
|
/// </remarks>
|
public (bool, TaskPosition?, string) GetPosition(Dt_Container container, int length, int width, int height)
|
{
|
List<Dt_Container> containers = Db.Queryable<Dt_Container>().Where(x => x.ContainerType == ContainerTypeEnum.PutContainer.ObjToInt()).Includes(x => x.Items).ToList();
|
List<PlacedBlock> placedBlocks = new List<PlacedBlock>();
|
if (container != null)
|
{
|
int edge = 0;
|
if (container.ContainerNo == containers.Where(x => x.DeviceCode == container.DeviceCode && x.ContainerType == ContainerTypeEnum.PutContainer.ObjToInt()).Min(x => x.ContainerNo))
|
{
|
edge = 1;
|
}
|
int containerLength = container.ContainerLength;
|
int containerWidth = container.ContainerWidth;
|
int maxValue = Math.Max(length, width);
|
int minValue = Math.Min(length, width);
|
if (minValue > container.ContainerWidth)
|
{
|
containerLength = maxValue;
|
containerWidth = minValue;
|
}
|
ContainerSize containerSize = new ContainerSize(containerLength, containerWidth, container.ContainerHeight);
|
TaskPosition? taskPosition = GetTaskPosition(length, width, height, containerSize, placedBlocks, edge);
|
if (taskPosition != null)
|
{
|
taskPosition.PutPosition = container.ContainerCode;
|
return (true, taskPosition, "");
|
}
|
}
|
|
return (false, null, "10002");
|
}
|
|
/// <summary>
|
/// 根据订单信息获取可用货位位置
|
/// </summary>
|
/// <param name="orderId">订单ID</param>
|
/// <param name="orderNo">订单编号</param>
|
/// <param name="length">物品长度</param>
|
/// <param name="width">物品宽度</param>
|
/// <param name="height">物品高度</param>
|
/// <returns>返回符合条件的任务位置信息,若找不到则返回null</returns>
|
/// <remarks>
|
/// 该方法会查询订单关联的容器,并检查每个容器中已放置物品的布局, <br/>
|
/// 通过算法计算新物品在容器中的最佳放置位置
|
/// </remarks>
|
public TaskPosition? GetPositionByOrder(int orderId, string orderNo, int length, int width, int height)
|
{
|
List<Dt_OrderContainer> orderContainers = Db.Queryable<Dt_OrderContainer>().Where(it => it.OrderNo == orderNo && it.OrderId == orderId).ToList();
|
if (orderContainers != null && orderContainers.Count > 0)
|
{
|
List<Dt_Container> dt_Containers = Db.Queryable<Dt_Container>().ToList();
|
|
List<Dt_Container> containers = dt_Containers.Where(it => orderContainers.Any(x => x.ContainerId == it.Id && x.ContainerCode == it.ContainerCode) && it.ContainerEnable && it.ContainerStatus != ContainerStatusEnum.ReadyRelease.ObjToInt()).ToList().OrderBy(x => x.ContainerSort).ToList();
|
|
for (int i = 0; i < containers.Count; i++)
|
{
|
containers[i].Items = Db.Queryable<Dt_ContainerItem>().Where(x => x.ContainerId == containers[i].Id).ToList();
|
}
|
|
foreach (var container in containers)
|
{
|
List<PlacedBlock> placedBlocks = new List<PlacedBlock>();
|
|
foreach (var item in container.Items)
|
{
|
int tempLength = item.ItemLength;
|
int tempWidth = item.ItemWidth;
|
|
if (item.ItemLength < item.ItemWidth)
|
{
|
tempLength = item.ItemWidth;
|
tempWidth = item.ItemLength;
|
}
|
|
Point3D point3D = new Point3D(item.ItemPositionX, item.ItemPositionY, item.ItemPositionZ);
|
placedBlocks.Add(new PlacedBlock(point3D, tempLength, tempWidth, item.ItemHeight));
|
}
|
Dt_OrderContainer? orderContainer = orderContainers.FirstOrDefault(x => x.ContainerId == container.Id && x.ContainerCode == container.ContainerCode);
|
if (orderContainer == null)
|
{
|
continue;
|
}
|
|
int edge = 0;
|
if (container.ContainerNo == dt_Containers.Where(x => x.DeviceCode == container.DeviceCode && x.ContainerType == ContainerTypeEnum.PutContainer.ObjToInt()).Min(x => x.ContainerNo))
|
{
|
edge = 1;
|
}
|
|
ContainerSize containerSize = new ContainerSize(orderContainer.MaxLength, orderContainer.MaxWidth, container.ContainerHeight);
|
TaskPosition? taskPosition = GetTaskPosition(length, width, height, containerSize, placedBlocks, edge);
|
if (taskPosition != null)
|
{
|
taskPosition.PutPosition = container.ContainerCode;
|
return taskPosition;
|
}
|
}
|
}
|
return null;
|
}
|
|
/// <summary>
|
/// 释放指定容器及其关联项
|
/// </summary>
|
/// <param name="keys">容器ID数组</param>
|
/// <returns>操作结果响应</returns>
|
/// <remarks>
|
/// 1. 查询并获取指定容器及其关联项 <br/>
|
/// 2. 更新容器状态为空闲 <br/>
|
/// 3. 在事务中执行以下操作: <br/>
|
/// - 删除容器项并移入历史表 <br/>
|
/// - 删除订单容器关联并移入历史表 <br/>
|
/// - 更新容器状态 <br/>
|
/// 4. 操作成功返回OK,失败回滚事务并返回错误信息
|
/// </remarks>
|
public WebResponseContent ReleaseContainer(int[] keys)
|
{
|
try
|
{
|
List<Dt_Container> containers = Db.Queryable<Dt_Container>().Where(x => keys.Contains(x.Id)).Includes(x => x.Items).ToList();
|
List<Dt_ContainerItem> containerItems = new List<Dt_ContainerItem>();
|
List<Dt_OrderContainer> orderContainers = Db.Queryable<Dt_OrderContainer>().Where(x => containers.Select(x => x.Id).ToList().Contains(x.ContainerId)).ToList();
|
|
for (int i = 0; i < containers.Count; i++)
|
{
|
containers[i].ContainerStatus = ContainerStatusEnum.Empty.ObjToInt();
|
containerItems.AddRange(containers[i].Items);
|
}
|
|
_unitOfWorkManage.BeginTran();
|
_containerItemRepository.DeleteAndMoveIntoHty(containerItems, App.User?.UserId > 0 ? WIDESEAWCS_Core.Enums.OperateTypeEnum.人工删除 : WIDESEAWCS_Core.Enums.OperateTypeEnum.自动删除);
|
_orderContainerRepository.DeleteAndMoveIntoHty(orderContainers, App.User?.UserId > 0 ? WIDESEAWCS_Core.Enums.OperateTypeEnum.人工删除 : WIDESEAWCS_Core.Enums.OperateTypeEnum.自动删除);
|
Db.Updateable(containers).ExecuteCommand();
|
_unitOfWorkManage.CommitTran();
|
return WebResponseContent.Instance.OK();
|
}
|
catch (Exception ex)
|
{
|
_unitOfWorkManage.RollbackTran();
|
return WebResponseContent.Instance.Error(ex.Message);
|
}
|
}
|
|
public WebResponseContent AutoReleaseContainer(int[] keys)
|
{
|
try
|
{
|
List<Dt_Container> containers = Db.Queryable<Dt_Container>().Where(x => keys.Contains(x.Id)).Includes(x => x.Items).ToList();
|
|
for (int i = 0; i < containers.Count; i++)
|
{
|
AutoReleaseContainer(containers[i].ContainerCode);
|
}
|
|
return WebResponseContent.Instance.OK();
|
}
|
catch (Exception ex)
|
{
|
return WebResponseContent.Instance.Error(ex.Message);
|
}
|
}
|
|
/// <summary>
|
/// 自动释放容器
|
/// </summary>
|
/// <param name="containerCode">容器编号</param>
|
/// <returns>Web响应结果</returns>
|
/// <remarks>
|
/// 1. 根据容器编号查询容器信息 <br/>
|
/// 2. 检查容器是否存在关联任务 <br/>
|
/// 3. 若无任务则清空容器状态并删除订单容器记录 <br/>
|
/// 4. 若有任务则更新任务状态为"龙门架重新分配" <br/>
|
/// 5. 事务处理所有数据库操作
|
/// </remarks>
|
public WebResponseContent AutoReleaseContainer(string containerCode)
|
{
|
try
|
{
|
Dt_Container container = BaseDal.QueryFirst(x => x.ContainerCode == containerCode);
|
if (container == null)
|
{
|
return WebResponseContent.Instance.Error("容器不存在");
|
}
|
|
if (container.ContainerStatus == ContainerStatusEnum.Empty.ObjToInt())
|
{
|
return WebResponseContent.Instance.Error("容器已处于空闲状态");
|
}
|
|
if (container.ContainerType == ContainerTypeEnum.ExceptionContainer.ObjToInt())
|
{
|
if (container.Items != null && container.Items.Count > 0)
|
{
|
_containerItemRepository.DeleteAndMoveIntoHty(container.Items, App.User?.UserId > 0 ? WIDESEAWCS_Core.Enums.OperateTypeEnum.人工删除 : WIDESEAWCS_Core.Enums.OperateTypeEnum.自动删除);
|
}
|
return WebResponseContent.Instance.OK("异常工位已释放");
|
}
|
List<Dt_Task> tasks = _taskRepository.QueryData(x => x.NextAddress.Contains(container.ContainerCode));
|
|
Dt_OrderContainer orderContainer = _orderContainerRepository.QueryFirst(x => x.ContainerCode == container.ContainerCode && x.ContainerId == container.Id);
|
|
if (tasks != null && tasks.Count > 0)
|
{
|
List<string> codes = tasks.Select(x => x.PalletCode).ToList();
|
|
container.Items = _containerItemRepository.QueryData(x => x.ContainerId == container.Id && !codes.Contains(x.ItemCode));
|
|
if (container.ContainerType == ContainerTypeEnum.ExceptionContainer.ObjToInt())
|
{
|
tasks.ForEach(x =>
|
{
|
if (x.TaskState == TaskStatusEnum.Gantry_BeRelease.ObjToInt())
|
{
|
x.TaskState = TaskStatusEnum.Gantry_New.ObjToInt();
|
}
|
});
|
}
|
else
|
{
|
tasks.ForEach(x =>
|
{
|
if (x.TaskState == TaskStatusEnum.Gantry_Wait.ObjToInt())
|
{
|
x.TaskState = TaskStatusEnum.Gantry_BeReassign.ObjToInt();
|
}
|
});
|
}
|
}
|
else
|
{
|
container.Items = _containerItemRepository.QueryData(x => x.ContainerId == container.Id);
|
}
|
|
_unitOfWorkManage.BeginTran();
|
if (container.Items != null && container.Items.Count > 0)
|
{
|
_containerItemRepository.DeleteAndMoveIntoHty(container.Items, App.User?.UserId > 0 ? WIDESEAWCS_Core.Enums.OperateTypeEnum.人工删除 : WIDESEAWCS_Core.Enums.OperateTypeEnum.自动删除);
|
}
|
|
if (tasks == null || tasks.Count == 0)
|
{
|
container.ContainerStatus = ContainerStatusEnum.Empty.ObjToInt();
|
|
BaseDal.UpdateData(container);
|
if (orderContainer != null)
|
{
|
_orderContainerRepository.DeleteAndMoveIntoHty(orderContainer, App.User?.UserId > 0 ? WIDESEAWCS_Core.Enums.OperateTypeEnum.人工删除 : WIDESEAWCS_Core.Enums.OperateTypeEnum.自动删除);
|
}
|
}
|
else
|
{
|
orderContainer.MaxLength = container.ContainerLength;
|
orderContainer.MaxWidth = container.ContainerWidth;
|
_orderContainerRepository.UpdateData(orderContainer);
|
_taskRepository.UpdateData(tasks);
|
}
|
|
_unitOfWorkManage.CommitTran();
|
|
//_webSocketServer发送数据
|
_webSocketServer.PublishAllClientPayload(new
|
{
|
stationCode = container.ContainerCode,
|
release = 1,
|
}.Serialize());
|
|
return WebResponseContent.Instance.OK();
|
}
|
catch (Exception ex)
|
{
|
_unitOfWorkManage.RollbackTran();
|
return WebResponseContent.Instance.Error(ex.Message);
|
}
|
}
|
}
|
}
|