refactor: modularize WIDESEAWCS_Tasks workflows
| | |
| | | #region << ç æ¬ 注 é >> |
| | | #region << ç æ¬ 注 é >> |
| | | |
| | | /*---------------------------------------------------------------- |
| | | * å½å空é´ï¼WIDESEAWCS_Tasks.ConveyorLineJob |
| | | * å建è
ï¼è¡ç«¥åº |
| | | * å建æ¶é´ï¼2024/8/2 16:13:36 |
| | | * çæ¬ï¼V1.0.0 |
| | | * æè¿°ï¼ |
| | | * å½å空é´ï¼WIDESEAWCS_Tasks.ConveyorLineJob |
| | | * å建è
ï¼è¡ç«¥åº |
| | | * å建æ¶é´ï¼2024/8/2 16:13:36 |
| | | * çæ¬ï¼V1.0.0 |
| | | * æè¿°ï¼ |
| | | * |
| | | * ---------------------------------------------------------------- |
| | | * ä¿®æ¹äººï¼ |
| | | * ä¿®æ¹æ¶é´ï¼ |
| | | * çæ¬ï¼V1.0.1 |
| | | * ä¿®æ¹è¯´æï¼ |
| | | * ä¿®æ¹äººï¼ |
| | | * ä¿®æ¹æ¶é´ï¼ |
| | | * çæ¬ï¼V1.0.1 |
| | | * ä¿®æ¹è¯´æï¼ |
| | | * |
| | | *----------------------------------------------------------------*/ |
| | | |
| | | #endregion << ç æ¬ 注 é >> |
| | | #endregion << ç æ¬ 注 é >> |
| | | |
| | | using AutoMapper; |
| | | using System.Data; |
| | | using WIDESEAWCS_Common.TaskEnum; |
| | | using WIDESEAWCS_Core; |
| | | using WIDESEAWCS_Core.Helper; |
| | |
| | | private readonly IRouterService _routerService; |
| | | private readonly IMapper _mapper; |
| | | |
| | | private readonly ConveyorLineTaskFilter _taskFilter; |
| | | private readonly ConveyorLineTargetAddressSelector _targetAddressSelector; |
| | | |
| | | public ConveyorLineDispatchHandler(ITaskService taskService, ITaskExecuteDetailService taskExecuteDetailService, IRouterService routerService, IMapper mapper) |
| | | { |
| | | _taskService = taskService; |
| | | _taskExecuteDetailService = taskExecuteDetailService; |
| | | _routerService = routerService; |
| | | _mapper = mapper; |
| | | |
| | | _taskFilter = new ConveyorLineTaskFilter(taskService); |
| | | _targetAddressSelector = new ConveyorLineTargetAddressSelector(); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å¿è·³å¤ç |
| | | /// å¿è·³å¤ç |
| | | /// </summary> |
| | | /// <param name="conveyorLine"></param> |
| | | /// <param name="command"></param> |
| | | /// <param name="childDeviceCode"></param> |
| | | public void HeartBeat(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode) |
| | | { |
| | | //å¿è·³å¤çé»è¾ |
| | | conveyorLine.SetValue(ConveyorLineDBNameNew.TaskNo, 0, childDeviceCode); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è¾é线请æ±å
¥åº |
| | | /// è¾é线请æ±å
¥åº |
| | | /// </summary> |
| | | /// <param name="conveyorLine">è¾é线å®ä¾å¯¹è±¡</param> |
| | | /// <param name="command">读åç请æ±ä¿¡æ¯</param> |
| | | /// <param name="childDeviceCode">å设å¤ç¼å·</param> |
| | | public void RequestInbound(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode) |
| | | { |
| | | if (_taskService.RequestWMSTask(command.Barcode, childDeviceCode).Status) |
| | | if (_taskFilter.RequestWmsTask(command.Barcode, childDeviceCode)) |
| | | { |
| | | Dt_Task task = _taskService.QueryConveyorLineTask(conveyorLine.DeviceCode, childDeviceCode); |
| | | Dt_Task? task = _taskFilter.QueryPendingTask(conveyorLine.DeviceCode, childDeviceCode); |
| | | if (task != null) |
| | | { |
| | | ConveyorLineTaskCommandNew taskCommand = _mapper.Map<ConveyorLineTaskCommandNew>(task); |
| | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è¾é线请æ±å
¥åºä¸ä¸å°å |
| | | /// è¾é线请æ±å
¥åºä¸ä¸å°å |
| | | /// </summary> |
| | | /// <param name="conveyorLine">è¾é线å®ä¾å¯¹è±¡</param> |
| | | /// <param name="command">读åç请æ±ä¿¡æ¯</param> |
| | | /// <param name="childDeviceCode">å设å¤ç¼å·</param> |
| | | public void RequestInNextAddress(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode) |
| | | { |
| | | Dt_Task task = _taskService.QueryExecutingConveyorLineTask(command.TaskNo, childDeviceCode); |
| | | if (task != null) |
| | | Dt_Task? task = _taskFilter.QueryExecutingTask(command.TaskNo, childDeviceCode); |
| | | if (task == null) |
| | | { |
| | | const string ConstraintMachineName = "æææº"; |
| | | const string PinMachineName = "ææéæº"; |
| | | |
| | | var devices = Storage.Devices; |
| | | |
| | | if (string.Equals(task.NextAddress, ConstraintMachineName, StringComparison.Ordinal)) |
| | | { |
| | | ConstraintMachine? constraint = devices.OfType<ConstraintMachine>().FirstOrDefault(d => d.DeviceName == ConstraintMachineName); |
| | | if (constraint == null) return; |
| | | |
| | | ProcessDeviceRequest(conveyorLine, constraint, childDeviceCode, |
| | | () => constraint.GetValue<ConstraintMachineDBName, bool>(ConstraintMachineDBName.MaterialRequestUpper), |
| | | () => constraint.GetValue<ConstraintMachineDBName, bool>(ConstraintMachineDBName.OutputRequestUpper), |
| | | outputReq => constraint.SetValue(ConstraintMachineDBName.ConstraintTrayOutputReadyUpper, outputReq ? 1 : 0)); |
| | | return; |
| | | } |
| | | else if (string.Equals(task.NextAddress, PinMachineName, StringComparison.Ordinal)) |
| | | { |
| | | PinMachine? pinMachine = devices.OfType<PinMachine>().FirstOrDefault(d => d.DeviceName == PinMachineName); |
| | | if (pinMachine == null) return; |
| | | |
| | | ProcessDeviceRequest(conveyorLine, pinMachine, childDeviceCode, |
| | | () => pinMachine.GetValue<PinMachineDBName, bool>(PinMachineDBName.MaterialRequestUpper), |
| | | () => pinMachine.GetValue<PinMachineDBName, bool>(PinMachineDBName.OutputRequestUpper), |
| | | outputReq => pinMachine.SetValue(PinMachineDBName.PlugPinTrayOutputReadyUpper, outputReq ? 1 : 0)); |
| | | } |
| | | _targetAddressSelector.HandleInboundNextAddress(conveyorLine, task.NextAddress, childDeviceCode); |
| | | |
| | | Dt_Task? newTask = _taskService.UpdatePosition(task.TaskNum, task.CurrentAddress); |
| | | if (newTask != null) |
| | |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è¾é线å
¥åºå®æ |
| | | /// è¾é线å
¥åºå®æ |
| | | /// </summary> |
| | | /// <param name="conveyorLine">è¾é线å®ä¾å¯¹è±¡</param> |
| | | /// <param name="command">读åç请æ±ä¿¡æ¯</param> |
| | | /// <param name="childDeviceCode">å设å¤ç¼å·</param> |
| | | public void ConveyorLineInFinish(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode) |
| | | { |
| | | Dt_Task task = _taskService.QueryExecutingConveyorLineTask(command.TaskNo, childDeviceCode); |
| | | Dt_Task? task = _taskFilter.QueryExecutingTask(command.TaskNo, childDeviceCode); |
| | | if (task != null) |
| | | { |
| | | conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, 1, childDeviceCode); |
| | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è¾é线请æ±åºä¿¡æ¯ |
| | | /// è¾é线请æ±åºä¿¡æ¯ |
| | | /// </summary> |
| | | /// <param name="conveyorLine">è¾é线å®ä¾å¯¹è±¡</param> |
| | | /// <param name="command">读åç请æ±ä¿¡æ¯</param> |
| | | /// <param name="childDeviceCode">å设å¤ç¼å·</param> |
| | | public void RequestOutbound(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode) |
| | | { |
| | | Dt_Task task = _taskService.QueryConveyorLineTask(conveyorLine.DeviceCode, childDeviceCode); |
| | | Dt_Task? task = _taskFilter.QueryPendingTask(conveyorLine.DeviceCode, childDeviceCode); |
| | | if (task != null) |
| | | { |
| | | //ConveyorLineTaskCommandNew taskCommand = _mapper.Map<ConveyorLineTaskCommandNew>(task); |
| | | //taskCommand.WCS_ACK = command.WCS_ACK; |
| | | //conveyorLine.SendCommand(taskCommand, childDeviceCode); |
| | | |
| | | conveyorLine.SetValue(ConveyorLineDBNameNew.TaskNo, task.TaskNum, childDeviceCode); |
| | | conveyorLine.SetValue(ConveyorLineDBNameNew.Barcode, task.PalletCode, childDeviceCode); |
| | | conveyorLine.SetValue(ConveyorLineDBNameNew.Target, task.TargetAddress, childDeviceCode); |
| | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è¾é线请æ±åºåºä¸ä¸å°å |
| | | /// è¾é线请æ±åºåºä¸ä¸å°å |
| | | /// </summary> |
| | | /// <param name="conveyorLine">è¾é线å®ä¾å¯¹è±¡</param> |
| | | /// <param name="command">读åç请æ±ä¿¡æ¯</param> |
| | | /// <param name="childDeviceCode">å设å¤ç¼å·</param> |
| | | public void RequestOutNextAddress(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode) |
| | | { |
| | | Dt_Task task = _taskService.QueryExecutingConveyorLineTask(command.TaskNo, childDeviceCode); |
| | | if (task != null) |
| | | Dt_Task? task = _taskFilter.QueryExecutingTask(command.TaskNo, childDeviceCode); |
| | | if (task == null) |
| | | { |
| | | const string ConstraintMachineName = "æææº"; |
| | | const string PinMachineName = "ææéæº"; |
| | | |
| | | var devices = Storage.Devices; |
| | | |
| | | if (string.Equals(task.NextAddress, ConstraintMachineName, StringComparison.Ordinal)) |
| | | { |
| | | ConstraintMachine? constraint = devices.OfType<ConstraintMachine>().FirstOrDefault(d => d.DeviceName == ConstraintMachineName); |
| | | if (constraint == null) |
| | | { |
| | | // å¤ç processing 为空çæ
åµï¼å¯æ ¹æ®å®é
ä¸å¡éæ±æ·»å å¤çé»è¾ï¼ |
| | | return; |
| | | } |
| | | ProcessDeviceRequest(conveyorLine, constraint, childDeviceCode, |
| | | () => constraint.GetValue<ConstraintMachineDBName, bool>(ConstraintMachineDBName.MaterialRequestLower), |
| | | () => constraint.GetValue<ConstraintMachineDBName, bool>(ConstraintMachineDBName.OutputRequestLower), |
| | | outputReq => constraint.SetValue(ConstraintMachineDBName.ConstraintTrayOutputReadyLower, outputReq ? 1 : 0)); |
| | | } |
| | | else if (string.Equals(task.NextAddress, PinMachineName, StringComparison.Ordinal)) |
| | | { |
| | | PinMachine? pinMachine = devices.OfType<PinMachine>().FirstOrDefault(d => d.DeviceName == PinMachineName); |
| | | if (pinMachine == null) |
| | | { |
| | | // å¤ç pinMachine 为空çæ
åµï¼å¯æ ¹æ®å®é
ä¸å¡éæ±æ·»å å¤çé»è¾ï¼ |
| | | return; |
| | | } |
| | | ProcessDeviceRequest(conveyorLine, pinMachine, childDeviceCode, |
| | | () => pinMachine.GetValue<PinMachineDBName, bool>(PinMachineDBName.MaterialRequestLower), |
| | | () => pinMachine.GetValue<PinMachineDBName, bool>(PinMachineDBName.OutputRequestLower), |
| | | outputReq => pinMachine.SetValue(PinMachineDBName.PlugPinTrayOutputReadyLower, outputReq ? 1 : 0)); |
| | | } |
| | | |
| | | Dt_Task? newTask = _taskService.UpdatePosition(task.TaskNum, task.CurrentAddress); |
| | | _targetAddressSelector.HandleOutboundNextAddress(conveyorLine, task.NextAddress, childDeviceCode); |
| | | |
| | | } |
| | | _ = _taskService.UpdatePosition(task.TaskNum, task.CurrentAddress); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è¾é线åºåºå®æ |
| | | /// è¾é线åºåºå®æ |
| | | /// </summary> |
| | | /// <param name="conveyorLine">è¾é线å®ä¾å¯¹è±¡</param> |
| | | /// <param name="command">读åç请æ±ä¿¡æ¯</param> |
| | | /// <param name="childDeviceCode">å设å¤ç¼å·</param> |
| | | public void ConveyorLineOutFinish(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode) |
| | | { |
| | | Dt_Task task = _taskService.QueryExecutingConveyorLineTask(command.TaskNo, childDeviceCode); |
| | | Dt_Task? task = _taskFilter.QueryExecutingTask(command.TaskNo, childDeviceCode); |
| | | if (task != null) |
| | | { |
| | | conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, 1, childDeviceCode); |
| | | WebResponseContent content = _taskService.UpdateTaskStatusToNext(task); |
| | | Console.Out.WriteLine(content.Serialize()); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// éç¨ç设å¤è¯·æ±å¤çæ¹æ³ |
| | | /// </summary> |
| | | private void ProcessDeviceRequest<T>(CommonConveyorLine conveyorLine, T device, string childDeviceCode, |
| | | Func<bool> getMaterialRequest, Func<bool> getOutputRequest, Action<bool> setOutputReady) |
| | | { |
| | | bool materialReq = getMaterialRequest(); |
| | | bool outputReq = getOutputRequest(); |
| | | |
| | | if (materialReq) |
| | | { |
| | | conveyorLine.SetValue(ConveyorLineDBNameNew.Target, 1, childDeviceCode); |
| | | conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, 1, childDeviceCode); |
| | | } |
| | | else |
| | | { |
| | | setOutputReady(outputReq); |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using WIDESEAWCS_QuartzJob; |
| | | namespace WIDESEAWCS_Tasks |
| | | { |
| | | /// <summary> |
| | | /// è¾é线设å¤è¯·æ±å¤çå¨ï¼å¤çæææº/ææéæºä¸ä¸å±è¯·æ±ã |
| | | /// </summary> |
| | | public class ConveyorLineTargetAddressSelector |
| | | { |
| | | private const string ConstraintMachineName = "æææº"; |
| | | private const string PinMachineName = "ææéæº"; |
| | | |
| | | public void HandleInboundNextAddress(CommonConveyorLine conveyorLine, string nextAddress, string childDeviceCode) |
| | | { |
| | | HandleDeviceRequest(conveyorLine, nextAddress, childDeviceCode, isUpper: true); |
| | | } |
| | | |
| | | public void HandleOutboundNextAddress(CommonConveyorLine conveyorLine, string nextAddress, string childDeviceCode) |
| | | { |
| | | HandleDeviceRequest(conveyorLine, nextAddress, childDeviceCode, isUpper: false); |
| | | } |
| | | |
| | | private void HandleDeviceRequest(CommonConveyorLine conveyorLine, string nextAddress, string childDeviceCode, bool isUpper) |
| | | { |
| | | var devices = Storage.Devices; |
| | | |
| | | if (string.Equals(nextAddress, ConstraintMachineName, StringComparison.Ordinal)) |
| | | { |
| | | ConstraintMachine? constraint = devices.OfType<ConstraintMachine>().FirstOrDefault(d => d.DeviceName == ConstraintMachineName); |
| | | if (constraint == null) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | ProcessDeviceRequest( |
| | | conveyorLine, |
| | | childDeviceCode, |
| | | getMaterialRequest: () => isUpper |
| | | ? constraint.GetValue<ConstraintMachineDBName, bool>(ConstraintMachineDBName.MaterialRequestUpper) |
| | | : constraint.GetValue<ConstraintMachineDBName, bool>(ConstraintMachineDBName.MaterialRequestLower), |
| | | getOutputRequest: () => isUpper |
| | | ? constraint.GetValue<ConstraintMachineDBName, bool>(ConstraintMachineDBName.OutputRequestUpper) |
| | | : constraint.GetValue<ConstraintMachineDBName, bool>(ConstraintMachineDBName.OutputRequestLower), |
| | | setOutputReady: outputReq => |
| | | { |
| | | if (isUpper) |
| | | { |
| | | constraint.SetValue(ConstraintMachineDBName.ConstraintTrayOutputReadyUpper, outputReq ? 1 : 0); |
| | | } |
| | | else |
| | | { |
| | | constraint.SetValue(ConstraintMachineDBName.ConstraintTrayOutputReadyLower, outputReq ? 1 : 0); |
| | | } |
| | | }); |
| | | } |
| | | else if (string.Equals(nextAddress, PinMachineName, StringComparison.Ordinal)) |
| | | { |
| | | PinMachine? pinMachine = devices.OfType<PinMachine>().FirstOrDefault(d => d.DeviceName == PinMachineName); |
| | | if (pinMachine == null) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | ProcessDeviceRequest( |
| | | conveyorLine, |
| | | childDeviceCode, |
| | | getMaterialRequest: () => isUpper |
| | | ? pinMachine.GetValue<PinMachineDBName, bool>(PinMachineDBName.MaterialRequestUpper) |
| | | : pinMachine.GetValue<PinMachineDBName, bool>(PinMachineDBName.MaterialRequestLower), |
| | | getOutputRequest: () => isUpper |
| | | ? pinMachine.GetValue<PinMachineDBName, bool>(PinMachineDBName.OutputRequestUpper) |
| | | : pinMachine.GetValue<PinMachineDBName, bool>(PinMachineDBName.OutputRequestLower), |
| | | setOutputReady: outputReq => |
| | | { |
| | | if (isUpper) |
| | | { |
| | | pinMachine.SetValue(PinMachineDBName.PlugPinTrayOutputReadyUpper, outputReq ? 1 : 0); |
| | | } |
| | | else |
| | | { |
| | | pinMachine.SetValue(PinMachineDBName.PlugPinTrayOutputReadyLower, outputReq ? 1 : 0); |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | |
| | | private static void ProcessDeviceRequest( |
| | | CommonConveyorLine conveyorLine, |
| | | string childDeviceCode, |
| | | Func<bool> getMaterialRequest, |
| | | Func<bool> getOutputRequest, |
| | | Action<bool> setOutputReady) |
| | | { |
| | | bool materialReq = getMaterialRequest(); |
| | | bool outputReq = getOutputRequest(); |
| | | |
| | | if (materialReq) |
| | | { |
| | | conveyorLine.SetValue(ConveyorLineDBNameNew.Target, 1, childDeviceCode); |
| | | conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, 1, childDeviceCode); |
| | | } |
| | | else |
| | | { |
| | | setOutputReady(outputReq); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using WIDESEAWCS_ITaskInfoService; |
| | | using WIDESEAWCS_Model.Models; |
| | | |
| | | namespace WIDESEAWCS_Tasks |
| | | { |
| | | /// <summary> |
| | | /// è¾é线任å¡è®¿é®å¨ï¼ç»ä¸å°è£
任塿¥è¯¢ä¸ WMS 请æ±ã |
| | | /// </summary> |
| | | public class ConveyorLineTaskFilter |
| | | { |
| | | private readonly ITaskService _taskService; |
| | | |
| | | public ConveyorLineTaskFilter(ITaskService taskService) |
| | | { |
| | | _taskService = taskService; |
| | | } |
| | | |
| | | public Dt_Task? QueryPendingTask(string deviceCode, string childDeviceCode) |
| | | { |
| | | return _taskService.QueryConveyorLineTask(deviceCode, childDeviceCode); |
| | | } |
| | | |
| | | public Dt_Task? QueryExecutingTask(int taskNo, string childDeviceCode) |
| | | { |
| | | return _taskService.QueryExecutingConveyorLineTask(taskNo, childDeviceCode); |
| | | } |
| | | |
| | | public bool RequestWmsTask(string barcode, string childDeviceCode) |
| | | { |
| | | return _taskService.RequestWMSTask(barcode, childDeviceCode).Status; |
| | | } |
| | | } |
| | | } |
| | |
| | | using Quartz; |
| | | using System.Diagnostics.CodeAnalysis; |
| | | using WIDESEAWCS_Common.TaskEnum; |
| | | using WIDESEAWCS_ITaskInfoRepository; |
| | | using WIDESEAWCS_ITaskInfoService; |
| | | using WIDESEAWCS_Model.Models; |
| | | using WIDESEAWCS_QuartzJob; |
| | | using WIDESEAWCS_QuartzJob.Models; |
| | | using WIDESEAWCS_QuartzJob.Service; |
| | | using WIDESEAWCS_QuartzJob.StackerCrane; |
| | | using WIDESEAWCS_QuartzJob.StackerCrane.Enum; |
| | | using WIDESEAWCS_Tasks.StackerCraneJob; |
| | | |
| | |
| | | private readonly ITaskService _taskService; |
| | | private readonly ITaskExecuteDetailService _taskExecuteDetailService; |
| | | private readonly ITaskRepository _taskRepository; |
| | | private readonly IRouterService _routerService; |
| | | |
| | | public FormationCommonStackerCraneJob(ITaskService taskService, ITaskExecuteDetailService taskExecuteDetailService, ITaskRepository taskRepository, IRouterService routerService) |
| | | private readonly FormationStackerCraneTaskSelector _taskSelector; |
| | | private readonly FormationStackerCraneCommandBuilder _commandBuilder; |
| | | |
| | | public FormationCommonStackerCraneJob( |
| | | ITaskService taskService, |
| | | ITaskExecuteDetailService taskExecuteDetailService, |
| | | ITaskRepository taskRepository, |
| | | IRouterService routerService) |
| | | { |
| | | _taskService = taskService; |
| | | _taskExecuteDetailService = taskExecuteDetailService; |
| | | _taskRepository = taskRepository; |
| | | _routerService = routerService; |
| | | |
| | | _taskSelector = new FormationStackerCraneTaskSelector(taskService, routerService); |
| | | _commandBuilder = new FormationStackerCraneCommandBuilder(taskService, routerService); |
| | | } |
| | | |
| | | public Task Execute(IJobExecutionContext context) |
| | |
| | | |
| | | commonStackerCrane.CheckStackerCraneTaskCompleted(); |
| | | |
| | | if (commonStackerCrane.StackerCraneWorkStatusValue == FormationStackerCraneOperationStatus.Idle) |
| | | if (commonStackerCrane.StackerCraneWorkStatusValue != FormationStackerCraneOperationStatus.Idle) |
| | | { |
| | | Dt_Task? task = GetTask(commonStackerCrane); |
| | | if (task != null) |
| | | return Task.CompletedTask; |
| | | } |
| | | |
| | | Dt_Task? task = _taskSelector.SelectTask(commonStackerCrane); |
| | | if (task == null) |
| | | { |
| | | FormationStackerCraneTaskCommand? stackerCraneTaskCommand = ConvertToStackerCraneTaskCommand(task); |
| | | if (stackerCraneTaskCommand != null) |
| | | return Task.CompletedTask; |
| | | } |
| | | |
| | | var stackerCraneTaskCommand = _commandBuilder.ConvertToStackerCraneTaskCommand(task); |
| | | if (stackerCraneTaskCommand == null) |
| | | { |
| | | return Task.CompletedTask; |
| | | } |
| | | |
| | | bool sendFlag = commonStackerCrane.SendCommand(stackerCraneTaskCommand); |
| | | if (sendFlag) |
| | | { |
| | |
| | | _taskService.UpdateTaskStatusToNext(task.TaskNum); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Console.WriteLine($"FormationCommonStackerCraneJob Error: {ex.Message}"); |
| | | } |
| | | |
| | | return Task.CompletedTask; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// ä»»å¡å®æäºä»¶è®¢é
çæ¹æ³ |
| | | /// </summary> |
| | | /// <param name="sender"></param> |
| | | /// <param name="e"></param> |
| | | private void CommonStackerCrane_StackerCraneTaskCompletedEventHandler(object? sender, WIDESEAWCS_QuartzJob.StackerCrane.StackerCraneTaskCompletedEventArgs e) |
| | | private void CommonStackerCrane_StackerCraneTaskCompletedEventHandler(object? sender, StackerCraneTaskCompletedEventArgs e) |
| | | { |
| | | SpeFormationStackerCrane? commonStackerCrane = sender as SpeFormationStackerCrane; |
| | | if (commonStackerCrane != null) |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è·åä»»å¡ |
| | | /// </summary> |
| | | /// <param name="commonStackerCrane">å åæºå¯¹è±¡</param> |
| | | /// <returns></returns> |
| | | private Dt_Task? GetTask(SpeFormationStackerCrane commonStackerCrane) |
| | | { |
| | | Dt_Task? task = null; |
| | | |
| | | if (commonStackerCrane.LastTaskType == null) |
| | | { |
| | | task = _taskService.QueryStackerCraneTask(commonStackerCrane.DeviceCode); |
| | | } |
| | | else |
| | | { |
| | | if (commonStackerCrane.LastTaskType.GetValueOrDefault().GetTaskTypeGroup() == TaskTypeGroup.OutbondGroup) |
| | | { |
| | | task = _taskService.QueryStackerCraneInTask(commonStackerCrane.DeviceCode); |
| | | task ??= _taskService.QueryStackerCraneOutTask(commonStackerCrane.DeviceCode); |
| | | } |
| | | else |
| | | { |
| | | task = _taskService.QueryStackerCraneOutTask(commonStackerCrane.DeviceCode); |
| | | } |
| | | } |
| | | |
| | | if (task != null && task.TaskType.GetTaskTypeGroup() == TaskTypeGroup.OutbondGroup) |
| | | { |
| | | if (IsOutTaskStationAvailable(task)) |
| | | { |
| | | return task; |
| | | } |
| | | |
| | | List<string> otherOutStationCodes = _routerService.QueryNextRoutes(commonStackerCrane.DeviceCode, task.NextAddress, task.TaskType) |
| | | .Select(x => x.ChildPosi).ToList(); |
| | | |
| | | List<Dt_Task> tasks = _taskService.QueryStackerCraneOutTasks(commonStackerCrane.DeviceCode, otherOutStationCodes); |
| | | |
| | | foreach (var alternativeTask in tasks) |
| | | { |
| | | if (IsOutTaskStationAvailable(alternativeTask)) |
| | | { |
| | | return alternativeTask; |
| | | } |
| | | } |
| | | |
| | | task = _taskService.QueryStackerCraneInTask(commonStackerCrane.DeviceCode); |
| | | } |
| | | |
| | | return task; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æ£æ¥åºåºä»»å¡çåºåºç«å°æ¯å¦å¯ç¨ |
| | | /// </summary> |
| | | /// <param name="task">ä»»å¡å®ä½</param> |
| | | /// <returns>妿ç«å°å¯ç¨è¿åtrueï¼å¦åè¿åfalse</returns> |
| | | private bool IsOutTaskStationAvailable([NotNull] Dt_Task task) |
| | | { |
| | | Dt_Router? router = _routerService.QueryNextRoute(task.Roadway, task.NextAddress, task.TaskType); |
| | | if (router == null) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"æªæ¾å°ç«å°ã{task.NextAddress}ãä¿¡æ¯ï¼æ æ³æ ¡éªç«å°"); |
| | | return false; |
| | | } |
| | | |
| | | IDevice? device = Storage.Devices.FirstOrDefault(x => x.DeviceCode == router.ChildPosiDeviceCode); |
| | | if (device == null) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"æªæ¾å°åºåºç«å°ã{router.ChildPosiDeviceCode}ã对åºçéè®¯å¯¹è±¡ï¼æ æ³å¤æåºåºç«å°æ¯å¦è¢«å ç¨"); |
| | | return false; |
| | | } |
| | | |
| | | CommonConveyorLine conveyorLine = (CommonConveyorLine)device; |
| | | return conveyorLine.IsOccupied(router.ChildPosi); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// ä»»å¡å®ä½è½¬æ¢æå½ä»¤Model |
| | | /// </summary> |
| | | /// <param name="task">ä»»å¡å®ä½</param> |
| | | /// <returns></returns> |
| | | public FormationStackerCraneTaskCommand? ConvertToStackerCraneTaskCommand([NotNull] Dt_Task task) |
| | | { |
| | | FormationStackerCraneTaskCommand stackerCraneTaskCommand = new FormationStackerCraneTaskCommand |
| | | { |
| | | Barcode = task.PalletCode, |
| | | TaskNum = task.TaskNum, |
| | | WorkType = 4, |
| | | WorkAction = 1, |
| | | FireAlarm = 0, |
| | | FieldName = "" |
| | | }; |
| | | |
| | | TaskTypeGroup taskTypeGroup = task.TaskType.GetTaskTypeGroup(); |
| | | |
| | | return taskTypeGroup switch |
| | | { |
| | | TaskTypeGroup.InboundGroup => BuildInboundCommand(task, stackerCraneTaskCommand), |
| | | TaskTypeGroup.OutbondGroup => BuildOutboundCommand(task, stackerCraneTaskCommand), |
| | | TaskTypeGroup.RelocationGroup => BuildRelocationCommand(task, stackerCraneTaskCommand), |
| | | _ => stackerCraneTaskCommand |
| | | }; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æå»ºå
¥åºå½ä»¤ |
| | | /// </summary> |
| | | private FormationStackerCraneTaskCommand? BuildInboundCommand(Dt_Task task, FormationStackerCraneTaskCommand command) |
| | | { |
| | | Dt_Router? router = _routerService.QueryNextRoute(task.CurrentAddress, task.Roadway, task.TaskType); |
| | | if (router == null) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"æªæ¾å°ç«å°ã{task.CurrentAddress}ãä¿¡æ¯ï¼æ æ³è·å对åºçå åæºåè´§ç«å°ä¿¡æ¯"); |
| | | return null; |
| | | } |
| | | |
| | | command.StartRow = Convert.ToInt16(router.SrmRow); |
| | | command.StartColumn = Convert.ToInt16(router.SrmColumn); |
| | | command.StartLayer = Convert.ToInt16(router.SrmLayer); |
| | | |
| | | if (!TryParseAddress(task.NextAddress, out short endRow, out short endColumn, out short endLayer)) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"å
¥åºä»»å¡ç»ç¹é误ï¼ç»ç¹ï¼ã{task.NextAddress}ã"); |
| | | return null; |
| | | } |
| | | |
| | | command.EndRow = endRow; |
| | | command.EndColumn = endColumn; |
| | | command.EndLayer = endLayer; |
| | | |
| | | return command; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æå»ºåºåºå½ä»¤ |
| | | /// </summary> |
| | | private FormationStackerCraneTaskCommand? BuildOutboundCommand(Dt_Task task, FormationStackerCraneTaskCommand command) |
| | | { |
| | | Dt_Router? router = _routerService.QueryNextRoute(task.Roadway, task.TargetAddress, task.TaskType); |
| | | if (router == null) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"æªæ¾å°ç«å°ã{task.TargetAddress}ãä¿¡æ¯ï¼æ æ³è·å对åºçå åæºæ¾è´§ç«å°ä¿¡æ¯"); |
| | | return null; |
| | | } |
| | | |
| | | command.EndRow = Convert.ToInt16(router.SrmRow); |
| | | command.EndColumn = Convert.ToInt16(router.SrmColumn); |
| | | command.EndLayer = Convert.ToInt16(router.SrmLayer); |
| | | |
| | | if (!TryParseAddress(task.CurrentAddress, out short startRow, out short startColumn, out short startLayer)) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"åºåºä»»å¡èµ·ç¹é误ï¼èµ·ç¹ï¼ã{task.CurrentAddress}ã"); |
| | | return null; |
| | | } |
| | | |
| | | command.StartRow = startRow; |
| | | command.StartColumn = startColumn; |
| | | command.StartLayer = startLayer; |
| | | |
| | | return command; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æå»ºç§»åºå½ä»¤ |
| | | /// </summary> |
| | | private FormationStackerCraneTaskCommand? BuildRelocationCommand(Dt_Task task, FormationStackerCraneTaskCommand command) |
| | | { |
| | | if (!TryParseAddress(task.NextAddress, out short endRow, out short endColumn, out short endLayer)) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"ç§»åºä»»å¡ç»ç¹é误ï¼ç»ç¹ï¼ã{task.NextAddress}ã"); |
| | | return null; |
| | | } |
| | | |
| | | command.EndRow = endRow; |
| | | command.EndColumn = endColumn; |
| | | command.EndLayer = endLayer; |
| | | |
| | | if (!TryParseAddress(task.CurrentAddress, out short startRow, out short startColumn, out short startLayer)) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"ç§»åºä»»å¡èµ·ç¹é误ï¼èµ·ç¹ï¼ã{task.CurrentAddress}ã"); |
| | | return null; |
| | | } |
| | | |
| | | command.StartRow = startRow; |
| | | command.StartColumn = startColumn; |
| | | command.StartLayer = startLayer; |
| | | |
| | | return command; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è§£æå°ååç¬¦ä¸²ï¼æ ¼å¼ï¼è¡-å-å±ï¼ |
| | | /// </summary> |
| | | private bool TryParseAddress(string address, out short row, out short column, out short layer) |
| | | { |
| | | row = column = layer = 0; |
| | | |
| | | string[] parts = address.Split("-"); |
| | | if (parts.Length != 3) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | return short.TryParse(parts[0], out row) |
| | | && short.TryParse(parts[1], out column) |
| | | && short.TryParse(parts[2], out layer); |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using WIDESEAWCS_Common.TaskEnum; |
| | | using WIDESEAWCS_ITaskInfoService; |
| | | using WIDESEAWCS_Model.Models; |
| | | using WIDESEAWCS_QuartzJob.Models; |
| | | using WIDESEAWCS_QuartzJob.Service; |
| | | using WIDESEAWCS_Tasks.StackerCraneJob; |
| | | |
| | | namespace WIDESEAWCS_Tasks |
| | | { |
| | | /// <summary> |
| | | /// å容å åæºå½ä»¤æå»ºå¨ï¼è´è´£ä»»å¡å°å½ä»¤å¯¹è±¡ç转æ¢ã |
| | | /// </summary> |
| | | public class FormationStackerCraneCommandBuilder |
| | | { |
| | | private readonly ITaskService _taskService; |
| | | private readonly IRouterService _routerService; |
| | | |
| | | public FormationStackerCraneCommandBuilder(ITaskService taskService, IRouterService routerService) |
| | | { |
| | | _taskService = taskService; |
| | | _routerService = routerService; |
| | | } |
| | | |
| | | public FormationStackerCraneTaskCommand? ConvertToStackerCraneTaskCommand(Dt_Task task) |
| | | { |
| | | FormationStackerCraneTaskCommand command = new() |
| | | { |
| | | Barcode = task.PalletCode, |
| | | TaskNum = task.TaskNum, |
| | | WorkType = 4, |
| | | WorkAction = 1, |
| | | FireAlarm = 0, |
| | | FieldName = string.Empty |
| | | }; |
| | | |
| | | TaskTypeGroup taskTypeGroup = task.TaskType.GetTaskTypeGroup(); |
| | | |
| | | return taskTypeGroup switch |
| | | { |
| | | TaskTypeGroup.InboundGroup => BuildInboundCommand(task, command), |
| | | TaskTypeGroup.OutbondGroup => BuildOutboundCommand(task, command), |
| | | TaskTypeGroup.RelocationGroup => BuildRelocationCommand(task, command), |
| | | _ => command |
| | | }; |
| | | } |
| | | |
| | | private FormationStackerCraneTaskCommand? BuildInboundCommand(Dt_Task task, FormationStackerCraneTaskCommand command) |
| | | { |
| | | Dt_Router? router = _routerService.QueryNextRoute(task.CurrentAddress, task.Roadway, task.TaskType); |
| | | if (router == null) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"æªæ¾å°ç«å°ã{task.CurrentAddress}ãä¿¡æ¯ï¼æ æ³è·å对åºçå åæºåè´§ç«å°ä¿¡æ¯"); |
| | | return null; |
| | | } |
| | | |
| | | command.StartRow = Convert.ToInt16(router.SrmRow); |
| | | command.StartColumn = Convert.ToInt16(router.SrmColumn); |
| | | command.StartLayer = Convert.ToInt16(router.SrmLayer); |
| | | |
| | | if (!TryParseAddress(task.NextAddress, out short endRow, out short endColumn, out short endLayer)) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"å
¥åºä»»å¡ç»ç¹é误ï¼ç»ç¹ï¼ã{task.NextAddress}ã"); |
| | | return null; |
| | | } |
| | | |
| | | command.EndRow = endRow; |
| | | command.EndColumn = endColumn; |
| | | command.EndLayer = endLayer; |
| | | return command; |
| | | } |
| | | |
| | | private FormationStackerCraneTaskCommand? BuildOutboundCommand(Dt_Task task, FormationStackerCraneTaskCommand command) |
| | | { |
| | | Dt_Router? router = _routerService.QueryNextRoute(task.Roadway, task.TargetAddress, task.TaskType); |
| | | if (router == null) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"æªæ¾å°ç«å°ã{task.TargetAddress}ãä¿¡æ¯ï¼æ æ³è·å对åºçå åæºæ¾è´§ç«å°ä¿¡æ¯"); |
| | | return null; |
| | | } |
| | | |
| | | command.EndRow = Convert.ToInt16(router.SrmRow); |
| | | command.EndColumn = Convert.ToInt16(router.SrmColumn); |
| | | command.EndLayer = Convert.ToInt16(router.SrmLayer); |
| | | |
| | | if (!TryParseAddress(task.CurrentAddress, out short startRow, out short startColumn, out short startLayer)) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"åºåºä»»å¡èµ·ç¹é误ï¼èµ·ç¹ï¼ã{task.CurrentAddress}ã"); |
| | | return null; |
| | | } |
| | | |
| | | command.StartRow = startRow; |
| | | command.StartColumn = startColumn; |
| | | command.StartLayer = startLayer; |
| | | return command; |
| | | } |
| | | |
| | | private FormationStackerCraneTaskCommand? BuildRelocationCommand(Dt_Task task, FormationStackerCraneTaskCommand command) |
| | | { |
| | | if (!TryParseAddress(task.NextAddress, out short endRow, out short endColumn, out short endLayer)) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"ç§»åºä»»å¡ç»ç¹é误ï¼ç»ç¹ï¼ã{task.NextAddress}ã"); |
| | | return null; |
| | | } |
| | | |
| | | command.EndRow = endRow; |
| | | command.EndColumn = endColumn; |
| | | command.EndLayer = endLayer; |
| | | |
| | | if (!TryParseAddress(task.CurrentAddress, out short startRow, out short startColumn, out short startLayer)) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"ç§»åºä»»å¡èµ·ç¹é误ï¼èµ·ç¹ï¼ã{task.CurrentAddress}ã"); |
| | | return null; |
| | | } |
| | | |
| | | command.StartRow = startRow; |
| | | command.StartColumn = startColumn; |
| | | command.StartLayer = startLayer; |
| | | return command; |
| | | } |
| | | |
| | | private static bool TryParseAddress(string address, out short row, out short column, out short layer) |
| | | { |
| | | row = column = layer = 0; |
| | | string[] parts = address.Split("-"); |
| | | if (parts.Length != 3) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | return short.TryParse(parts[0], out row) |
| | | && short.TryParse(parts[1], out column) |
| | | && short.TryParse(parts[2], out layer); |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using System.Diagnostics.CodeAnalysis; |
| | | using WIDESEAWCS_Common.TaskEnum; |
| | | using WIDESEAWCS_ITaskInfoService; |
| | | using WIDESEAWCS_Model.Models; |
| | | using WIDESEAWCS_QuartzJob; |
| | | using WIDESEAWCS_QuartzJob.Models; |
| | | using WIDESEAWCS_QuartzJob.Service; |
| | | |
| | | namespace WIDESEAWCS_Tasks |
| | | { |
| | | /// <summary> |
| | | /// å容å åæºä»»å¡éæ©å¨ï¼å°è£
任塿éä¸ç«å°å¯ç¨æ§å¤æã |
| | | /// </summary> |
| | | public class FormationStackerCraneTaskSelector |
| | | { |
| | | private readonly ITaskService _taskService; |
| | | private readonly IRouterService _routerService; |
| | | |
| | | public FormationStackerCraneTaskSelector(ITaskService taskService, IRouterService routerService) |
| | | { |
| | | _taskService = taskService; |
| | | _routerService = routerService; |
| | | } |
| | | |
| | | public Dt_Task? SelectTask(SpeFormationStackerCrane commonStackerCrane) |
| | | { |
| | | Dt_Task? task; |
| | | if (commonStackerCrane.LastTaskType == null) |
| | | { |
| | | task = _taskService.QueryStackerCraneTask(commonStackerCrane.DeviceCode); |
| | | } |
| | | else if (commonStackerCrane.LastTaskType.GetValueOrDefault().GetTaskTypeGroup() == TaskTypeGroup.OutbondGroup) |
| | | { |
| | | task = _taskService.QueryStackerCraneInTask(commonStackerCrane.DeviceCode); |
| | | task ??= _taskService.QueryStackerCraneOutTask(commonStackerCrane.DeviceCode); |
| | | } |
| | | else |
| | | { |
| | | task = _taskService.QueryStackerCraneOutTask(commonStackerCrane.DeviceCode); |
| | | } |
| | | |
| | | if (task != null && task.TaskType.GetTaskTypeGroup() == TaskTypeGroup.OutbondGroup) |
| | | { |
| | | if (IsOutTaskStationAvailable(task)) |
| | | { |
| | | return task; |
| | | } |
| | | |
| | | var otherOutStationCodes = _routerService |
| | | .QueryNextRoutes(commonStackerCrane.DeviceCode, task.NextAddress, task.TaskType) |
| | | .Select(x => x.ChildPosi) |
| | | .ToList(); |
| | | |
| | | var tasks = _taskService.QueryStackerCraneOutTasks(commonStackerCrane.DeviceCode, otherOutStationCodes); |
| | | foreach (var alternativeTask in tasks) |
| | | { |
| | | if (IsOutTaskStationAvailable(alternativeTask)) |
| | | { |
| | | return alternativeTask; |
| | | } |
| | | } |
| | | |
| | | task = _taskService.QueryStackerCraneInTask(commonStackerCrane.DeviceCode); |
| | | } |
| | | |
| | | return task; |
| | | } |
| | | |
| | | private bool IsOutTaskStationAvailable([NotNull] Dt_Task task) |
| | | { |
| | | Dt_Router? router = _routerService.QueryNextRoute(task.Roadway, task.NextAddress, task.TaskType); |
| | | if (router == null) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"æªæ¾å°ç«å°ã{task.NextAddress}ãä¿¡æ¯ï¼æ æ³æ ¡éªç«å°"); |
| | | return false; |
| | | } |
| | | |
| | | IDevice? device = Storage.Devices.FirstOrDefault(x => x.DeviceCode == router.ChildPosiDeviceCode); |
| | | if (device == null) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"æªæ¾å°åºåºç«å°ã{router.ChildPosiDeviceCode}ã对åºçéè®¯å¯¹è±¡ï¼æ æ³å¤æåºåºç«å°æ¯å¦è¢«å ç¨"); |
| | | return false; |
| | | } |
| | | |
| | | CommonConveyorLine conveyorLine = (CommonConveyorLine)device; |
| | | return conveyorLine.IsOccupied(router.ChildPosi); |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using System.Net.Sockets; |
| | | |
| | | namespace WIDESEAWCS_Tasks.Workflow.Abstractions |
| | | { |
| | | /// <summary> |
| | | /// æºå¨äººæ¶æ¯è·¯ç±å
¥å£ãç¨äºæ¿æ¥ TcpSocketServer çæ¶æ¯äºä»¶ã |
| | | /// </summary> |
| | | public interface IRobotMessageRouter |
| | | { |
| | | Task<string?> HandleMessageReceivedAsync(string message, bool isJson, TcpClient client, RobotSocketState state); |
| | | } |
| | | } |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using System.Net.Sockets; |
| | | |
| | | namespace WIDESEAWCS_Tasks.Workflow.Abstractions |
| | | { |
| | | /// <summary> |
| | | /// æºå¨äººåç¼å½ä»¤å¤çå¨ï¼pickfinished / putfinishedï¼ã |
| | | /// </summary> |
| | | public interface IRobotPrefixCommandHandler |
| | | { |
| | | bool IsPrefixCommand(string message); |
| | | |
| | | Task HandleAsync(string message, RobotSocketState state, TcpClient client); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | namespace WIDESEAWCS_Tasks.Workflow.Abstractions |
| | | { |
| | | /// <summary> |
| | | /// æºå¨äººç®åå½ä»¤å¤çå¨ï¼å¦è¿è¡ç¶æã模å¼åæ¢ãå
¨æµç¨å®æå½ä»¤ï¼ã |
| | | /// </summary> |
| | | public interface IRobotSimpleCommandHandler |
| | | { |
| | | Task<bool> HandleAsync(string message, RobotSocketState state); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using WIDESEAWCS_Model.Models; |
| | | |
| | | namespace WIDESEAWCS_Tasks.Workflow.Abstractions |
| | | { |
| | | /// <summary> |
| | | /// æºå¨äººæµç¨ç¼æå¨ãè´è´£ RobotJob å
çç¶ææºåæ¯æ§è¡ã |
| | | /// </summary> |
| | | public interface IRobotWorkflowOrchestrator |
| | | { |
| | | Task ExecuteAsync(RobotSocketState latestState, Dt_RobotTask task, string ipAddress); |
| | | } |
| | | } |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using System.Net.Sockets; |
| | | using WIDESEAWCS_QuartzJob; |
| | | |
| | | namespace WIDESEAWCS_Tasks.Workflow.Abstractions |
| | | { |
| | | /// <summary> |
| | | /// Socket 客æ·ç«¯ç½å
³ãç¨äºé离 Robot ä¸å¡å¯¹ TcpSocketServer çç´æ¥ä¾èµã |
| | | /// </summary> |
| | | public interface ISocketClientGateway |
| | | { |
| | | Task<bool> SendToClientAsync(string clientId, string message); |
| | | |
| | | Task SendMessageAsync(TcpClient client, string message); |
| | | |
| | | IReadOnlyList<string> GetClientIds(); |
| | | |
| | | Task HandleClientAsync(TcpClient client, string clientId, CancellationToken cancellationToken, RobotSocketState robotCrane); |
| | | } |
| | | } |
| | | |
| | |
| | | using Quartz; |
| | | using Quartz; |
| | | using WIDESEA_Core; |
| | | using WIDESEAWCS_Common.TaskEnum; |
| | | using WIDESEAWCS_Core.Caches; |
| | | using WIDESEAWCS_Core.Helper; |
| | | using WIDESEAWCS_ITaskInfoService; |
| | | using WIDESEAWCS_Model.Models; |
| | | using WIDESEAWCS_QuartzJob; |
| | | using WIDESEAWCS_Tasks.Workflow.Abstractions; |
| | | using WIDESEAWCS_Tasks.Workflow; |
| | | using WIDESEAWCS_Tasks.SocketServer; |
| | | |
| | | namespace WIDESEAWCS_Tasks |
| | | { |
| | | /// <summary> |
| | | /// æºæ¢°æä»»å¡ä½ä¸ - è´è´£åè°æºæ¢°æå®¢æ·ç«¯è¿æ¥ãæ¶æ¯å¤çå任塿§è¡ |
| | | /// æºå¨äººä»»å¡ä½ä¸ï¼è´è´£è°åº¦ä¸çå½å¨æç®¡çï¼å
·ä½ç¶ææºæµç¨äº¤ç»ç¼æå¨ã |
| | | /// </summary> |
| | | [DisallowConcurrentExecution] |
| | | public class RobotJob : IJob |
| | |
| | | |
| | | private readonly RobotClientManager _clientManager; |
| | | private readonly RobotStateManager _stateManager; |
| | | private readonly RobotMessageHandler _messageHandler; |
| | | private readonly IRobotMessageRouter _messageRouter; |
| | | private readonly RobotTaskProcessor _taskProcessor; |
| | | private readonly IRobotTaskService _robotTaskService; |
| | | private readonly IRobotWorkflowOrchestrator _workflowOrchestrator; |
| | | |
| | | public RobotJob( |
| | | TcpSocketServer tcpSocket, |
| | |
| | | ICacheService cache, |
| | | HttpClientHelper httpClientHelper) |
| | | { |
| | | _robotTaskService = robotTaskService; |
| | | |
| | | // åå§å管çå¨ |
| | | _stateManager = new RobotStateManager(cache); |
| | | _taskProcessor = new RobotTaskProcessor(tcpSocket, _stateManager, robotTaskService, taskService, httpClientHelper); |
| | | _clientManager = new RobotClientManager(tcpSocket, _stateManager); |
| | | _messageHandler = new RobotMessageHandler(tcpSocket, _stateManager, cache, robotTaskService, _taskProcessor); |
| | | |
| | | // 订é
客æ·ç«¯ç®¡çå¨çäºä»¶ |
| | | // æ¶å£ Socket 访é®ï¼åç»è¥æ¿æ¢éä¿¡å®ç°åªéæ¿æ¢ç½å
³å±ã |
| | | ISocketClientGateway socketGateway = new SocketClientGateway(tcpSocket); |
| | | |
| | | _taskProcessor = new RobotTaskProcessor(socketGateway, _stateManager, robotTaskService, taskService, httpClientHelper); |
| | | _clientManager = new RobotClientManager(tcpSocket, _stateManager); |
| | | |
| | | var simpleCommandHandler = new RobotSimpleCommandHandler(_taskProcessor); |
| | | var prefixCommandHandler = new RobotPrefixCommandHandler(robotTaskService, _taskProcessor, _stateManager, socketGateway); |
| | | _messageRouter = new RobotMessageHandler(socketGateway, _stateManager, cache, simpleCommandHandler, prefixCommandHandler); |
| | | |
| | | _workflowOrchestrator = new RobotWorkflowOrchestrator(_stateManager, _clientManager, _taskProcessor, robotTaskService); |
| | | |
| | | _clientManager.OnClientDisconnected += OnClientDisconnected; |
| | | |
| | | // 订é
TCPæå¡å¨çæ¶æ¯äºä»¶ï¼å
¨å±åªè®¢é
䏿¬¡ï¼ |
| | | // å
¨å±åªè®¢é
䏿¬¡æ¶æ¯äºä»¶ï¼ä¿æåæè¡ä¸ºã |
| | | if (System.Threading.Interlocked.CompareExchange(ref _messageSubscribedFlag, 1, 0) == 0) |
| | | { |
| | | tcpSocket.MessageReceived += _messageHandler.HandleMessageReceivedAsync; |
| | | Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] æºå¨äººTCPæ¶æ¯äºä»¶å·²è®¢é
"); |
| | | tcpSocket.MessageReceived += _messageRouter.HandleMessageReceivedAsync; |
| | | Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] æºå¨æTCPæ¶æ¯äºä»¶å·²è®¢é
"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 客æ·ç«¯æå¼è¿æ¥æ¶çå¤ç |
| | | /// </summary> |
| | | private void OnClientDisconnected(object? sender, RobotSocketState state) |
| | | { |
| | | // å¯ä»¥å¨è¿éæ·»å æå¼è¿æ¥åçå¤çé»è¾ |
| | | Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 客æ·ç«¯å·²æå¼è¿æ¥: {state.IPAddress}"); |
| | | } |
| | | |
| | |
| | | |
| | | string ipAddress = robotCrane.IPAddress; |
| | | |
| | | // è·åæåå»ºç¶æ |
| | | RobotSocketState state = _stateManager.GetOrCreateState(ipAddress, robotCrane); |
| | | state.RobotCrane = robotCrane; |
| | | |
| | | try |
| | | { |
| | | // ç¡®ä¿å®¢æ·ç«¯å·²è¿æ¥å¹¶è®¢é
æ¶æ¯äºä»¶ |
| | | if (!_clientManager.EnsureClientSubscribed(ipAddress, robotCrane)) |
| | | { |
| | | return; // 客æ·ç«¯æªè¿æ¥æè®¢é
失败ï¼è·³è¿æ¬æ¬¡æ§è¡ |
| | | return; |
| | | } |
| | | |
| | | // è·åä»»å¡å¹¶å¤ç |
| | | Dt_RobotTask? task = _taskProcessor.GetTask(robotCrane); |
| | | var task = _taskProcessor.GetTask(robotCrane); |
| | | if (task != null) |
| | | { |
| | | // æ¯æ¬¡å¤æåéæ°ä»ç¼åè·åææ°ç¶æ |
| | | var latestState = _stateManager.GetState(ipAddress); |
| | | if (latestState == null) return; |
| | | if (latestState == null) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | if (latestState.RobotTaskTotalNum < MaxTaskTotalNum) |
| | | { |
| | | await ProcessTaskAsync(latestState, task, ipAddress); |
| | | await _workflowOrchestrator.ExecuteAsync(latestState, task, ipAddress); |
| | | } |
| | | } |
| | | } |
| | | catch (Exception) |
| | | { |
| | | // å¼å¸¸å¤çå·²å¨åç»ä»¶ä¸å¤ç |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å¤çæºæ¢°æä»»å¡ |
| | | /// </summary> |
| | | private async Task ProcessTaskAsync(RobotSocketState latestState, Dt_RobotTask task, string ipAddress) |
| | | { |
| | | // å¤çæ£å¨æ§è¡çä»»å¡ |
| | | if (latestState.RobotRunMode == 2 && latestState.RobotControlMode == 1 && latestState.OperStatus != "Running") |
| | | { |
| | | // åè´§å®æç¶æå¤ç |
| | | if ((latestState.CurrentAction == "PickFinished" || latestState.CurrentAction == "AllPickFinished") && latestState.RobotArmObject == 1 && |
| | | task.RobotTaskState == TaskRobotStatusEnum.RobotPickFinish.GetHashCode()) |
| | | { |
| | | await HandlePickFinishedStateAsync(latestState, task, ipAddress); |
| | | } |
| | | // æ¾è´§å®æç¶æå¤ç |
| | | else if ((latestState.CurrentAction == "PutFinished" || latestState.CurrentAction == "AllPutFinished") && latestState.OperStatus == "Homed" && |
| | | latestState.RobotArmObject == 0 && |
| | | (task.RobotTaskState == TaskRobotStatusEnum.RobotPutFinish.GetHashCode() || |
| | | task.RobotTaskState != TaskRobotStatusEnum.RobotExecuting.GetHashCode())) |
| | | { |
| | | await HandlePutFinishedStateAsync(latestState, task, ipAddress); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å¤çåè´§å®æç¶æ |
| | | /// </summary> |
| | | private async Task HandlePickFinishedStateAsync(RobotSocketState latestState, Dt_RobotTask task, string ipAddress) |
| | | { |
| | | string taskString = $"Putbattery,{task.RobotTargetAddress}"; |
| | | bool result = await _clientManager.SendToClientAsync(ipAddress, taskString); |
| | | if (result) |
| | | { |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode(); |
| | | |
| | | // éæ°è·åææ°ç¶æå¹¶æ´æ° |
| | | var stateToUpdate = _stateManager.GetState(ipAddress); |
| | | if (stateToUpdate != null) |
| | | { |
| | | stateToUpdate.CurrentTask = task; |
| | | if (_stateManager.TryUpdateStateSafely(ipAddress, stateToUpdate)) |
| | | await _robotTaskService.UpdateRobotTaskAsync(task); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å¤çæ¾è´§å®æç¶æ |
| | | /// </summary> |
| | | private async Task HandlePutFinishedStateAsync(RobotSocketState latestState, Dt_RobotTask task, string ipAddress) |
| | | { |
| | | // éæ°è·åææ°ç¶æ |
| | | var stateForUpdate = _stateManager.GetState(ipAddress); |
| | | if (stateForUpdate == null) return; |
| | | |
| | | if (!stateForUpdate.IsSplitPallet && !stateForUpdate.IsGroupPallet) |
| | | { |
| | | stateForUpdate.IsSplitPallet = task.RobotTaskType == RobotTaskTypeEnum.SplitPallet.GetHashCode(); |
| | | stateForUpdate.IsGroupPallet = task.RobotTaskType == RobotTaskTypeEnum.GroupPallet.GetHashCode() || |
| | | task.RobotTaskType == RobotTaskTypeEnum.ChangePallet.GetHashCode(); |
| | | } |
| | | |
| | | if (task.RobotTaskType == RobotTaskTypeEnum.GroupPallet.GetHashCode()) |
| | | { |
| | | string prefix = "TRAY"; |
| | | |
| | | // çæä¸¤ä¸ªæçæ¡ç |
| | | string trayBarcode1 = RobotBarcodeGenerator.GenerateTrayBarcode(prefix); |
| | | string trayBarcode2 = RobotBarcodeGenerator.GenerateTrayBarcode(prefix); |
| | | if (!trayBarcode1.IsNullOrEmpty() && !trayBarcode2.IsNullOrEmpty()) |
| | | { |
| | | stateForUpdate.CellBarcode.Add(trayBarcode1); |
| | | stateForUpdate.CellBarcode.Add(trayBarcode2); |
| | | await _taskProcessor.SendSocketRobotPickAsync(task, stateForUpdate); |
| | | } |
| | | } |
| | | else // ä»»å¡å¼å§æ§è¡ç´æ¥åéåè´§å°å |
| | | { |
| | | await _taskProcessor.SendSocketRobotPickAsync(task, stateForUpdate); |
| | | // å¼å¸¸å¤çå·²å¨ç»ä»¶å
é¨è¿è¡ï¼Job å±ä¿æå
åºååè¯ä¹ã |
| | | } |
| | | } |
| | | } |
| | |
| | | using System.Net.Sockets; |
| | | using System.Net.Sockets; |
| | | using WIDESEAWCS_Common; |
| | | using WIDESEAWCS_Common.HttpEnum; |
| | | using WIDESEAWCS_Common.TaskEnum; |
| | | using WIDESEAWCS_Core.Caches; |
| | | using WIDESEAWCS_Core.Helper; |
| | | using WIDESEAWCS_DTO.Stock; |
| | | using WIDESEAWCS_DTO.TaskInfo; |
| | | using WIDESEAWCS_ITaskInfoService; |
| | | using WIDESEAWCS_Model.Models; |
| | | using WIDESEAWCS_Tasks.SocketServer; |
| | | using WIDESEAWCS_Tasks.Workflow.Abstractions; |
| | | |
| | | namespace WIDESEAWCS_Tasks |
| | | { |
| | | /// <summary> |
| | | /// æºæ¢°ææ¶æ¯å¤çå¨ - è´è´£å¤çæ¥èªTCP客æ·ç«¯çæ¶æ¯ |
| | | /// æºå¨äººæ¶æ¯è·¯ç±å
¥å£ï¼è´è´£ç¼åç¶æè¯»åãå½ä»¤ååååå
触åã |
| | | /// </summary> |
| | | public class RobotMessageHandler |
| | | public class RobotMessageHandler : IRobotMessageRouter |
| | | { |
| | | private readonly TcpSocketServer _tcpSocket; |
| | | private readonly ISocketClientGateway _socketClientGateway; |
| | | private readonly RobotStateManager _stateManager; |
| | | private readonly ICacheService _cache; |
| | | private readonly IRobotTaskService _robotTaskService; |
| | | private readonly RobotTaskProcessor _taskProcessor; |
| | | private readonly IRobotSimpleCommandHandler _simpleCommandHandler; |
| | | private readonly IRobotPrefixCommandHandler _prefixCommandHandler; |
| | | |
| | | public RobotMessageHandler( |
| | | TcpSocketServer tcpSocket, |
| | | ISocketClientGateway socketClientGateway, |
| | | RobotStateManager stateManager, |
| | | ICacheService cache, |
| | | IRobotTaskService robotTaskService, |
| | | RobotTaskProcessor taskProcessor) |
| | | IRobotSimpleCommandHandler simpleCommandHandler, |
| | | IRobotPrefixCommandHandler prefixCommandHandler) |
| | | { |
| | | _tcpSocket = tcpSocket; |
| | | _socketClientGateway = socketClientGateway; |
| | | _stateManager = stateManager; |
| | | _cache = cache; |
| | | _robotTaskService = robotTaskService; |
| | | _taskProcessor = taskProcessor; |
| | | _simpleCommandHandler = simpleCommandHandler; |
| | | _prefixCommandHandler = prefixCommandHandler; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å¤çæ¥æ¶å°çæ¶æ¯ |
| | | /// å¤çæ¥æ¶å°çæ¶æ¯ãä¿æåæè¡ä¸ºï¼ç®åå½ä»¤ååç¼å½ä»¤é½åååæ¶æ¯ã |
| | | /// </summary> |
| | | public async Task<string?> HandleMessageReceivedAsync(string message, bool isJson, TcpClient client, RobotSocketState state) |
| | | { |
| | | if (!(_cache?.TryGetValue($"{RedisPrefix.Code}:{RedisName.SocketDevices}:{client.Client.RemoteEndPoint}", out state)) ?? false) |
| | | var cacheKey = $"{RedisPrefix.Code}:{RedisName.SocketDevices}:{client.Client.RemoteEndPoint}"; |
| | | if (!_cache.TryGetValue(cacheKey, out RobotSocketState? cachedState) || cachedState == null) |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | var activeState = cachedState; |
| | | string messageLower = message.ToLowerInvariant(); |
| | | |
| | | if (await IsSimpleCommandAsync(messageLower, state)) |
| | | if (await _simpleCommandHandler.HandleAsync(messageLower, activeState)) |
| | | { |
| | | await _tcpSocket.SendMessageAsync(client, message); |
| | | if (_stateManager.TryUpdateStateSafely(state.IPAddress, state)) |
| | | return null; |
| | | } |
| | | else if (IsPrefixCommand(messageLower)) |
| | | { |
| | | await HandlePrefixCommandAsync(message, state, client); |
| | | } |
| | | |
| | | await _socketClientGateway.SendMessageAsync(client, message); |
| | | _stateManager.TryUpdateStateSafely(activeState.IPAddress, activeState); |
| | | return null; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å¤çåç¼å½ä»¤ï¼pickfinished, putfinishedï¼ |
| | | /// </summary> |
| | | private async Task HandlePrefixCommandAsync(string message, RobotSocketState state, TcpClient client) |
| | | if (_prefixCommandHandler.IsPrefixCommand(messageLower)) |
| | | { |
| | | try |
| | | { |
| | | var parts = message.Split(','); |
| | | if (parts.Length >= 1 && state.CurrentTask != null) |
| | | { |
| | | var cmd = parts[0].ToLowerInvariant(); |
| | | int[] positions = parts.Skip(1) |
| | | .Select(p => int.TryParse(p, out int value) ? value : (int?)null) |
| | | .Where(v => v.HasValue && v.Value != 0) |
| | | .Select(v => v!.Value) |
| | | .ToArray(); |
| | | |
| | | var task = await _robotTaskService.Repository.QueryFirstAsync(x => x.RobotTaskId == state.CurrentTask.RobotTaskId); |
| | | |
| | | if (cmd.StartsWith("pickfinished")) |
| | | { |
| | | await HandlePickFinishedAsync(state, positions, task, client); |
| | | } |
| | | else if (cmd.StartsWith("putfinished")) |
| | | { |
| | | await HandlePutFinishedAsync(state, positions, task, client); |
| | | await _prefixCommandHandler.HandleAsync(message, activeState, client); |
| | | } |
| | | |
| | | await _tcpSocket.SendMessageAsync(client, message); |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Console.WriteLine($"RobotJob MessageReceived Error: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å¤çåè´§å®æå½ä»¤ |
| | | /// </summary> |
| | | private async Task HandlePickFinishedAsync(RobotSocketState state, int[] positions, Dt_RobotTask? task, TcpClient client) |
| | | { |
| | | if (state.IsSplitPallet) |
| | | { |
| | | var stockDTO = RobotTaskProcessor.BuildStockDTO(state, positions); |
| | | state.LastPickPositions = positions; |
| | | |
| | | var result = _taskProcessor.PostSplitPalletAsync(stockDTO); |
| | | |
| | | if (result.Data.Status && result.IsSuccess) |
| | | { |
| | | state.CurrentAction = "PickFinished"; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | state.CurrentAction = "PickFinished"; |
| | | } |
| | | |
| | | state.LastPickPositions = positions; |
| | | if (task != null) |
| | | { |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotPickFinish.GetHashCode(); |
| | | if (_stateManager.TryUpdateStateSafely(state.IPAddress, state)) |
| | | await _robotTaskService.Repository.UpdateDataAsync(task); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å¤çæ¾è´§å®æå½ä»¤ |
| | | /// </summary> |
| | | private async Task HandlePutFinishedAsync(RobotSocketState state, int[] positions, Dt_RobotTask? task, TcpClient client) |
| | | { |
| | | bool putSuccess = true; |
| | | if (state.IsGroupPallet) |
| | | { |
| | | state.LastPutPositions = positions; |
| | | var stockDTO = RobotTaskProcessor.BuildStockDTO(state, positions); |
| | | var configKey = state.CurrentTask?.RobotTaskType == RobotTaskTypeEnum.ChangePallet.GetHashCode() |
| | | ? nameof(ConfigKey.ChangePalletAsync) : nameof(ConfigKey.GroupPalletAsync); |
| | | |
| | | var result = _taskProcessor.PostGroupPalletAsync(configKey, stockDTO); |
| | | putSuccess = result.Data.Status && result.IsSuccess; |
| | | } |
| | | |
| | | if (putSuccess) |
| | | { |
| | | state.CurrentAction = "PutFinished"; |
| | | state.RobotTaskTotalNum += positions.Length; |
| | | if (task != null) |
| | | { |
| | | task.RobotTaskTotalNum += positions.Length; |
| | | } |
| | | } |
| | | |
| | | if (task != null) |
| | | { |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotPutFinish.GetHashCode(); |
| | | if (_stateManager.TryUpdateStateSafely(state.IPAddress, state)) |
| | | await _robotTaskService.Repository.UpdateDataAsync(task); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æºæ¢°æç®åå½ä»¤å¤ç |
| | | /// </summary> |
| | | private async Task<bool> IsSimpleCommandAsync(string message, RobotSocketState state) |
| | | { |
| | | RobotTaskTypeEnum? GetRobotTaskType() => state.CurrentTask != null ? (RobotTaskTypeEnum)state.CurrentTask.RobotTaskType : null; |
| | | switch (message) |
| | | { |
| | | case "homing": |
| | | state.OperStatus = "Homing"; |
| | | return true; |
| | | |
| | | case "homed": |
| | | state.OperStatus = "Homed"; |
| | | return true; |
| | | |
| | | case "picking": |
| | | state.CurrentAction = "Picking"; |
| | | return true; |
| | | |
| | | case "puting": |
| | | state.CurrentAction = "Putting"; |
| | | return true; |
| | | |
| | | case "allpickfinished": // åè´§å®æ |
| | | state.CurrentAction = "AllPickFinished"; |
| | | var robotTaskType = GetRobotTaskType(); |
| | | |
| | | if (robotTaskType == RobotTaskTypeEnum.SplitPallet || robotTaskType == RobotTaskTypeEnum.ChangePallet) |
| | | { |
| | | if (await _taskProcessor.HandleInboundTaskAsync(state, useSourceAddress: true)) |
| | | { |
| | | _taskProcessor.DeleteTask(state.CurrentTask.RobotTaskId); |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | |
| | | case "allputfinished": // æ¾è´§å®æ |
| | | state.CurrentAction = "AllPutFinished"; |
| | | robotTaskType = GetRobotTaskType(); |
| | | |
| | | if (robotTaskType == RobotTaskTypeEnum.GroupPallet || robotTaskType == RobotTaskTypeEnum.ChangePallet) |
| | | { |
| | | if (await _taskProcessor.HandleInboundTaskAsync(state, useSourceAddress: false)) |
| | | { |
| | | _taskProcessor.DeleteTask(state.CurrentTask.RobotTaskId); |
| | | state.CurrentTask = null; |
| | | state.RobotTaskTotalNum = 0; |
| | | state.CellBarcode = new List<string>(); |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | |
| | | case "running": |
| | | state.OperStatus = "Running"; |
| | | return true; |
| | | |
| | | case "pausing": |
| | | state.OperStatus = "Pausing"; |
| | | return true; |
| | | |
| | | case "warming": |
| | | state.OperStatus = "Warming"; |
| | | return true; |
| | | |
| | | case "emstoping": |
| | | state.OperStatus = "Emstoping"; |
| | | return true; |
| | | |
| | | case "runmode,1": |
| | | state.RobotRunMode = 1; |
| | | return true; |
| | | |
| | | case "runmode,2": |
| | | state.RobotRunMode = 2; |
| | | return true; |
| | | |
| | | case "controlmode,1": |
| | | state.RobotControlMode = 1; |
| | | return true; |
| | | |
| | | case "controlmode,2": |
| | | state.RobotControlMode = 2; |
| | | return true; |
| | | |
| | | case "armobject,1": |
| | | state.RobotArmObject = 1; |
| | | return true; |
| | | |
| | | case "armobject,0": |
| | | state.RobotArmObject = 0; |
| | | return true; |
| | | |
| | | default: |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 夿æ¯å¦ä¸ºåç¼å½ä»¤ |
| | | /// </summary> |
| | | private static bool IsPrefixCommand(string message) |
| | | { |
| | | return message.StartsWith("pickfinished") || message.StartsWith("putfinished"); |
| | | return null; |
| | | } |
| | | } |
| | | } |
| | |
| | | using Newtonsoft.Json; |
| | | using Newtonsoft.Json; |
| | | using WIDESEA_Core; |
| | | using WIDESEAWCS_Common; |
| | | using WIDESEAWCS_Common.HttpEnum; |
| | |
| | | using WIDESEAWCS_ITaskInfoService; |
| | | using WIDESEAWCS_Model.Models; |
| | | using WIDESEAWCS_QuartzJob; |
| | | using WIDESEAWCS_Tasks.SocketServer; |
| | | using WIDESEAWCS_Tasks.Workflow.Abstractions; |
| | | |
| | | namespace WIDESEAWCS_Tasks |
| | | { |
| | | /// <summary> |
| | | /// æºæ¢°æä»»å¡å¤çå¨ - è´è´£æºæ¢°æä»»å¡æ§è¡åå¤ç |
| | | /// æºå¨äººä»»å¡å¤çå¨ï¼è´è´£ä»»å¡è·åãä¸åãå
¥åºä»»å¡åä¼ ååºå DTO æå»ºã |
| | | /// </summary> |
| | | public class RobotTaskProcessor |
| | | { |
| | | private readonly TcpSocketServer _tcpSocket; |
| | | // éè¿ç½å
³è®¿é® Socketï¼é¿å
ä¸å¡å±ç´æ¥ä¾èµ TcpSocketServerã |
| | | private readonly ISocketClientGateway _socketClientGateway; |
| | | private readonly RobotStateManager _stateManager; |
| | | private readonly IRobotTaskService _robotTaskService; |
| | | private readonly ITaskService _taskService; |
| | | private readonly HttpClientHelper _httpClientHelper; |
| | | |
| | | public RobotTaskProcessor( |
| | | TcpSocketServer tcpSocket, |
| | | ISocketClientGateway socketClientGateway, |
| | | RobotStateManager stateManager, |
| | | IRobotTaskService robotTaskService, |
| | | ITaskService taskService, |
| | | HttpClientHelper httpClientHelper) |
| | | { |
| | | _tcpSocket = tcpSocket; |
| | | _socketClientGateway = socketClientGateway; |
| | | _stateManager = stateManager; |
| | | _robotTaskService = robotTaskService; |
| | | _taskService = taskService; |
| | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è·åæºæ¢°æä»»å¡ |
| | | /// æè®¾å¤ç¼ç è·åå½åæºå¨äººä»»å¡ã |
| | | /// </summary> |
| | | public Dt_RobotTask? GetTask(RobotCraneDevice robotCrane) |
| | | { |
| | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è·åæºæ¢°æä»»å¡ |
| | | /// å 餿ºå¨äººä»»å¡ã |
| | | /// </summary> |
| | | public bool? DeleteTask(int ID) |
| | | { |
| | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// åéæºæ¢°æåè´§å½ä»¤ |
| | | /// ä¸ååè´§æä»¤ï¼Pickbatteryï¼å°æºå¨äººå®¢æ·ç«¯ã |
| | | /// </summary> |
| | | public async Task SendSocketRobotPickAsync(Dt_RobotTask task, RobotSocketState state) |
| | | { |
| | | string taskString = $"Pickbattery,{task.RobotSourceAddress}"; |
| | | // åé任塿令 |
| | | bool result = await _tcpSocket.SendToClientAsync(state.IPAddress, taskString); |
| | | bool result = await _socketClientGateway.SendToClientAsync(state.IPAddress, taskString); |
| | | if (result) |
| | | { |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode(); |
| | | state.CurrentTask = task; |
| | | |
| | | // æ´æ°ç¼åä¸çç¶æï¼ä½¿ç¨å®å
¨æ´æ°é²æ¢å¹¶åè¦çï¼ |
| | | // ä¿æåè¯ä¹ï¼ä»
å¨ç¶æå®å
¨åå
¥æåååæ´æ°ä»»å¡ç¶æã |
| | | if (_stateManager.TryUpdateStateSafely(state.IPAddress, state)) |
| | | { |
| | | await _robotTaskService.UpdateRobotTaskAsync(task); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å¤çå
¥åºä»»å¡ |
| | | /// å¤çå
¥åºä»»å¡åä¼ ï¼æç/ç»ç/æ¢çåºæ¯ï¼ã |
| | | /// </summary> |
| | | public async Task<bool> HandleInboundTaskAsync(RobotSocketState state, bool useSourceAddress) |
| | | { |
| | |
| | | string SourceAddress = currentTask.RobotTargetAddressLineCode; |
| | | string TargetAddress = currentTask.RobotSourceAddressLineCode; |
| | | string PalletCode = string.Empty; |
| | | // ç´æ¥è½¬æ¢ä¸ºæä¸¾ç±»åè¿è¡æ¯è¾ |
| | | var robotTaskType = (RobotTaskTypeEnum)currentTask.RobotTaskType; |
| | | |
| | | if (useSourceAddress) |
| | |
| | | PalletType = 1, |
| | | TaskType = taskType |
| | | }; |
| | | |
| | | var result = _httpClientHelper.Post<WebResponseContent>(nameof(ConfigKey.CreateTaskInboundAsync), taskDto.ToJson()); |
| | | if (!result.Data.Status && result.IsSuccess) |
| | | { |
| | |
| | | |
| | | WMSTaskDTO taskDTO = JsonConvert.DeserializeObject<WMSTaskDTO>(result.Data.Data.ToJson() ?? string.Empty) ?? new WMSTaskDTO(); |
| | | var content = _taskService.ReceiveWMSTask(new List<WMSTaskDTO> { taskDTO }); |
| | | if (!content.Status) return false; |
| | | if (!content.Status) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | var taskInfo = JsonConvert.DeserializeObject<Dt_Task>(content.Data.ToJson() ?? string.Empty) ?? new Dt_Task(); |
| | | |
| | | string sourceAddress = taskDTO.SourceAddress; |
| | | |
| | | IDevice? device = Storage.Devices.FirstOrDefault(x => x.DeviceProDTOs.Any(d => d.DeviceChildCode == sourceAddress)); |
| | |
| | | return true; |
| | | } |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æå»ºåºåDTO |
| | | /// æå»ºåºååä¼ DTOã |
| | | /// </summary> |
| | | public static StockDTO BuildStockDTO(RobotSocketState state, int[] positions) |
| | | { |
| | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è°ç¨æçAPI |
| | | /// è°ç¨æç APIã |
| | | /// </summary> |
| | | public HttpResponseResult<WebResponseContent> PostSplitPalletAsync(StockDTO stockDTO) |
| | | { |
| | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è°ç¨ç»çææ¢çAPI |
| | | /// è°ç¨ç»ç/æ¢ç APIã |
| | | /// </summary> |
| | | public HttpResponseResult<WebResponseContent> PostGroupPalletAsync(string configKey, StockDTO stockDTO) |
| | | { |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using System.Net.Sockets; |
| | | using WIDESEAWCS_Common.HttpEnum; |
| | | using WIDESEAWCS_Common.TaskEnum; |
| | | using WIDESEAWCS_DTO.TaskInfo; |
| | | using WIDESEAWCS_ITaskInfoService; |
| | | using WIDESEAWCS_Model.Models; |
| | | using WIDESEAWCS_Tasks.Workflow.Abstractions; |
| | | |
| | | namespace WIDESEAWCS_Tasks.Workflow |
| | | { |
| | | /// <summary> |
| | | /// åç¼å½ä»¤å¤çï¼è¿ç§»å RobotMessageHandler ç pickfinished/putfinished 忝ã |
| | | /// </summary> |
| | | public class RobotPrefixCommandHandler : IRobotPrefixCommandHandler |
| | | { |
| | | private readonly IRobotTaskService _robotTaskService; |
| | | private readonly RobotTaskProcessor _taskProcessor; |
| | | private readonly RobotStateManager _stateManager; |
| | | private readonly ISocketClientGateway _socketClientGateway; |
| | | |
| | | public RobotPrefixCommandHandler( |
| | | IRobotTaskService robotTaskService, |
| | | RobotTaskProcessor taskProcessor, |
| | | RobotStateManager stateManager, |
| | | ISocketClientGateway socketClientGateway) |
| | | { |
| | | _robotTaskService = robotTaskService; |
| | | _taskProcessor = taskProcessor; |
| | | _stateManager = stateManager; |
| | | _socketClientGateway = socketClientGateway; |
| | | } |
| | | |
| | | public bool IsPrefixCommand(string message) |
| | | { |
| | | return message.StartsWith("pickfinished") || message.StartsWith("putfinished"); |
| | | } |
| | | |
| | | public async Task HandleAsync(string message, RobotSocketState state, TcpClient client) |
| | | { |
| | | try |
| | | { |
| | | var parts = message.Split(','); |
| | | if (parts.Length < 1 || state.CurrentTask == null) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | var cmd = parts[0].ToLowerInvariant(); |
| | | int[] positions = parts.Skip(1) |
| | | .Select(p => int.TryParse(p, out int value) ? value : (int?)null) |
| | | .Where(v => v.HasValue && v.Value != 0) |
| | | .Select(v => v!.Value) |
| | | .ToArray(); |
| | | |
| | | var task = await _robotTaskService.Repository.QueryFirstAsync(x => x.RobotTaskId == state.CurrentTask.RobotTaskId); |
| | | |
| | | if (cmd.StartsWith("pickfinished")) |
| | | { |
| | | await HandlePickFinishedAsync(state, positions, task); |
| | | } |
| | | else if (cmd.StartsWith("putfinished")) |
| | | { |
| | | await HandlePutFinishedAsync(state, positions, task); |
| | | } |
| | | |
| | | await _socketClientGateway.SendMessageAsync(client, message); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Console.WriteLine($"RobotJob MessageReceived Error: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | private async Task HandlePickFinishedAsync(RobotSocketState state, int[] positions, Dt_RobotTask? task) |
| | | { |
| | | if (state.IsSplitPallet) |
| | | { |
| | | var stockDTO = RobotTaskProcessor.BuildStockDTO(state, positions); |
| | | state.LastPickPositions = positions; |
| | | |
| | | var result = _taskProcessor.PostSplitPalletAsync(stockDTO); |
| | | if (result.Data.Status && result.IsSuccess) |
| | | { |
| | | state.CurrentAction = "PickFinished"; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | state.CurrentAction = "PickFinished"; |
| | | } |
| | | |
| | | state.LastPickPositions = positions; |
| | | if (task != null) |
| | | { |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotPickFinish.GetHashCode(); |
| | | if (_stateManager.TryUpdateStateSafely(state.IPAddress, state)) |
| | | { |
| | | await _robotTaskService.Repository.UpdateDataAsync(task); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private async Task HandlePutFinishedAsync(RobotSocketState state, int[] positions, Dt_RobotTask? task) |
| | | { |
| | | bool putSuccess = true; |
| | | if (state.IsGroupPallet) |
| | | { |
| | | state.LastPutPositions = positions; |
| | | var stockDTO = RobotTaskProcessor.BuildStockDTO(state, positions); |
| | | var configKey = state.CurrentTask?.RobotTaskType == RobotTaskTypeEnum.ChangePallet.GetHashCode() |
| | | ? nameof(ConfigKey.ChangePalletAsync) |
| | | : nameof(ConfigKey.GroupPalletAsync); |
| | | |
| | | var result = _taskProcessor.PostGroupPalletAsync(configKey, stockDTO); |
| | | putSuccess = result.Data.Status && result.IsSuccess; |
| | | } |
| | | |
| | | if (putSuccess) |
| | | { |
| | | state.CurrentAction = "PutFinished"; |
| | | state.RobotTaskTotalNum += positions.Length; |
| | | if (task != null) |
| | | { |
| | | task.RobotTaskTotalNum += positions.Length; |
| | | } |
| | | } |
| | | |
| | | if (task != null) |
| | | { |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotPutFinish.GetHashCode(); |
| | | if (_stateManager.TryUpdateStateSafely(state.IPAddress, state)) |
| | | { |
| | | await _robotTaskService.Repository.UpdateDataAsync(task); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using WIDESEAWCS_Common.TaskEnum; |
| | | using WIDESEAWCS_Tasks.Workflow.Abstractions; |
| | | |
| | | namespace WIDESEAWCS_Tasks.Workflow |
| | | { |
| | | /// <summary> |
| | | /// ç®åå½ä»¤å¤çï¼ä»
è¿ç§»å RobotMessageHandler ä¸çå½ä»¤åæ¯ï¼ä¸æ¹åä¸å¡è¯ä¹ã |
| | | /// </summary> |
| | | public class RobotSimpleCommandHandler : IRobotSimpleCommandHandler |
| | | { |
| | | private readonly RobotTaskProcessor _taskProcessor; |
| | | |
| | | public RobotSimpleCommandHandler(RobotTaskProcessor taskProcessor) |
| | | { |
| | | _taskProcessor = taskProcessor; |
| | | } |
| | | |
| | | public async Task<bool> HandleAsync(string message, RobotSocketState state) |
| | | { |
| | | switch (message) |
| | | { |
| | | case "homing": |
| | | state.OperStatus = "Homing"; |
| | | return true; |
| | | |
| | | case "homed": |
| | | state.OperStatus = "Homed"; |
| | | return true; |
| | | |
| | | case "picking": |
| | | state.CurrentAction = "Picking"; |
| | | return true; |
| | | |
| | | case "puting": |
| | | state.CurrentAction = "Putting"; |
| | | return true; |
| | | |
| | | case "allpickfinished": |
| | | { |
| | | state.CurrentAction = "AllPickFinished"; |
| | | var currentTask = state.CurrentTask; |
| | | if (currentTask == null) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | var robotTaskType = (RobotTaskTypeEnum)currentTask.RobotTaskType; |
| | | if (robotTaskType == RobotTaskTypeEnum.SplitPallet || robotTaskType == RobotTaskTypeEnum.ChangePallet) |
| | | { |
| | | if (await _taskProcessor.HandleInboundTaskAsync(state, useSourceAddress: true)) |
| | | { |
| | | _taskProcessor.DeleteTask(currentTask.RobotTaskId); |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | case "allputfinished": |
| | | { |
| | | state.CurrentAction = "AllPutFinished"; |
| | | var currentTask = state.CurrentTask; |
| | | if (currentTask == null) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | var robotTaskType = (RobotTaskTypeEnum)currentTask.RobotTaskType; |
| | | if (robotTaskType == RobotTaskTypeEnum.GroupPallet || robotTaskType == RobotTaskTypeEnum.ChangePallet) |
| | | { |
| | | if (await _taskProcessor.HandleInboundTaskAsync(state, useSourceAddress: false)) |
| | | { |
| | | _taskProcessor.DeleteTask(currentTask.RobotTaskId); |
| | | state.CurrentTask = null; |
| | | state.RobotTaskTotalNum = 0; |
| | | state.CellBarcode = new List<string>(); |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | case "running": |
| | | state.OperStatus = "Running"; |
| | | return true; |
| | | |
| | | case "pausing": |
| | | state.OperStatus = "Pausing"; |
| | | return true; |
| | | |
| | | case "warming": |
| | | state.OperStatus = "Warming"; |
| | | return true; |
| | | |
| | | case "emstoping": |
| | | state.OperStatus = "Emstoping"; |
| | | return true; |
| | | |
| | | case "runmode,1": |
| | | state.RobotRunMode = 1; |
| | | return true; |
| | | |
| | | case "runmode,2": |
| | | state.RobotRunMode = 2; |
| | | return true; |
| | | |
| | | case "controlmode,1": |
| | | state.RobotControlMode = 1; |
| | | return true; |
| | | |
| | | case "controlmode,2": |
| | | state.RobotControlMode = 2; |
| | | return true; |
| | | |
| | | case "armobject,1": |
| | | state.RobotArmObject = 1; |
| | | return true; |
| | | |
| | | case "armobject,0": |
| | | state.RobotArmObject = 0; |
| | | return true; |
| | | |
| | | default: |
| | | return false; |
| | | } |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using WIDESEA_Core; |
| | | using WIDESEAWCS_Common.TaskEnum; |
| | | using WIDESEAWCS_ITaskInfoService; |
| | | using WIDESEAWCS_Model.Models; |
| | | using WIDESEAWCS_Tasks.Workflow.Abstractions; |
| | | |
| | | namespace WIDESEAWCS_Tasks.Workflow |
| | | { |
| | | /// <summary> |
| | | /// RobotJob æµç¨ç¼æå¨ï¼è¿ç§»å RobotJob ç¶ææºåæ¯ï¼éä½ Job ç±»å¤æåº¦ã |
| | | /// </summary> |
| | | public class RobotWorkflowOrchestrator : IRobotWorkflowOrchestrator |
| | | { |
| | | private readonly RobotStateManager _stateManager; |
| | | private readonly RobotClientManager _clientManager; |
| | | private readonly RobotTaskProcessor _taskProcessor; |
| | | private readonly IRobotTaskService _robotTaskService; |
| | | |
| | | public RobotWorkflowOrchestrator( |
| | | RobotStateManager stateManager, |
| | | RobotClientManager clientManager, |
| | | RobotTaskProcessor taskProcessor, |
| | | IRobotTaskService robotTaskService) |
| | | { |
| | | _stateManager = stateManager; |
| | | _clientManager = clientManager; |
| | | _taskProcessor = taskProcessor; |
| | | _robotTaskService = robotTaskService; |
| | | } |
| | | |
| | | public async Task ExecuteAsync(RobotSocketState latestState, Dt_RobotTask task, string ipAddress) |
| | | { |
| | | // ä¿æåæåæ¯å¤å®æ¡ä»¶ä¸åï¼ç¡®ä¿è¡ä¸ºä¸è´ã |
| | | if (latestState.RobotRunMode == 2 && latestState.RobotControlMode == 1 && latestState.OperStatus != "Running") |
| | | { |
| | | if ((latestState.CurrentAction == "PickFinished" || latestState.CurrentAction == "AllPickFinished") |
| | | && latestState.RobotArmObject == 1 |
| | | && task.RobotTaskState == TaskRobotStatusEnum.RobotPickFinish.GetHashCode()) |
| | | { |
| | | await HandlePickFinishedStateAsync(task, ipAddress); |
| | | } |
| | | else if ((latestState.CurrentAction == "PutFinished" || latestState.CurrentAction == "AllPutFinished") |
| | | && latestState.OperStatus == "Homed" |
| | | && latestState.RobotArmObject == 0 |
| | | && (task.RobotTaskState == TaskRobotStatusEnum.RobotPutFinish.GetHashCode() |
| | | || task.RobotTaskState != TaskRobotStatusEnum.RobotExecuting.GetHashCode())) |
| | | { |
| | | await HandlePutFinishedStateAsync(task, ipAddress); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private async Task HandlePickFinishedStateAsync(Dt_RobotTask task, string ipAddress) |
| | | { |
| | | string taskString = $"Putbattery,{task.RobotTargetAddress}"; |
| | | bool result = await _clientManager.SendToClientAsync(ipAddress, taskString); |
| | | if (result) |
| | | { |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode(); |
| | | |
| | | var stateToUpdate = _stateManager.GetState(ipAddress); |
| | | if (stateToUpdate != null) |
| | | { |
| | | stateToUpdate.CurrentTask = task; |
| | | if (_stateManager.TryUpdateStateSafely(ipAddress, stateToUpdate)) |
| | | { |
| | | await _robotTaskService.UpdateRobotTaskAsync(task); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | private async Task HandlePutFinishedStateAsync(Dt_RobotTask task, string ipAddress) |
| | | { |
| | | var stateForUpdate = _stateManager.GetState(ipAddress); |
| | | if (stateForUpdate == null) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | if (!stateForUpdate.IsSplitPallet && !stateForUpdate.IsGroupPallet) |
| | | { |
| | | stateForUpdate.IsSplitPallet = task.RobotTaskType == RobotTaskTypeEnum.SplitPallet.GetHashCode(); |
| | | stateForUpdate.IsGroupPallet = task.RobotTaskType == RobotTaskTypeEnum.GroupPallet.GetHashCode() |
| | | || task.RobotTaskType == RobotTaskTypeEnum.ChangePallet.GetHashCode(); |
| | | } |
| | | |
| | | if (task.RobotTaskType == RobotTaskTypeEnum.GroupPallet.GetHashCode()) |
| | | { |
| | | const string prefix = "TRAY"; |
| | | string trayBarcode1 = RobotBarcodeGenerator.GenerateTrayBarcode(prefix); |
| | | string trayBarcode2 = RobotBarcodeGenerator.GenerateTrayBarcode(prefix); |
| | | if (!string.IsNullOrEmpty(trayBarcode1) && !string.IsNullOrEmpty(trayBarcode2)) |
| | | { |
| | | stateForUpdate.CellBarcode.Add(trayBarcode1); |
| | | stateForUpdate.CellBarcode.Add(trayBarcode2); |
| | | await _taskProcessor.SendSocketRobotPickAsync(task, stateForUpdate); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | await _taskProcessor.SendSocketRobotPickAsync(task, stateForUpdate); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using System.Net.Sockets; |
| | | using WIDESEAWCS_QuartzJob; |
| | | using WIDESEAWCS_Tasks.Workflow.Abstractions; |
| | | |
| | | namespace WIDESEAWCS_Tasks.SocketServer |
| | | { |
| | | /// <summary> |
| | | /// TcpSocketServer çéé
å¨å®ç°ï¼ä¿æåºå±è¡ä¸ºä¸åï¼ä»
åè®¿é®æ¶å£ã |
| | | /// </summary> |
| | | public class SocketClientGateway : ISocketClientGateway |
| | | { |
| | | private readonly TcpSocketServer _tcpSocket; |
| | | |
| | | public SocketClientGateway(TcpSocketServer tcpSocket) |
| | | { |
| | | _tcpSocket = tcpSocket; |
| | | } |
| | | |
| | | public Task<bool> SendToClientAsync(string clientId, string message) |
| | | { |
| | | return _tcpSocket.SendToClientAsync(clientId, message); |
| | | } |
| | | |
| | | public Task SendMessageAsync(TcpClient client, string message) |
| | | { |
| | | return _tcpSocket.SendMessageAsync(client, message); |
| | | } |
| | | |
| | | public IReadOnlyList<string> GetClientIds() |
| | | { |
| | | return _tcpSocket.GetClientIds(); |
| | | } |
| | | |
| | | public Task HandleClientAsync(TcpClient client, string clientId, CancellationToken cancellationToken, RobotSocketState robotCrane) |
| | | { |
| | | return _tcpSocket.HandleClientAsync(client, clientId, cancellationToken, robotCrane); |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | using System; |
| | | using System.Collections.Generic; |
| | | using Quartz; |
| | | using System; |
| | | using System.Diagnostics.CodeAnalysis; |
| | | using System.Linq; |
| | | using System.Text; |
| | | using System.IO; |
| | | using System.Threading.Tasks; |
| | | using Microsoft.AspNetCore.Components.Routing; |
| | | using Quartz; |
| | | using WIDESEAWCS_Common.TaskEnum; |
| | | using WIDESEAWCS_Communicator; |
| | | using WIDESEAWCS_Core.Enums; |
| | | using WIDESEAWCS_Core.Helper; |
| | | using WIDESEAWCS_ITaskInfoRepository; |
| | | using WIDESEAWCS_ITaskInfoService; |
| | | using WIDESEAWCS_Model.Models; |
| | | using WIDESEAWCS_QuartzJob; |
| | | using WIDESEAWCS_QuartzJob.DeviceBase; |
| | | using WIDESEAWCS_QuartzJob.Models; |
| | | using WIDESEAWCS_QuartzJob.Service; |
| | | using WIDESEAWCS_QuartzJob.StackerCrane.Enum; |
| | | using WIDESEAWCS_TaskInfoService; |
| | | using WIDESEAWCS_QuartzJob.StackerCrane; |
| | | using WIDESEAWCS_Tasks.StackerCraneJob; |
| | | |
| | | namespace WIDESEAWCS_Tasks |
| | |
| | | private readonly ITaskService _taskService; |
| | | private readonly ITaskExecuteDetailService _taskExecuteDetailService; |
| | | private readonly ITaskRepository _taskRepository; |
| | | private readonly IRouterService _routerService; |
| | | private readonly StackerCraneCommandConfig _config; |
| | | |
| | | public CommonStackerCraneJob(ITaskService taskService, ITaskExecuteDetailService taskExecuteDetailService, ITaskRepository taskRepository, IRouterService routerService) |
| | | private readonly StackerCraneCommandConfig _config; |
| | | private readonly StackerCraneTaskSelector _taskSelector; |
| | | private readonly StackerCraneCommandBuilder _commandBuilder; |
| | | |
| | | public CommonStackerCraneJob( |
| | | ITaskService taskService, |
| | | ITaskExecuteDetailService taskExecuteDetailService, |
| | | ITaskRepository taskRepository, |
| | | WIDESEAWCS_QuartzJob.Service.IRouterService routerService) |
| | | { |
| | | _taskService = taskService; |
| | | _taskExecuteDetailService = taskExecuteDetailService; |
| | | _taskRepository = taskRepository; |
| | | _routerService = routerService; |
| | | |
| | | _config = LoadConfig(); |
| | | _taskSelector = new StackerCraneTaskSelector(taskService, routerService); |
| | | _commandBuilder = new StackerCraneCommandBuilder(taskService, routerService, _config); |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | { |
| | | Console.WriteLine($"é
ç½®å 载失败: {ex.Message}ï¼ä½¿ç¨é»è®¤é
ç½®"); |
| | | } |
| | | |
| | | return new StackerCraneCommandConfig(); |
| | | } |
| | | |
| | |
| | | return Task.CompletedTask; |
| | | } |
| | | |
| | | // 订é
䏿¬¡ä»»å¡å®æäºä»¶ã |
| | | if (!commonStackerCrane.IsEventSubscribed) |
| | | { |
| | | commonStackerCrane.StackerCraneTaskCompletedEventHandler += CommonStackerCrane_StackerCraneTaskCompletedEventHandler; |
| | | } |
| | | |
| | | if (commonStackerCrane.IsCanSendTask(commonStackerCrane.Communicator, commonStackerCrane.DeviceProDTOs, commonStackerCrane.DeviceProtocolDetailDTOs)) |
| | | if (!commonStackerCrane.IsCanSendTask(commonStackerCrane.Communicator, commonStackerCrane.DeviceProDTOs, commonStackerCrane.DeviceProtocolDetailDTOs)) |
| | | { |
| | | return Task.CompletedTask; |
| | | } |
| | | |
| | | commonStackerCrane.CheckStackerCraneTaskCompleted(); |
| | | |
| | | Dt_Task? task = GetTask(commonStackerCrane); |
| | | if (task != null) |
| | | // ä»»å¡éæ©ä¸æ²å°ä¸ç¨éæ©å¨ã |
| | | Dt_Task? task = _taskSelector.SelectTask(commonStackerCrane); |
| | | if (task == null) |
| | | { |
| | | object? stackerCraneTaskCommand = ConvertToStackerCraneTaskCommand(task); |
| | | if (stackerCraneTaskCommand != null) |
| | | return Task.CompletedTask; |
| | | } |
| | | |
| | | // å½ä»¤æå»ºä¸æ²å°ä¸ç¨æå»ºå¨ã |
| | | object? stackerCraneTaskCommand = _commandBuilder.ConvertToStackerCraneTaskCommand(task); |
| | | if (stackerCraneTaskCommand == null) |
| | | { |
| | | return Task.CompletedTask; |
| | | } |
| | | |
| | | bool sendFlag = SendStackerCraneCommand(commonStackerCrane, stackerCraneTaskCommand); |
| | | if (sendFlag) |
| | | { |
| | |
| | | _taskService.UpdateTaskStatusToNext(task.TaskNum); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Console.WriteLine($"CommonStackerCraneJob Error: {ex.Message}"); |
| | | } |
| | | |
| | | return Task.CompletedTask; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// ä»»å¡å®æäºä»¶è®¢é
çæ¹æ³ |
| | | /// </summary> |
| | | /// <param name="sender"></param> |
| | | /// <param name="e"></param> |
| | | private void CommonStackerCrane_StackerCraneTaskCompletedEventHandler(object? sender, WIDESEAWCS_QuartzJob.StackerCrane.StackerCraneTaskCompletedEventArgs e) |
| | | private void CommonStackerCrane_StackerCraneTaskCompletedEventHandler(object? sender, StackerCraneTaskCompletedEventArgs e) |
| | | { |
| | | CommonStackerCrane? commonStackerCrane = sender as CommonStackerCrane; |
| | | if (commonStackerCrane != null) |
| | | { |
| | | //if (commonStackerCrane.GetValue<StackerCraneDBName, short>(StackerCraneDBName.WorkType) != 5) |
| | | //{ |
| | | Console.Out.WriteLine("TaskCompleted" + e.TaskNum); |
| | | _taskService.StackCraneTaskCompleted(e.TaskNum); |
| | | commonStackerCrane.SetValue(StackerCraneDBName.WorkAction, 5); |
| | | //} |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è·åä»»å¡ |
| | | /// </summary> |
| | | /// <param name="commonStackerCrane">å åæºå¯¹è±¡</param> |
| | | /// <returns></returns> |
| | | private Dt_Task? GetTask(IStackerCrane commonStackerCrane) |
| | | { |
| | | Dt_Task? task = null; |
| | | if (commonStackerCrane.LastTaskType == null) |
| | | { |
| | | task = _taskService.QueryStackerCraneTask(commonStackerCrane.DeviceCode); |
| | | } |
| | | else |
| | | { |
| | | if (commonStackerCrane.LastTaskType.GetValueOrDefault().GetTaskTypeGroup() == TaskTypeGroup.OutbondGroup) |
| | | { |
| | | task = _taskService.QueryStackerCraneInTask(commonStackerCrane.DeviceCode); |
| | | task ??= _taskService.QueryStackerCraneOutTask(commonStackerCrane.DeviceCode); |
| | | } |
| | | else |
| | | { |
| | | task = _taskService.QueryStackerCraneOutTask(commonStackerCrane.DeviceCode); |
| | | } |
| | | } |
| | | |
| | | if (task != null && task.TaskType.GetTaskTypeGroup() == TaskTypeGroup.OutbondGroup) |
| | | { |
| | | if (IsOutTaskStationAvailable(task)) |
| | | { |
| | | return task; |
| | | } |
| | | |
| | | List<string> otherOutStationCodes = _routerService.QueryNextRoutes(commonStackerCrane.DeviceCode, task.NextAddress, task.TaskType) |
| | | .Select(x => x.ChildPosi).ToList(); |
| | | List<Dt_Task> tasks = _taskService.QueryStackerCraneOutTasks(commonStackerCrane.DeviceCode, otherOutStationCodes); |
| | | foreach (var alternativeTask in tasks) |
| | | { |
| | | if (IsOutTaskStationAvailable(alternativeTask)) |
| | | { |
| | | return alternativeTask; |
| | | } |
| | | } |
| | | task = _taskService.QueryStackerCraneInTask(commonStackerCrane.DeviceCode); |
| | | } |
| | | |
| | | return task; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// åºåºä»»å¡å¤æåºåºç«å°æ¯å¦å¯ç¨ |
| | | /// </summary> |
| | | /// <param name="task">ä»»å¡å®ä½</param> |
| | | /// <returns>妿ç«å°å¯ç¨è¿åtrueï¼å¦åè¿åfalse</returns> |
| | | private bool IsOutTaskStationAvailable([NotNull] Dt_Task task) |
| | | { |
| | | Dt_Router? router = _routerService.QueryNextRoute(task.Roadway, task.NextAddress, task.TaskType); |
| | | if (router == null) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"æªæ¾å°ç«å°ã{task.NextAddress}ãä¿¡æ¯ï¼æ æ³æ ¡éªç«å°"); |
| | | return false; |
| | | } |
| | | |
| | | IDevice? device = Storage.Devices.FirstOrDefault(x => x.DeviceCode == router.ChildPosiDeviceCode); |
| | | if (device == null) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"æªæ¾å°åºåºç«å°ã{router.ChildPosiDeviceCode}ã对åºçéè®¯å¯¹è±¡ï¼æ æ³å¤æåºåºç«å°æ¯å¦è¢«å ç¨"); |
| | | return false; |
| | | } |
| | | |
| | | CommonConveyorLine conveyorLine = (CommonConveyorLine)device; |
| | | return conveyorLine.IsOccupied(router.ChildPosi); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// ä»»å¡å®ä½è½¬æ¢æå½ä»¤Model |
| | | /// </summary> |
| | | public object? ConvertToStackerCraneTaskCommand([NotNull] Dt_Task task) |
| | | { |
| | | // æ ¹æ®é
ç½®å¤æå½ä»¤ç±»å |
| | | string commandType = GetCommandType(task.Roadway); |
| | | |
| | | // å建并æå»ºå½ä»¤ |
| | | return commandType switch |
| | | { |
| | | "Formation" => BuildCommand(task, CreateFormationCommand(task)), |
| | | _ => BuildCommand(task, CreateStandardCommand(task)) |
| | | }; |
| | | } |
| | | |
| | | private static bool SendStackerCraneCommand(IStackerCrane commonStackerCrane, object command) |
| | |
| | | StackerCraneTaskCommand standardCommand => commonStackerCrane.SendCommand(standardCommand), |
| | | _ => false |
| | | }; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æ ¹æ® Roadway è·åå½ä»¤ç±»å |
| | | /// </summary> |
| | | private string GetCommandType(string roadway) |
| | | { |
| | | foreach (var mapping in _config.RoadwayCommandMapping) |
| | | { |
| | | if (roadway.Contains(mapping.Key)) |
| | | { |
| | | return mapping.Value; |
| | | } |
| | | } |
| | | return _config.DefaultCommandType; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å建æ åå åæºå½ä»¤ |
| | | /// </summary> |
| | | private static StackerCraneTaskCommand CreateStandardCommand(Dt_Task task) |
| | | { |
| | | return new StackerCraneTaskCommand |
| | | { |
| | | //Barcode = task.PalletCode, |
| | | TaskNum = task.TaskNum, |
| | | WorkType = 1, |
| | | WorkAction = 1 |
| | | }; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å建å容å åæºå½ä»¤ |
| | | /// </summary> |
| | | private static FormationStackerCraneTaskCommand CreateFormationCommand(Dt_Task task) |
| | | { |
| | | return new FormationStackerCraneTaskCommand |
| | | { |
| | | Barcode = task.PalletCode, |
| | | TaskNum = task.TaskNum, |
| | | WorkType = 1, |
| | | WorkAction = 1, |
| | | FireAlarm = 0, |
| | | HeartBeat = 0, |
| | | FieldName = string.Empty |
| | | }; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// éç¨å½ä»¤æå»ºæ¹æ³ |
| | | /// </summary> |
| | | private T? BuildCommand<T>(Dt_Task task, T command) where T : class |
| | | { |
| | | TaskTypeGroup taskTypeGroup = task.TaskType.GetTaskTypeGroup(); |
| | | |
| | | return taskTypeGroup switch |
| | | { |
| | | TaskTypeGroup.InboundGroup => BuildInboundCommand(task, command), |
| | | TaskTypeGroup.OutbondGroup => BuildOutboundCommand(task, command), |
| | | TaskTypeGroup.RelocationGroup => BuildRelocationCommand(task, command), |
| | | _ => command |
| | | }; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// éç¨å
¥åºå½ä»¤æå»º |
| | | /// </summary> |
| | | private T? BuildInboundCommand<T>(Dt_Task task, T command) where T : class |
| | | { |
| | | Dt_Router? router = _routerService.QueryNextRoute(task.CurrentAddress, task.Roadway, task.TaskType); |
| | | if (router == null) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"æªæ¾å°ç«å°ã{task.CurrentAddress}ãä¿¡æ¯ï¼æ æ³è·å对åºçå åæºåè´§ç«å°ä¿¡æ¯"); |
| | | return null; |
| | | } |
| | | |
| | | SetCommandProperty(command, "StartRow", Convert.ToInt16(router.SrmRow)); |
| | | SetCommandProperty(command, "StartColumn", Convert.ToInt16(router.SrmColumn)); |
| | | SetCommandProperty(command, "StartLayer", Convert.ToInt16(router.SrmLayer)); |
| | | |
| | | if (!TryParseAddress(task.NextAddress, out short endRow, out short endColumn, out short endLayer)) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"å
¥åºä»»å¡ç»ç¹é误ï¼ç»ç¹ï¼ã{task.NextAddress}ã"); |
| | | return null; |
| | | } |
| | | |
| | | SetCommandProperty(command, "EndRow", endRow); |
| | | SetCommandProperty(command, "EndColumn", endColumn); |
| | | SetCommandProperty(command, "EndLayer", endLayer); |
| | | |
| | | return command; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// éç¨åºåºå½ä»¤æå»º |
| | | /// </summary> |
| | | private T? BuildOutboundCommand<T>(Dt_Task task, T command) where T : class |
| | | { |
| | | Dt_Router? router = _routerService.QueryNextRoute(task.Roadway, task.TargetAddress, task.TaskType); |
| | | if (router == null) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"æªæ¾å°ç«å°ã{task.TargetAddress}ãä¿¡æ¯ï¼æ æ³è·å对åºçå åæºæ¾è´§ç«å°ä¿¡æ¯"); |
| | | return null; |
| | | } |
| | | |
| | | SetCommandProperty(command, "EndRow", Convert.ToInt16(router.SrmRow)); |
| | | SetCommandProperty(command, "EndColumn", Convert.ToInt16(router.SrmColumn)); |
| | | SetCommandProperty(command, "EndLayer", Convert.ToInt16(router.SrmLayer)); |
| | | |
| | | if (!TryParseAddress(task.CurrentAddress, out short startRow, out short startColumn, out short startLayer)) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"åºåºä»»å¡èµ·ç¹é误ï¼èµ·ç¹ï¼ã{task.CurrentAddress}ã"); |
| | | return null; |
| | | } |
| | | |
| | | SetCommandProperty(command, "StartRow", startRow); |
| | | SetCommandProperty(command, "StartColumn", startColumn); |
| | | SetCommandProperty(command, "StartLayer", startLayer); |
| | | |
| | | return command; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// éç¨ç§»åºå½ä»¤æå»º |
| | | /// </summary> |
| | | private T? BuildRelocationCommand<T>(Dt_Task task, T command) where T : class |
| | | { |
| | | if (!TryParseAddress(task.NextAddress, out short endRow, out short endColumn, out short endLayer)) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"ç§»åºä»»å¡ç»ç¹é误ï¼ç»ç¹ï¼ã{task.NextAddress}ã"); |
| | | return null; |
| | | } |
| | | |
| | | SetCommandProperty(command, "EndRow", endRow); |
| | | SetCommandProperty(command, "EndColumn", endColumn); |
| | | SetCommandProperty(command, "EndLayer", endLayer); |
| | | |
| | | if (!TryParseAddress(task.CurrentAddress, out short startRow, out short startColumn, out short startLayer)) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"ç§»åºä»»å¡èµ·ç¹é误ï¼èµ·ç¹ï¼ã{task.CurrentAddress}ã"); |
| | | return null; |
| | | } |
| | | |
| | | SetCommandProperty(command, "StartRow", startRow); |
| | | SetCommandProperty(command, "StartColumn", startColumn); |
| | | SetCommandProperty(command, "StartLayer", startLayer); |
| | | |
| | | return command; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 使ç¨åå°è®¾ç½®å½ä»¤å±æ§ |
| | | /// </summary> |
| | | private static void SetCommandProperty<T>(T command, string propertyName, object value) where T : class |
| | | { |
| | | var property = typeof(T).GetProperty(propertyName); |
| | | property?.SetValue(command, value); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è§£æå°ååç¬¦ä¸²ï¼æ ¼å¼ï¼è¡-å-å±ï¼ |
| | | /// </summary> |
| | | private bool TryParseAddress(string address, out short row, out short column, out short layer) |
| | | { |
| | | row = column = layer = 0; |
| | | |
| | | string[] parts = address.Split("-"); |
| | | if (parts.Length != 3) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | return short.TryParse(parts[0], out row) |
| | | && short.TryParse(parts[1], out column) |
| | | && short.TryParse(parts[2], out layer); |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using System; |
| | | using System.Diagnostics.CodeAnalysis; |
| | | using WIDESEAWCS_Common.TaskEnum; |
| | | using WIDESEAWCS_ITaskInfoService; |
| | | using WIDESEAWCS_Model.Models; |
| | | using WIDESEAWCS_QuartzJob.Models; |
| | | using WIDESEAWCS_QuartzJob.Service; |
| | | using WIDESEAWCS_Tasks.StackerCraneJob; |
| | | |
| | | namespace WIDESEAWCS_Tasks |
| | | { |
| | | /// <summary> |
| | | /// å åæºå½ä»¤æå»ºå¨ï¼å°è£
ä»»å¡å°å½ä»¤å¯¹è±¡ç转æ¢ä¸å°åè§£æã |
| | | /// </summary> |
| | | public class StackerCraneCommandBuilder |
| | | { |
| | | private readonly ITaskService _taskService; |
| | | private readonly IRouterService _routerService; |
| | | private readonly StackerCraneCommandConfig _config; |
| | | |
| | | public StackerCraneCommandBuilder( |
| | | ITaskService taskService, |
| | | IRouterService routerService, |
| | | StackerCraneCommandConfig config) |
| | | { |
| | | _taskService = taskService; |
| | | _routerService = routerService; |
| | | _config = config; |
| | | } |
| | | |
| | | public object? ConvertToStackerCraneTaskCommand([NotNull] Dt_Task task) |
| | | { |
| | | string commandType = GetCommandType(task.Roadway); |
| | | return commandType switch |
| | | { |
| | | "Formation" => BuildCommand(task, CreateFormationCommand(task)), |
| | | _ => BuildCommand(task, CreateStandardCommand(task)) |
| | | }; |
| | | } |
| | | |
| | | private string GetCommandType(string roadway) |
| | | { |
| | | foreach (var mapping in _config.RoadwayCommandMapping) |
| | | { |
| | | if (roadway.Contains(mapping.Key)) |
| | | { |
| | | return mapping.Value; |
| | | } |
| | | } |
| | | |
| | | return _config.DefaultCommandType; |
| | | } |
| | | |
| | | private static StackerCraneTaskCommand CreateStandardCommand(Dt_Task task) |
| | | { |
| | | return new StackerCraneTaskCommand |
| | | { |
| | | TaskNum = task.TaskNum, |
| | | WorkType = 1, |
| | | WorkAction = 1 |
| | | }; |
| | | } |
| | | |
| | | private static FormationStackerCraneTaskCommand CreateFormationCommand(Dt_Task task) |
| | | { |
| | | return new FormationStackerCraneTaskCommand |
| | | { |
| | | Barcode = task.PalletCode, |
| | | TaskNum = task.TaskNum, |
| | | WorkType = 1, |
| | | WorkAction = 1, |
| | | FireAlarm = 0, |
| | | HeartBeat = 0, |
| | | FieldName = string.Empty |
| | | }; |
| | | } |
| | | |
| | | private T? BuildCommand<T>(Dt_Task task, T command) where T : class |
| | | { |
| | | TaskTypeGroup taskTypeGroup = task.TaskType.GetTaskTypeGroup(); |
| | | |
| | | return taskTypeGroup switch |
| | | { |
| | | TaskTypeGroup.InboundGroup => BuildInboundCommand(task, command), |
| | | TaskTypeGroup.OutbondGroup => BuildOutboundCommand(task, command), |
| | | TaskTypeGroup.RelocationGroup => BuildRelocationCommand(task, command), |
| | | _ => command |
| | | }; |
| | | } |
| | | |
| | | private T? BuildInboundCommand<T>(Dt_Task task, T command) where T : class |
| | | { |
| | | Dt_Router? router = _routerService.QueryNextRoute(task.CurrentAddress, task.Roadway, task.TaskType); |
| | | if (router == null) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"æªæ¾å°ç«å°ã{task.CurrentAddress}ãä¿¡æ¯ï¼æ æ³è·å对åºçå åæºåè´§ç«å°ä¿¡æ¯"); |
| | | return null; |
| | | } |
| | | |
| | | SetCommandProperty(command, "StartRow", Convert.ToInt16(router.SrmRow)); |
| | | SetCommandProperty(command, "StartColumn", Convert.ToInt16(router.SrmColumn)); |
| | | SetCommandProperty(command, "StartLayer", Convert.ToInt16(router.SrmLayer)); |
| | | |
| | | if (!TryParseAddress(task.NextAddress, out short endRow, out short endColumn, out short endLayer)) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"å
¥åºä»»å¡ç»ç¹é误ï¼ç»ç¹ï¼ã{task.NextAddress}ã"); |
| | | return null; |
| | | } |
| | | |
| | | SetCommandProperty(command, "EndRow", endRow); |
| | | SetCommandProperty(command, "EndColumn", endColumn); |
| | | SetCommandProperty(command, "EndLayer", endLayer); |
| | | |
| | | return command; |
| | | } |
| | | |
| | | private T? BuildOutboundCommand<T>(Dt_Task task, T command) where T : class |
| | | { |
| | | Dt_Router? router = _routerService.QueryNextRoute(task.Roadway, task.TargetAddress, task.TaskType); |
| | | if (router == null) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"æªæ¾å°ç«å°ã{task.TargetAddress}ãä¿¡æ¯ï¼æ æ³è·å对åºçå åæºæ¾è´§ç«å°ä¿¡æ¯"); |
| | | return null; |
| | | } |
| | | |
| | | SetCommandProperty(command, "EndRow", Convert.ToInt16(router.SrmRow)); |
| | | SetCommandProperty(command, "EndColumn", Convert.ToInt16(router.SrmColumn)); |
| | | SetCommandProperty(command, "EndLayer", Convert.ToInt16(router.SrmLayer)); |
| | | |
| | | if (!TryParseAddress(task.CurrentAddress, out short startRow, out short startColumn, out short startLayer)) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"åºåºä»»å¡èµ·ç¹é误ï¼èµ·ç¹ï¼ã{task.CurrentAddress}ã"); |
| | | return null; |
| | | } |
| | | |
| | | SetCommandProperty(command, "StartRow", startRow); |
| | | SetCommandProperty(command, "StartColumn", startColumn); |
| | | SetCommandProperty(command, "StartLayer", startLayer); |
| | | |
| | | return command; |
| | | } |
| | | |
| | | private T? BuildRelocationCommand<T>(Dt_Task task, T command) where T : class |
| | | { |
| | | if (!TryParseAddress(task.NextAddress, out short endRow, out short endColumn, out short endLayer)) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"ç§»åºä»»å¡ç»ç¹é误ï¼ç»ç¹ï¼ã{task.NextAddress}ã"); |
| | | return null; |
| | | } |
| | | |
| | | SetCommandProperty(command, "EndRow", endRow); |
| | | SetCommandProperty(command, "EndColumn", endColumn); |
| | | SetCommandProperty(command, "EndLayer", endLayer); |
| | | |
| | | if (!TryParseAddress(task.CurrentAddress, out short startRow, out short startColumn, out short startLayer)) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"ç§»åºä»»å¡èµ·ç¹é误ï¼èµ·ç¹ï¼ã{task.CurrentAddress}ã"); |
| | | return null; |
| | | } |
| | | |
| | | SetCommandProperty(command, "StartRow", startRow); |
| | | SetCommandProperty(command, "StartColumn", startColumn); |
| | | SetCommandProperty(command, "StartLayer", startLayer); |
| | | |
| | | return command; |
| | | } |
| | | |
| | | private static void SetCommandProperty<T>(T command, string propertyName, object value) where T : class |
| | | { |
| | | var property = typeof(T).GetProperty(propertyName); |
| | | property?.SetValue(command, value); |
| | | } |
| | | |
| | | private static bool TryParseAddress(string address, out short row, out short column, out short layer) |
| | | { |
| | | row = column = layer = 0; |
| | | |
| | | string[] parts = address.Split("-"); |
| | | if (parts.Length != 3) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | return short.TryParse(parts[0], out row) |
| | | && short.TryParse(parts[1], out column) |
| | | && short.TryParse(parts[2], out layer); |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using System.Diagnostics.CodeAnalysis; |
| | | using System.Linq; |
| | | using WIDESEAWCS_Common.TaskEnum; |
| | | using WIDESEAWCS_ITaskInfoService; |
| | | using WIDESEAWCS_Model.Models; |
| | | using WIDESEAWCS_QuartzJob; |
| | | using WIDESEAWCS_QuartzJob.Models; |
| | | using WIDESEAWCS_QuartzJob.Service; |
| | | |
| | | namespace WIDESEAWCS_Tasks |
| | | { |
| | | /// <summary> |
| | | /// å åæºä»»å¡éæ©å¨ï¼å°è£
任塿éä¸ç«å°å¯ç¨æ§å¤æã |
| | | /// </summary> |
| | | public class StackerCraneTaskSelector |
| | | { |
| | | private readonly ITaskService _taskService; |
| | | private readonly IRouterService _routerService; |
| | | |
| | | public StackerCraneTaskSelector(ITaskService taskService, IRouterService routerService) |
| | | { |
| | | _taskService = taskService; |
| | | _routerService = routerService; |
| | | } |
| | | |
| | | public Dt_Task? SelectTask(IStackerCrane commonStackerCrane) |
| | | { |
| | | Dt_Task? task; |
| | | if (commonStackerCrane.LastTaskType == null) |
| | | { |
| | | task = _taskService.QueryStackerCraneTask(commonStackerCrane.DeviceCode); |
| | | } |
| | | else if (commonStackerCrane.LastTaskType.GetValueOrDefault().GetTaskTypeGroup() == TaskTypeGroup.OutbondGroup) |
| | | { |
| | | task = _taskService.QueryStackerCraneInTask(commonStackerCrane.DeviceCode); |
| | | task ??= _taskService.QueryStackerCraneOutTask(commonStackerCrane.DeviceCode); |
| | | } |
| | | else |
| | | { |
| | | task = _taskService.QueryStackerCraneOutTask(commonStackerCrane.DeviceCode); |
| | | } |
| | | |
| | | if (task != null && task.TaskType.GetTaskTypeGroup() == TaskTypeGroup.OutbondGroup) |
| | | { |
| | | if (IsOutTaskStationAvailable(task)) |
| | | { |
| | | return task; |
| | | } |
| | | |
| | | var otherOutStationCodes = _routerService |
| | | .QueryNextRoutes(commonStackerCrane.DeviceCode, task.NextAddress, task.TaskType) |
| | | .Select(x => x.ChildPosi) |
| | | .ToList(); |
| | | |
| | | var tasks = _taskService.QueryStackerCraneOutTasks(commonStackerCrane.DeviceCode, otherOutStationCodes); |
| | | foreach (var alternativeTask in tasks) |
| | | { |
| | | if (IsOutTaskStationAvailable(alternativeTask)) |
| | | { |
| | | return alternativeTask; |
| | | } |
| | | } |
| | | |
| | | task = _taskService.QueryStackerCraneInTask(commonStackerCrane.DeviceCode); |
| | | } |
| | | |
| | | return task; |
| | | } |
| | | |
| | | private bool IsOutTaskStationAvailable([NotNull] Dt_Task task) |
| | | { |
| | | Dt_Router? router = _routerService.QueryNextRoute(task.Roadway, task.NextAddress, task.TaskType); |
| | | if (router == null) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"æªæ¾å°ç«å°ã{task.NextAddress}ãä¿¡æ¯ï¼æ æ³æ ¡éªç«å°"); |
| | | return false; |
| | | } |
| | | |
| | | IDevice? device = Storage.Devices.FirstOrDefault(x => x.DeviceCode == router.ChildPosiDeviceCode); |
| | | if (device == null) |
| | | { |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"æªæ¾å°åºåºç«å°ã{router.ChildPosiDeviceCode}ã对åºçéè®¯å¯¹è±¡ï¼æ æ³å¤æåºåºç«å°æ¯å¦è¢«å ç¨"); |
| | | return false; |
| | | } |
| | | |
| | | CommonConveyorLine conveyorLine = (CommonConveyorLine)device; |
| | | return conveyorLine.IsOccupied(router.ChildPosi); |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # CommonStackerCraneJob Refactor Implementation Plan |
| | | |
| | | > **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking. |
| | | |
| | | **Goal:** å¨ä¸æ¹åä¸å¡è¡ä¸ºçåæä¸ï¼éä½ `CommonStackerCraneJob` å¤æåº¦ï¼æåâä»»å¡éæ©âåâå½ä»¤æå»ºâèè´£ã |
| | | |
| | | **Architecture:** ä¿ç `CommonStackerCraneJob` ä½ä¸º Quartz Job å
¥å£ï¼åªè´è´£è°åº¦æµç¨ï¼å°ä»»å¡éæ©é»è¾åå½ä»¤æå»ºé»è¾åå«ä¸æ²å°åç®å½æ°ç±»ãéè¿æé 彿°å
ç»è£
ä¾èµï¼ä¸ä¿®æ¹å
¨å± DIï¼ï¼ç¡®ä¿æ¹å¨èå´ä»
é `StackerCraneJob` ç®å½ã |
| | | |
| | | **Tech Stack:** .NET 6, Quartz, ç°æ WCS Task/Router æå¡æ¥å£ |
| | | |
| | | --- |
| | | |
| | | ## Chunk 1: ç»ææåä¸èè´£è½ä½ |
| | | |
| | | ### Task 1: æåä»»å¡éæ©å¨ |
| | | |
| | | **Files:** |
| | | - Create: `WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneTaskSelector.cs` |
| | | - Modify: `WIDESEAWCS_Tasks/StackerCraneJob/CommonStackerCraneJob.cs` |
| | | |
| | | - [ ] Step 1: æ°å»ºä»»å¡éæ©å¨ç±»ï¼è¿ç§» `GetTask` ä¸ `IsOutTaskStationAvailable` é»è¾ |
| | | - [ ] Step 2: å¨ Job ä¸éè¿ç§æå段ææä»»å¡éæ©å¨å®ä¾ |
| | | - [ ] Step 3: å° `GetTask` è°ç¨æ¹ä¸ºä»»å¡éæ©å¨ |
| | | |
| | | ### Task 2: æåå½ä»¤æå»ºå¨ |
| | | |
| | | **Files:** |
| | | - Create: `WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneCommandBuilder.cs` |
| | | - Modify: `WIDESEAWCS_Tasks/StackerCraneJob/CommonStackerCraneJob.cs` |
| | | |
| | | - [ ] Step 1: æ°å»ºå½ä»¤æå»ºå¨ç±»ï¼è¿ç§» `ConvertToStackerCraneTaskCommand` ä¸å½ä»¤æå»ºåæ¯é»è¾ |
| | | - [ ] Step 2: ä¿çé
置读åå¨ Job ä¸ï¼å½ä»¤æå»ºå¨æ¥åé
ç½®ä¸æå¡ä¾èµ |
| | | - [ ] Step 3: Job 䏿¿æ¢åæå½ä»¤æå»ºè°ç¨ |
| | | |
| | | ## Chunk 2: æ¸
çä¸éªè¯ |
| | | |
| | | ### Task 3: Job å
¥å£ç¦èº«ä¸æ³¨éè¡¥å
¨ |
| | | |
| | | **Files:** |
| | | - Modify: `WIDESEAWCS_Tasks/StackerCraneJob/CommonStackerCraneJob.cs` |
| | | |
| | | - [ ] Step 1: ç§»é¤å·²è¿ç§»ç§ææ¹æ³åæ ç¨ using |
| | | - [ ] Step 2: ä¿çå¹¶è¡¥å
å
³é®æµç¨æ³¨éï¼è®¢é
ãä»»å¡è·åãå½ä»¤ä¸åï¼ |
| | | |
| | | ### Task 4: ç¼è¯éªæ¶ |
| | | |
| | | **Files:** |
| | | - Modify: `WIDESEAWCS_Tasks/StackerCraneJob/CommonStackerCraneJob.cs`ï¼å¦éå¾®è°ï¼ |
| | | - Modify: `WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneTaskSelector.cs`ï¼å¦éå¾®è°ï¼ |
| | | - Modify: `WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneCommandBuilder.cs`ï¼å¦éå¾®è°ï¼ |
| | | |
| | | - [ ] Step 1: è¿è¡ `dotnet build WIDESEAWCS_Tasks/WIDESEAWCS_Tasks.csproj -c Debug` |
| | | - [ ] Step 2: è¥å¤±è´¥ï¼æç¼è¯é误æå°ä¿®å¤ç´å°éè¿ |
| | | - [ ] Step 3: è¾åºç»æä¸æ¹å¨æä»¶æ¸
å |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # ConveyorLineDispatchHandler Refactor Implementation Plan |
| | | |
| | | > **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking. |
| | | |
| | | **Goal:** å¨ä¸æ¹åè°åº¦è¡ä¸ºçåæä¸ï¼éä½ `ConveyorLineDispatchHandler` å¤æåº¦ï¼æåâå°å轮询çç¥âåâä»»å¡çéé»è¾âã |
| | | |
| | | **Architecture:** ä¿ç `ConveyorLineDispatchHandler` ä½ä¸ºæµç¨å
¥å£ï¼å°å¯æ¿æ¢çç¥ï¼è½®è¯¢å°åéæ©ï¼ä¸ä»»å¡è¿æ»¤é»è¾æå°åç®å½æ°ç±»ä¸ï¼é¿å
åæä»¶æ¿æ
å
¨é¨èè´£ã |
| | | |
| | | **Tech Stack:** .NET 6, ç°æ TaskService/RouterService ä¸ DTO 模å |
| | | |
| | | --- |
| | | |
| | | ## Chunk 1: ç»ææå |
| | | |
| | | ### Task 1: æåå°åéæ©çç¥ |
| | | |
| | | **Files:** |
| | | - Create: `WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineTargetAddressSelector.cs` |
| | | - Modify: `WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineDispatchHandler.cs` |
| | | |
| | | - [ ] Step 1: è¿ç§»è½®è¯¢å°åéæ©ä¸ç¶æç»´æ¤é»è¾ |
| | | - [ ] Step 2: Handler 䏿¹ä¸ºè°ç¨éæ©å¨ |
| | | |
| | | ### Task 2: æåä»»å¡çéå¨ |
| | | |
| | | **Files:** |
| | | - Create: `WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineTaskFilter.cs` |
| | | - Modify: `WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineDispatchHandler.cs` |
| | | |
| | | - [ ] Step 1: è¿ç§»ä»»å¡è¿æ»¤æ¡ä»¶ååéä»»å¡æå»º |
| | | - [ ] Step 2: Handler 䏿¹ä¸ºè°ç¨è¿æ»¤å¨ |
| | | |
| | | ## Chunk 2: æ¸
çä¸éªè¯ |
| | | |
| | | ### Task 3: å
¥å£ç¦èº«ä¸æ³¨é |
| | | |
| | | **Files:** |
| | | - Modify: `WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineDispatchHandler.cs` |
| | | |
| | | - [ ] Step 1: å é¤è¿ç§»åç§ææ¹æ³ |
| | | - [ ] Step 2: ä¿çå
³é®æµç¨æ³¨éï¼è¯´æè¡ä¸ºä¿æç¹ |
| | | |
| | | ### Task 4: ç¼è¯éªæ¶ |
| | | |
| | | **Files:** |
| | | - Modify: `WIDESEAWCS_Tasks/ConveyorLineNewJob/*.cs`ï¼å¦éå¾®è°ï¼ |
| | | |
| | | - [ ] Step 1: è¿è¡ `dotnet build WIDESEAWCS_Tasks/WIDESEAWCS_Tasks.csproj -c Debug` |
| | | - [ ] Step 2: æå°ä¿®å¤ç´å°éè¿ |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # FormationCommonStackerCraneJob Refactor Implementation Plan |
| | | |
| | | > **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking. |
| | | |
| | | **Goal:** å¨ä¸æ¹åä¸å¡è¡ä¸ºçåæä¸ï¼éä½ `FormationCommonStackerCraneJob` å¤æåº¦ï¼æåâä»»å¡éæ©âåâå½ä»¤æå»ºâèè´£ã |
| | | |
| | | **Architecture:** ä¿ç `FormationCommonStackerCraneJob` ä½ä¸º Quartz Job å
¥å£ï¼ä»
è´è´£è°åº¦ï¼å°ä»»å¡éæ©åå½ä»¤æå»ºä¸æ²å°åç®å½æ°ç±»ï¼éè¿æé 彿°ç»è£
ä¾èµï¼æ¹å¨éå®å¨ `FormationStackerCraneJob` ç®å½ã |
| | | |
| | | **Tech Stack:** .NET 6, Quartz, ç°æ WCS Task/Router æå¡æ¥å£ |
| | | |
| | | --- |
| | | |
| | | ## Chunk 1: ç»ææå |
| | | |
| | | ### Task 1: æåä»»å¡éæ©å¨ |
| | | |
| | | **Files:** |
| | | - Create: `WIDESEAWCS_Tasks/FormationStackerCraneJob/FormationStackerCraneTaskSelector.cs` |
| | | - Modify: `WIDESEAWCS_Tasks/FormationStackerCraneJob/FormationCommonStackerCraneJob.cs` |
| | | |
| | | - [ ] Step 1: è¿ç§»ä»»å¡æéåç«å°å¯ç¨æ§é»è¾ |
| | | - [ ] Step 2: Job 䏿¹ä¸ºè°ç¨ä»»å¡éæ©å¨ |
| | | |
| | | ### Task 2: æåå½ä»¤æå»ºå¨ |
| | | |
| | | **Files:** |
| | | - Create: `WIDESEAWCS_Tasks/FormationStackerCraneJob/FormationStackerCraneCommandBuilder.cs` |
| | | - Modify: `WIDESEAWCS_Tasks/FormationStackerCraneJob/FormationCommonStackerCraneJob.cs` |
| | | |
| | | - [ ] Step 1: è¿ç§»å½ä»¤è½¬æ¢ä¸å°åè§£æé»è¾ |
| | | - [ ] Step 2: Job 䏿¹ä¸ºè°ç¨å½ä»¤æå»ºå¨ |
| | | |
| | | ## Chunk 2: æ¸
çä¸éªè¯ |
| | | |
| | | ### Task 3: Job å
¥å£ç¦èº« |
| | | |
| | | **Files:** |
| | | - Modify: `WIDESEAWCS_Tasks/FormationStackerCraneJob/FormationCommonStackerCraneJob.cs` |
| | | |
| | | - [ ] Step 1: å é¤è¿ç§»åçç§ææ¹æ³åæ ç¨ using |
| | | - [ ] Step 2: è¡¥å
³é®æµç¨æ³¨é |
| | | |
| | | ### Task 4: ç¼è¯éªæ¶ |
| | | |
| | | **Files:** |
| | | - Modify: `WIDESEAWCS_Tasks/FormationStackerCraneJob/*.cs`ï¼å¦éå¾®è°ï¼ |
| | | |
| | | - [ ] Step 1: è¿è¡ `dotnet build WIDESEAWCS_Tasks/WIDESEAWCS_Tasks.csproj -c Debug` |
| | | - [ ] Step 2: æå°ä¿®å¤ç´å°éè¿ |