1
z8018
2025-06-10 e46aa927d231af83724683c7286d9db503e24cf7
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Server/WIDESEAWCS_BasicInfoService/ContainerService.cs
@@ -4,6 +4,8 @@
using System.Text;
using System.Threading.Tasks;
using AutoMapper;
using HslCommunication.WebSocket;
using WIDESEAWCS_BasicInfoRepository;
using WIDESEAWCS_BasicInfoService;
using WIDESEAWCS_Common;
using WIDESEAWCS_Core;
@@ -15,49 +17,84 @@
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;
        public ContainerService(IContainerRepository BaseDal, IMapper mapper, IUnitOfWorkManage unitOfWorkManage, IContainerItemRepository containerItemRepository, IOrderContainerRepository orderContainerRepository) : base(BaseDal)
        /// <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;
        }
        public SuctionInfo GetPlacementResult(Block block, List<Block> blocks)
        {
            ContainerPlacer containerPlacer = new ContainerPlacer();
            return containerPlacer.CalculatePlacement(block, blocks);
            _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></returns>
        /// <exception cref="Exception"></exception>
        /// <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);
@@ -69,6 +106,62 @@
            }
        }
        /// <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);
@@ -81,24 +174,43 @@
            {
                return (true, taskPosition, "");
            }
            return (true, taskPosition, "10001");
            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 && (containers.Any(x => (x.Items.Any(v => v.ItemLength > 1600 || v.ItemWidth > 1600)) && x.DeviceCode == container.DeviceCode) || containers.Where(x => x.ContainerStatus == ContainerStatusEnum.Empty.ObjToInt() && x.ContainerEnable && x.DeviceCode == container.DeviceCode).Count() > 1 || length > 1600 || width > 1600))
            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;
                }
                ContainerSize containerSize = new ContainerSize(container.ContainerLength, container.ContainerWidth, container.ContainerHeight);
                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)
                {
@@ -110,6 +222,67 @@
            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();
@@ -142,7 +315,11 @@
                        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 == x.Id && x.ContainerCode == x.ContainerCode);
                    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))
@@ -150,32 +327,32 @@
                        edge = 1;
                    }
                    ContainerSize containerSize = new ContainerSize(container.ContainerLength, container.ContainerWidth, container.ContainerHeight);
                    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;
                    }
                    //else
                    //{
                    //    if (LightStatusStorage.LightStatusDic.ContainsKey(container.ContainerCode))//LightStatusEnum
                    //    {
                    //        LightStatusStorage.LightStatusDic[container.ContainerCode] = LightStatusEnum.ReadyRelease;
                    //        if (container.Items.Count > 10)
                    //        {
                    //            container.ContainerStatus = ContainerStatusEnum.ReadyRelease.ObjToInt();
                    //            BaseDal.UpdateData(container);
                    //        }
                    //    }
                    //}
                }
            }
            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
@@ -203,7 +380,137 @@
                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);
            }
        }
    }
}