wanshenmean
昨天 05999b9c77f009ac9a7e98366bc3d23fbb8e83e7
feat: 更新数据库连接配置和机器人任务处理逻辑

- 修改appsettings.json中的数据库连接字符串为本地连接
- 移除TcpSocketServer中的客户端消息去重逻辑
- 优化RobotPrefixCommandHandler的任务查询条件
- 在RobotWorkflowOrchestrator中添加发送机器人数量消息的逻辑
- 在StockService中增加幂等写入临时表的逻辑
- 调整ConveyorLineDispatchHandler中的目标地址设置逻辑
- 在RobotSimpleCommandHandler中更新任务完成消息格式
已修改8个文件
174 ■■■■ 文件已修改
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/appsettings.json 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineDispatchHandler.cs 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotPrefixCommandHandler.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotSimpleCommandHandler.cs 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotWorkflowOrchestrator.cs 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Messaging.cs 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_StockService/StockSerivce.cs 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/appsettings.json
@@ -34,7 +34,7 @@
  //5.PostgreSQL
  "DBType": "SqlServer",
  //连接字符串
  "ConnectionString": "Data Source=192.168.60.30;Initial Catalog=WIDESEAWCS_ShanMei;User ID=sa;Password=P@ssw0rd;Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
  "ConnectionString": "Data Source=.;Initial Catalog=WIDESEAWCS_ShanMei;User ID=sa;Password=P@ssw0rd;Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
  //"ConnectionString": "Data Source=.;Initial Catalog=WIDESEAWCS_ShanMei;User ID=sa;Password=123456;Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
  //跨域
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineDispatchHandler.cs
@@ -271,18 +271,19 @@
            Thread.Sleep(100); // 确保 PLC 能正确读取任务号后再写入条码
            var isPalletSet = conveyorLine.SetValue(ConveyorLineDBNameNew.Barcode, task.PalletCode, childDeviceCode);
            bool isTargetSet = true;
            if (targetAddress == "2217" && !isEmptyTask)
            {
                QuartzLogHelper.LogDebug(_logger, $"子设备: {childDeviceCode},出库目标地址: {targetAddress}", conveyorLine.DeviceCode);
                Thread.Sleep(100); // 确保 PLC 能正确读取任务号后再写入条码
                isTargetSet = conveyorLine.SetValue(ConveyorLineDBNameNew.Target, targetAddress, childDeviceCode);
            }
            if (!isTargetSet || !isTaskNoSet || !isPalletSet)
            {
                QuartzLogHelper.LogError(_logger, $"RequestOutbound:下发出库任务失败,任务号: {task.TaskNum},子设备: {childDeviceCode}", conveyorLine.DeviceCode);
                return Task.CompletedTask;
            }
            bool isTargetSet = conveyorLine.SetValue(ConveyorLineDBNameNew.Target, targetAddress, childDeviceCode);
            //if (targetAddress == "2217" && !isEmptyTask)
            //{
            //    QuartzLogHelper.LogDebug(_logger, $"子设备: {childDeviceCode},出库目标地址: {targetAddress}", conveyorLine.DeviceCode);
            //    Thread.Sleep(100); // 确保 PLC 能正确读取任务号后再写入条码
            //    isTargetSet = conveyorLine.SetValue(ConveyorLineDBNameNew.Target, targetAddress, childDeviceCode);
            //}
            //if (!isTargetSet || !isTaskNoSet || !isPalletSet)
            //{
            //    QuartzLogHelper.LogError(_logger, $"RequestOutbound:下发出库任务失败,任务号: {task.TaskNum},子设备: {childDeviceCode}", conveyorLine.DeviceCode);
            //    return Task.CompletedTask;
            //}
            bool isWmsResult = false;
            // 更新任务状态或位置
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs
@@ -1,3 +1,4 @@
using Masuit.Tools;
using Newtonsoft.Json;
using Serilog;
using WIDESEA_Core;
@@ -137,6 +138,16 @@
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="robotCrane"></param>
        /// <returns></returns>
        public Dt_RobotTask? GetRobotTask(RobotCraneDevice robotCrane)
        {
            return _robotTaskService.Repository.QueryFirst(x => x.RobotRoadway == robotCrane.DeviceCode);
        }
        /// <summary>
        /// 删除机器人任务
        /// </summary>
        /// <remarks>
