From 5bf10c1dafe485d506ec534f98e5220a3b83dd17 Mon Sep 17 00:00:00 2001
From: wanshenmean <cathay_xy@163.com>
Date: 星期四, 16 四月 2026 23:16:46 +0800
Subject: [PATCH] feat(WCS&WMS): 机械手扫码NG处理与线体条码读取与添加批量MES绑定解绑接口

---
 Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs |  203 ++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 178 insertions(+), 25 deletions(-)

diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs
index fb5ef53..c50f942 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs
@@ -164,7 +164,8 @@
         /// </remarks>
         /// <param name="task">瑕佷笅鍙戠殑浠诲姟瀵硅薄</param>
         /// <param name="state">鏈哄櫒浜哄綋鍓嶇姸鎬�</param>
-        public async Task SendSocketRobotPickAsync(Dt_RobotTask task, RobotSocketState state)
+        /// <param name="isScanNG">鏄惁鎵爜NG</param>
+        public async Task SendSocketRobotPickAsync(Dt_RobotTask task, RobotSocketState state, bool isScanNG)
         {
             // 鏋勫缓鍙栬揣鎸囦护锛屾牸寮忥細Pickbattery,{婧愬湴鍧�}
             string taskString = $"Pickbattery,{task.RobotSourceAddress}";
@@ -183,6 +184,11 @@
 
                 // 灏嗕换鍔″叧鑱斿埌鐘舵�佸璞�
                 state.CurrentTask = task;
+
+                if(isScanNG)
+                {
+                    state.IsScanNG = true;
+                }
 
                 // 淇濇寔鍘熻涔夛細浠呭湪鐘舵�佸畨鍏ㄥ啓鍏ユ垚鍔熷悗鍐嶆洿鏂颁换鍔$姸鎬�
                 // 杩欐牱鍙互纭繚鐘舵�佸拰浠诲姟璁板綍鐨勪竴鑷存��
@@ -265,6 +271,125 @@
         public List<int> GetNextAvailableFakeBatteryPositions(int count)
         {
             return _fakeBatteryPositionService.GetNextAvailable(count);
+        }
+
+        /// <summary>
+        /// 璁$畻鎵规缂栧彿鑼冨洿
+        /// </summary>
+        /// <remarks>
+        /// 杩斿洖鏍煎紡锛�(start, end)
+        /// - remaining >= 4: (currentIndex, currentIndex + 3)
+        /// - remaining > 1: (currentIndex, currentIndex + remaining - 1)
+        /// - remaining == 1: (currentIndex, 0)  -- 鍗曚釜鐗╁搧鐢� 0 琛ㄧず end
+        /// </remarks>
+        /// <param name="currentIndex">褰撳墠鎵规璧峰缂栧彿</param>
+        /// <param name="remaining">鍓╀綑鏁伴噺</param>
+        /// <returns>(start, end) 鍏冪粍</returns>
+        public (int Start, int End) BuildBatchRange(int currentIndex, int remaining)
+        {
+            if (remaining >= 4)
+                return (currentIndex, currentIndex + 3);
+            else if (remaining > 1)
+                return (currentIndex, currentIndex + remaining - 1);
+            else  // remaining == 1
+                return (currentIndex, 0);
+        }
+
+        /// <summary>
+        /// 涓嬪彂鍙栬揣鎸囦护锛堝甫鎵规鏍煎紡鍜屾�绘暟锛�
+        /// </summary>
+        /// <remarks>
+        /// 鍙戦�侀『搴忥細
+        /// 1. PickTotalNum,{N} -- 鐪熷疄鐢佃姱鎬绘暟
+        /// 2. Pickbattery,{浣嶇疆},{start}-{end} -- 鎵规鍙栬揣鎸囦护
+        ///
+        /// 涓嬪彂鎴愬姛鍚庢洿鏂颁换鍔$姸鎬佷负"鏈哄櫒浜烘墽琛屼腑"銆�
+        /// </remarks>
+        /// <param name="task">瑕佷笅鍙戠殑浠诲姟瀵硅薄</param>
+        /// <param name="state">鏈哄櫒浜哄綋鍓嶇姸鎬�</param>
+        /// <param name="position">鍙栬揣浣嶇疆</param>
+        /// <param name="batchStart">鎵规璧峰缂栧彿</param>
+        /// <param name="batchEnd">鎵规缁撴潫缂栧彿</param>
+        public async Task SendPickWithBatchAsync(Dt_RobotTask task, RobotSocketState state, string position, int batchStart, int batchEnd)
+        {
+            // 鍏堝彂閫佹�绘暟鎸囦护
+            string totalNumCmd = $"PickTotalNum,{task.RobotTaskTotalNum}";
+            await _socketClientGateway.SendToClientAsync(state.IPAddress, totalNumCmd);
+
+            // 鍐嶅彂閫佹壒娆″彇璐ф寚浠�
+            string range = batchEnd == 0 ? $"{batchStart}-0" : $"{batchStart}-{batchEnd}";
+            string taskString = $"Pickbattery,{position},{range}";
+
+            bool result = await _socketClientGateway.SendToClientAsync(state.IPAddress, taskString);
+
+            if (result)
+            {
+                _logger.LogInformation("涓嬪彂鎵规鍙栬揣鎸囦护鎴愬姛锛屾寚浠�: {TaskString}锛屾壒娆�: {Range}锛岃澶�: {DeviceName}",
+                    taskString, range, state.RobotCrane?.DeviceName);
+                QuartzLogger.Info($"涓嬪彂鎵规鍙栬揣鎸囦护鎴愬姛锛屾寚浠�: {taskString}锛屾壒娆�: {range}", state.RobotCrane?.DeviceName);
+
+                task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode();
+                state.CurrentTask = task;
+
+                if (_stateManager.TryUpdateStateSafely(state.IPAddress, state))
+                {
+                    await _robotTaskService.UpdateRobotTaskAsync(task);
+                }
+            }
+            else
+            {
+                _logger.LogError("涓嬪彂鎵规鍙栬揣鎸囦护澶辫触锛屾寚浠�: {TaskString}锛岃澶�: {DeviceName}", taskString, state.RobotCrane?.DeviceName);
+                QuartzLogger.Error($"涓嬪彂鎵规鍙栬揣鎸囦护澶辫触锛屾寚浠�: {taskString}", state.RobotCrane?.DeviceName);
+            }
+        }
+
+        /// <summary>
+        /// 涓嬪彂鏀捐揣鎸囦护锛堝甫鎵规鏍煎紡鍜屾�绘暟锛�
+        /// </summary>
+        /// <remarks>
+        /// 鍙戦�侀『搴忥細
+        /// 1. PutTotalNum,{N} -- 鐪熷疄鐢佃姱鎬绘暟
+        /// 2. Putbattery,{浣嶇疆},{start}-{end} -- 鎵规鏀捐揣鎸囦护
+        ///
+        /// 涓嬪彂鎴愬姛鍚庢洿鏂颁换鍔$姸鎬佷负"鏈哄櫒浜烘墽琛屼腑"銆�
+        /// </remarks>
+        /// <param name="task">瑕佷笅鍙戠殑浠诲姟瀵硅薄</param>
+        /// <param name="state">鏈哄櫒浜哄綋鍓嶇姸鎬�</param>
+        /// <param name="position">鏀捐揣浣嶇疆</param>
+        /// <param name="batchStart">鎵规璧峰缂栧彿</param>
+        /// <param name="batchEnd">鎵规缁撴潫缂栧彿</param>
+        public async Task SendPutWithBatchAsync(Dt_RobotTask task, RobotSocketState state, string position, int batchStart, int batchEnd)
+        {
+            // 鍏堝彂閫佹�绘暟鎸囦护
+            string totalNumCmd = $"PutTotalNum,{task.RobotTaskTotalNum}";
+            await _socketClientGateway.SendToClientAsync(state.IPAddress, totalNumCmd);
+
+            // 鍐嶅彂閫佹壒娆℃斁璐ф寚浠�
+            string range = batchEnd == 0 ? $"{batchStart}-0" : $"{batchStart}-{batchEnd}";
+            string taskString = $"Putbattery,{position},{range}";
+
+            bool result = await _socketClientGateway.SendToClientAsync(state.IPAddress, taskString);
+
+            if (result)
+            {
+                _logger.LogInformation("涓嬪彂鏀捐揣鎸囦护鎴愬姛锛屾寚浠�: {TaskString}锛屾壒娆�: {Range}锛岃澶�: {DeviceName}",
+                    taskString, range, state.RobotCrane?.DeviceName);
+                QuartzLogger.Info($"涓嬪彂鏀捐揣鎸囦护鎴愬姛锛屾寚浠�: {taskString}锛屾壒娆�: {range}", state.RobotCrane?.DeviceName);
+
+                task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode();
+                state.CurrentTask = task;
+
+                if (_stateManager.TryUpdateStateSafely(state.IPAddress, state))
+                {
+                    await _robotTaskService.UpdateRobotTaskAsync(task);
+                }
+            }
+            else
+            {
+                _logger.LogError("涓嬪彂鏀捐揣鎸囦护澶辫触锛屾寚浠�: {TaskString}锛岃澶�: {DeviceName}",
+                    taskString, state.RobotCrane?.DeviceName);
+                QuartzLogger.Error($"涓嬪彂鏀捐揣鎸囦护澶辫触锛屾寚浠�: {taskString}", state.RobotCrane?.DeviceName);
+            }
         }
 
         /// <summary>
@@ -395,37 +520,37 @@
             }
 
             // 瑙f瀽杩斿洖鐨勪换鍔′俊鎭�
