1
z8018
2025-05-05 b6837f097e9cdb2645368aed4ddb03f580c331e4
ÏîÄ¿´úÂë/WCS/WIDESEAWCS_Server/WIDESEAWCS_BasicInfoService/PlaceBlockService.cs
@@ -3,13 +3,49 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Math;
using WIDESEAWCS_Core.Helper;
using WIDESEAWCS_DTO.PlacedBlockDTO;
using WIDESEAWCS_DTO.TaskInfo;
using WIDESEAWCS_IBasicInfoService;
namespace WIDESEAWCS_BasicInfoService
{
    public class PlaceBlockService
    {
        public const int SPACING = 5;
        public static int SPACING = AppSettings.GetValue("Spacing").ObjToInt();
        public static int MaxRotateLength = AppSettings.GetValue("MaxRotateLength").ObjToInt();
        /// <summary>
        /// æœ€å¤§Y坐标限制(毫米)
        /// </summary>
        public const int MaxY = 600;
        /// <summary>
        /// æœ€å°æ¨ªå‘Y坐标限制(毫米)
        /// </summary>
        public static int MinY = AppSettings.GetValue("MinY").ObjToInt();
        /// <summary>
        /// å¸ç›˜æ¨ªå‘长度
        /// </summary>
        public const int SuctionLengthH = 920;
        /// <summary>
        /// å¸ç›˜æ¨ªå‘宽度
        /// </summary>
        public const int SuctionWidthH = 530;
        /// <summary>
        /// å¸ç›˜çºµå‘长度
        /// </summary>
        public const int SuctionLengthZ = 530;
        /// <summary>
        /// å¸ç›˜çºµå‘宽度
        /// </summary>
        public const int SuctionWidthZ = 130;
        /// <summary>
        /// å®¹å™¨å°ºå¯¸
@@ -25,6 +61,8 @@
        public PlaceBlockService(ContainerSize containerSize, List<PlacedBlock>? placedBlocks = null)
        {
            containerSize.Length = containerSize.Length;
            containerSize.Width = containerSize.Width;
            ContainerSize = containerSize;
            if (placedBlocks == null || placedBlocks.Count == 0)
            {
@@ -34,6 +72,7 @@
            {
                PlacedBlocks = placedBlocks;
            }
            containerFloor = new PlacedBlock(new Point3D(SPACING, SPACING, 0), ContainerSize.Length - 2 * SPACING, ContainerSize.Width - 2 * SPACING, 0);
        }
@@ -50,10 +89,46 @@
        /// </returns>
        public Point3D? PlaceBlock(int length, int width, int height)
        {
            int tempLength = length;
            int tempWidth = width;
            if (length < width)
            {
                length = tempWidth;
                width = tempLength;
            }
            if (!IsValidBlock(length, width, height))
                return null;
            return FindStackablePosition(length, width, height);
        }
        /// <summary>
        /// ä¸»æ”¾ç½®æ–¹æ³•:尝试放置指定尺寸的货物
        /// </summary>
        /// <param name="length">货物长度(X轴方向)</param>
        /// <param name="width">货物宽度(Y轴方向)</param>
        /// <param name="height">货物高度(Z轴方向)</param>
        /// <returns>
        /// æˆåŠŸï¼šè¿”å›žå¯æ”¾ç½®ä½ç½®çš„å·¦ä¸‹å‰è§’Point3D坐标
        /// å¤±è´¥ï¼šè¿”回null(尺寸无效或空间不足)
        /// </returns>
        public TaskPosition? PlaceBlock(int length, int width, int height, int edge)
        {
            int tempLength = length;
            int tempWidth = width;
            if (length < width)
            {
                length = tempWidth;
                width = tempLength;
            }
            if (!IsValidBlock(length, width, height))
                return null;
            return FindStackablePosition(length, width, height, edge);
        }
        /// <summary>
@@ -133,24 +208,101 @@
                    xEnd = Math.Min(xEnd, ContainerSize.Length - l - SPACING);
                    yEnd = Math.Min(yEnd, ContainerSize.Width - w - SPACING);
                    // éªŒè¯æ”¯æ’‘面积
                    //int supportArea = support.Length * support.Width;
                    //int requiredSupportArea = (int)(l * w * 0.7); // æ”¯æ’‘面积 >= è¢«æ”¯æ’‘面面积的70%
                    //if (supportArea < requiredSupportArea) continue;
                    if (xStart > xEnd || yStart > yEnd) continue;
                    // ä¼˜åŒ–搜索:优先角落位置
                    for (int y = yStart; y <= yEnd; y += 10)
                    for (int x = xStart; x <= xEnd; x += 10)
                    {
                        for (int x = xStart; x <= xEnd; x += 10)
                        for (int y = yStart; y <= yEnd; y += 10)
                        {
                            var candidate = new Point3D(x, y, baseZ);
                            if (IsPositionValid(candidate, l, w, h))
                            {
                                var placed = new PlacedBlock(candidate, l, w, h);
                                PlacedBlocks.Add(placed);
                                //PlacedBlocks.Add(placed);
                                return candidate;
                            }
                        }
                    }
                }
            }
            return null;
        }
        /// <summary>
        /// åœ¨çŽ°æœ‰è´§ç‰©é¡¶éƒ¨å¯»æ‰¾å æ”¾ä½ç½®ï¼ˆå †å æ¨¡å¼ï¼‰
        /// å æ”¾æ¡ä»¶ï¼š
        /// - æ”¯æ’‘面积 >= è¢«æ”¯æ’‘面面积的70%
        /// - æ–°è´§ç‰©å®Œå…¨ä½äºŽæ”¯æ’‘货物上方
        /// - æ»¡è¶³é—´éš”要求
        /// </summary>
        /// <param name="l">长度</param>
        /// <param name="w">宽度</param>
        /// <param name="h">高度</param>
        /// <returns></returns>
        private TaskPosition? FindStackablePosition(int l, int w, int h, int edge)
        {
            // ç”Ÿæˆå€™é€‰æ”¯æ’‘层(包含容器底部)
            var candidateLayers = PlacedBlocks
                .Select(b => b.Position.Z + b.Height)
                .Append(0)
                .Distinct()
                .OrderBy(z => z)
                .ToList();
            foreach (var baseZ in candidateLayers)
            {
                if (baseZ + h > ContainerSize.Height) continue;
                // èŽ·å–å½“å‰å±‚çš„æ”¯æ’‘å—ï¼ˆåŒ…å«è™šæ‹Ÿå®¹å™¨åº•éƒ¨ï¼‰
                var supports = GetSupportBlocks(baseZ);
                foreach (var support in supports)
                {
                    // è®¡ç®—有效叠放区域(修正间隔逻辑)
                    int xStart = support.Position.X;
                    int yStart = support.Position.Y;
                    // å®¹å™¨åº•部支撑必须内缩间隔(即使尺寸相同)
                    if (support == containerFloor)
                    {
                        xStart = support.Position.X;
                        yStart = support.Position.Y;
                    }
                    int xEnd = support.Position.X + support.Length - l;
                    int yEnd = support.Position.Y + support.Width - w;
                    // æ™®é€šæ”¯æ’‘块仅在尺寸不同时加间隔
                    if (support != containerFloor &&
                       (l != support.Length && w != support.Width))
                    {
                        xStart += SPACING;
                        yStart += SPACING;
                        xEnd -= SPACING;
                        yEnd -= SPACING;
                    }
                    // æœ€ç»ˆå®¹å™¨è¾¹ç•Œçº¦æŸ
                    xEnd = Math.Min(xEnd, ContainerSize.Length - l);
                    yEnd = Math.Min(yEnd, ContainerSize.Width - w);
                    if (xStart > xEnd || yStart > yEnd) continue;
                    // ä¼˜åŒ–搜索:优先角落位置
                    for (int x = xStart; x <= xEnd; x += 10)
                    {
                        for (int y = yStart; y <= yEnd; y += 10)
                        {
                            var candidate = new Point3D(x, y, baseZ);
                            if (IsPositionValid(candidate, l, w, h))
                            {
                                TaskPosition taskPosition = GetTaskPosition(candidate, l, w, h, edge);
                                if (IsPositionValid(taskPosition))
                                {
                                    return taskPosition;
                                }
                            }
                        }
                    }
@@ -170,47 +322,15 @@
        {
            var blocks = PlacedBlocks
                .Where(b => b.Position.Z + b.Height == baseZ)
                .OrderByDescending(b => b.Length * b.Width);
                /*.OrderByDescending(b => b.Length * b.Width)*/.ToList();
            // å½“baseZ=0时添加容器底部支撑
            if (baseZ == 0 && !blocks.Any())
            if (baseZ == 0 && blocks.Count == 0)
            {
                return new List<PlacedBlock> { containerFloor };
            }
            return blocks;
            //// èŽ·å–æ‰€æœ‰åœ¨ baseZ é«˜åº¦çš„æ”¯æ’‘块
            //var blocks = PlacedBlocks
            //    .Where(b => b.Position.Z + b.Height == baseZ)
            //    .ToList();
            //// å½“ baseZ=0 æ—¶æ·»åŠ å®¹å™¨åº•éƒ¨æ”¯æ’‘
            //if (baseZ == 0 && !blocks.Any())
            //{
            //    return new List<PlacedBlock> { containerFloor };
            //}
            //// è®¡ç®—整体平面
            //if (blocks.Any())
            //{
            //    int minX = blocks.Min(b => b.Position.X);
            //    int minY = blocks.Min(b => b.Position.Y);
            //    int maxX = blocks.Max(b => b.Position.X + b.Length);
            //    int maxY = blocks.Max(b => b.Position.Y + b.Width);
            //    var overallSupport = new PlacedBlock(
            //        new Point3D(minX, minY, baseZ),
            //        maxX - minX,
            //        maxY - minY,
            //        0
            //    );
            //    return new List<PlacedBlock> { overallSupport };
            //}
            //return blocks;
        }
        /// <summary>
@@ -234,6 +354,11 @@
                pos.Y + w > ContainerSize.Width - SPACING)
                return false;
            if (pos.X > 1600)
                return false;
            if (pos.X > MaxY && l > MaxRotateLength) return false;
            // ä¸‰ç»´ç¢°æ’žæ£€æµ‹
            var newBlock = new PlacedBlock(pos, l, w, h);
            return !PlacedBlocks.Any(existing =>
@@ -250,74 +375,212 @@
                return xOverlap && yOverlap && zOverlap;
            });
        }
    }
    /// <summary>
    /// ä¸‰ç»´åæ ‡ç»“构体
    /// è¡¨ç¤ºè´§ç‰©æ”¾ç½®ä½ç½®çš„左下前角坐标(X,Y,Z)
    /// åæ ‡ç³»è¯´æ˜Žï¼š
    /// - X轴:沿容器长度方向
    /// - Y轴:沿容器宽度方向
    /// - Z轴:垂直方向(高度)
    /// </summary>
    public struct Point3D
    {
        /// <summary>X轴坐标(毫米)</summary>
        public int X { get; }
        /// <summary>Y轴坐标(毫米)</summary>
        public int Y { get; }
        /// <summary>Z轴坐标(毫米)</summary>
        public int Z { get; }
        public Point3D(int x, int y, int z)
        private bool IsPositionValid(TaskPosition pos)
        {
            X = x;
            Y = y;
            Z = z;
            // è¾¹ç•Œæ£€æŸ¥ï¼ˆå«å®¹å™¨è¾¹ç¼˜é—´éš”)
            //if (pos.X < SPACING ||
            //    pos.Y < SPACING ||
            //    pos.X + l > ContainerSize.Length - SPACING ||
            //    pos.Y + w > ContainerSize.Width - SPACING)
            //    return false;
            return pos.PutPositionY <= MaxY && pos.PutPositionY >= 0 && pos.TakePositionY >= 0;
        }
    }
    /// <summary>
    /// å·²æ”¾ç½®è´§ç‰©ä¿¡æ¯ç±»
    /// è®°å½•每个货物的位置和尺寸信息
    /// åæ ‡ç³»è¯´æ˜Žï¼šä½ç½®ç‚¹ä¸ºè´§ç‰©çš„左下前角坐标
    /// </summary>
    public class PlacedBlock
    {
        /// <summary>货物左下前角坐标</summary>
        public Point3D Position { get; }
        /// <summary>沿X轴方向长度(毫米)</summary>
        public int Length { get; }
        /// <summary>沿Y轴方向宽度(毫米)</summary>
        public int Width { get; }
        /// <summary>沿Z轴方向高度(毫米)</summary>
        public int Height { get; }
        public PlacedBlock(Point3D position, int length, int width, int height)
        public TaskPosition GetTaskPosition(Point3D point3D, int length, int width, int height, int edge)
        {
            Position = position;
            Length = length;
            Width = width;
            Height = height;
        }
    }
            //放货位置板材中心点
            Point3D putCenter = new Point3D(point3D.X + length / 2, point3D.Y + width / 2, point3D.Z + height / 2);
    public struct ContainerSize
    {
        public int Length { get; set; }
        public int Width { get; set; }
        public int Height { get; set; }
            //取货位置板材中心点
            Point3D takeCenter = new Point3D(length / 2, width / 2, height / 2);
        public ContainerSize(int length, int width, int height)
        {
            Length = length;
            Width = width;
            Height = height;
            //吸盘长530 é—´éš”660  æœ€å¤§920 å¸ç›˜å®½130
            int positionR = 1;
            int takePositionX = 0;
            int takePositionY = 0;
            int takePositionZ = 0;
            int putPositionX = 0;
            int putPositionY = 0;
            int putPositionZ = 0;
            //1.如果长度大于920,宽度大于等于300,则可以使用双吸盘横向吸取
            if (length > 920) //横向双吸
            {
                //吸盘尺寸
                Point3D deviceCenter = new Point3D(530 / 2, 920 / 2, 0);
                positionR = 1;
                takePositionX = (takeCenter.Y - deviceCenter.X);
                takePositionY = (takeCenter.X - deviceCenter.Y);
                takePositionZ = 10;
                putPositionX = (putCenter.Y - deviceCenter.X);
                putPositionY = (putCenter.X - deviceCenter.Y);
                putPositionZ = point3D.Z; // putCenter.Z /*+ 10*/;
            }
            else//横向单吸
            {
                //吸盘尺寸
                Point3D deviceCenter = new Point3D(530 / 2, 130 / 2, 0);
                positionR = 1;
                takePositionX = (takeCenter.Y - deviceCenter.X);
                takePositionY = (takeCenter.X - deviceCenter.Y);
                takePositionZ = 10;
                putPositionX = (putCenter.Y - deviceCenter.X);
                putPositionY = (putCenter.X - deviceCenter.Y);
                putPositionZ = point3D.Z; // putCenter.Z /*+ 10*/;
            }
            //1.如果取货位最小Y坐标小于155
            if (takePositionY <= MinY)
            {
                takePositionY = 0;
                putPositionY = point3D.X + MinY;
            }
            else
            {
                takePositionY -= MinY;
            }
            if (putPositionY > MaxY)
            {
                int moreY = putPositionY - MaxY;
                if (takePositionY - moreY > 0)
                {
                    takePositionY -= moreY;
                    putPositionY = MaxY;
                }
                else if (Math.Abs(takePositionY - moreY) < SPACING)
                {
                    takePositionY = 0;
                    putPositionY = Math.Abs(takePositionY - moreY);
                }
                else
                {
                    int count = PlacedBlocks.Where(x => x.Position.Y == 10).Count();
                    //putPositionY -= (920 - 130 + takePositionY - (count + 1) * SPACING * 2);
                    putPositionY = point3D.X - 920 + 130;
                    takePositionY = length - MinY - 130;
                    if (putPositionY < 0 && takePositionY + putPositionY >= 0)
                    {
                        takePositionY += putPositionY;
                        putPositionY = 0;
                    }
                    positionR = 2;
                }
            }
            //横向时,最小Y坐标为155,纵向时,最小Y坐标为350。最大Y坐标为700
            //if (positionR == 1 && putPositionY < MinY)
            //{
            //    takePositionY = 0;
            //    putPositionY = point3D.X + MinY;
            //}
            //else if (positionR == 1 && putPositionY >= MinY && putPositionY <= MaxY)
            //{
            //    if (takePositionY >= MinY)
            //        takePositionY -= MinY;
            //    else
            //    {
            //        putPositionY += MinY - takePositionY;
            //        takePositionY = 0;
            //    }
            //}
            //else if (positionR == 1 && putPositionY > MaxY && putPositionY < 1700)
            //{
            //    int moreY = putPositionY - MaxY;
            //    if (takePositionY - moreY - MinY > 0)
            //    {
            //        takePositionY -= moreY + MinY;
            //        putPositionY = MaxY;
            //    }
            //    else if (Math.Abs(takePositionY - moreY - MinY) < SPACING)
            //    {
            //        if (takePositionY - moreY - MinY > 0)
            //        {
            //            takePositionY -= moreY + MinY;
            //        }
            //        else
            //        {
            //            takePositionY = 0;
            //        }
            //        putPositionY = MaxY;
            //    }
            //    else
            //    {
            //        int count = PlacedBlocks.Where(x => x.Position.Y == 10).Count();
            //        //putPositionY -= (920 - 130 + takePositionY - (count + 1) * SPACING * 2);
            //        putPositionY = point3D.X - 920 + 130;
            //        takePositionY = length - MinY - 130;
            //        if (putPositionY < 0 && takePositionY + putPositionY >= 0)
            //        {
            //            takePositionY += putPositionY;
            //            putPositionY = 0;
            //        }
            //        positionR = 2;
            //    }
            //}
            if (positionR == 2 && edge == 0)
            {
                takePositionX = width - 530;
                putPositionX = point3D.Y;
            }
            else if (positionR == 2 && edge == 1)
            {
                takePositionX = 0;
                putPositionX = point3D.Y + width - 530;
            }
            else if (positionR == 1 && edge == 1)
            {
                takePositionX = width - 530;
                putPositionX = point3D.Y + (width - 530);
            }
            else if (positionR == 1 && edge == 0)
            {
                if (putPositionX < 0)
                {
                    takePositionX = 0;
                    putPositionX = point3D.Y;
                }
            }
            if (takePositionY < 0 && Math.Abs(takePositionY) < SPACING)
            {
                takePositionY = 0;
            }
            TaskPosition taskPosition = new TaskPosition()
            {
                PositionR = positionR,
                TakePositionX = takePositionX,
                TakePositionY = takePositionY,
                TakePositionZ = takePositionZ,
                PutPositionX = putPositionX,
                PutPositionY = putPositionY,
                PutPositionZ = putPositionZ,
                TakeCenterPositionX = takeCenter.X,
                TakeCenterPositionY = takeCenter.Y,
                TakeCenterPositionZ = takeCenter.Z,
                PutCenterPositionX = putCenter.X,
                PutCenterPositionY = putCenter.Y,
                PutCenterPositionZ = putCenter.Z,
                PositionX = point3D.X,
                PositionY = point3D.Y,
                PositionZ = point3D.Z
            };
            return taskPosition;
        }
    }
}