@@ -199,6 +210,33 @@
                    // 发送失败,记录 Error 日志
                    QuartzLogHelper.LogError(_logger, $"下发取货指令失败,指令: {taskString},设备: {state.RobotCrane?.DeviceName}", state.RobotCrane?.DeviceName);
                }
            }
        }
        public async Task SendSocketRobotNumAsync(Dt_RobotTask task, RobotSocketState state, bool isPick = true)
        {
            string taskString = string.Empty;
            if (isPick)
                // 构建指令,格式:PickTotalNum,{数量}||PutTotalNum,{数量}
                taskString = $"PickTotalNum,{task.RobotTaskTotalNum + state.RobotTaskTotalNum}";
            else
                // 构建指令,格式:PutTotalNum,{数量}||PutTotalNum,{数量}
                taskString = $"PutTotalNum,{task.RobotTaskTotalNum + state.RobotTaskTotalNum}";
            // 通过 Socket 网关发送指令到机器人客户端
            bool result = await _socketClientGateway.SendToClientAsync(state.IPAddress, taskString);
            if (result)
            {
                // 发送成功,记录 Info 日志
                QuartzLogHelper.LogInfo(_logger, $"下发总数指令成功,指令: {taskString},设备: {state.RobotCrane?.DeviceName}", state.RobotCrane?.DeviceName);
                //await _robotTaskService.UpdateRobotTaskAsync(task);
            }
            else
            {
                // 发送失败,记录 Error 日志
                QuartzLogHelper.LogError(_logger, $"下发总数指令失败,指令: {taskString},设备: {state.RobotCrane?.DeviceName}", state.RobotCrane?.DeviceName);
            }
        }
@@ -477,16 +515,18 @@
                    case RobotTaskTypeEnum.GroupPallet:
                        warehouseId = 1;
                        roadway = "GWSC1";
                        SourceAddress = currentTask.RobotSourceAddressLineCode;
                        TargetAddress = currentTask.RobotTargetAddressLineCode;
                        break;
                    case RobotTaskTypeEnum.ChangePallet:
                        // 换盘/拆盘场景:托盘需要入库
                        taskType = TaskTypeEnum.InEmpty.GetHashCode();  // 空托盘入库
                        PalletCode = currentTask.RobotSourceAddressPalletCode;  // 使用源地址的托盘码
                        if (isRoadway == "HWSC1")
                        if (isRoadway == "HCSC1")
                        {
                            warehouseId = 2;
                            roadway = "HWSC1";
                            roadway = "HCSC1";
                        }
                        else if (isRoadway == "GWSC1")
                        {
@@ -494,6 +534,8 @@
                            roadway = "GWSC1";
                        }
                        SourceAddress = currentTask.RobotSourceAddressLineCode;
                        TargetAddress = currentTask.RobotTargetAddressLineCode;
                        break;
                    case RobotTaskTypeEnum.SplitPallet:
                        // 换盘/拆盘场景:托盘需要入库
@@ -502,6 +544,9 @@
                        warehouseId = 3;
                        roadway = "CWSC1";
                        SourceAddress = currentTask.RobotSourceAddressLineCode;
                        TargetAddress = currentTask.RobotTargetAddressLineCode;
                        break;
                }
            }
@@ -515,10 +560,10 @@
                        taskType = TaskTypeEnum.Inbound.GetHashCode();  // 成品入库
                        PalletCode = currentTask.RobotTargetAddressPalletCode;  // 使用目标地址的托盘码
                        if (isRoadway == "HWSC1")
                        if (isRoadway == "HCSC1")
                        {
                            warehouseId = 2;
                            roadway = "HWSC1";
                            roadway = "HCSC1";
                        }
                        else if (isRoadway == "GWSC1")
                        {
@@ -639,7 +684,7 @@
                        // 电池条码:如果状态中有条码列表,取对应位置的条码;否则为空
                        //CellBarcode = state.CellBarcode?.Count > 0 ? state.CellBarcode[x - 1] : ""
                        CellBarcode = state.CellBarcode[idx].ToString()
                        CellBarcode = !state.CellBarcode.IsNullOrEmpty() ? state.CellBarcode[idx].ToString() ?? string.Empty : string.Empty
                    })
                    .ToList()
            };
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotPrefixCommandHandler.cs
@@ -153,7 +153,7 @@
                    .ToArray();
                // 从数据库重新查询当前任务(确保获取最新状态)
                var task = await _robotTaskService.Repository.QueryFirstAsync(x => x.RobotTaskState == TaskRobotStatusEnum.RobotExecuting.GetHashCode() && x.RobotRoadway == state.RobotCrane.DeviceName);
                var task = await _robotTaskService.Repository.QueryFirstAsync(x => /*x.RobotTaskState == TaskRobotStatusEnum.RobotExecuting.GetHashCode() &&*/ x.RobotRoadway == state.RobotCrane.DeviceName);
                if (task != null)
                {
@@ -319,7 +319,7 @@
                            task.RobotTaskTotalNum -= positions.Length;
                        var stockDTO = RobotTaskProcessor.BuildStockDTO(state, positions);
                        var result = _taskProcessor.PostGroupPalletAsync(nameof(ConfigKey.GroupPalletAsync), stockDTO);
                        var result = _taskProcessor.PostGroupPalletAsync(nameof(ConfigKey.ChangePalletAsync), stockDTO);
                        putSuccess = result.Data.Status && result.IsSuccess;
                    }
                }
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotSimpleCommandHandler.cs
@@ -159,6 +159,8 @@
                    var isResult = UpdateStatus(state, true);
                    if (!isResult)
                        return false;
                    await _socketClientGateway.SendToClientAsync(state.IPAddress, "pickbatteryover");
                    return true;
                // 放货接收