-            var taskInfos = JsonConvert.DeserializeObject<List<Dt_Task>>(content.Data.ToJson() ?? string.Empty) ?? new List<Dt_Task>();
-            var taskInfo = taskInfos.FirstOrDefault();
+            //var taskInfos = JsonConvert.DeserializeObject<List<Dt_Task>>(content.Data.ToJson() ?? string.Empty) ?? new List<Dt_Task>();
+            //var taskInfo = taskInfos.FirstOrDefault();
 
-            // 鑾峰彇婧愬湴鍧�
-            string sourceAddress = taskDTO.SourceAddress;
+            //// 鑾峰彇婧愬湴鍧�
+            //string sourceAddress = taskDTO.SourceAddress;
 
-            // 鏌ユ壘婧愬湴鍧�瀵瑰簲鐨勮緭閫佺嚎璁惧
-            IDevice? device = Storage.Devices.FirstOrDefault(x => x.DeviceProDTOs.Any(d => d.DeviceChildCode == sourceAddress));
+            //// 鏌ユ壘婧愬湴鍧�瀵瑰簲鐨勮緭閫佺嚎璁惧
+            //IDevice? device = Storage.Devices.FirstOrDefault(x => x.DeviceProDTOs.Any(d => d.DeviceChildCode == sourceAddress));
 
