feat: 更新数据库连接配置和机器人任务处理逻辑
- 修改appsettings.json中的数据库连接字符串为本地连接
- 移除TcpSocketServer中的客户端消息去重逻辑
- 优化RobotPrefixCommandHandler的任务查询条件
- 在RobotWorkflowOrchestrator中添加发送机器人数量消息的逻辑
- 在StockService中增加幂等写入临时表的逻辑
- 调整ConveyorLineDispatchHandler中的目标地址设置逻辑
- 在RobotSimpleCommandHandler中更新任务完成消息格式
| | |
| | | //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", |
| | | |
| | | //跨域 |
| | |
| | | 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; |
| | | // 更新任务状态或位置 |
| | |
| | | using Masuit.Tools;
|
| | | using Newtonsoft.Json; |
| | | using Serilog; |
| | | using WIDESEA_Core; |
| | |
| | | } |
| | | |
| | | /// <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> |
| | |
| | | // 发送失败,记录 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);
|
| | | } |
| | | } |
| | | |
| | |
| | | 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") |
| | | { |
| | |
| | | roadway = "GWSC1"; |
| | | } |
| | | |
| | | SourceAddress = currentTask.RobotSourceAddressLineCode;
|
| | | TargetAddress = currentTask.RobotTargetAddressLineCode;
|
| | | break; |
| | | case RobotTaskTypeEnum.SplitPallet: |
| | | // 换盘/拆盘场景:托盘需要入库 |
| | |
| | | |
| | | warehouseId = 3; |
| | | roadway = "CWSC1"; |
| | |
|
| | | SourceAddress = currentTask.RobotSourceAddressLineCode;
|
| | | TargetAddress = currentTask.RobotTargetAddressLineCode;
|
| | | break; |
| | | } |
| | | } |
| | |
| | | taskType = TaskTypeEnum.Inbound.GetHashCode(); // 成品入库 |
| | | PalletCode = currentTask.RobotTargetAddressPalletCode; // 使用目标地址的托盘码 |
| | | |
| | | if (isRoadway == "HWSC1") |
| | | if (isRoadway == "HCSC1")
|
| | | { |
| | | warehouseId = 2; |
| | | roadway = "HWSC1"; |
| | | roadway = "HCSC1";
|
| | | } |
| | | else if (isRoadway == "GWSC1") |
| | | { |
| | |
| | | |
| | | // 电池条码:如果状态中有条码列表,取对应位置的条码;否则为空 |
| | | //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() |
| | | }; |
| | |
| | | .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) |
| | | { |
| | |
| | | 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; |
| | | } |
| | | } |
| | |
| | | var isResult = UpdateStatus(state, true); |
| | | if (!isResult) |
| | | return false; |
| | | |
| | | await _socketClientGateway.SendToClientAsync(state.IPAddress, "pickbatteryover"); |
| | | return true; |
| | | |
| | | // 放货接收 |
| | |
| | | isResult = UpdateStatus(state, false); |
| | | if (!isResult) |
| | | return false; |
| | | |
| | | await _socketClientGateway.SendToClientAsync(state.IPAddress, "putbatteryover"); |
| | | return true; |
| | | |
| | | // ==================== 全部完成命令 ==================== |
| | |
| | | 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; |
| | |
| | | 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; |
| | |
| | | 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; |
| | |
| | | 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; |
| | |
| | | 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; |
| | |
| | | |
| | | 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); |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | 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) |
| | |
| | | return; |
| | | } |
| | | |
| | | // 非批次模式或其他阶段不下发指令 |
| | | return; |
| | | } |
| | | |
| | | // 非换盘任务:使用原有格式 |
| | |
| | | // 判断流向(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; |
| | |
| | | } |
| | | |
| | | // 按客户端去重:检查是否与该客户端上次消息相同 |
| | | 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); |
| | |
| | | |
| | | 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("源托盘不存在"); |
| | | |