@@ -166,6 +168,8 @@
                    isResult = UpdateStatus(state, false);
                    if (!isResult)
                        return false;
                    await _socketClientGateway.SendToClientAsync(state.IPAddress, "putbatteryover");
                    return true;
                // ==================== 全部完成命令 ====================
@@ -213,8 +217,8 @@
                                    return false;
                                }
                                await _socketClientGateway.SendToClientAsync(state.IPAddress, $"Swap,diskFinished");
                                QuartzLogHelper.LogInfo(_logger, $"发送消息:【Swap,diskFinished】", state.RobotCrane.DeviceName);
                                await _socketClientGateway.SendToClientAsync(state.IPAddress, $"Group,diskFinished");
                                QuartzLogHelper.LogInfo(_logger, $"发送消息:【Group,diskFinished】", state.RobotCrane.DeviceName);
                                state.CurrentTask = null;
                                state.RobotTaskTotalNum = 0;
@@ -242,14 +246,14 @@
                                return false;
                            }
                            if (_taskProcessor.DeleteTask(currentTask.RobotTaskId) != true)
                            {
                                QuartzLogHelper.LogError(_logger, $"allpickfinished:删除任务记录失败,任务号: {currentTask.RobotTaskNum}", state.RobotCrane?.DeviceName ?? "Unknown");
                                return false;
                            }
                            //if (_taskProcessor.DeleteTask(currentTask.RobotTaskId) != true)
                            //{
                            //    QuartzLogHelper.LogError(_logger, $"allpickfinished:删除任务记录失败,任务号: {currentTask.RobotTaskNum}", state.RobotCrane?.DeviceName ?? "Unknown");
                            //    return false;
                            //}
                            await _socketClientGateway.SendToClientAsync(state.IPAddress, $"Swap,diskFinished");
                            QuartzLogHelper.LogInfo(_logger, $"发送消息:【Swap,diskFinished】", state.RobotCrane.DeviceName);
                            await _socketClientGateway.SendToClientAsync(state.IPAddress, $"Group,diskFinished");
                            QuartzLogHelper.LogInfo(_logger, $"发送消息:【Group,diskFinished】", state.RobotCrane.DeviceName);
                            state.ChangePalletPhase = 0;
                            state.CurrentBatchIndex = 1;
@@ -281,8 +285,8 @@
                                return false;
                            }
                            await _socketClientGateway.SendToClientAsync(state.IPAddress, $"Swap,diskFinished");
                            QuartzLogHelper.LogInfo(_logger, $"发送消息:【Swap,diskFinished】", state.RobotCrane.DeviceName);
                            await _socketClientGateway.SendToClientAsync(state.IPAddress, $"Group,diskFinished");
                            QuartzLogHelper.LogInfo(_logger, $"发送消息:【Group,diskFinished】", state.RobotCrane.DeviceName);
                            return true;
                        }
                        return false;
@@ -334,8 +338,8 @@
                                state.RobotTaskTotalNum = 0;
                                state.CellBarcode = new List<string>();
                                await _socketClientGateway.SendToClientAsync(state.IPAddress, $"Swap,diskFinished");
                                QuartzLogHelper.LogInfo(_logger, $"发送消息:【Swap,diskFinished】", state.RobotCrane.DeviceName);
                                await _socketClientGateway.SendToClientAsync(state.IPAddress, $"Group,diskFinished");
                                QuartzLogHelper.LogInfo(_logger, $"发送消息:【Group,diskFinished】", state.RobotCrane.DeviceName);
                                state.ChangePalletPhase = 0;
                                state.CurrentBatchIndex = 1;