-            if (device != null)
-            {
-                // 灏嗚澶囪浆鎹负杈撻�佺嚎绫诲瀷
-                CommonConveyorLine conveyorLine = (CommonConveyorLine)device;
+            //if (device != null)
+            //{
+            //    // 灏嗚澶囪浆鎹负杈撻�佺嚎绫诲瀷
+            //    CommonConveyorLine conveyorLine = (CommonConveyorLine)device;
 
-                // 璁剧疆杈撻�佺嚎鐨勭洰鏍囧湴鍧�
-                conveyorLine.SetValue(ConveyorLineDBNameNew.Target, taskInfo.NextAddress, sourceAddress);
+            //    // 璁剧疆杈撻�佺嚎鐨勭洰鏍囧湴鍧�
+            //    conveyorLine.SetValue(ConveyorLineDBNameNew.Target, taskInfo.NextAddress, sourceAddress);
 
-                // 璁剧疆杈撻�佺嚎鐨勪换鍔″彿
-                conveyorLine.SetValue(ConveyorLineDBNameNew.TaskNo, taskInfo.TaskNum, sourceAddress);
+            //    // 璁剧疆杈撻�佺嚎鐨勪换鍔″彿
+            //    conveyorLine.SetValue(ConveyorLineDBNameNew.TaskNo, taskInfo.TaskNum, sourceAddress);
 
-                // 瑙﹀彂杈撻�佺嚎寮�濮嬫墽琛岋紙鍐欏叆 WCS_ACK = 1锛�
-                conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)1, sourceAddress);
+            //    // 瑙﹀彂杈撻�佺嚎寮�濮嬫墽琛岋紙鍐欏叆 WCS_ACK = 1锛�
+            //    conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)1, sourceAddress);
 