@@ -370,8 +374,8 @@
                            state.RobotTaskTotalNum = 0;
                            state.CellBarcode = new List<string>();
                            await _socketClientGateway.SendToClientAsync(state.IPAddress, $"Swap,diskFinished");
                            QuartzLogHelper.LogInfo(_logger, $"发送消息:【Swap,diskFinished】", state.RobotCrane.DeviceName);
                            await _socketClientGateway.SendToClientAsync(state.IPAddress, $"Group,diskFinished");
                            QuartzLogHelper.LogInfo(_logger, $"发送消息:【Group,diskFinished】", state.RobotCrane.DeviceName);
                            state.ChangePalletPhase = 0;
                            state.CurrentBatchIndex = 1;
@@ -461,14 +465,14 @@
        public bool UpdateStatus(RobotSocketState state, bool isPick)
        {
            var task = _taskProcessor.GetTask(state?.RobotCrane);
            var task = _taskProcessor.GetRobotTask(state?.RobotCrane);
            if (task == null)
            {
                QuartzLogHelper.LogError(_logger, $"取货接收失败: 未找到【{state?.RobotCrane}】的任务", state.RobotCrane?.DeviceName ?? "Unknown");
                return false;
            }
            task.RobotTaskState = (int)TaskRobotStatusEnum.RobotExecuting;
            _taskProcessor.UpdateRobotTask(task);
            return true;
            return _taskProcessor.UpdateRobotTask(task);
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotWorkflowOrchestrator.cs
@@ -169,6 +169,9 @@
                bool isFlowA = task.RobotSourceAddressLineCode is "11001" or "11010";
                await _taskProcessor.SendSocketRobotNumAsync(task, state, false);
                Thread.Sleep(500);
                // ==================== Phase 2: 放正常电芯到目标托盘(两流向相同)====================
                // PickFinished 到达:Phase 1 的 Pick 命令完成,现在下发 Put 命令放正常电芯
                if (state?.ChangePalletPhase == 2)
@@ -239,8 +242,6 @@
                    return;
                }
                // 非批次模式或其他阶段不下发指令
                return;
            }
            // 非换盘任务:使用原有格式
@@ -389,8 +390,11 @@
                // 判断流向(null-safe)
                bool isFlowA = task.RobotSourceAddressLineCode is "11001" or "11010";
                await _taskProcessor.SendSocketRobotNumAsync(task, stateForUpdate);
                Thread.Sleep(500);
                // 目标数量为48:直接走原有逻辑,不进入批次模式
                if (targetNormalCount == targetTotal)
                if (targetNormalCount + currentCompletedCount == targetTotal)
                {
                    await _taskProcessor.SendSocketRobotPickAsync(task, stateForUpdate);
                    return;
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Messaging.cs
@@ -60,14 +60,14 @@
                        }
                        // 按客户端去重:检查是否与该客户端上次消息相同
                        lock (_syncRoot)
                        {
                            if (_clientLastMessage.TryGetValue(clientId, out var prev) && message == prev)
                            {
                                continue;
                            }
                            _clientLastMessage[clientId] = message;
                        }
                        //lock (_syncRoot)
                        //{
                        //    if (_clientLastMessage.TryGetValue(clientId, out var prev) && message == prev)
                        //    {
                        //        continue;
                        //    }
                        //    _clientLastMessage[clientId] = message;
                        //}
                        // 更新客户端状态
                        UpdateClientStatus(clientId, message);
Code/WMS/WIDESEA_WMSServer/WIDESEA_StockService/StockSerivce.cs
@@ -209,6 +209,30 @@
                return await ExecuteWithinTransactionAsync(async () =>
                {
                    // 幂等写入:检查临时表是否已有该托盘记录,无则写入
                    var existingTemp = SqlSugarClient.Queryable<Dt_SplitTemp>()
                        .Where(t => t.PalletCode == stock.SourcePalletNo)
                        .First();
                    if (existingTemp == null)
                    {
                        // 查询该托盘当前所有电芯,存入临时表
                        var sourceStockForTemp = StockInfoService.Repository.QueryFirst(s => s.PalletCode == stock.SourcePalletNo);
                        if (sourceStockForTemp != null)
                        {
                            var allDetails = StockInfoDetailService.Repository.QueryData(d => d.StockId == sourceStockForTemp.Id);
                            if (allDetails != null && allDetails.Any())
                            {
                                var sfcListJson = JsonConvert.SerializeObject(allDetails.Select(d => d.SerialNumber).ToList());
                                await SqlSugarClient.Insertable(new Dt_SplitTemp
                                {
                                    PalletCode = stock.SourcePalletNo,
                                    SfcList = sfcListJson,
                                    CreateTime = DateTime.Now
                                }).ExecuteCommandAsync();
                            }
                        }
                    }
                    var sourceStock = await StockInfoService.Repository.QueryDataNavFirstAsync(s => s.PalletCode == stock.SourcePalletNo);
                    if (sourceStock == null) return content.Error("源托盘不存在");