-                // 鏇存柊浠诲姟鐘舵�佸埌涓嬩竴闃舵
-                if (_taskService.UpdateTaskStatusToNext(taskInfo).Status)
-                {
-                    _logger.LogInformation("HandleInboundTaskAsync锛氬叆搴撲换鍔″鐞嗘垚鍔燂紝浠诲姟鍙�: {TaskNum}", taskInfo.TaskNum);
-                    QuartzLogger.Info($"HandleInboundTaskAsync锛氬叆搴撲换鍔″鐞嗘垚鍔燂紝浠诲姟鍙�: {taskInfo.TaskNum}", state.RobotCrane?.DeviceName ?? "Unknown");
-                    return true;
-                }
-            }
+            //    // 鏇存柊浠诲姟鐘舵�佸埌涓嬩竴闃舵
+            //    if (_taskService.UpdateTaskStatusToNext(taskInfo).Status)
+            //    {
+            //        _logger.LogInformation("HandleInboundTaskAsync锛氬叆搴撲换鍔″鐞嗘垚鍔燂紝浠诲姟鍙�: {TaskNum}", taskInfo.TaskNum);
+            //        QuartzLogger.Info($"HandleInboundTaskAsync锛氬叆搴撲换鍔″鐞嗘垚鍔燂紝浠诲姟鍙�: {taskInfo.TaskNum}", state.RobotCrane?.DeviceName ?? "Unknown");
+            //        return true;
+            //    }
+            //}
 
             return false;
         }
@@ -513,5 +638,33 @@
         {
             return _httpClientHelper.Post<WebResponseContent>(configKey, stockDTO.ToJson());
         }
+
+        /// <summary>
+        /// 璋冪敤鎵归噺鎷嗙洏纭 API
+        /// </summary>
+        /// <remarks>
+        /// 褰撴媶鐩樹换鍔″叏閮ㄥ彇瀹屾椂璋冪敤锛屼竴娆℃�т笂浼犳暣涓墭鐩樼殑瑙g粦鏁版嵁鍒� MES銆�
+        /// </remarks>
+        /// <param name="palletCode">婧愭墭鐩樺彿</param>
+        /// <returns>HTTP 鍝嶅簲缁撴灉</returns>
+        public HttpResponseResult<WebResponseContent> PostSplitPalletConfirmAsync(string palletCode)
+        {
+            var request = new { PalletCode = palletCode };
+            return _httpClientHelper.Post<WebResponseContent>(nameof(ConfigKey.SplitPalletConfirm), request.ToJson());
+        }
+
+        /// <summary>
+        /// 璋冪敤鎵归噺缁勭洏纭 API
+        /// </summary>
+        /// <remarks>
+        /// 褰撶粍鐩樹换鍔″叏閮ㄦ斁瀹屾椂璋冪敤锛屼竴娆℃�т笂浼犳暣涓墭鐩樼殑缁戝畾鏁版嵁鍒� MES銆�
+        /// </remarks>
+        /// <param name="palletCode">鐩爣鎵樼洏鍙�</param>
+        /// <returns>HTTP 鍝嶅簲缁撴灉</returns>
+        public HttpResponseResult<WebResponseContent> PostGroupPalletConfirmAsync(string palletCode)
+        {
+            var request = new { PalletCode = palletCode };
+            return _httpClientHelper.Post<WebResponseContent>(nameof(ConfigKey.GroupPalletConfirm), request.ToJson());
+        }
     }
 }

--
Gitblit v1.9.3