From 8e42d0c1b7ae36cff2e7c69999117911a4b6f300 Mon Sep 17 00:00:00 2001
From: wanshenmean <cathay_xy@163.com>
Date: 星期四, 26 三月 2026 17:31:06 +0800
Subject: [PATCH] feat(WCS): 完善 WIDESEAWCS_Tasks 模块代码注释
---
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/PinMachine/PinMachineCommand.cs | 42
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotBarcodeGenerator.cs | 20
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Abstractions/ISocketClientGateway.cs | 38
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Abstractions/IRobotMessageRouter.cs | 11
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Abstractions/IRobotWorkflowOrchestrator.cs | 9
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/SocketServerOptions.cs | 67
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotWorkflowOrchestrator.cs | 148 ++
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLine/ConveyorLineDBNameNew.cs | 145 +
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineTaskFilter.cs | 53
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs | 225 +++
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneTaskCommand.cs | 77
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotSocketState.cs | 108 +
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotClientManager.cs | 137 +
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/CommonConveyorLineNewJob.cs | 170 ++
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineDispatchHandler.cs | 178 ++
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/CommonStackerCraneJob.cs | 149 ++
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/SocketServerHostedService.cs | 33
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneCommandBuilder.cs | 191 ++
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.cs | 146 +
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConstraintMachine/ConstraintMachineDBName.cs | 44
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotPrefixCommandHandler.cs | 160 ++
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneCommandConfig.cs | 26
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotMessageHandler.cs | 103 +
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneTaskSelector.cs | 129 ++
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLine/ConveyorLineTaskCommandNew.cs | 126 +
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Dispose.cs | 17
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotJob.cs | 160 ++
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Clients.cs | 77
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Server.cs | 133 +
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotStateManager.cs | 102 +
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLine/CheckPalletPosition.cs | 23
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Abstractions/IRobotPrefixCommandHandler.cs | 19
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/SocketClientGateway.cs | 37
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/PinMachine/PinMachineDBName.cs | 42
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotSimpleCommandHandler.cs | 108 +
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneDBName.cs | 80
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineTargetAddressSelector.cs | 108 +
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Messaging.cs | 139 +
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Abstractions/IRobotSimpleCommandHandler.cs | 14
39 files changed, 3,015 insertions(+), 579 deletions(-)
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/CommonConveyorLineNewJob.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/CommonConveyorLineNewJob.cs
index d71d17d..2177cbb 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/CommonConveyorLineNewJob.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/CommonConveyorLineNewJob.cs
@@ -1,22 +1,3 @@
-锘�#region << 鐗� 鏈� 娉� 閲� >>
-
-/*----------------------------------------------------------------
- * 鍛藉悕绌洪棿锛歐IDESEAWCS_Tasks.ConveyorLineJob
- * 鍒涘缓鑰咃細鑳$搴�
- * 鍒涘缓鏃堕棿锛�2024/8/2 16:13:36
- * 鐗堟湰锛歏1.0.0
- * 鎻忚堪锛�
- *
- * ----------------------------------------------------------------
- * 淇敼浜猴細
- * 淇敼鏃堕棿锛�
- * 鐗堟湰锛歏1.0.1
- * 淇敼璇存槑锛�
- *
- *----------------------------------------------------------------*/
-
-#endregion << 鐗� 鏈� 娉� 閲� >>
-
using MapsterMapper;
using Microsoft.Extensions.Configuration;
using Quartz;
@@ -34,74 +15,154 @@
namespace WIDESEAWCS_Tasks
{
+ /// <summary>
+ /// 杈撻�佺嚎浠诲姟浣滀笟锛圦uartz Job锛�- 鏍稿績鎵ц閫昏緫
+ /// </summary>
+ /// <remarks>
+ /// Quartz 瀹氭椂浠诲姟锛岃礋璐e鐞嗚緭閫佺嚎鐨勪换鍔¤皟搴︺��
+ /// 浣跨敤 [DisallowConcurrentExecution] 绂佹骞跺彂鎵ц锛岀‘淇濆悓涓�璁惧鐨勪换鍔′覆琛屽鐞嗐��
+ ///
+ /// 鏍稿績鑱岃矗锛�
+ /// 1. 鑾峰彇杈撻�佺嚎鐨勬墍鏈夊瓙璁惧浣嶇疆
+ /// 2. 骞惰澶勭悊姣忎釜瀛愯澶囩殑娑堟伅
+ /// 3. 鏍规嵁浠诲姟鐘舵�佽皟鐢ㄧ浉搴旂殑璋冨害鏂规硶
+ /// 4. 澶勭悊鍏ュ簱鍜屽嚭搴撲袱澶х被浠诲姟
+ ///
+ /// 璇� Job 閫氳繃 Parallel.For 骞惰澶勭悊澶氫釜瀛愯澶囷紝鎻愰珮澶勭悊鏁堢巼銆�
+ /// </remarks>
[DisallowConcurrentExecution]
public class CommonConveyorLineNewJob : IJob
{
+ /// <summary>
+ /// 浠诲姟鏈嶅姟
+ /// </summary>
private readonly ITaskService _taskService;
+
+ /// <summary>
+ /// 浠诲姟鎵ц鏄庣粏鏈嶅姟
+ /// </summary>
private readonly ITaskExecuteDetailService _taskExecuteDetailService;
+
+ /// <summary>
+ /// 璺敱鏈嶅姟
+ /// </summary>
private readonly IRouterService _routerService;
+
+ /// <summary>
+ /// 瀵硅薄鏄犲皠鍣�
+ /// </summary>
private readonly IMapper _mapper;
+
+ /// <summary>
+ /// 杈撻�佺嚎璋冨害澶勭悊鍣�
+ /// </summary>
+ /// <remarks>
+ /// 灏佽浜嗚緭閫佺嚎涓氬姟閫昏緫鐨勫鐞嗘柟娉曘��
+ /// </remarks>
private ConveyorLineDispatchHandler _conveyorLineDispatch;
+
+ /// <summary>
+ /// HTTP 瀹㈡埛绔府鍔╃被
+ /// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬璋冪敤 WMS 绯荤粺鐨� HTTP 鎺ュ彛銆�
+ /// </remarks>
private readonly HttpClientHelper _httpClientHelper;
+ /// <summary>
+ /// 鏋勯�犲嚱鏁�
+ /// </summary>
+ /// <param name="taskService">浠诲姟鏈嶅姟</param>
+ /// <param name="taskExecuteDetailService">浠诲姟鎵ц鏄庣粏鏈嶅姟</param>
+ /// <param name="routerService">璺敱鏈嶅姟</param>
+ /// <param name="mapper">瀵硅薄鏄犲皠鍣�</param>
+ /// <param name="httpClientHelper">HTTP 瀹㈡埛绔府鍔╃被</param>
public CommonConveyorLineNewJob(ITaskService taskService, ITaskExecuteDetailService taskExecuteDetailService, IRouterService routerService, IMapper mapper, HttpClientHelper httpClientHelper)
{
_taskService = taskService;
_taskExecuteDetailService = taskExecuteDetailService;
_routerService = routerService;
_mapper = mapper;
- _conveyorLineDispatch = new ConveyorLineDispatchHandler(_taskService, _taskExecuteDetailService, _routerService, _mapper);
_httpClientHelper = httpClientHelper;
+
+ // 鍒濆鍖栬皟搴﹀鐞嗗櫒
+ _conveyorLineDispatch = new ConveyorLineDispatchHandler(_taskService, _taskExecuteDetailService, _routerService, _mapper);
}
+ /// <summary>
+ /// Quartz Job 鐨勬墽琛屽叆鍙�
+ /// </summary>
+ /// <remarks>
+ /// 鎵ц娴佺▼锛�
+ /// 1. 浠� JobDataMap 鑾峰彇杈撻�佺嚎璁惧淇℃伅
+ /// 2. 鑾峰彇璇ヨ緭閫佺嚎鐨勬墍鏈夊瓙璁惧浣嶇疆鍒楄〃
+ /// 3. 骞惰澶勭悊姣忎釜瀛愯澶囩殑娑堟伅
+ /// 4. 妫�鏌ユ墭鐩樹綅缃紙鐗瑰畾閰嶇疆鐨勪綅缃級
+ /// 5. 鏍规嵁浠诲姟鐘舵�佸垎鍙戝埌鐩稿簲鐨勫鐞嗘柟娉�
+ ///
+ /// 骞惰澶勭悊鎻愰珮浜嗗澶氱珯鍙拌緭閫佺嚎鐨勫鐞嗘晥鐜囥��
+ /// </remarks>
+ /// <param name="context">Quartz 浣滀笟鎵ц涓婁笅鏂�</param>
public Task Execute(IJobExecutionContext context)
{
try
{
+ // 浠� JobDataMap 鑾峰彇杈撻�佺嚎璁惧鍙傛暟
CommonConveyorLine conveyorLine = (CommonConveyorLine)context.JobDetail.JobDataMap.Get("JobParams");
if (conveyorLine != null)
{
+ // 鑾峰彇璇ヨ緭閫佺嚎涓嬬殑鎵�鏈夊瓙璁惧浣嶇疆缂栫爜
List<string> childDeviceCodes = _routerService.QueryAllPositions(conveyorLine.DeviceCode);
if (childDeviceCodes == null || childDeviceCodes.Count == 0)
{
+ // 娌℃湁瀛愯澶囷紝鐩存帴杩斿洖
Console.WriteLine($"杈撻�佺嚎 {conveyorLine.DeviceCode} 娌℃湁瀛愯澶�");
return Task.CompletedTask;
}
- // 鍒涘缓骞惰閫夐」
+ // 鍒涘缓骞惰閫夐」锛岄檺鍒舵渶澶у苟鍙戞暟
var parallelOptions = new ParallelOptions
{
- MaxDegreeOfParallelism = Math.Min(childDeviceCodes.Count, Environment.ProcessorCount * 2), // 鍚堢悊闄愬埗骞跺彂鏁�
+ // 闄愬埗骞跺彂鏁帮細瀛愯澶囨暟閲忓拰 CPU 鏍稿績鏁�*2 鐨勮緝灏忓��
+ MaxDegreeOfParallelism = Math.Min(childDeviceCodes.Count, Environment.ProcessorCount * 2),
};
+
+ // 骞惰澶勭悊姣忎釜瀛愯澶�
Parallel.For(0, childDeviceCodes.Count, parallelOptions, i =>
{
string childDeviceCode = childDeviceCodes[i];
var correlationId = Guid.NewGuid().ToString("N");
try
{
+ // 璇诲彇璇ヤ綅缃殑 PLC 鍛戒护鏁版嵁
ConveyorLineTaskCommandNew command = conveyorLine.ReadCustomer<ConveyorLineTaskCommandNew>(childDeviceCode);
-
+ // 濡傛灉鍛戒护涓虹┖锛岃烦杩�
if (command == null)
{
return;
}
- if(command.WCS_ACK == 1)
+ // 濡傛灉 WCS_ACK 涓� 1锛屽厛娓呴櫎锛堣〃绀哄鐞嗚繃涓婁竴娆¤姹傦級
+ if (command.WCS_ACK == 1)
conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, 0, childDeviceCode);
- #region 妫�鏌ョ壒瀹氫綅缃槸鍚︽湁鎵樼洏
-
+ // ========== 妫�鏌ョ壒瀹氫綅缃槸鍚︽湁鎵樼洏 ==========
+ // 浠庨厤缃腑璇诲彇闇�瑕佹鏌ユ墭鐩樼殑浣嶇疆鍒楄〃
var checkPalletPositions = App.Configuration.GetSection("CheckPalletPositions")
.Get<List<CheckPalletPosition>>() ?? new List<CheckPalletPosition>();
+ // 濡傛灉褰撳墠璁惧鍦ㄦ鏌ュ垪琛ㄤ腑
if (checkPalletPositions.Any(x => x.Code == childDeviceCode))
{
+ // 妫�鏌ヨ緭閫佺嚎鐘舵�侊紙鏄惁鏈夋墭鐩橈級
if (command.CV_State.ObjToBool())
{
+ // 妫�鏌ヨ浣嶇疆鏄惁宸叉湁浠诲姟
var existingTask = _taskService.Repository.QueryFirst(x => x.TargetAddress == childDeviceCode);
if (existingTask.IsNullOrEmpty())
{
+ // 娌℃湁浠诲姟锛屽悜 WMS 璇锋眰鍑哄簱鎵樼洏浠诲姟
var position = checkPalletPositions.FirstOrDefault(x => x.Code == childDeviceCode);
var responseResult = _httpClientHelper.Post<WebResponseContent>("GetOutBoundTrayTaskAsync", new CreateTaskDto()
{
@@ -109,6 +170,7 @@
TargetAddress = childDeviceCode
}.Serialize());
+ // 濡傛灉璇锋眰鎴愬姛锛屾帴鏀� WMS 杩斿洖鐨勪换鍔�
if (responseResult.IsSuccess && responseResult.Data.Status)
{
var wmsTask = JsonSerializer.Deserialize<List<WMSTaskDTO>>(responseResult.Data.Data.Serialize());
@@ -119,32 +181,34 @@
}
}
- #endregion
+ // ========== 妫�鏌� PLC_STB 鏍囧織 ==========
+ // 鍙湁褰� PLC_STB 涓� 1 鏃舵墠澶勭悊浠诲姟
+ if (command.PLC_STB != 1) return;
- if (command.PLC_STB != 1) return;//PLC_STB=1鏃舵墠澶勭悊浠诲姟
-
+ // ========== 澶勭悊鏃犳墭鐩樻潯鐮佺殑鎯呭喌 ==========
+ // 鏃犳墭鐩樻潯鐮佹椂锛岃姹傚嚭搴撲换鍔�
if (command.Barcode.IsNullOrEmpty() || command.Barcode.Replace("\0", "") == "")
{
- //鏃犳墭鐩樺彿鏃�
_conveyorLineDispatch.RequestOutbound(conveyorLine, command, childDeviceCode);
return;
}
+ // ========== 澶勭悊宸叉湁浠诲姟鍙风殑鎯呭喌 ==========
if (command.TaskNo > 0)
{
+ // 鏌ヨ姝e湪鎵ц鐨勪换鍔�
Dt_Task task = _taskService.QueryExecutingConveyorLineTask(command.TaskNo, childDeviceCode);
if (!task.IsNullOrEmpty())
{
- // 澶勭悊浠诲姟鐘舵��
+ // 澶勭悊浠诲姟鐘舵�侊紙鏍规嵁鐘舵�佸垎鍙戝埌涓嶅悓鏂规硶锛�
ProcessTaskState(conveyorLine, command, task, childDeviceCode);
- //_conveyorLineDispatch.RequestInbound(conveyorLine, command, childDeviceCode);
return;
}
-
}
}
catch (Exception innerEx)
{
+ // 璁板綍寮傚父锛屼絾涓嶅奖鍝嶅叾浠栧瓙璁惧鐨勫鐞�
Console.Error.WriteLine($"{DateTime.UtcNow:O} [{childDeviceCode}] CorrelationId={correlationId} {innerEx}");
}
});
@@ -152,6 +216,7 @@
}
catch (Exception ex)
{
+ // 璁板綍鏁翠綋寮傚父
Console.Error.WriteLine(ex);
}
return Task.CompletedTask;
@@ -160,43 +225,56 @@
/// <summary>
/// 澶勭悊浠诲姟鐘舵��
/// </summary>
- /// <param name="conveyorLine">杈撻�佺嚎瀹炰緥瀵硅薄</param>
- /// <param name="command">璇诲彇鐨勮姹備俊鎭�</param>
- /// <param name="task">瀛愯澶囩紪鍙�</param>
- /// <param name="childDeviceCode"></param>
+ /// <remarks>
+ /// 鏍规嵁浠诲姟鐨勫綋鍓嶇姸鎬侊紝璋冪敤鐩稿簲鐨勮皟搴︽柟娉曪細
+ /// - InExecuting: 鍏ュ簱鎵ц涓� -> 璋冪敤 RequestInNextAddress
+ /// - OutExecuting: 鍑哄簱鎵ц涓� -> 鏍规嵁鏄惁鍒拌揪鐩爣鍦板潃璋冪敤瀵瑰簲鏂规硶
+ /// - InFinish: 鍏ュ簱瀹屾垚 -> 璋冪敤 ConveyorLineInFinish
+ /// - OutFinish: 鍑哄簱瀹屾垚 -> 璋冪敤 ConveyorLineOutFinish
+ /// </remarks>
+ /// <param name="conveyorLine">杈撻�佺嚎璁惧瀵硅薄</param>
+ /// <param name="command">PLC 鍛戒护鏁版嵁</param>
+ /// <param name="task">浠诲姟瀵硅薄</param>
+ /// <param name="childDeviceCode">瀛愯澶囩紪鐮�</param>
private void ProcessTaskState(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, Dt_Task task, string childDeviceCode)
{
- // 瀹氫箟鐘舵�佸父閲忥紙濡傛灉绫讳腑宸插畾涔夊垯鍙Щ闄わ級
- const int InExecuting = (int)TaskInStatusEnum.Line_InExecuting;
- const int OutExecuting = (int)TaskOutStatusEnum.Line_OutExecuting;
- const int InFinish = (int)TaskInStatusEnum.InFinish;
- const int OutFinish = (int)TaskOutStatusEnum.OutFinish;
+ // 瀹氫箟浠诲姟鐘舵�佸父閲�
+ const int InExecuting = (int)TaskInStatusEnum.Line_InExecuting; // 鍏ュ簱鎵ц涓�
+ const int OutExecuting = (int)TaskOutStatusEnum.Line_OutExecuting; // 鍑哄簱鎵ц涓�
+ const int InFinish = (int)TaskInStatusEnum.InFinish; // 鍏ュ簱瀹屾垚
+ const int OutFinish = (int)TaskOutStatusEnum.OutFinish; // 鍑哄簱瀹屾垚
+ // 鑾峰彇褰撳墠浠诲姟鐘舵��
int state = task.TaskStatus;
+
+ // 鍒ゆ柇褰撳墠瀛愯澶囨槸鍚︿负鐩爣鍦板潃
bool isTargetAddress = task.TargetAddress == childDeviceCode;
- // 澶勭悊鐘舵�侀�昏緫
+ // 鏍规嵁鐘舵�佸垎鍙戝鐞�
switch (state)
{
case InExecuting:
- //if (isTargetAddress)
- // _conveyorLineDispatch.ConveyorLineInFinish(conveyorLine, command, childDeviceCode);
- //else
+ // 鍏ュ簱鎵ц涓紝璋冪敤涓嬩竴鍦板潃澶勭悊
_conveyorLineDispatch.RequestInNextAddress(conveyorLine, command, childDeviceCode);
break;
case OutExecuting:
+ // 鍑哄簱鎵ц涓�
if (isTargetAddress)
+ // 鍒拌揪鐩爣鍦板潃锛岃皟鐢ㄥ嚭搴撳畬鎴�
_conveyorLineDispatch.ConveyorLineOutFinish(conveyorLine, command, childDeviceCode);
else
+ // 鏈埌杈剧洰鏍囧湴鍧�锛岃皟鐢ㄥ嚭搴撲笅涓�鍦板潃澶勭悊
_conveyorLineDispatch.RequestOutNextAddress(conveyorLine, command, childDeviceCode);
break;
case InFinish:
+ // 鍏ュ簱瀹屾垚
_conveyorLineDispatch.ConveyorLineInFinish(conveyorLine, command, childDeviceCode);
break;
case OutFinish:
+ // 鍑哄簱瀹屾垚
_conveyorLineDispatch.ConveyorLineOutFinish(conveyorLine, command, childDeviceCode);
break;
}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConstraintMachine/ConstraintMachineDBName.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConstraintMachine/ConstraintMachineDBName.cs
index b61f868..c24692d 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConstraintMachine/ConstraintMachineDBName.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConstraintMachine/ConstraintMachineDBName.cs
@@ -1,51 +1,81 @@
-锘縰sing System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
namespace WIDESEAWCS_Tasks
{
+ /// <summary>
+ /// 鎷樻潫鏈� PLC 瀵勫瓨鍣ㄥ悕绉版灇涓�
+ /// </summary>
+ /// <remarks>
+ /// 瀹氫箟鎷樻潫鏈轰笌 WCS 閫氫俊鏃朵娇鐢ㄧ殑 PLC 瀵勫瓨鍣ㄥ湴鍧�鍚嶇О銆�
+ /// 鎷樻潫鏈烘槸涓�绉嶇敤浜庣數姹犵敓浜х殑璁惧锛岃礋璐e浐瀹�/绾︽潫鐢垫睜鎵樼洏銆�
+ /// 璁惧鏈変笂涓嬩袱灞傜粨鏋勶紝姣忓眰閮芥湁鐙珛鐨勭墿鏂欒姹傚拰鍑烘枡淇″彿銆�
+ /// </remarks>
public enum ConstraintMachineDBName
{
/// <summary>
/// 鐗╂祦绾胯繍琛屼俊鍙�
/// </summary>
+ /// <remarks>
+ /// 琛ㄧず鐗╂祦绾匡紙杩炴帴鎷樻潫鏈虹殑杈撻�佺嚎锛夋槸鍚﹀湪杩愯銆�
+ /// </remarks>
LogisticsLineRunningSignal,
/// <summary>
/// 鎷樻潫鏈鸿繍琛屼俊鍙�
/// </summary>
+ /// <remarks>
+ /// 琛ㄧず鎷樻潫鏈烘湰韬殑杩愯鐘舵�併��
+ /// </remarks>
ConstraintMachineRunningSignal,
/// <summary>
/// 瑕佹枡璇锋眰-涓婂眰
/// </summary>
+ /// <remarks>
+ /// 涓婂眰宸ヤ綅鍚戜笂绾ц澶囧彂鍑虹殑鍘熸枡璇锋眰銆�
+ /// 闈為浂鍊艰〃绀轰笂灞傞渶瑕佽ˉ鍏呯墿鏂欍��
+ /// </remarks>
MaterialRequestUpper,
/// <summary>
/// 鎷樻潫鐩樺彲鍑烘枡-涓婂眰
/// </summary>
+ /// <remarks>
+ /// WCS 鍐欏叆鐨勪俊鍙凤紝鍛婄煡涓婂眰鎵樼洏鍙互鍑烘枡銆�
+ /// </remarks>
ConstraintTrayOutputReadyUpper,
/// <summary>
/// 鍑烘枡璇锋眰-涓婂眰
/// </summary>
+ /// <remarks>
+ /// 涓婂眰宸ヤ綅瀹屾垚鍔犲伐鍚庡彂鍑虹殑鍑烘枡璇锋眰銆�
+ /// 闈為浂鍊艰〃绀烘湁鏂欓渶瑕佽緭鍑恒��
+ /// </remarks>
OutputRequestUpper,
/// <summary>
/// 瑕佹枡璇锋眰-涓嬪眰
/// </summary>
+ /// <remarks>
+ /// 涓嬪眰宸ヤ綅鍚戜笂绾ц澶囧彂鍑虹殑鍘熸枡璇锋眰銆�
+ /// 闈為浂鍊艰〃绀轰笅灞傞渶瑕佽ˉ鍏呯墿鏂欍��
+ /// </remarks>
MaterialRequestLower,
/// <summary>
/// 鎷樻潫鐩樺彲鍑烘枡-涓嬪眰
/// </summary>
+ /// <remarks>
+ /// WCS 鍐欏叆鐨勪俊鍙凤紝鍛婄煡涓嬪眰鎵樼洏鍙互鍑烘枡銆�
+ /// </remarks>
ConstraintTrayOutputReadyLower,
/// <summary>
/// 鍑烘枡璇锋眰-涓嬪眰
/// </summary>
+ /// <remarks>
+ /// 涓嬪眰宸ヤ綅瀹屾垚鍔犲伐鍚庡彂鍑虹殑鍑烘枡璇锋眰銆�
+ /// 闈為浂鍊艰〃绀烘湁鏂欓渶瑕佽緭鍑恒��
+ /// </remarks>
OutputRequestLower
}
-}
\ No newline at end of file
+}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLine/CheckPalletPosition.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLine/CheckPalletPosition.cs
index 85a4ade..725fdca 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLine/CheckPalletPosition.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLine/CheckPalletPosition.cs
@@ -1,9 +1,32 @@
namespace WIDESEAWCS_Tasks
{
+ /// <summary>
+ /// 托盘位置检查配置实体
+ /// </summary>
+ /// <remarks>
+ /// 用于配置需要检查托盘位置的站点信息。
+ /// 当系统需要检查特定位置是否有托盘时使用此配置。
+ /// 配置来源于 appsettings.json 中的 CheckPalletPositions 节点。
+ /// </remarks>
public class CheckPalletPosition
{
+ /// <summary>
+ /// 子设备编码/位置编码
+ /// </summary>
+ /// <remarks>
+ /// 用于标识需要检查托盘的位置点。
+ /// </remarks>
public string Code { get; set; } = string.Empty;
+ /// <summary>
+ /// 仓库 ID
+ /// </summary>
+ /// <remarks>
+ /// 标识托盘所在的仓库编号。
+ /// 1: ZYRB1 对应的仓库
+ /// 2: HPRB001 对应的仓库
+ /// 3: 其他仓库
+ /// </remarks>
public int WarehouseId { get; set; } = 0;
}
}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLine/ConveyorLineDBNameNew.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLine/ConveyorLineDBNameNew.cs
index bf134f8..43e1dd8 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLine/ConveyorLineDBNameNew.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLine/ConveyorLineDBNameNew.cs
@@ -1,93 +1,164 @@
-锘�#region << 鐗� 鏈� 娉� 閲� >>
-/*----------------------------------------------------------------
- * 鍛藉悕绌洪棿锛歐IDESEAWCS_Tasks.ConveyorLineJob
- * 鍒涘缓鑰咃細鑳$搴�
- * 鍒涘缓鏃堕棿锛�2024/8/2 16:13:36
- * 鐗堟湰锛歏1.0.0
- * 鎻忚堪锛�
- *
- * ----------------------------------------------------------------
- * 淇敼浜猴細
- * 淇敼鏃堕棿锛�
- * 鐗堟湰锛歏1.0.1
- * 淇敼璇存槑锛�
- *
- *----------------------------------------------------------------*/
-#endregion << 鐗� 鏈� 娉� 閲� >>
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
namespace WIDESEAWCS_Tasks
{
+ /// <summary>
+ /// 杈撻�佺嚎 PLC 瀵勫瓨鍣ㄥ悕绉版灇涓撅紙鏂扮増锛�
+ /// </summary>
+ /// <remarks>
+ /// 瀹氫箟杈撻�佺嚎涓� WCS 閫氫俊鏃朵娇鐢ㄧ殑 PLC 瀵勫瓨鍣ㄥ湴鍧�鍚嶇О銆�
+ /// 鍖呭惈浠诲姟鍙枫�佸湴鍧�銆佺姸鎬佹爣蹇椼�佹潯鐮佺瓑瀛楁銆�
+ /// WCS 閫氳繃杩欎簺瀵勫瓨鍣ㄤ笌 PLC 浜や簰锛屽疄鐜颁换鍔$殑涓嬪彂銆佺姸鎬佸悓姝ュ拰瀹屾垚纭銆�
+ /// </remarks>
public enum ConveyorLineDBNameNew
{
/// <summary>
/// 浠诲姟鍙�
/// </summary>
+ /// <remarks>
+ /// PLC 鍜� WCS 涔嬮棿鍏变韩鐨勪换鍔℃爣璇嗗彿銆�
+ /// WCS 涓嬪彂浠诲姟鏃跺啓鍏ワ紝PLC 瀹屾垚浠诲姟鍚庝繚鎸併��
+ /// </remarks>
TaskNo,
+
/// <summary>
- /// 寮�濮嬪湴鍧�
+ /// 寮�濮嬪湴鍧�/婧愬湴鍧�
/// </summary>
+ /// <remarks>
+ /// 浠诲姟鐨勮捣濮嬩綅缃湴鍧�銆�
+ /// 鐢ㄤ簬鍏ュ簱浠诲姟鏃惰〃绀鸿揣鐗╂潵婧愶紝鍑哄簱浠诲姟鏃惰〃绀鸿揣鐗╁綋鍓嶄綅缃��
+ /// </remarks>
Source,
+
/// <summary>
- /// 鐩殑鍦板潃
+ /// 鐩爣鍦板潃
/// </summary>
+ /// <remarks>
+ /// 浠诲姟鐨勭洰鏍囦綅缃湴鍧�銆�
+ /// 鐢ㄤ簬鍏ュ簱浠诲姟鏃惰〃绀鸿揣鐗╁瓨鏀句綅缃紝鍑哄簱浠诲姟鏃惰〃绀鸿揣鐗╅�佽揪浣嶇疆銆�
+ /// </remarks>
Target,
+
/// <summary>
/// 鎵樼洏绫诲瀷
/// </summary>
+ /// <remarks>
+ /// 鏍囪瘑鎵樼洏鐨勮鏍肩被鍨嬨��
+ /// </remarks>
BoxType,
+
/// <summary>
- /// 璁惧绌洪棽鐘舵�佽緭閫佸甫鐘舵��
+ /// 杈撻�佺嚎绌洪棽鐘舵��
/// </summary>
+ /// <remarks>
+ /// 杈撻�佺嚎褰撳墠鏄惁绌洪棽銆�
+ /// 閫氬父鐢ㄤ簬鍒ゆ柇杈撻�佺嚎涓婃槸鍚︽湁璐х墿姝e湪绉诲姩銆�
+ /// </remarks>
CV_State,
+
/// <summary>
- /// 杈撻�佹晠闅滀唬鐮�
+ /// 杈撻�佺嚎鏁呴殰浠g爜
/// </summary>
+ /// <remarks>
+ /// PLC 鎶ュ憡鐨勮澶囨晠闅滀唬鐮併��
+ /// 0 琛ㄧず鏃犳晠闅滐紝闈為浂鍊艰〃绀哄叿浣撴晠闅滅被鍨嬨��
+ /// </remarks>
CV_ERRCode,
+
/// <summary>
- /// WCS涓嬪彂瀹屾垚鏃讹紝瑙﹀彂涓�1
+ /// WCS 涓嬪彂瀹屾垚鏍囧織
/// </summary>
+ /// <remarks>
+ /// WCS 涓嬪彂浠诲姟瀹屾垚鏃剁疆 1銆�
+ /// 閫氱煡 PLC 鍙互寮�濮嬪鐞嗚浠诲姟銆�
+ /// PLC 璇诲彇鍚庡簲绔嬪嵆娓呴櫎姝ゆ爣蹇椼��
+ /// </remarks>
WCS_STB,
+
/// <summary>
- /// WCS鏀跺埌瀹屾垚鏃讹紝瑙﹀彂涓�1
+ /// WCS 搴旂瓟鏍囧織
/// </summary>
+ /// <remarks>
+ /// WCS 鏀跺埌 PLC 璇锋眰鍚庡洖澶嶇殑纭鏍囧織銆�
+ /// PLC 鍙戝嚭璇锋眰鍚庣瓑寰� WCS 姝ゆ爣蹇楃疆 1銆�
+ /// </remarks>
WCS_ACK,
+
/// <summary>
- /// 瀹屾垚浠诲姟鏃讹紝瑙﹀彂涓�1
+ /// PLC 浠诲姟瀹屾垚鏍囧織
/// </summary>
+ /// <remarks>
+ /// PLC 瀹屾垚浠诲姟鏃剁疆 1銆�
+ /// 閫氱煡 WCS 浠诲姟宸插畬鎴愶紝鍙互杩涜鍚庣画澶勭悊銆�
+ /// WCS 璇诲彇鍚庡簲绔嬪嵆娓呴櫎姝ゆ爣蹇椼��
+ /// </remarks>
PLC_STB,
+
/// <summary>
- /// 鏀跺埌浠诲姟鏃讹紝瑙﹀彂涓�1
+ /// PLC 搴旂瓟鏍囧織
/// </summary>
+ /// <remarks>
+ /// PLC 鏀跺埌 WCS 鍛戒护鍚庡洖澶嶇殑纭鏍囧織銆�
+ /// WCS 涓嬪彂鍛戒护鍚庣瓑寰� PLC 姝ゆ爣蹇楃疆 1銆�
+ /// </remarks>
PLC_ACK,
+
/// <summary>
- /// 鍏ュ簱绔欏彴锛屽埌浣嶅啓1
+ /// PLC 璇锋眰鏍囧織
/// </summary>
+ /// <remarks>
+ /// PLC 涓诲姩璇锋眰鏈嶅姟鏃剁疆 1銆�
+ /// 閫氬父鐢ㄤ簬鍏ュ簱绔欏彴锛岃〃绀鸿揣鐗╁凡鍒颁綅锛岃姹� WCS 涓嬪彂浠诲姟銆�
+ /// </remarks>
PLC_REQ,
+
/// <summary>
- /// WCS鏁呴殰浠g爜
+ /// WCS 閿欒浠g爜
/// </summary>
+ /// <remarks>
+ /// WCS 鎶ュ憡鐨勪笟鍔¢敊璇唬鐮併��
+ /// 鐢ㄤ簬鏍囪瘑浠诲姟鎵ц杩囩▼涓殑涓氬姟閫昏緫閿欒銆�
+ /// </remarks>
WCS_ERRCode,
+
/// <summary>
- /// WCS鐗规畩澶勭悊鏍囪瘑(鏃嬭浆鏍囪瘑銆佸己鍒舵斁琛屻�佸惊鐜�佺壒娈婄敵璇枫�佹槸鍚﹀彔鐩樸�佹槸鍚﹀牭濉�)
+ /// WCS 鐗规畩澶勭悊鏍囪瘑
/// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬鏍囪瘑鐗规畩澶勭悊闇�姹傦紝鍖呭惈浠ヤ笅浣嶆爣蹇楋紙浠庝綆浣嶅埌楂樹綅锛夛細
+ /// - 浣�0: 鏃嬭浆鏍囪瘑
+ /// - 浣�1: 寮哄埗鏀捐
+ /// - 浣�2: 寰幆
+ /// - 浣�3: 鐗规畩鐢宠
+ /// - 浣�4: 鏄惁鍙犵洏
+ /// - 浣�5: 鏄惁鍫靛
+ /// </remarks>
WCS_Special,
+
/// <summary>
- /// 鎵嬪姩1锛岃嚜鍔�2
+ /// 璁惧鑷姩妯″紡
/// </summary>
+ /// <remarks>
+ /// 鏍囪瘑璁惧鐨勮繍琛屾ā寮忥細
+ /// - 1: 鎵嬪姩妯″紡
+ /// - 2: 鑷姩妯″紡
+ /// </remarks>
Equ_Auto,
+
/// <summary>
- /// 灏剧洏鏍囪瘑
+ /// 灏剧洏/灏炬澘鏍囪瘑
/// </summary>
+ /// <remarks>
+ /// 鏍囪瘑褰撳墠鎵樼洏鏄惁涓烘渶鍚庝竴涓紙灏剧洏锛夈��
+ /// 鐢ㄤ簬鐢垫睜鐢熶骇绾跨殑鏈�鍚庝竴閬撳伐搴忋��
+ /// </remarks>
Last_pallet,
+
/// <summary>
- /// 瀹瑰櫒鏉$爜1,瀛楃鏁扮粍
+ /// 鎵樼洏鏉$爜
/// </summary>
+ /// <remarks>
+ /// 瀛樺偍鎵樼洏鐨勬潯鐮佷俊鎭紙22涓瓧绗︼級銆�
+ /// 鐢ㄤ簬璐х墿杩借釜鍜屽簱浣嶇鐞嗐��
+ /// </remarks>
Barcode
}
}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLine/ConveyorLineTaskCommandNew.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLine/ConveyorLineTaskCommandNew.cs
index 63be0bd..da3a0fa 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLine/ConveyorLineTaskCommandNew.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLine/ConveyorLineTaskCommandNew.cs
@@ -1,110 +1,164 @@
-锘�#region << 鐗� 鏈� 娉� 閲� >>
-/*----------------------------------------------------------------
- * 鍛藉悕绌洪棿锛歐IDESEAWCS_Tasks.ConveyorLineJob
- * 鍒涘缓鑰咃細鑳$搴�
- * 鍒涘缓鏃堕棿锛�2024/8/2 16:13:36
- * 鐗堟湰锛歏1.0.0
- * 鎻忚堪锛�
- *
- * ----------------------------------------------------------------
- * 淇敼浜猴細
- * 淇敼鏃堕棿锛�
- * 鐗堟湰锛歏1.0.1
- * 淇敼璇存槑锛�
- *
- *----------------------------------------------------------------*/
-#endregion << 鐗� 鏈� 娉� 閲� >>
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Runtime.InteropServices;
-using System.Text;
-using System.Threading.Tasks;
using WIDESEAWCS_QuartzJob.DeviceBase;
namespace WIDESEAWCS_Tasks
{
+ /// <summary>
+ /// 杈撻�佺嚎 PLC 閫氫俊鍛戒护鏁版嵁绫伙紙鏂扮増锛�
+ /// </summary>
+ /// <remarks>
+ /// 缁ф壙鑷� DeviceCommand锛岀敤浜庝笌杈撻�佺嚎 PLC 杩涜閫氫俊銆�
+ /// 鍖呭惈浠诲姟鍙枫�佹簮/鐩爣鍦板潃銆佹墭鐩樻潯鐮併�乄CS/PLC 搴旂瓟鏍囧織绛夊瓧娈点��
+ /// WCS 閫氳繃杩欎簺瀛楁涓� PLC 浜や簰锛屽疄鐜颁换鍔$殑涓嬪彂銆佺姸鎬佸悓姝ュ拰瀹屾垚纭銆�
+ /// </remarks>
public class ConveyorLineTaskCommandNew : DeviceCommand
{
/// <summary>
/// 浠诲姟鍙�
/// </summary>
+ /// <remarks>
+ /// WCS 鍒嗛厤鐨勪换鍔″敮涓�鏍囪瘑鍙枫��
+ /// 鐢ㄤ簬鍦� WCS 鍜� PLC 涔嬮棿寤虹珛浠诲姟瀵瑰簲鐨勫叧鑱斻��
+ /// </remarks>
public short TaskNo { get; set; }
/// <summary>
- /// 婧愪綅缃� 寮�濮嬪湴鍧�
+ /// 婧愪綅缃�/璧峰鍦板潃
/// </summary>
- public short Source { get; set; }
+ /// <remarks>
+ /// 浠诲姟鐨勮捣濮嬩綅缃湴鍧�缂栫爜銆�
+ /// 鍏ュ簱浠诲姟鏃惰〃绀鸿揣鐗╂潵鑷摢涓珯鍙帮紝鍑哄簱浠诲姟鏃惰〃绀鸿揣鐗╁綋鍓嶆墍鍦ㄤ綅缃��
+ /// </remarks>
+ public short Source { get; set; }
/// <summary>
/// 鐩爣浣嶇疆
/// </summary>
+ /// <remarks>
+ /// 浠诲姟鐨勭洰鏍囦綅缃湴鍧�缂栫爜銆�
+ /// 鍏ュ簱浠诲姟鏃惰〃绀鸿揣鐗╁瓨鍏ュ摢涓簱浣嶏紝鍑哄簱浠诲姟鏃惰〃绀鸿揣鐗╅�佽揪鍝釜绔欏彴銆�
+ /// </remarks>
public short Target { get; set; }
/// <summary>
- /// 绠卞瀷
+ /// 绠卞瀷/鎵樼洏绫诲瀷
/// </summary>
+ /// <remarks>
+ /// 鏍囪瘑鎵樼洏鐨勮鏍肩被鍨嬶紝鐢ㄤ簬鍖哄垎涓嶅悓鐨勮揣鐗╄浇浣撱��
+ /// </remarks>
public byte BoxType { get; set; }
/// <summary>
- /// 杈撻�佺嚎鐘舵�� 璁惧绌洪棽鐘舵��
+ /// 杈撻�佺嚎鐘舵��
/// </summary>
+ /// <remarks>
+ /// 杈撻�佺嚎鐨勭┖闂�/鍗犵敤鐘舵�併��
+ /// 鐢ㄤ簬鍒ゆ柇杈撻�佺嚎鏄惁鍙互鎺ュ彈鏂颁换鍔°��
+ /// </remarks>
public byte CV_State { get; set; }
/// <summary>
/// 杈撻�佺嚎閿欒浠g爜
/// </summary>
+ /// <remarks>
+ /// PLC 鎶ュ憡鐨勮澶囨晠闅滀唬鐮併��
+ /// 0 琛ㄧず姝e父杩愯锛岄潪闆跺�艰〃绀哄叿浣撶殑鏁呴殰绫诲瀷銆�
+ /// </remarks>
public byte CV_ERRCode { get; set; }
/// <summary>
- /// WCS灏辩华鏍囧織 WCS涓嬪彂瀹屾垚鏃讹紝瑙﹀彂涓�1
+ /// WCS 涓嬪彂瀹屾垚鏍囧織
/// </summary>
+ /// <remarks>
+ /// WCS 涓嬪彂浠诲姟鏃剁疆 1锛岄�氱煡 PLC 寮�濮嬫墽琛屼换鍔°��
+ /// PLC 璇诲彇鍚庡簲绔嬪嵆娓呴櫎姝ゆ爣蹇椼��
+ /// </remarks>
public byte WCS_STB { get; set; }
/// <summary>
- /// WCS搴旂瓟鏍囧織 WCS鏀跺埌瀹屾垚鏃讹紝瑙﹀彂涓�1
+ /// WCS 搴旂瓟鏍囧織
/// </summary>
+ /// <remarks>
+ /// WCS 鏀跺埌 PLC 璇锋眰鍚庣殑鍥炲鏍囧織銆�
+ /// 褰撳�间负 1 鏃惰〃绀� WCS 宸叉敹鍒拌姹傚苟澶勭悊銆�
+ /// </remarks>
public byte WCS_ACK { get; set; }
/// <summary>
- /// PLC灏辩华鏍囧織 瀹屾垚浠诲姟鏃讹紝瑙﹀彂涓�1
+ /// PLC 浠诲姟瀹屾垚鏍囧織
/// </summary>
+ /// <remarks>
+ /// PLC 瀹屾垚浠诲姟鏃剁疆 1锛岄�氱煡 WCS 浠诲姟宸插畬鎴愩��
+ /// WCS 璇诲彇鍚庡簲娓呴櫎姝ゆ爣蹇椼��
+ /// </remarks>
public byte PLC_STB { get; set; }
/// <summary>
- /// PLC搴旂瓟鏍囧織 鏀跺埌浠诲姟鏃讹紝瑙﹀彂涓�1
+ /// PLC 搴旂瓟鏍囧織
/// </summary>
+ /// <remarks>
+ /// PLC 鏀跺埌 WCS 鍛戒护鍚庣殑鍥炲鏍囧織銆�
+ /// 褰撳�间负 1 鏃惰〃绀� PLC 宸叉敹鍒板懡浠ゃ��
+ /// </remarks>
public byte PLC_ACK { get; set; }
/// <summary>
- /// PLC璇锋眰鏍囧織 鍏ュ簱绔欏彴锛屽埌浣嶅啓1
+ /// PLC 璇锋眰鏍囧織
/// </summary>
+ /// <remarks>
+ /// PLC 涓诲姩璇锋眰鏈嶅姟鏃剁疆 1銆�
+ /// 閫氬父鐢ㄤ簬鍏ュ簱绔欏彴锛岃〃绀鸿揣鐗╁凡鍒颁綅鍙互寮�濮嬪鐞嗐��
+ /// </remarks>
public byte PLC_REQ { get; set; }
/// <summary>
- /// WCS閿欒浠g爜
+ /// WCS 閿欒浠g爜
/// </summary>
+ /// <remarks>
+ /// WCS 鎶ュ憡鐨勪笟鍔¢敊璇唬鐮併��
+ /// 鐢ㄤ簬鏍囪瘑浠诲姟鎵ц杩囩▼涓殑涓氬姟閫昏緫閿欒銆�
+ /// </remarks>
public byte WCS_ERRCode { get; set; }
/// <summary>
- /// WCS鐗规畩鏍囧織 (鏃嬭浆鏍囪瘑銆佸己鍒舵斁琛屻�佸惊鐜�佺壒娈婄敵璇枫�佹槸鍚﹀彔鐩樸�佹槸鍚﹀牭濉�)
+ /// WCS 鐗规畩鏍囧織
/// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬鏍囪瘑鐗规畩澶勭悊闇�姹傦紝鍖呭惈澶氫釜浣嶆爣蹇楋細
+ /// - 浣�0: 鏃嬭浆鏍囪瘑 - 鏄惁闇�瑕佹棆杞墭鐩�
+ /// - 浣�1: 寮哄埗鏀捐 - 蹇界暐甯歌妫�鏌ョ洿鎺ユ斁琛�
+ /// - 浣�2: 寰幆 - 鏄惁寰幆鎵ц
+ /// - 浣�3: 鐗规畩鐢宠 - 鏄惁鏈夌壒娈婅姹傞渶瑕佸鐞�
+ /// - 浣�4: 鏄惁鍙犵洏 - 鏄惁闇�瑕佸彔鐩樺鐞�
+ /// - 浣�5: 鏄惁鍫靛 - 鏄惁澶勪簬鍫靛鐘舵��
+ /// </remarks>
public byte WCS_Special { get; set; }
/// <summary>
- /// 璁惧鑷姩妯″紡 鎵嬪姩1锛岃嚜鍔�2
+ /// 璁惧鑷姩妯″紡
/// </summary>
+ /// <remarks>
+ /// 鏍囪瘑璁惧鐨勮繍琛屾ā寮忥細
+ /// - 1: 鎵嬪姩妯″紡
+ /// - 2: 鑷姩妯″紡
+ /// </remarks>
public byte Equ_Auto { get; set; }
/// <summary>
- /// 灏炬澘鏍囧織
+ /// 灏炬澘/灏剧洏鏍囧織
/// </summary>
+ /// <remarks>
+ /// 鏍囪瘑褰撳墠鎵樼洏鏄惁涓烘渶鍚庝竴涓紙灏剧洏锛夈��
+ /// 鐢ㄤ簬鐢垫睜鐢熶骇绾跨殑鏈�鍚庝竴閬撳伐搴忥紝鏍囪鏁存壒浠诲姟鐨勭粨鏉熴��
+ /// </remarks>
public byte Last_pallet { get; set; }
/// <summary>
- /// 鏉$爜锛�22涓瓧绗︼級
+ /// 鎵樼洏鏉$爜锛�22涓瓧绗︼級
/// </summary>
+ /// <remarks>
+ /// 瀛樺偍鎵樼洏鐨勬潯鐮佷俊鎭紝鐢ㄤ簬璐х墿杩借釜鍜屽簱浣嶇鐞嗐��
+ /// </remarks>
[DataLength(22)]
public string Barcode { get; set; }
}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineDispatchHandler.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineDispatchHandler.cs
index 048b49a..24700ff 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineDispatchHandler.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineDispatchHandler.cs
@@ -1,22 +1,3 @@
-锘�#region << 鐗� 鏈� 娉� 閲� >>
-
-/*----------------------------------------------------------------
- * 鍛藉悕绌洪棿锛歐IDESEAWCS_Tasks.ConveyorLineJob
- * 鍒涘缓鑰咃細鑳$搴�
- * 鍒涘缓鏃堕棿锛�2024/8/2 16:13:36
- * 鐗堟湰锛歏1.0.0
- * 鎻忚堪锛�
- *
- * ----------------------------------------------------------------
- * 淇敼浜猴細
- * 淇敼鏃堕棿锛�
- * 鐗堟湰锛歏1.0.1
- * 淇敼璇存槑锛�
- *
- *----------------------------------------------------------------*/
-
-#endregion << 鐗� 鏈� 娉� 閲� >>
-
using MapsterMapper;
using WIDESEAWCS_Common.TaskEnum;
using WIDESEAWCS_Core;
@@ -28,16 +9,66 @@
namespace WIDESEAWCS_Tasks
{
+ /// <summary>
+ /// 杈撻�佺嚎璋冨害澶勭悊鍣� - 澶勭悊杈撻�佺嚎鐨勫悇绉嶄笟鍔¤姹�
+ /// </summary>
+ /// <remarks>
+ /// 鏍稿績鑱岃矗锛�
+ /// 1. 澶勭悊杈撻�佺嚎鐨勫績璺筹紙淇濇寔杩炴帴锛�
+ /// 2. 澶勭悊鍏ュ簱璇锋眰锛圥LC 璇锋眰鍏ュ簱浠诲姟锛�
+ /// 3. 澶勭悊鍏ュ簱涓嬩竴鍦板潃锛堜换鍔℃墽琛屼腑鐨勫湴鍧�鏇存柊锛�
+ /// 4. 澶勭悊鍏ュ簱瀹屾垚
+ /// 5. 澶勭悊鍑哄簱璇锋眰
+ /// 6. 澶勭悊鍑哄簱涓嬩竴鍦板潃
+ /// 7. 澶勭悊鍑哄簱瀹屾垚
+ ///
+ /// 璇ョ被鏄緭閫佺嚎涓氬姟閫昏緫鐨勬牳蹇冿紝鏍规嵁 PLC 鐨勮姹傜被鍨嬭皟鐢ㄧ浉搴旂殑澶勭悊鏂规硶銆�
+ /// </remarks>
public class ConveyorLineDispatchHandler
{
+ /// <summary>
+ /// 浠诲姟鏈嶅姟
+ /// </summary>
private readonly ITaskService _taskService;
+
+ /// <summary>
+ /// 浠诲姟鎵ц鏄庣粏鏈嶅姟
+ /// </summary>
private readonly ITaskExecuteDetailService _taskExecuteDetailService;
+
+ /// <summary>
+ /// 璺敱鏈嶅姟
+ /// </summary>
private readonly IRouterService _routerService;
+
+ /// <summary>
+ /// 瀵硅薄鏄犲皠鍣�
+ /// </summary>
private readonly IMapper _mapper;
+ /// <summary>
+ /// 杈撻�佺嚎浠诲姟杩囨护鍣�
+ /// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬鏌ヨ寰呭鐞嗗拰鎵ц涓殑浠诲姟銆�
+ /// </remarks>
private readonly ConveyorLineTaskFilter _taskFilter;
+
+ /// <summary>
+ /// 鐩爣鍦板潃閫夋嫨鍣�
+ /// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬澶勭悊鎷樻潫鏈�/鎻掓嫈閽夋満绛夎澶囩殑涓婁笅灞傝姹傘��
+ /// </remarks>
private readonly ConveyorLineTargetAddressSelector _targetAddressSelector;
+ /// <summary>
+ /// 鏋勯�犲嚱鏁�
+ /// </summary>
+ /// <param name="taskService">浠诲姟鏈嶅姟</param>
+ /// <param name="taskExecuteDetailService">浠诲姟鎵ц鏄庣粏鏈嶅姟</param>
+ /// <param name="routerService">璺敱鏈嶅姟</param>
+ /// <param name="mapper">瀵硅薄鏄犲皠鍣�</param>
public ConveyorLineDispatchHandler(ITaskService taskService, ITaskExecuteDetailService taskExecuteDetailService, IRouterService routerService, IMapper mapper)
{
_taskService = taskService;
@@ -45,123 +76,214 @@
_routerService = routerService;
_mapper = mapper;
+ // 鍒濆鍖栦换鍔¤繃婊ゅ櫒鍜岀洰鏍囧湴鍧�閫夋嫨鍣�
_taskFilter = new ConveyorLineTaskFilter(taskService);
_targetAddressSelector = new ConveyorLineTargetAddressSelector();
}
/// <summary>
- /// 蹇冭烦澶勭悊
+ /// 澶勭悊杈撻�佺嚎蹇冭烦
/// </summary>
+ /// <remarks>
+ /// 褰撴敹鍒� PLC 鐨勫績璺充俊鍙锋椂璋冪敤銆�
+ /// 娓呴櫎浠诲姟鍙凤紝琛ㄧず褰撳墠娌℃湁鎵ц浠诲姟銆�
+ /// 杩欐槸涓轰簡淇濇寔涓� PLC 鐨勮繛鎺ユ椿璺冦��
+ /// </remarks>
+ /// <param name="conveyorLine">杈撻�佺嚎璁惧瀵硅薄</param>
+ /// <param name="command">PLC 鍛戒护鏁版嵁</param>
+ /// <param name="childDeviceCode">瀛愯澶囩紪鐮�</param>
public void HeartBeat(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode)
{
+ // 娓呴櫎浠诲姟鍙凤紝琛ㄧず褰撳墠绌洪棽
conveyorLine.SetValue(ConveyorLineDBNameNew.TaskNo, 0, childDeviceCode);
}
/// <summary>
- /// 杈撻�佺嚎璇锋眰鍏ュ簱
+ /// 澶勭悊杈撻�佺嚎鍏ュ簱璇锋眰
/// </summary>
+ /// <remarks>
+ /// 褰� PLC 璇锋眰鍏ュ簱浠诲姟鏃惰皟鐢ㄣ��
+ /// 娴佺▼锛�
+ /// 1. 鍚� WMS 璇锋眰鏂颁换鍔�
+ /// 2. 鏌ヨ寰呭鐞嗕换鍔�
+ /// 3. 涓嬪彂浠诲姟鍒� PLC
+ /// 4. 鏇存柊浠诲姟鐘舵��
+ /// </remarks>
+ /// <param name="conveyorLine">杈撻�佺嚎璁惧瀵硅薄</param>
+ /// <param name="command">PLC 鍛戒护鏁版嵁</param>
+ /// <param name="childDeviceCode">瀛愯澶囩紪鐮�</param>
public void RequestInbound(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode)
{
+ // 鍚� WMS 璇锋眰鏂颁换鍔★紙鍩轰簬鏉$爜锛�
if (_taskFilter.RequestWmsTask(command.Barcode, childDeviceCode))
{
+ // WMS 杩斿洖鎴愬姛锛屾煡璇㈠緟澶勭悊浠诲姟
Dt_Task? task = _taskFilter.QueryPendingTask(conveyorLine.DeviceCode, childDeviceCode);
if (task != null)
{
+ // 灏嗕换鍔℃槧灏勪负 PLC 鍛戒护
ConveyorLineTaskCommandNew taskCommand = _mapper.Map<ConveyorLineTaskCommandNew>(task);
+
+ // 缁ф壙 WCS_ACK 鏍囧織
taskCommand.WCS_ACK = command.WCS_ACK;
+
+ // 鍙戦�佸懡浠ゅ埌 PLC
conveyorLine.SendCommand(taskCommand, childDeviceCode);
+ // 鏇存柊浠诲姟鐘舵�佸埌涓嬩竴闃舵
_taskService.UpdateTaskStatusToNext(task);
}
}
}
/// <summary>
- /// 杈撻�佺嚎璇锋眰鍏ュ簱涓嬩竴鍦板潃
+ /// 澶勭悊杈撻�佺嚎鍏ュ簱涓嬩竴鍦板潃璇锋眰
/// </summary>
+ /// <remarks>
+ /// 褰撳叆搴撲换鍔℃墽琛屽埌鏌愪釜涓棿绔欑偣鏃惰皟鐢ㄣ��
+ /// 鏍规嵁涓嬩竴鍦板潃鍒ゆ柇鏄惁闇�瑕佷笌鎷樻潫鏈�/鎻掓嫈閽夋満绛夎澶囦氦浜掋��
+ /// </remarks>
+ /// <param name="conveyorLine">杈撻�佺嚎璁惧瀵硅薄</param>
+ /// <param name="command">PLC 鍛戒护鏁版嵁</param>
+ /// <param name="childDeviceCode">瀛愯澶囩紪鐮�</param>
public void RequestInNextAddress(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode)
{
+ // 鏌ヨ姝e湪鎵ц鐨勪换鍔�
Dt_Task? task = _taskFilter.QueryExecutingTask(command.TaskNo, childDeviceCode);
if (task == null)
{
return;
}
+
+ // 濡傛灉涓嶆槸绌烘墭鐩樹换鍔★紝澶勭悊鐩爣鍦板潃锛堜笌鎷樻潫鏈�/鎻掓嫈閽夋満浜や簰锛�
if (task.TaskType != (int)TaskOutboundTypeEnum.OutEmpty)
{
_targetAddressSelector.HandleInboundNextAddress(conveyorLine, task.NextAddress, childDeviceCode);
}
+ // 鏇存柊浠诲姟褰撳墠浣嶇疆
_ = _taskService.UpdatePosition(task.TaskNum, task.CurrentAddress);
+ // 璁剧疆 WCS_STB 鏍囧織锛岃〃绀� WCS 宸插鐞�
conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_STB, 1, childDeviceCode);
}
/// <summary>
- /// 杈撻�佺嚎鍏ュ簱瀹屾垚
+ /// 澶勭悊杈撻�佺嚎鍏ュ簱瀹屾垚
/// </summary>
+ /// <remarks>
+ /// 褰撳叆搴撲换鍔″畬鎴愭椂璋冪敤銆�
+ /// 鏇存柊浠诲姟鐘舵�佸苟鍥炲 PLC銆�
+ /// </remarks>
+ /// <param name="conveyorLine">杈撻�佺嚎璁惧瀵硅薄</param>
+ /// <param name="command">PLC 鍛戒护鏁版嵁</param>
+ /// <param name="childDeviceCode">瀛愯澶囩紪鐮�</param>
public void ConveyorLineInFinish(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode)
{
+ // 鏌ヨ姝e湪鎵ц鐨勪换鍔�
Dt_Task? task = _taskFilter.QueryExecutingTask(command.TaskNo, childDeviceCode);
if (task != null)
{
+ // 鍥炲 ACK 纭
conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, 1, childDeviceCode);
+
+ // 鏇存柊浠诲姟鐘舵�佸埌涓嬩竴闃舵锛堥�氬父鏄畬鎴愶級
WebResponseContent content = _taskService.UpdateTaskStatusToNext(task);
Console.Out.WriteLine(content.Serialize());
}
}
/// <summary>
- /// 杈撻�佺嚎璇锋眰鍑轰俊鎭�
+ /// 澶勭悊杈撻�佺嚎鍑哄簱璇锋眰
/// </summary>
+ /// <remarks>
+ /// 褰� PLC 璇锋眰鍑哄簱浠诲姟鏃惰皟鐢ㄣ��
+ /// 娴佺▼锛�
+ /// 1. 鏌ヨ寰呭鐞嗕换鍔�
+ /// 2. 涓嬪彂浠诲姟鍒� PLC
+ /// 3. 鏇存柊浠诲姟鐘舵��
+ /// </remarks>
+ /// <param name="conveyorLine">杈撻�佺嚎璁惧瀵硅薄</param>
+ /// <param name="command">PLC 鍛戒护鏁版嵁</param>
+ /// <param name="childDeviceCode">瀛愯澶囩紪鐮�</param>
public void RequestOutbound(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode)
{
+ // 鏌ヨ寰呭鐞嗕换鍔�
Dt_Task? task = _taskFilter.QueryPendingTask(conveyorLine.DeviceCode, childDeviceCode);
if (task != null)
{
+ // 璁剧疆浠诲姟鍙�
conveyorLine.SetValue(ConveyorLineDBNameNew.TaskNo, task.TaskNum, childDeviceCode);
+
+ // 璁剧疆鎵樼洏鏉$爜
conveyorLine.SetValue(ConveyorLineDBNameNew.Barcode, task.PalletCode, childDeviceCode);
+
+ // 璁剧疆鐩爣鍦板潃
conveyorLine.SetValue(ConveyorLineDBNameNew.Target, task.NextAddress, childDeviceCode);
+
+ // 鍥炲 ACK 纭
conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, 1, childDeviceCode);
+ // 鏇存柊浠诲姟鐘舵��
_taskService.UpdateTaskStatusToNext(task);
}
}
/// <summary>
- /// 杈撻�佺嚎璇锋眰鍑哄簱涓嬩竴鍦板潃
+ /// 澶勭悊杈撻�佺嚎鍑哄簱涓嬩竴鍦板潃璇锋眰
/// </summary>
+ /// <remarks>
+ /// 褰撳嚭搴撲换鍔℃墽琛屽埌鏌愪釜涓棿绔欑偣鏃惰皟鐢ㄣ��
+ /// 鏍规嵁涓嬩竴鍦板潃鍒ゆ柇鏄惁闇�瑕佷笌鎷樻潫鏈�/鎻掓嫈閽夋満绛夎澶囦氦浜掋��
+ /// </remarks>
+ /// <param name="conveyorLine">杈撻�佺嚎璁惧瀵硅薄</param>
+ /// <param name="command">PLC 鍛戒护鏁版嵁</param>
+ /// <param name="childDeviceCode">瀛愯澶囩紪鐮�</param>
public void RequestOutNextAddress(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode)
{
+ // 鏌ヨ姝e湪鎵ц鐨勪换鍔�
Dt_Task? task = _taskFilter.QueryExecutingTask(command.TaskNo, childDeviceCode);
if (task == null)
{
return;
}
+ // 濡傛灉涓嶆槸绌烘墭鐩樹换鍔★紝澶勭悊鐩爣鍦板潃
if (task.TaskType != (int)TaskOutboundTypeEnum.OutEmpty)
{
_targetAddressSelector.HandleOutboundNextAddress(conveyorLine, task.NextAddress, childDeviceCode);
}
+ // 鏇存柊浠诲姟褰撳墠浣嶇疆
_ = _taskService.UpdatePosition(task.TaskNum, task.CurrentAddress);
-
+ // 鍥炲 ACK 纭
conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, 1, childDeviceCode);
}
/// <summary>
- /// 杈撻�佺嚎鍑哄簱瀹屾垚
+ /// 澶勭悊杈撻�佺嚎鍑哄簱瀹屾垚
/// </summary>
+ /// <remarks>
+ /// 褰撳嚭搴撲换鍔″畬鎴愭椂璋冪敤銆�
+ /// 鏇存柊浠诲姟鐘舵�佸苟鍥炲 PLC銆�
+ /// </remarks>
+ /// <param name="conveyorLine">杈撻�佺嚎璁惧瀵硅薄</param>
+ /// <param name="command">PLC 鍛戒护鏁版嵁</param>
+ /// <param name="childDeviceCode">瀛愯澶囩紪鐮�</param>
public void ConveyorLineOutFinish(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode)
{
+ // 鏌ヨ姝e湪鎵ц鐨勪换鍔�
Dt_Task? task = _taskFilter.QueryExecutingTask(command.TaskNo, childDeviceCode);
if (task != null)
{
+ // 鍥炲 ACK 纭
conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, 1, childDeviceCode);
+
+ // 鏇存柊浠诲姟鐘舵�佸埌涓嬩竴闃舵锛堥�氬父鏄畬鎴愶級
WebResponseContent content = _taskService.UpdateTaskStatusToNext(task);
Console.Out.WriteLine(content.Serialize());
}
}
}
}
-
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineTargetAddressSelector.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineTargetAddressSelector.cs
index 16f7735..46e6167 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineTargetAddressSelector.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineTargetAddressSelector.cs
@@ -1,50 +1,121 @@
-锘縰sing WIDESEAWCS_QuartzJob;
+using WIDESEAWCS_QuartzJob;
+
namespace WIDESEAWCS_Tasks
{
/// <summary>
- /// 杈撻�佺嚎璁惧璇锋眰澶勭悊鍣細澶勭悊鎷樻潫鏈�/鎻掓嫈閽夋満涓婁笅灞傝姹傘��
+ /// 杈撻�佺嚎鐩爣鍦板潃閫夋嫨鍣� - 澶勭悊鎷樻潫鏈�/鎻掓嫈閽夋満鐨勪笂涓嬪眰璇锋眰
/// </summary>
+ /// <remarks>
+ /// 鏍稿績鑱岃矗锛�
+ /// 1. 澶勭悊鍏ュ簱鍦烘櫙鐨勭洰鏍囧湴鍧�閫夋嫨
+ /// 2. 澶勭悊鍑哄簱鍦烘櫙鐨勭洰鏍囧湴鍧�閫夋嫨
+ /// 3. 鍒ゆ柇鎷樻潫鏈哄拰鎻掓嫈閽夋満鐨勭墿鏂欒姹傜姸鎬�
+ /// 4. 鍗忚皟杈撻�佺嚎涓庝笂涓嬪眰璁惧涔嬮棿鐨勭墿鏂欐祦杞�
+ ///
+ /// 鎷樻潫鏈哄拰鎻掓嫈閽夋満閮芥湁涓婁笅涓ゅ眰缁撴瀯锛�
+ /// 姣忓眰閮芥湁鐙珛鐨勭墿鏂欒姹傚拰鍑烘枡淇″彿锛岄渶瑕佸垎鍒鐞嗐��
+ /// </remarks>
public class ConveyorLineTargetAddressSelector
{
+ /// <summary>
+ /// 鎷樻潫鏈哄悕绉板父閲�
+ /// </summary>
private const string ConstraintMachineName = "鎷樻潫鏈�";
+
+ /// <summary>
+ /// 鎻掓嫈閽夋満鍚嶇О甯搁噺
+ /// </summary>
private const string PinMachineName = "鎻掓嫈閽夋満";
- // 鎷樻潫鏈虹偣浣�
+ /// <summary>
+ /// 鎷樻潫鏈哄搴旂殑鐐逛綅缂栫爜鍒楄〃
+ /// </summary>
+ /// <remarks>
+ /// 褰撶洰鏍囧湴鍧�鍦ㄨ繖浜涚紪鐮佷腑鏃讹紝琛ㄧず闇�瑕佷笌鎷樻潫鏈轰氦浜掋��
+ /// </remarks>
private static readonly List<string> ConstraintMachineCodes = new List<string> { "10180", "20090" };
- // 鎻掓嫈閽夋満鐐逛綅
+
+ /// <summary>
+ /// 鎻掓嫈閽夋満瀵瑰簲鐨勭偣浣嶇紪鐮佸垪琛�
+ /// </summary>
+ /// <remarks>
+ /// 褰撶洰鏍囧湴鍧�鍦ㄨ繖浜涚紪鐮佷腑鏃讹紝琛ㄧず闇�瑕佷笌鎻掓嫈閽夋満浜や簰銆�
+ /// </remarks>
private static readonly List<string> PinMachineCodes = new List<string> { "10190", "20100" };
+ /// <summary>
+ /// 澶勭悊鍏ュ簱鍦烘櫙鐨勪笅涓�鍦板潃璇锋眰
+ /// </summary>
+ /// <remarks>
+ /// 褰撳叆搴撲换鍔℃墽琛屽埌鏌愪釜浣嶇疆鏃惰皟鐢ㄦ鏂规硶銆�
+ /// 鍒ゆ柇鐩爣璁惧鏄惁闇�瑕佺墿鏂欐垨鍙互鍑烘枡銆�
+ /// </remarks>
+ /// <param name="conveyorLine">杈撻�佺嚎璁惧瀵硅薄</param>
+ /// <param name="nextAddress">涓嬩竴鍦板潃/鐩爣璁惧缂栫爜</param>
+ /// <param name="childDeviceCode">褰撳墠瀛愯澶囩紪鐮�</param>
public void HandleInboundNextAddress(CommonConveyorLine conveyorLine, string nextAddress, string childDeviceCode)
{
+ // 璋冪敤閫氱敤澶勭悊鏂规硶锛宨sUpper = true 琛ㄧず澶勭悊涓婂眰
HandleDeviceRequest(conveyorLine, nextAddress, childDeviceCode, isUpper: true);
}
+ /// <summary>
+ /// 澶勭悊鍑哄簱鍦烘櫙鐨勪笅涓�鍦板潃璇锋眰
+ /// </summary>
+ /// <remarks>
+ /// 褰撳嚭搴撲换鍔℃墽琛屽埌鏌愪釜浣嶇疆鏃惰皟鐢ㄦ鏂规硶銆�
+ /// 鍒ゆ柇鐩爣璁惧鏄惁闇�瑕佺墿鏂欐垨鍙互鍑烘枡銆�
+ /// </remarks>
+ /// <param name="conveyorLine">杈撻�佺嚎璁惧瀵硅薄</param>
+ /// <param name="nextAddress">涓嬩竴鍦板潃/鐩爣璁惧缂栫爜</param>
+ /// <param name="childDeviceCode">褰撳墠瀛愯澶囩紪鐮�</param>
public void HandleOutboundNextAddress(CommonConveyorLine conveyorLine, string nextAddress, string childDeviceCode)
{
+ // 璋冪敤閫氱敤澶勭悊鏂规硶锛宨sUpper = false 琛ㄧず澶勭悊涓嬪眰
HandleDeviceRequest(conveyorLine, nextAddress, childDeviceCode, isUpper: false);
}
+ /// <summary>
+ /// 閫氱敤璁惧璇锋眰澶勭悊鏂规硶
+ /// </summary>
+ /// <remarks>
+ /// 鏍规嵁鐩爣鍦板潃绫诲瀷锛堟嫎鏉熸満/鎻掓嫈閽夋満锛夎皟鐢ㄧ浉搴旂殑澶勭悊閫昏緫銆�
+ /// 澶勭悊涓婁笅灞傝澶囩殑鐗╂枡璇锋眰鍜屽嚭鏂欏崗璋冦��
+ /// </remarks>
+ /// <param name="conveyorLine">杈撻�佺嚎璁惧瀵硅薄</param>
+ /// <param name="nextAddress">涓嬩竴鍦板潃/鐩爣璁惧缂栫爜</param>
+ /// <param name="childDeviceCode">褰撳墠瀛愯澶囩紪鐮�</param>
+ /// <param name="isUpper">鏄惁澶勭悊涓婂眰锛坱rue=涓婂眰锛宖alse=涓嬪眰锛�</param>
private void HandleDeviceRequest(CommonConveyorLine conveyorLine, string nextAddress, string childDeviceCode, bool isUpper)
{
+ // 鑾峰彇鍏ㄥ眬璁惧鍒楄〃
var devices = Storage.Devices;
+ // 鍒ゆ柇鐩爣璁惧绫诲瀷
if (ConstraintMachineCodes.Contains(nextAddress))
{
+ // 鎷樻潫鏈哄鐞嗗垎鏀�
+ // 鏌ユ壘鎷樻潫鏈鸿澶�
ConstraintMachine? constraint = devices.OfType<ConstraintMachine>().FirstOrDefault(d => d.DeviceName == ConstraintMachineName);
if (constraint == null)
{
+ // 鏈壘鍒版嫎鏉熸満璁惧锛岀洿鎺ヨ繑鍥�
return;
}
+ // 澶勭悊鎷樻潫鏈虹殑璇锋眰
ProcessDeviceRequest(
conveyorLine,
childDeviceCode,
+ // 鑾峰彇鐗╂枡璇锋眰鏍囧織锛堜笂灞傛垨涓嬪眰锛�
getMaterialRequest: () => isUpper
? constraint.GetValue<ConstraintMachineDBName, short>(ConstraintMachineDBName.MaterialRequestUpper) != 0
: constraint.GetValue<ConstraintMachineDBName, short>(ConstraintMachineDBName.MaterialRequestLower) != 0,
+ // 鑾峰彇鍑烘枡璇锋眰鏍囧織锛堜笂灞傛垨涓嬪眰锛�
getOutputRequest: () => isUpper
? constraint.GetValue<ConstraintMachineDBName, short>(ConstraintMachineDBName.OutputRequestUpper) != 0
: constraint.GetValue<ConstraintMachineDBName, short>(ConstraintMachineDBName.OutputRequestLower) != 0,
+ // 璁剧疆杈撳嚭灏辩华鏍囧織锛堜笂灞傛垨涓嬪眰锛�
setOutputReady: outputReq =>
{
if (isUpper)
@@ -59,21 +130,27 @@
}
else if (PinMachineCodes.Contains(nextAddress))
{
+ // 鎻掓嫈閽夋満澶勭悊鍒嗘敮
+ // 鏌ユ壘鎻掓嫈閽夋満璁惧
PinMachine? pinMachine = devices.OfType<PinMachine>().FirstOrDefault(d => d.DeviceName == PinMachineName);
if (pinMachine == null)
{
return;
}
+ // 澶勭悊鎻掓嫈閽夋満鐨勮姹�
ProcessDeviceRequest(
conveyorLine,
childDeviceCode,
+ // 鑾峰彇鐗╂枡璇锋眰鏍囧織锛堜笂灞傛垨涓嬪眰锛�
getMaterialRequest: () => isUpper
? pinMachine.GetValue<PinMachineDBName, short>(PinMachineDBName.MaterialRequestUpper) != 0
: pinMachine.GetValue<PinMachineDBName, short>(PinMachineDBName.MaterialRequestLower) != 0,
+ // 鑾峰彇鍑烘枡璇锋眰鏍囧織锛堜笂灞傛垨涓嬪眰锛�
getOutputRequest: () => isUpper
? pinMachine.GetValue<PinMachineDBName, short>(PinMachineDBName.OutputRequestUpper) != 0
: pinMachine.GetValue<PinMachineDBName, short>(PinMachineDBName.OutputRequestLower) != 0,
+ // 璁剧疆杈撳嚭灏辩华鏍囧織锛堜笂灞傛垨涓嬪眰锛�
setOutputReady: outputReq =>
{
if (isUpper)
@@ -88,6 +165,19 @@
}
}
+ /// <summary>
+ /// 澶勭悊璁惧璇锋眰鐨勬牳蹇冮�昏緫
+ /// </summary>
+ /// <remarks>
+ /// 鏍规嵁鐗╂枡璇锋眰鍜屽嚭鏂欒姹傜殑鐘舵�侊細
+ /// - 濡傛灉鏈夌墿鏂欒姹傦紝璁剧疆鐩爣鍦板潃骞跺彂閫� ACK
+ /// - 濡傛灉鏈夊嚭鏂欒姹傦紝璁剧疆璁惧鐨勮緭鍑哄氨缁爣蹇�
+ /// </remarks>
+ /// <param name="conveyorLine">杈撻�佺嚎璁惧瀵硅薄</param>
+ /// <param name="childDeviceCode">褰撳墠瀛愯澶囩紪鐮�</param>
+ /// <param name="getMaterialRequest">鑾峰彇鐗╂枡璇锋眰鐘舵�佺殑濮旀墭</param>
+ /// <param name="getOutputRequest">鑾峰彇鍑烘枡璇锋眰鐘舵�佺殑濮旀墭</param>
+ /// <param name="setOutputReady">璁剧疆杈撳嚭灏辩华鏍囧織鐨勫鎵�</param>
private static void ProcessDeviceRequest(
CommonConveyorLine conveyorLine,
string childDeviceCode,
@@ -95,19 +185,27 @@
Func<bool> getOutputRequest,
Action<bool> setOutputReady)
{
+ // 鑾峰彇鐗╂枡璇锋眰鐘舵��
bool materialReq = getMaterialRequest();
+
+ // 鑾峰彇鍑烘枡璇锋眰鐘舵��
bool outputReq = getOutputRequest();
+ // 濡傛灉璁惧闇�瑕佺墿鏂�
if (materialReq)
{
+ // 璁剧疆鐩爣鍦板潃涓� 1锛堣〃绀烘湁鏂欒繘鏉ワ級
conveyorLine.SetValue(ConveyorLineDBNameNew.Target, 1, childDeviceCode);
+
+ // 鍥炲 ACK 纭淇″彿
conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, 1, childDeviceCode);
}
else
{
+ // 璁惧涓嶉渶瑕佺墿鏂欙紝璁剧疆杈撳嚭灏辩华鏍囧織
+ // 閫氱煡璁惧鍙互缁х画鍑烘枡
setOutputReady(outputReq);
}
}
}
}
-
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineTaskFilter.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineTaskFilter.cs
index 96fdf1c..297fbd0 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineTaskFilter.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineTaskFilter.cs
@@ -1,30 +1,79 @@
-锘縰sing WIDESEAWCS_ITaskInfoService;
+using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
namespace WIDESEAWCS_Tasks
{
/// <summary>
- /// 杈撻�佺嚎浠诲姟璁块棶鍣細缁熶竴灏佽浠诲姟鏌ヨ涓� WMS 璇锋眰銆�
+ /// 杈撻�佺嚎浠诲姟杩囨护鍣� - 缁熶竴灏佽浠诲姟鏌ヨ涓� WMS 璇锋眰
/// </summary>
+ /// <remarks>
+ /// 鏍稿績鑱岃矗锛�
+ /// 1. 鏌ヨ杈撻�佺嚎鐨勫緟澶勭悊浠诲姟
+ /// 2. 鏌ヨ姝e湪鎵ц鐨勪换鍔�
+ /// 3. 鍚� WMS 璇锋眰鏂颁换鍔�
+ ///
+ /// 璇ョ被浣滀负涓氬姟灞備笌浠诲姟鏈嶅姟涔嬮棿鐨勪腑闂村眰锛�
+ /// 灏佽浜嗗父鐢ㄧ殑浠诲姟鏌ヨ鎿嶄綔锛屾彁渚涚畝娲佺殑鎺ュ彛缁欒皟鐢ㄦ柟銆�
+ /// </remarks>
public class ConveyorLineTaskFilter
{
+ /// <summary>
+ /// 浠诲姟鏈嶅姟瀹炰緥
+ /// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬璁块棶鏁版嵁搴撲腑鐨勪换鍔℃暟鎹紝浠ュ強涓� WMS 绯荤粺浜や簰銆�
+ /// </remarks>
private readonly ITaskService _taskService;
+ /// <summary>
+ /// 鏋勯�犲嚱鏁�
+ /// </summary>
+ /// <param name="taskService">浠诲姟鏈嶅姟瀹炰緥</param>
public ConveyorLineTaskFilter(ITaskService taskService)
{
_taskService = taskService;
}
+ /// <summary>
+ /// 鏌ヨ寰呭鐞嗙殑杈撻�佺嚎浠诲姟
+ /// </summary>
+ /// <remarks>
+ /// 鏍规嵁璁惧缂栫爜鍜屽瓙璁惧缂栫爜鏌ヨ鐘舵�佷负"寰呭鐞�"鐨勪换鍔°��
+ /// 鐢ㄤ簬褰� PLC 璇锋眰浠诲姟鏃讹紝WCS 鍚戞暟鎹簱鏌ヨ鍙笅鍙戠殑浠诲姟銆�
+ /// </remarks>
+ /// <param name="deviceCode">璁惧缂栫爜锛堜富璁惧锛�</param>
+ /// <param name="childDeviceCode">瀛愯澶囩紪鐮侊紙浣嶇疆缂栫爜锛�</param>
+ /// <returns>寰呭鐞嗙殑浠诲姟瀵硅薄锛屽鏋滄病鏈夊垯杩斿洖 null</returns>
public Dt_Task? QueryPendingTask(string deviceCode, string childDeviceCode)
{
return _taskService.QueryConveyorLineTask(deviceCode, childDeviceCode);
}
+ /// <summary>
+ /// 鏌ヨ姝e湪鎵ц鐨勮緭閫佺嚎浠诲姟
+ /// </summary>
+ /// <remarks>
+ /// 鏍规嵁浠诲姟鍙峰拰瀛愯澶囩紪鐮佹煡璇㈢姸鎬佷负"鎵ц涓�"鐨勪换鍔°��
+ /// 鐢ㄤ簬璺熻釜浠诲姟鐨勬墽琛岃繘搴︺��
+ /// </remarks>
+ /// <param name="taskNo">浠诲姟鍙�</param>
+ /// <param name="childDeviceCode">瀛愯澶囩紪鐮�</param>
+ /// <returns>鎵ц涓殑浠诲姟瀵硅薄锛屽鏋滄病鏈夊垯杩斿洖 null</returns>
public Dt_Task? QueryExecutingTask(int taskNo, string childDeviceCode)
{
return _taskService.QueryExecutingConveyorLineTask(taskNo, childDeviceCode);
}
+ /// <summary>
+ /// 鍚� WMS 璇锋眰鏂颁换鍔�
+ /// </summary>
+ /// <remarks>
+ /// 褰撹緭閫佺嚎鏈夎揣鐗╁埌杈炬椂锛屽悜 WMS 璇锋眰鏂扮殑浠诲姟銆�
+ /// WMS 浼氭牴鎹粨搴撴儏鍐靛拰璋冨害绛栫暐杩斿洖鍚堥�傜殑浠诲姟銆�
+ /// </remarks>
+ /// <param name="barcode">璐х墿/鎵樼洏鏉$爜</param>
+ /// <param name="childDeviceCode">璇锋眰鐨勫瓙璁惧缂栫爜</param>
+ /// <returns>璇锋眰鏄惁鎴愬姛</returns>
public bool RequestWmsTask(string barcode, string childDeviceCode)
{
return _taskService.RequestWMSTask(barcode, childDeviceCode).Status;
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/PinMachine/PinMachineCommand.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/PinMachine/PinMachineCommand.cs
index f96393f..b2fa093 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/PinMachine/PinMachineCommand.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/PinMachine/PinMachineCommand.cs
@@ -1,52 +1,84 @@
-锘縰sing System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using WIDESEAWCS_QuartzJob.DeviceBase;
namespace WIDESEAWCS_Tasks
{
+ /// <summary>
+ /// 鎻掓嫈閽夋満 PLC 閫氫俊鍛戒护鏁版嵁绫�
+ /// </summary>
+ /// <remarks>
+ /// 缁ф壙鑷� DeviceCommand锛岀敤浜庝笌鎻掓嫈閽夋満杩涜 PLC 閫氫俊銆�
+ /// 鍖呭惈璁惧鐨勮繍琛岀姸鎬併�佺墿鏂欒姹傘�佸嚭鏂欒姹傜瓑淇″彿銆�
+ /// </remarks>
public class PinMachineCommand : DeviceCommand
{
/// <summary>
/// 鐗╂祦绾胯繍琛屼俊鍙�
/// </summary>
+ /// <remarks>
+ /// 琛ㄧず鐗╂祦绾匡紙杩炴帴鎻掓嫈閽夋満鐨勮緭閫佺嚎锛夋槸鍚﹀湪杩愯銆�
+ /// </remarks>
public short LogisticsLineRunningSignal { get; set; }
/// <summary>
/// 鎻掓嫈閽夋満杩愯淇″彿
/// </summary>
+ /// <remarks>
+ /// 琛ㄧず鎻掓嫈閽夋満鏈韩鐨勮繍琛岀姸鎬併��
+ /// </remarks>
public short PlugPinMachineRunningSignal { get; set; }
/// <summary>
/// 瑕佹枡璇锋眰-涓婂眰
/// </summary>
+ /// <remarks>
+ /// 涓婂眰宸ヤ綅鍚戜笂绾ц澶囷紙濡傝緭閫佺嚎锛夊彂鍑虹殑鍘熸枡璇锋眰銆�
+ /// 闈為浂鍊艰〃绀洪渶瑕佽ˉ鍏呯墿鏂欍��
+ /// </remarks>
public short MaterialRequestUpper { get; set; }
/// <summary>
/// 鍑烘枡璇锋眰-涓婂眰
/// </summary>
+ /// <remarks>
+ /// 涓婂眰宸ヤ綅瀹屾垚鍔犲伐鍚庯紝鍚戜笅绾ц澶囧彂鍑虹殑鍑烘枡璇锋眰銆�
+ /// 闈為浂鍊艰〃绀烘湁鏂欓渶瑕佽緭鍑恒��
+ /// </remarks>
public short OutputRequestUpper { get; set; }
/// <summary>
/// 鎻掓嫈閽夌洏鍙嚭鏂�-涓婂眰
/// </summary>
+ /// <remarks>
+ /// WCS 鍥炲缁欎笂灞傚伐浣嶇殑纭淇″彿銆�
+ /// 鍛婄煡涓婂眰鎵樼洏宸茶鎺ユ敹锛屽彲浠ョ户缁嚭鏂欍��
+ /// </remarks>
public short PlugPinTrayOutputReadyUpper { get; set; }
/// <summary>
/// 瑕佹枡璇锋眰-涓嬪眰
/// </summary>
+ /// <remarks>
+ /// 涓嬪眰宸ヤ綅鍚戜笂绾ц澶囷紙濡傝緭閫佺嚎锛夊彂鍑虹殑鍘熸枡璇锋眰銆�
+ /// 闈為浂鍊艰〃绀洪渶瑕佽ˉ鍏呯墿鏂欍��
+ /// </remarks>
public short MaterialRequestLower { get; set; }
/// <summary>
/// 鍑烘枡璇锋眰-涓嬪眰
/// </summary>
+ /// <remarks>
+ /// 涓嬪眰宸ヤ綅瀹屾垚鍔犲伐鍚庯紝鍚戜笅绾ц澶囧彂鍑虹殑鍑烘枡璇锋眰銆�
+ /// 闈為浂鍊艰〃绀烘湁鏂欓渶瑕佽緭鍑恒��
+ /// </remarks>
public short OutputRequestLower { get; set; }
/// <summary>
/// 鎻掓嫈閽夌洏鍙嚭鏂�-涓嬪眰
/// </summary>
+ /// <remarks>
+ /// WCS 鍥炲缁欎笅灞傚伐浣嶇殑纭淇″彿銆�
+ /// 鍛婄煡涓嬪眰鎵樼洏宸茶鎺ユ敹锛屽彲浠ョ户缁嚭鏂欍��
+ /// </remarks>
public short PlugPinTrayOutputReadyLower { get; set; }
}
}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/PinMachine/PinMachineDBName.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/PinMachine/PinMachineDBName.cs
index 0e808f1..5bf993e 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/PinMachine/PinMachineDBName.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/PinMachine/PinMachineDBName.cs
@@ -1,45 +1,83 @@
-锘縩amespace WIDESEAWCS_Tasks
+namespace WIDESEAWCS_Tasks
{
+ /// <summary>
+ /// 鎻掓嫈閽夋満 PLC 瀵勫瓨鍣ㄥ悕绉版灇涓�
+ /// </summary>
+ /// <remarks>
+ /// 瀹氫箟鎻掓嫈閽夋満涓� WCS 閫氫俊鏃朵娇鐢ㄧ殑 PLC 瀵勫瓨鍣ㄥ湴鍧�鍚嶇О銆�
+ /// 鎻掓嫈閽夋満鏄竴绉嶇敤浜庣數姹犵敓浜х殑璁惧锛岃礋璐f彃閽堝拰鎷旈拡鎿嶄綔銆�
+ /// 璁惧鏈変笂涓嬩袱灞傜粨鏋勶紝姣忓眰閮芥湁鐙珛鐨勭墿鏂欒姹傚拰鍑烘枡淇″彿銆�
+ /// </remarks>
public enum PinMachineDBName
{
/// <summary>
/// 鐗╂祦绾胯繍琛屼俊鍙�
/// </summary>
+ /// <remarks>
+ /// 琛ㄧず鐗╂祦绾垮綋鍓嶆槸鍚﹀湪杩愯銆�
+ /// </remarks>
LogisticsLineRunningSignal,
/// <summary>
/// 鎻掓嫈閽夋満杩愯淇″彿
/// </summary>
+ /// <remarks>
+ /// 琛ㄧず鎻掓嫈閽夋満鏈韩鏄惁鍦ㄨ繍琛屻��
+ /// </remarks>
PlugPinMachineRunningSignal,
/// <summary>
/// 瑕佹枡璇锋眰-涓婂眰
/// </summary>
+ /// <remarks>
+ /// 涓婂眰宸ヤ綅闇�瑕佸師鏂欑殑璇锋眰淇″彿銆�
+ /// 褰撳�间负闈為浂鏃讹紝琛ㄧず涓婂眰闇�瑕佽ˉ鍏呯墿鏂欍��
+ /// </remarks>
MaterialRequestUpper,
/// <summary>
/// 鍑烘枡璇锋眰-涓婂眰
/// </summary>
+ /// <remarks>
+ /// 涓婂眰宸ヤ綅瀹屾垚鍔犲伐鍚庣殑鍑烘枡璇锋眰淇″彿銆�
+ /// 褰撳�间负闈為浂鏃讹紝琛ㄧず涓婂眰鏈夋枡闇�瑕佽緭鍑恒��
+ /// </remarks>
OutputRequestUpper,
/// <summary>
/// 鎻掓嫈閽夌洏鍙嚭鏂�-涓婂眰
/// </summary>
+ /// <remarks>
+ /// WCS 鍐欏叆鐨勪俊鍙凤紝鍛婄煡涓婂眰鎵樼洏鍙互鍑烘枡銆�
+ /// 褰� WCS 璁剧疆涓� 1 鏃讹紝琛ㄧず鍏佽涓婂眰鍑烘枡銆�
+ /// </remarks>
PlugPinTrayOutputReadyUpper,
/// <summary>
/// 瑕佹枡璇锋眰-涓嬪眰
/// </summary>
+ /// <remarks>
+ /// 涓嬪眰宸ヤ綅闇�瑕佸師鏂欑殑璇锋眰淇″彿銆�
+ /// 褰撳�间负闈為浂鏃讹紝琛ㄧず涓嬪眰闇�瑕佽ˉ鍏呯墿鏂欍��
+ /// </remarks>
MaterialRequestLower,
/// <summary>
/// 鍑烘枡璇锋眰-涓嬪眰
/// </summary>
+ /// <remarks>
+ /// 涓嬪眰宸ヤ綅瀹屾垚鍔犲伐鍚庣殑鍑烘枡璇锋眰淇″彿銆�
+ /// 褰撳�间负闈為浂鏃讹紝琛ㄧず涓嬪眰鏈夋枡闇�瑕佽緭鍑恒��
+ /// </remarks>
OutputRequestLower,
/// <summary>
/// 鎻掓嫈閽夌洏鍙嚭鏂�-涓嬪眰
/// </summary>
+ /// <remarks>
+ /// WCS 鍐欏叆鐨勪俊鍙凤紝鍛婄煡涓嬪眰鎵樼洏鍙互鍑烘枡銆�
+ /// 褰� WCS 璁剧疆涓� 1 鏃讹紝琛ㄧず鍏佽涓嬪眰鍑烘枡銆�
+ /// </remarks>
PlugPinTrayOutputReadyLower
}
-}
\ No newline at end of file
+}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Abstractions/IRobotMessageRouter.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Abstractions/IRobotMessageRouter.cs
index d7fd293..7a2731a 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Abstractions/IRobotMessageRouter.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Abstractions/IRobotMessageRouter.cs
@@ -3,11 +3,18 @@
namespace WIDESEAWCS_Tasks.Workflow.Abstractions
{
/// <summary>
- /// 机器人消息路由入口。用于承接 TcpSocketServer 的消息事件。
+ /// 鏈哄櫒浜烘秷鎭矾鐢辨帴鍙� - 璐熻矗鎺ユ敹鏉ヨ嚜 TcpSocketServer 鐨勬秷鎭苟鍒嗗彂缁欏悎閫傜殑澶勭悊鍣�
/// </summary>
public interface IRobotMessageRouter
{
+ /// <summary>
+ /// 澶勭悊鎺ユ敹鍒扮殑娑堟伅
+ /// </summary>
+ /// <param name="message">鍘熷娑堟伅瀛楃涓�</param>
+ /// <param name="isJson">娑堟伅鏄惁涓� JSON 鏍煎紡</param>
+ /// <param name="client">TCP 瀹㈡埛绔繛鎺�</param>
+ /// <param name="state">鏈哄櫒浜哄綋鍓嶇姸鎬�</param>
+ /// <returns>鍝嶅簲娑堟伅锛屽鏋滄棤闇�鍥炲鍒欒繑鍥� null</returns>
Task<string?> HandleMessageReceivedAsync(string message, bool isJson, TcpClient client, RobotSocketState state);
}
}
-
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Abstractions/IRobotPrefixCommandHandler.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Abstractions/IRobotPrefixCommandHandler.cs
index 6b28478..a2ee74b 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Abstractions/IRobotPrefixCommandHandler.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Abstractions/IRobotPrefixCommandHandler.cs
@@ -1,14 +1,29 @@
-锘縰sing System.Net.Sockets;
+using System.Net.Sockets;
namespace WIDESEAWCS_Tasks.Workflow.Abstractions
{
/// <summary>
- /// 鏈哄櫒浜哄墠缂�鍛戒护澶勭悊鍣紙pickfinished / putfinished锛夈��
+ /// 鏈哄櫒浜哄墠缂�鍛戒护澶勭悊鍣ㄦ帴鍙�
/// </summary>
+ /// <remarks>
+ /// 鍓嶇紑鍛戒护鏄寚浠ョ壒瀹氬墠缂�寮�澶寸殑鍛戒护锛屽悗闈㈣窡闅忛�楀彿鍒嗛殧鐨勫弬鏁般��
+ /// 褰撳墠鏀寔锛歱ickfinished锛堝彇璐у畬鎴愶級銆乸utfinished锛堟斁璐у畬鎴愶級
+ /// </remarks>
public interface IRobotPrefixCommandHandler
{
+ /// <summary>
+ /// 妫�鏌ユ秷鎭槸鍚︿负鍓嶇紑鍛戒护
+ /// </summary>
+ /// <param name="message">娑堟伅鍐呭锛堥�氬父鏄皬鍐欏舰寮忥級</param>
+ /// <returns>濡傛灉鏄墠缂�鍛戒护杩斿洖 true</returns>
bool IsPrefixCommand(string message);
+ /// <summary>
+ /// 澶勭悊鍓嶇紑鍛戒护
+ /// </summary>
+ /// <param name="message">鍘熷娑堟伅鍐呭</param>
+ /// <param name="state">鏈哄櫒浜哄綋鍓嶇姸鎬�</param>
+ /// <param name="client">TCP 瀹㈡埛绔繛鎺ワ紝鐢ㄤ簬鍙戦�佸搷搴�</param>
Task HandleAsync(string message, RobotSocketState state, TcpClient client);
}
}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Abstractions/IRobotSimpleCommandHandler.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Abstractions/IRobotSimpleCommandHandler.cs
index 89acb76..af6e6a2 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Abstractions/IRobotSimpleCommandHandler.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Abstractions/IRobotSimpleCommandHandler.cs
@@ -1,10 +1,20 @@
-锘縩amespace WIDESEAWCS_Tasks.Workflow.Abstractions
+namespace WIDESEAWCS_Tasks.Workflow.Abstractions
{
/// <summary>
- /// 鏈哄櫒浜虹畝鍗曞懡浠ゅ鐞嗗櫒锛堝杩愯鐘舵�併�佹ā寮忓垏鎹€�佸叏娴佺▼瀹屾垚鍛戒护锛夈��
+ /// 鏈哄櫒浜虹畝鍗曞懡浠ゅ鐞嗗櫒鎺ュ彛
/// </summary>
+ /// <remarks>
+ /// 绠�鍗曞懡浠ゆ槸鎸囦笉闇�瑕侀澶栧弬鏁扮殑鐘舵�佹洿鏂板懡浠わ紝濡傝繍琛岀姸鎬併�佹ā寮忓垏鎹㈢瓑銆�
+ /// 涓庡墠缂�鍛戒护锛堥渶瑕佽В鏋愪綅缃弬鏁帮級鐩稿銆�
+ /// </remarks>
public interface IRobotSimpleCommandHandler
{
+ /// <summary>
+ /// 澶勭悊绠�鍗曞懡浠�
+ /// </summary>
+ /// <param name="message">娑堟伅鍐呭锛堝皬鍐欏舰寮忥級</param>
+ /// <param name="state">鏈哄櫒浜哄綋鍓嶇姸鎬侊紙浼氳淇敼锛�</param>
+ /// <returns>鏄惁鎴愬姛澶勭悊锛涙棤娉曡瘑鍒殑鍛戒护杩斿洖 false</returns>
Task<bool> HandleAsync(string message, RobotSocketState state);
}
}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Abstractions/IRobotWorkflowOrchestrator.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Abstractions/IRobotWorkflowOrchestrator.cs
index de688d6..9a8f5d1 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Abstractions/IRobotWorkflowOrchestrator.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Abstractions/IRobotWorkflowOrchestrator.cs
@@ -3,11 +3,16 @@
namespace WIDESEAWCS_Tasks.Workflow.Abstractions
{
/// <summary>
- /// 机器人流程编排器。负责 RobotJob 内的状态机分支执行。
+ /// 鏈哄櫒浜轰换鍔$紪鎺掑櫒鎺ュ彛 - 璐熻矗 RobotJob 涓殑鐘舵�佹満娴佽浆鍜屾墽琛屾楠ょ紪鎺�
/// </summary>
public interface IRobotWorkflowOrchestrator
{
+ /// <summary>
+ /// 鎵ц浠诲姟缂栨帓娴佺▼
+ /// </summary>
+ /// <param name="latestState">鏈哄櫒浜烘渶鏂扮姸鎬�</param>
+ /// <param name="task">寰呮墽琛岀殑鏈哄櫒浜轰换鍔�</param>
+ /// <param name="ipAddress">鏈哄櫒浜� IP 鍦板潃</param>
Task ExecuteAsync(RobotSocketState latestState, Dt_RobotTask task, string ipAddress);
}
}
-
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Abstractions/ISocketClientGateway.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Abstractions/ISocketClientGateway.cs
index 68e4edc..b238bb0 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Abstractions/ISocketClientGateway.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Abstractions/ISocketClientGateway.cs
@@ -4,17 +4,51 @@
namespace WIDESEAWCS_Tasks.Workflow.Abstractions
{
/// <summary>
- /// Socket 客户端网关。用于隔离 Robot 业务对 TcpSocketServer 的直接依赖。
+ /// Socket 瀹㈡埛绔綉鍏虫帴鍙� - 灏佽 TcpSocketServer 鐨勮闂紝浣夸笟鍔″眰涓嶇洿鎺ヤ緷璧栧簳灞傞�氫俊瀹炵幇
/// </summary>
+ /// <remarks>
+ /// 璇ユ帴鍙f槸涓氬姟灞備笌搴曞眰 TCP 閫氫俊涔嬮棿鐨勬娊璞″眰銆�
+ /// 閫氳繃渚濊禆娉ㄥ叆鍜屼娇鐢ㄦ帴鍙o紝浣夸笂灞備唬鐮佷笉鐩存帴渚濊禆 TcpSocketServer锛�
+ /// 渚夸簬鍚庣画鏇挎崲閫氫俊瀹炵幇鎴栬繘琛屽崟鍏冩祴璇曘��
+ /// </remarks>
public interface ISocketClientGateway
{
+ /// <summary>
+ /// 寮傛鍙戦�佹秷鎭埌鎸囧畾瀹㈡埛绔�
+ /// </summary>
+ /// <param name="clientId">鐩爣瀹㈡埛绔殑 IP 鍦板潃</param>
+ /// <param name="message">瑕佸彂閫佺殑娑堟伅鍐呭</param>
+ /// <returns>鍙戦�佹槸鍚︽垚鍔�</returns>
Task<bool> SendToClientAsync(string clientId, string message);
+ /// <summary>
+ /// 閫氳繃 TcpClient 瀵硅薄鍙戦�佹秷鎭�
+ /// </summary>
+ /// <remarks>
+ /// 涓� SendToClientAsync 鐨勫尯鍒細姝ゆ柟娉曠洿鎺ヤ娇鐢� TcpClient 瀵硅薄锛�
+ /// 閫傜敤浜庨渶瑕佸洖鍐欏搷搴旂粰鍙戦�佹柟鐨勫満鏅��
+ /// </remarks>
+ /// <param name="client">TCP 瀹㈡埛绔繛鎺ュ璞�</param>
+ /// <param name="message">瑕佸彂閫佺殑娑堟伅鍐呭</param>
Task SendMessageAsync(TcpClient client, string message);
+ /// <summary>
+ /// 鑾峰彇鎵�鏈夊凡杩炴帴瀹㈡埛绔殑 ID 鍒楄〃
+ /// </summary>
+ /// <returns>瀹㈡埛绔� IP 鍦板潃鍒楄〃</returns>
IReadOnlyList<string> GetClientIds();
+ /// <summary>
+ /// 寮傛澶勭悊瀹㈡埛绔繛鎺ョ殑娑堟伅寰幆
+ /// </summary>
+ /// <remarks>
+ /// 鍚姩鍚庝細鎸佺画鎺ユ敹瀹㈡埛绔秷鎭紝鐩村埌杩炴帴鏂紑鎴栧彇娑堜护鐗岃瑙﹀彂銆�
+ /// 鐢ㄤ簬绠$悊鍗曚釜瀹㈡埛绔殑鐢熷懡鍛ㄦ湡銆�
+ /// </remarks>
+ /// <param name="client">TCP 瀹㈡埛绔繛鎺�</param>
+ /// <param name="clientId">瀹㈡埛绔爣璇嗭紙閫氬父鏄� IP 鍦板潃锛�</param>
+ /// <param name="cancellationToken">鍙栨秷浠ょ墝</param>
+ /// <param name="robotCrane">鏈哄櫒浜虹姸鎬佸璞�</param>
Task HandleClientAsync(TcpClient client, string clientId, CancellationToken cancellationToken, RobotSocketState robotCrane);
}
}
-
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotBarcodeGenerator.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotBarcodeGenerator.cs
index 1dc1bcd..0e5474b 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotBarcodeGenerator.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotBarcodeGenerator.cs
@@ -3,25 +3,33 @@
/// <summary>
/// 鏈烘鎵嬫潯鐮佺敓鎴愬櫒 - 璐熻矗鐢熸垚鎵樼洏鏉$爜
/// </summary>
+ /// <remarks>
+ /// 鏉$爜鏍煎紡锛氬墠缂�锛堝彲閫夛級+ 鏃ユ湡锛坹yyyMMdd锛�+ 鏃堕棿锛圚Hmmss锛�+ 闅忔満鏁帮紙100-999锛�
+ /// 渚嬪锛歍RAY20260326093045123
+ /// </remarks>
public static class RobotBarcodeGenerator
{
/// <summary>
/// 鐢熸垚鎵樼洏鏉$爜
/// </summary>
- /// <param name="prefix">鏉$爜鍓嶇紑锛岄粯璁や负绌�</param>
- /// <returns>鐢熸垚鐨勬潯鐮佸瓧绗︿覆</returns>
+ /// <param name="prefix">鏉$爜鍓嶇紑锛岄粯璁や负绌哄瓧绗︿覆锛屼緥濡� "TRAY"</param>
+ /// <returns>鐢熸垚鐨勬潯鐮佸瓧绗︿覆锛屾牸寮忥細鍓嶇紑+鏃ユ湡+鏃堕棿+闅忔満鏁�</returns>
public static string GenerateTrayBarcode(string prefix = "")
{
- // 褰撳墠鏃ユ湡
+ // 鑾峰彇褰撳墠鏃ユ湡锛屾牸寮忓寲涓� yyyyMMdd锛�8浣嶆暟瀛楋級
+ // 渚嬪锛�20260326
string datePart = DateTime.Now.ToString("yyyyMMdd");
- // 鏃堕棿鎴筹紙鏃跺垎绉掞級
+ // 鑾峰彇褰撳墠鏃堕棿锛堟椂鍒嗙锛夛紝鏍煎紡鍖栦负 HHmmss锛�6浣嶆暟瀛楋級
+ // 渚嬪锛�093045 琛ㄧず 09:30:45
string timePart = DateTime.Now.ToString("HHmmss");
- // 闅忔満鏁�
+ // 鐢熸垚3浣嶉殢鏈烘暟锛岃寖鍥� 100-999锛岀‘淇濇潯鐮佸敮涓�鎬�
+ // 浣跨敤 Random.Shared 鑾峰彇绾跨▼瀹夊叏鐨勯殢鏈烘暟鐢熸垚鍣�
string randomPart = Random.Shared.Next(100, 1000).ToString();
- // 缁勫悎锛氬墠缂� + 鏃ユ湡 + 鏃堕棿 + 闅忔満鏁�
+ // 缁勫悎鎵�鏈夐儴鍒嗭細鍓嶇紑 + 鏃ユ湡 + 鏃堕棿 + 闅忔満鏁�
+ // 濡傛灉鍓嶇紑涓虹┖锛屽垯鐩存帴杩斿洖鏃ユ湡+鏃堕棿+闅忔満鏁扮殑缁勫悎
return prefix + datePart + timePart + randomPart;
}
}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotClientManager.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotClientManager.cs
index 2e372bf..75d098b 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotClientManager.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotClientManager.cs
@@ -1,24 +1,64 @@
using System.Collections.Concurrent;
using System.Net.Sockets;
+using WIDESEAWCS_Core.LogHelper;
using WIDESEAWCS_QuartzJob;
using WIDESEAWCS_Tasks.SocketServer;
namespace WIDESEAWCS_Tasks
{
/// <summary>
- /// 鏈烘鎵嬪鎴风杩炴帴绠$悊鍣� - 璐熻矗TCP瀹㈡埛绔繛鎺ョ鐞嗗拰浜嬩欢璁㈤槄
+ /// 鏈烘鎵嬪鎴风杩炴帴绠$悊鍣� - 璐熻矗 TCP 瀹㈡埛绔繛鎺ョ鐞嗗拰浜嬩欢璁㈤槄
/// </summary>
+ /// <remarks>
+ /// 鏍稿績鑱岃矗锛�
+ /// 1. 缁存姢涓庢満姊版墜璁惧鐨� TCP 杩炴帴鐘舵��
+ /// 2. 纭繚姣忎釜瀹㈡埛绔彧鍚姩涓�娆℃秷鎭鐞嗗惊鐜�
+ /// 3. 绠$悊瀹㈡埛绔繛鎺�/鏂紑鐨勭敓鍛藉懆鏈熶簨浠�
+ /// 4. 鎻愪緵鍙戦�佹秷鎭埌瀹㈡埛绔殑鎺ュ彛
+ /// </remarks>
public class RobotClientManager
{
+ /// <summary>
+ /// TCP Socket 鏈嶅姟鍣ㄥ疄渚嬶紝鐢ㄤ簬绠$悊鎵�鏈夊鎴风杩炴帴
+ /// </summary>
private readonly TcpSocketServer _tcpSocket;
+
+ /// <summary>
+ /// 鏈烘鎵嬬姸鎬佺鐞嗗櫒锛岀敤浜庤鍐欒澶囩姸鎬�
+ /// </summary>
private readonly RobotStateManager _stateManager;
- // 璺熻釜宸茬粡鍚姩 HandleClientAsync 鐨勫鎴风
+ /// <summary>
+ /// 璺熻釜宸插惎鍔ㄦ秷鎭鐞嗙殑瀹㈡埛绔紝閬垮厤閲嶅鍚姩
+ /// </summary>
+ /// <remarks>
+ /// Key: 瀹㈡埛绔� IP 鍦板潃
+ /// Value: 鏄惁宸插惎鍔紙true 琛ㄧず宸插惎鍔級
+ /// 浣跨敤 ConcurrentDictionary 淇濊瘉绾跨▼瀹夊叏
+ /// </remarks>
private static readonly ConcurrentDictionary<string, bool> _handleClientStarted = new();
+
+ /// <summary>
+ /// 浜嬩欢璁㈤槄鏍囧織锛岀‘淇� RobotReceived 浜嬩欢鍙闃呬竴娆�
+ /// </summary>
+ /// <remarks>
+ /// 浣跨敤鍘熷瓙鎿嶄綔 Interlocked.CompareExchange 淇濊瘉鍏ㄥ眬鍙闃呬竴娆�
+ /// </remarks>
private static int _eventSubscribedFlag;
+ /// <summary>
+ /// 瀹㈡埛绔柇寮�杩炴帴鏃惰Е鍙戠殑浜嬩欢
+ /// </summary>
+ /// <remarks>
+ /// 浜嬩欢鍙傛暟鍖呭惈鏂紑杩炴帴鐨勬満姊版墜鐘舵�佷俊鎭�
+ /// </remarks>
public event EventHandler<RobotSocketState>? OnClientDisconnected;
+ /// <summary>
+ /// 鏋勯�犲嚱鏁�
+ /// </summary>
+ /// <param name="tcpSocket">TCP Socket 鏈嶅姟鍣ㄥ疄渚�</param>
+ /// <param name="stateManager">鐘舵�佺鐞嗗櫒瀹炰緥</param>
public RobotClientManager(TcpSocketServer tcpSocket, RobotStateManager stateManager)
{
_tcpSocket = tcpSocket;
@@ -28,66 +68,88 @@
/// <summary>
/// 纭繚瀹㈡埛绔凡杩炴帴骞惰闃呮秷鎭簨浠�
/// </summary>
- /// <param name="ipAddress">璁惧IP鍦板潃</param>
+ /// <remarks>
+ /// 杩欐槸 RobotJob Execute 鏂规硶涓殑鏍稿績妫�鏌ラ�昏緫锛�
+ /// 1. 楠岃瘉瀹㈡埛绔槸鍚﹀湪绾�
+ /// 2. 璁㈤槄鏂紑浜嬩欢锛堝叏灞�鍙墽琛屼竴娆★級
+ /// 3. 纭繚娑堟伅澶勭悊寰幆宸插惎鍔�
+ /// 4. 闃叉閲嶅鍚姩 HandleClientAsync
+ /// </remarks>
+ /// <param name="ipAddress">璁惧 IP 鍦板潃</param>
/// <param name="robotCrane">鏈哄櫒浜鸿澶囦俊鎭�</param>
/// <returns>瀹㈡埛绔槸鍚﹀彲鐢紙宸茶繛鎺ヤ笖娑堟伅澶勭悊宸插惎鍔級</returns>
public bool EnsureClientSubscribed(string ipAddress, RobotCraneDevice robotCrane)
{
- // 妫�鏌ユ槸鍚︽湁璇ュ鎴风杩炴帴
+ // 浠� TCP 鏈嶅姟鍣ㄨ幏鍙栨墍鏈夊凡杩炴帴瀹㈡埛绔殑 ID 鍒楄〃
var clientIds = _tcpSocket.GetClientIds();
+
+ // 妫�鏌ヨ IP 鍦板潃鐨勫鎴风鏄惁宸茶繛鎺�
bool isClientConnected = clientIds.Contains(ipAddress);
+ // 濡傛灉瀹㈡埛绔湭杩炴帴
if (!isClientConnected)
{
- // 瀹㈡埛绔湭杩炴帴锛屾竻鐞� HandleClientAsync 鐘舵��
+ // 娓呯悊璇ュ鎴风鐨� HandleClientAsync 鍚姩鏍囧織
+ // 浠ヤ究涓嬫閲嶈繛鏃跺彲浠ラ噸鏂板惎鍔ㄥ鐞�
_handleClientStarted.TryRemove(ipAddress, out _);
return false;
}
- // 璁㈤槄涓�娆� robot 浜嬩欢锛堝叏灞�涓�娆★級- message浜嬩欢鐢盧obotJob璁㈤槄
+ // 璁㈤槄涓�娆� RobotReceived 浜嬩欢锛堝叏灞�鍙闃呬竴娆★級
+ // 浣跨敤 Interlocked.CompareExchange 瀹炵幇鍘熷瓙鎿嶄綔锛岀‘淇濈嚎绋嬪畨鍏�
if (System.Threading.Interlocked.CompareExchange(ref _eventSubscribedFlag, 1, 0) == 0)
{
+ // 缁戝畾瀹㈡埛绔柇寮�杩炴帴鐨勪簨浠跺鐞�
_tcpSocket.RobotReceived += OnRobotReceived;
- Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 鏈哄櫒浜篢CP鏂紑浜嬩欢宸茶闃�");
+ // 璁板綍鏃ュ織锛堟敞鎰忥細鏃ュ織鍐呭涓�"瀹㈡埛绔凡鏂紑杩炴帴"锛屽彲鑳芥槸閬楃暀鐨勫崰浣嶆枃鏈級
+ QuartzLogger.Error($"瀹㈡埛绔凡鏂紑杩炴帴", robotCrane.DeviceName);
}
- // 鑾峰彇TcpClient
+ // 浠� TCP 鏈嶅姟鍣ㄧ殑瀹㈡埛绔瓧鍏镐腑鑾峰彇 TcpClient 瀵硅薄
TcpClient? tcpClient = null;
_tcpSocket._clients.TryGetValue(ipAddress, out tcpClient);
+ // 濡傛灉鑾峰彇澶辫触锛堣櫧鐒� isClientConnected 涓� true锛屼絾鍙兘瀛樺湪瀛楀吀涓嶅悓姝ョ殑鎯呭喌锛�
if (tcpClient == null)
{
- // isClientConnected涓簍rue浣嗘棤娉曡幏鍙杢cpClient锛屽垪琛ㄥ彲鑳戒笉鍚屾
+ // 绉婚櫎鍚姩鏍囧織锛岃繑鍥� false 琛ㄧず瀹㈡埛绔笉鍙敤
_handleClientStarted.TryRemove(ipAddress, out _);
return false;
}
- // 妫�鏌ユ槸鍚﹀凡缁忎负杩欎釜瀹㈡埛绔惎鍔ㄨ繃 HandleClientAsync
+ // 妫�鏌ユ槸鍚﹀凡缁忎负杩欎釜瀹㈡埛绔惎鍔ㄨ繃娑堟伅澶勭悊寰幆
bool alreadyStarted = _handleClientStarted.TryGetValue(ipAddress, out _);
+ // 濡傛灉灏氭湭鍚姩锛屽垯鍚姩娑堟伅澶勭悊寰幆
if (!alreadyStarted)
{
- Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 鍚姩瀹㈡埛绔秷鎭鐞�: {ipAddress}");
+ // 璁板綍鏃ュ織
+ QuartzLogger.Error($"鍚姩瀹㈡埛绔秷鎭鐞�", robotCrane.DeviceName);
- // 閲嶆柊鑾峰彇鏈�鏂扮殑 state 瀵硅薄
+ // 鑾峰彇鏈�鏂扮殑鐘舵�佸璞�
var latestStateForSubscribe = _stateManager.GetState(ipAddress);
if (latestStateForSubscribe != null)
{
- // 鏍囪涓哄凡鍚姩
+ // 鏍囪涓哄凡鍚姩锛岄槻姝㈤噸澶嶅惎鍔�
_handleClientStarted[ipAddress] = true;
+ // 寮傛鍚姩瀹㈡埛绔秷鎭鐞嗗惊鐜�
+ // 浣跨敤 TaskContinuationOptions.OnlyOnFaulted 鎹曡幏寮傚父鎯呭喌
_ = _tcpSocket.HandleClientAsync(tcpClient, robotCrane.IPAddress, _tcpSocket._cts.Token, latestStateForSubscribe)
.ContinueWith(t =>
{
+ // 濡傛灉澶勭悊鍑虹幇寮傚父
if (t.IsFaulted)
{
+ // 璁板綍閿欒鏃ュ織
+ QuartzLogger.Error($"鐩戝惉瀹㈡埛绔秷鎭簨浠跺紓甯�", robotCrane.DeviceName);
Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] HandleClientAsync error: {t.Exception?.GetBaseException().Message}");
// 鍙戠敓閿欒鏃讹紝绉婚櫎鍚姩鏍囧織锛屽厑璁镐笅娆¢噸璇�
_handleClientStarted.TryRemove(ipAddress, out _);
}
}, TaskContinuationOptions.OnlyOnFaulted);
- // 鏇存柊 IsEventSubscribed 鐘舵��
+ // 瀹夊叏鏇存柊鐘舵�侊紝鏍囪涓哄凡璁㈤槄娑堟伅浜嬩欢
_stateManager.TryUpdateStateSafely(ipAddress, s =>
{
s.IsEventSubscribed = true;
@@ -96,54 +158,81 @@
}
}
+ // 杩斿洖 true 琛ㄧず瀹㈡埛绔彲鐢�
return true;
}
/// <summary>
- /// 浜嬩欢锛氬鎴风鏂紑杩炴帴鏃惰Е鍙�
+ /// 浜嬩欢澶勭悊锛氬鎴风鏂紑杩炴帴鏃惰皟鐢�
/// </summary>
+ /// <remarks>
+ /// 瑙﹀彂鏃舵満锛氬綋 TCP 鏈嶅姟鍣ㄦ娴嬪埌瀹㈡埛绔柇寮�杩炴帴鏃�
+ /// 澶勭悊閫昏緫锛�
+ /// 1. 娓呯悊 HandleClientAsync 鍚姩鏍囧織
+ /// 2. 閲嶇疆璁惧鐘舵�侊紙鍙栨秷璁㈤槄銆佹竻闄ゅ姩浣滃拰鐘舵�侊級
+ /// 3. 瑙﹀彂 OnClientDisconnected 浜嬩欢閫氱煡涓婂眰
+ /// </remarks>
+ /// <param name="clientId">鏂紑杩炴帴鐨勫鎴风 IP 鍦板潃</param>
+ /// <returns>鍥哄畾杩斿洖 null锛屽洜涓烘槸浜嬩欢澶勭悊鍣ㄨ�岄潪鐪熸鐨勬秷鎭鐞嗗櫒</returns>
private Task<string?> OnRobotReceived(string clientId)
{
- // 瀹㈡埛绔柇寮�杩炴帴锛屾竻鐞� HandleClientAsync 鍚姩鏍囧織
+ // 绉婚櫎璇ュ鎴风鐨� HandleClientAsync 鍚姩鏍囧織
_handleClientStarted.TryRemove(clientId, out _);
+ // 閲嶇疆璇ュ鎴风鐨勭姸鎬佷俊鎭�
_stateManager.TryUpdateStateSafely(clientId, state =>
{
- state.IsEventSubscribed = false;
- state.CurrentAction = "";
- state.OperStatus = "";
- state.RobotArmObject = 0;
- state.RobotControlMode = 0;
- state.RobotRunMode = 0;
+ state.IsEventSubscribed = false; // 鍙栨秷璁㈤槄鏍囧織
+ state.CurrentAction = ""; // 娓呴櫎褰撳墠鍔ㄤ綔
+ state.OperStatus = ""; // 娓呴櫎杩愯鐘舵��
+ state.RobotArmObject = 0; // 閲嶇疆鎵嬭噦瀵硅薄鐘舵��
+ state.RobotControlMode = 0; // 閲嶇疆鎺у埗妯″紡
+ state.RobotRunMode = 0; // 閲嶇疆杩愯妯″紡
return state;
});
- // 瑙﹀彂鏂紑杩炴帴浜嬩欢
+ // 瑙﹀彂瀹㈡埛绔柇寮�杩炴帴浜嬩欢锛岄�氱煡涓婂眰锛堝 RobotJob锛�
+ // 浣跨敤绌虹殑鐘舵�佸璞′綔涓哄悗澶囷紙濡傛灉鑾峰彇涓嶅埌锛�
OnClientDisconnected?.Invoke(this, _stateManager.GetState(clientId) ?? new RobotSocketState { IPAddress = clientId });
+ // 杩斿洖 null锛屽洜涓鸿繖鏄簨浠跺鐞嗚�岄潪鐪熸鐨勬秷鎭矾鐢�
return Task.FromResult<string?>(null);
}
/// <summary>
/// 妫�鏌ュ鎴风鏄惁宸茶繛鎺�
/// </summary>
+ /// <param name="ipAddress">璁惧 IP 鍦板潃</param>
+ /// <returns>濡傛灉宸茶繛鎺ュ垯杩斿洖 true</returns>
public bool IsClientConnected(string ipAddress)
{
+ // 鑾峰彇鎵�鏈夊凡杩炴帴瀹㈡埛绔殑 ID 鍒楄〃
var clientIds = _tcpSocket.GetClientIds();
+ // 妫�鏌ュ垪琛ㄤ腑鏄惁鍖呭惈鎸囧畾鐨� IP 鍦板潃
return clientIds.Contains(ipAddress);
}
/// <summary>
/// 鍙戦�佹秷鎭埌瀹㈡埛绔�
/// </summary>
+ /// <remarks>
+ /// 灏佽 TcpSocketServer 鐨勫彂閫佹柟娉曪紝鎻愪緵鏇寸畝娲佺殑鎺ュ彛缁欎笟鍔″眰
+ /// </remarks>
+ /// <param name="ipAddress">鐩爣瀹㈡埛绔� IP 鍦板潃</param>
+ /// <param name="message">瑕佸彂閫佺殑娑堟伅鍐呭</param>
+ /// <returns>鍙戦�佹槸鍚︽垚鍔�</returns>
public async Task<bool> SendToClientAsync(string ipAddress, string message)
{
return await _tcpSocket.SendToClientAsync(ipAddress, message);
}
/// <summary>
- /// 鑾峰彇TcpSocketServer寮曠敤锛堢敤浜嶳obotJob鐩存帴璁块棶锛�
+ /// 鑾峰彇 TcpSocketServer 寮曠敤
/// </summary>
+ /// <remarks>
+ /// RobotJob 鍙兘闇�瑕佺洿鎺ヨ闂� TcpSocketServer 杩涜閰嶇疆
+ /// 姝ゅ睘鎬ф彁渚涘彧璇昏闂�
+ /// </remarks>
public TcpSocketServer TcpSocket => _tcpSocket;
}
}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotJob.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotJob.cs
index c7b91b3..8a55144 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotJob.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotJob.cs
@@ -1,33 +1,112 @@
-锘縰sing Quartz;
+using Microsoft.Extensions.Logging;
+using Quartz;
+using System.Net;
using WIDESEA_Core;
using WIDESEAWCS_Core.Caches;
using WIDESEAWCS_Core.Helper;
+using WIDESEAWCS_Core.LogHelper;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_QuartzJob;
-using WIDESEAWCS_Tasks.Workflow.Abstractions;
-using WIDESEAWCS_Tasks.Workflow;
using WIDESEAWCS_Tasks.SocketServer;
-using Microsoft.Extensions.Logging;
+using WIDESEAWCS_Tasks.Workflow;
+using WIDESEAWCS_Tasks.Workflow.Abstractions;
namespace WIDESEAWCS_Tasks
{
/// <summary>
- /// 鏈哄櫒浜轰换鍔′綔涓氾細璐熻矗璋冨害涓庣敓鍛藉懆鏈熺鐞嗭紝鍏蜂綋鐘舵�佹満娴佺▼浜ょ粰缂栨帓鍣ㄣ��
+ /// 鏈哄櫒浜轰换鍔′綔涓氾紙Quartz Job锛�- 璐熻矗璋冨害涓庣敓鍛藉懆鏈熺鐞�
/// </summary>
+ /// <remarks>
+ /// Quartz 瀹氭椂浠诲姟锛屾瘡绉掓墽琛屼竴娆★紙榛樿锛夛紝涓昏鑱岃矗锛�
+ /// 1. 浠� JobDataMap 鑾峰彇璁惧淇℃伅
+ /// 2. 纭繚 TCP 瀹㈡埛绔凡杩炴帴骞惰闃呮秷鎭�
+ /// 3. 杞寰呭鐞嗙殑鏈哄櫒浜轰换鍔�
+ /// 4. 璋冪敤宸ヤ綔娴佺紪鎺掑櫒鎵ц浠诲姟鐘舵�佹満
+ ///
+ /// 浣跨敤 [DisallowConcurrentExecution] 绂佹骞跺彂鎵ц锛岀‘淇濆悓涓�璁惧鐨勪换鍔′覆琛屽鐞嗐��
+ /// 鍏蜂綋鐨勭姸鎬佹満娴佺▼閫昏緫濮旀墭缁� <see cref="RobotWorkflowOrchestrator"/> 澶勭悊銆�
+ /// </remarks>
[DisallowConcurrentExecution]
public class RobotJob : IJob
{
+ /// <summary>
+ /// 浠诲姟鎬绘暟涓婇檺
+ /// </summary>
+ /// <remarks>
+ /// 褰撴満鍣ㄤ汉澶勭悊鐨勮揣鐗╂暟閲忚揪鍒版涓婇檺鏃讹紝涓嶅啀涓嬪彂鏂颁换鍔°��
+ /// 闃叉鏈哄櫒浜鸿繃搴﹀姵绱垨绯荤粺杩囪浇銆�
+ /// </remarks>
private const int MaxTaskTotalNum = 48;
+ /// <summary>
+ /// 娑堟伅浜嬩欢璁㈤槄鏍囧織
+ /// </summary>
+ /// <remarks>
+ /// 浣跨敤鍘熷瓙鎿嶄綔纭繚鍏ㄥ眬鍙闃呬竴娆� TCP 娑堟伅浜嬩欢銆�
+ /// 闃叉澶氫釜 Job 瀹炰緥閲嶅璁㈤槄瀵艰嚧娑堟伅琚娆″鐞嗐��
+ /// </remarks>
private static int _messageSubscribedFlag;
+ /// <summary>
+ /// 鏈烘鎵嬪鎴风杩炴帴绠$悊鍣�
+ /// </summary>
+ /// <remarks>
+ /// 璐熻矗绠$悊 TCP 杩炴帴鐨勭敓鍛藉懆鏈燂紝鍖呮嫭杩炴帴銆佹柇寮�銆佹秷鎭彂閫佺瓑銆�
+ /// </remarks>
private readonly RobotClientManager _clientManager;
+
+ /// <summary>
+ /// 鏈烘鎵嬬姸鎬佺鐞嗗櫒
+ /// </summary>
+ /// <remarks>
+ /// 璐熻矗绠$悊 Redis 缂撳瓨涓殑鏈烘鎵嬬姸鎬侊紝鍖呮嫭璇诲啓鍜屽苟鍙戞帶鍒躲��
+ /// </remarks>
private readonly RobotStateManager _stateManager;
+
+ /// <summary>
+ /// 娑堟伅璺敱鍣�
+ /// </summary>
+ /// <remarks>
+ /// 璐熻矗澶勭悊浠� TCP 鏈嶅姟鍣ㄦ帴鏀跺埌鐨勬秷鎭紝骞跺垎鍙戠粰鍚堥�傜殑澶勭悊鍣ㄣ��
+ /// 鏄秷鎭鐞嗙閬撶殑鍏ュ彛銆�
+ /// </remarks>
private readonly IRobotMessageRouter _messageRouter;
+
+ /// <summary>
+ /// 鏈哄櫒浜轰换鍔″鐞嗗櫒
+ /// </summary>
+ /// <remarks>
+ /// 璐熻矗浠诲姟鐨勪笅鍙戙�佺姸鎬佹洿鏂般�佷笌 WMS 鐨勪氦浜掔瓑銆�
+ /// </remarks>
private readonly RobotTaskProcessor _taskProcessor;
+
+ /// <summary>
+ /// 鏈哄櫒浜哄伐浣滄祦缂栨帓鍣�
+ /// </summary>
+ /// <remarks>
+ /// 璐熻矗鎵ц浠诲姟鐨勭姸鎬佹満娴佽浆锛屾牴鎹綋鍓嶇姸鎬佸喅瀹氫笅涓�姝ュ姩浣溿��
+ /// 杩欐槸鏍稿績鐨勪笟鍔¢�昏緫缂栨帓缁勪欢銆�
+ /// </remarks>
private readonly IRobotWorkflowOrchestrator _workflowOrchestrator;
+
+ /// <summary>
+ /// 鏃ュ織璁板綍鍣�
+ /// </summary>
private readonly ILogger<RobotJob> _logger;
+ /// <summary>
+ /// 鏋勯�犲嚱鏁�
+ /// </summary>
+ /// <remarks>
+ /// 閲囩敤渚濊禆娉ㄥ叆鏂瑰紡鑾峰彇鎵�闇�鏈嶅姟锛屽苟瀹屾垚缁勪欢鐨勫垵濮嬪寲鍜岀粍瑁呫��
+ /// 杩欓噷浣撶幇浜�"鎺у埗鍙嶈浆"鍜�"渚濊禆娉ㄥ叆"鐨勮璁″師鍒欍��
+ /// </remarks>
+ /// <param name="tcpSocket">TCP Socket 鏈嶅姟鍣ㄥ疄渚�</param>
+ /// <param name="robotTaskService">鏈哄櫒浜轰换鍔℃湇鍔�</param>
+ /// <param name="taskService">閫氱敤浠诲姟鏈嶅姟</param>
+ /// <param name="cache">缂撳瓨鏈嶅姟</param>
+ /// <param name="httpClientHelper">HTTP 瀹㈡埛绔府鍔╃被锛岀敤浜庤皟鐢� WMS 鎺ュ彛</param>
+ /// <param name="logger">鏃ュ織璁板綍鍣�</param>
public RobotJob(
TcpSocketServer tcpSocket,
IRobotTaskService robotTaskService,
@@ -36,75 +115,134 @@
HttpClientHelper httpClientHelper,
ILogger<RobotJob> logger)
{
+ // 鍒濆鍖栫姸鎬佺鐞嗗櫒锛屼紶鍏ョ紦瀛樻湇鍔�
_stateManager = new RobotStateManager(cache);
_logger = logger;
- // 鏀跺彛 Socket 璁块棶锛屽悗缁嫢鏇挎崲閫氫俊瀹炵幇鍙渶鏇挎崲缃戝叧灞傘��
+ // 鍒涘缓 Socket 缃戝叧锛屽皝瑁� TcpSocketServer 鐨勮闂�
+ // 鍚庣画鏇挎崲閫氫俊瀹炵幇鏃跺彧闇�鏇挎崲缃戝叧灞�
ISocketClientGateway socketGateway = new SocketClientGateway(tcpSocket);
+ // 鍒濆鍖栦换鍔″鐞嗗櫒
_taskProcessor = new RobotTaskProcessor(socketGateway, _stateManager, robotTaskService, taskService, httpClientHelper);
+
+ // 鍒濆鍖栧鎴风绠$悊鍣�
_clientManager = new RobotClientManager(tcpSocket, _stateManager);
+ // 鍒濆鍖栧懡浠ゅ鐞嗗櫒
+ // 绠�鍗曞懡浠ゅ鐞嗗櫒锛氬鐞嗙姸鎬佹洿鏂扮瓑绠�鍗曞懡浠�
var simpleCommandHandler = new RobotSimpleCommandHandler(_taskProcessor);
+ // 鍓嶇紑鍛戒护澶勭悊鍣細澶勭悊 pickfinished銆乸utfinished 绛夊甫鍙傛暟鐨勫懡浠�
var prefixCommandHandler = new RobotPrefixCommandHandler(robotTaskService, _taskProcessor, _stateManager, socketGateway);
+
+ // 鍒濆鍖栨秷鎭矾鐢卞櫒
_messageRouter = new RobotMessageHandler(socketGateway, _stateManager, cache, simpleCommandHandler, prefixCommandHandler, logger);
+ // 鍒濆鍖栧伐浣滄祦缂栨帓鍣�
_workflowOrchestrator = new RobotWorkflowOrchestrator(_stateManager, _clientManager, _taskProcessor, robotTaskService);
+ // 璁㈤槄瀹㈡埛绔柇寮�杩炴帴浜嬩欢
_clientManager.OnClientDisconnected += OnClientDisconnected;
- // 鍏ㄥ眬鍙闃呬竴娆℃秷鎭簨浠讹紝淇濇寔鍘熸湁琛屼负銆�
+ // 鍏ㄥ眬鍙闃呬竴娆� TCP 娑堟伅浜嬩欢锛堜繚鎸佸師鏈夎涓猴級
+ // 浣跨敤 Interlocked.CompareExchange 瀹炵幇鍘熷瓙鎿嶄綔
if (System.Threading.Interlocked.CompareExchange(ref _messageSubscribedFlag, 1, 0) == 0)
{
+ // 灏嗘秷鎭矾鐢卞櫒鐨勫鐞嗘柟娉曠粦瀹氬埌 TCP 鏈嶅姟鍣ㄧ殑娑堟伅鎺ユ敹浜嬩欢
tcpSocket.MessageReceived += _messageRouter.HandleMessageReceivedAsync;
- Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 鏈哄櫒鎵婽CP娑堟伅浜嬩欢宸茶闃�");
+ _logger.LogError("鏈哄櫒鎵婽CP娑堟伅浜嬩欢宸茶闃�");
+ QuartzLogger.Error($"鏈哄櫒鎵婽CP娑堟伅浜嬩欢宸茶闃�");
}
}
+ /// <summary>
+ /// 瀹㈡埛绔柇寮�杩炴帴鐨勪簨浠跺鐞�
+ /// </summary>
+ /// <remarks>
+ /// 褰撳鎴风鏂紑杩炴帴鏃惰褰曟棩蹇楋紝渚夸簬鎺掓煡闂銆�
+ /// </remarks>
+ /// <param name="sender">浜嬩欢鍙戦�佽��</param>
+ /// <param name="state">鏂紑杩炴帴鐨勬満姊版墜鐘舵��</param>
private void OnClientDisconnected(object? sender, RobotSocketState state)
{
- Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 瀹㈡埛绔凡鏂紑杩炴帴: {state.IPAddress}");
+ _logger.LogError("瀹㈡埛绔凡鏂紑杩炴帴");
+ QuartzLogger.Error($"瀹㈡埛绔凡鏂紑杩炴帴", state.RobotCrane.DeviceName);
}
+ /// <summary>
+ /// Quartz Job 鐨勬墽琛屽叆鍙�
+ /// </summary>
+ /// <remarks>
+ /// 鎵ц娴佺▼锛�
+ /// 1. 浠� JobDataMap 鑾峰彇璁惧淇℃伅
+ /// 2. 纭繚瀹㈡埛绔凡杩炴帴骞惰闃呮秷鎭�
+ /// 3. 杞寰呭鐞嗕换鍔�
+ /// 4. 璋冪敤宸ヤ綔娴佺紪鎺掑櫒鎵ц浠诲姟
+ ///
+ /// 娉ㄦ剰锛氭鏂规硶鍙兘棰戠箒璋冪敤锛堟瘡绉掍竴娆★級锛岄渶娉ㄦ剰鎬ц兘銆�
+ /// </remarks>
+ /// <param name="context">Quartz 浣滀笟鎵ц涓婁笅鏂囷紝鍖呭惈浣滀笟璇︽儏鍜屾暟鎹�</param>
public async Task Execute(IJobExecutionContext context)
{
+ // 浠� JobDataMap 涓幏鍙栦綔涓氬弬鏁�
bool flag = context.JobDetail.JobDataMap.TryGetValue("JobParams", out object? value);
+
+ // 灏嗗弬鏁拌浆鎹负鏈哄櫒浜鸿澶囦俊鎭�
RobotCraneDevice robotCrane = (RobotCraneDevice?)value ?? new RobotCraneDevice();
+
+ // 濡傛灉娌℃湁鑾峰彇鍒版湁鏁堢殑璁惧淇℃伅锛岀洿鎺ヨ繑鍥�
if (!flag || robotCrane.IsNullOrEmpty())
{
return;
}
+ // 鑾峰彇璁惧 IP 鍦板潃锛屼綔涓虹姸鎬佺紦瀛樼殑閿�
string ipAddress = robotCrane.IPAddress;
+ // 鑾峰彇鎴栧垱寤鸿澶囩姸鎬佸璞�
RobotSocketState state = _stateManager.GetOrCreateState(ipAddress, robotCrane);
+
+ // 鏇存柊璁惧鍩虹淇℃伅锛堜互闃茶澶囦俊鎭湪杩愯鏈熼棿鍙戠敓鍙樺寲锛�
state.RobotCrane = robotCrane;
try
{
+ // 纭繚瀹㈡埛绔凡杩炴帴骞惰闃呮秷鎭簨浠�
+ // 濡傛灉瀹㈡埛绔湭杩炴帴鎴栬闃呭け璐ワ紝鐩存帴杩斿洖绛夊緟涓嬫璋冨害
if (!_clientManager.EnsureClientSubscribed(ipAddress, robotCrane))
{
return;
}
+ // 杞鑾峰彇璇ヨ澶囩殑寰呭鐞嗕换鍔�
var task = _taskProcessor.GetTask(robotCrane);
+
+ // 濡傛灉鏈夊緟澶勭悊浠诲姟
if (task != null)
{
+ // 鑾峰彇鏈�鏂扮殑璁惧鐘舵��
var latestState = _stateManager.GetState(ipAddress);
if (latestState == null)
{
+ // 鐘舵�佷笉瀛樺湪锛屽彲鑳借澶囨湭鍒濆鍖�
return;
}
+ // 妫�鏌ヤ换鍔℃�绘暟鏄惁鏈揪鍒颁笂闄�
if (latestState.RobotTaskTotalNum < MaxTaskTotalNum)
{
+ // 璋冪敤宸ヤ綔娴佺紪鎺掑櫒鎵ц浠诲姟
+ // 缂栨帓鍣ㄤ細鏍规嵁褰撳墠鐘舵�佸喅瀹氫笅涓�姝ュ姩浣�
await _workflowOrchestrator.ExecuteAsync(latestState, task, ipAddress);
}
}
}
- catch (Exception)
+ catch (Exception ex)
{
- // 寮傚父澶勭悊宸插湪缁勪欢鍐呴儴杩涜锛孞ob 灞備繚鎸佸厹搴曞悶鍚愯涔夈��
+ // 寮傚父澶勭悊宸插湪缁勪欢鍐呴儴杩涜锛孞ob 灞備繚鎸佸厹搴曡涔�
+ // 璁板綍寮傚父鑰屼笉鏄潤榛樺悶鎺夛紝渚夸簬鎺掓煡闂
+ _logger?.LogError(ex, "RobotJob鎵ц寮傚父锛孖P: {IpAddress}", ipAddress);
+ QuartzLogger.Error($"RobotJob鎵ц寮傚父锛孖P: {ipAddress}", state.RobotCrane.DeviceName, ex);
}
}
}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotMessageHandler.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotMessageHandler.cs
index 603704e..b6d0a2e 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotMessageHandler.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotMessageHandler.cs
@@ -1,23 +1,82 @@
-锘縰sing Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging;
+using System.Net;
using System.Net.Sockets;
using WIDESEAWCS_Common;
using WIDESEAWCS_Core.Caches;
+using WIDESEAWCS_Core.LogHelper;
using WIDESEAWCS_Tasks.Workflow.Abstractions;
namespace WIDESEAWCS_Tasks
{
/// <summary>
- /// 鏈哄櫒浜烘秷鎭矾鐢卞叆鍙o細璐熻矗缂撳瓨鐘舵�佽鍙栥�佸懡浠ゅ垎鍙戝拰鍥炲寘瑙﹀彂銆�
+ /// 鏈哄櫒浜烘秷鎭鐞嗗櫒 - 娑堟伅璺敱鍏ュ彛
/// </summary>
+ /// <remarks>
+ /// 鏍稿績鑱岃矗锛�
+ /// 1. 缂撳瓨鐘舵�佽鍙栵細浠� Redis 涓幏鍙栨満鍣ㄤ汉鏈�鏂扮殑鐘舵��
+ /// 2. 鍛戒护鍒嗗彂锛氭牴鎹秷鎭被鍨嬪垎鍙戠粰涓嶅悓鐨勫鐞嗗櫒
+ /// - 绠�鍗曞懡浠わ紙濡� homing銆乺unning锛夛細鐢� <see cref="IRobotSimpleCommandHandler"/> 澶勭悊
+ /// - 鍓嶇紑鍛戒护锛堝 pickfinished銆乸utfinished锛夛細鐢� <see cref="IRobotPrefixCommandHandler"/> 澶勭悊
+ /// 3. 鍥炲寘瑙﹀彂锛氬皢鍘熷娑堟伅鍥炲啓鍒板鎴风
+ ///
+ /// 杩欐槸娑堟伅澶勭悊绠¢亾鐨勫叆鍙g偣锛岀敱 TcpSocketServer 鐨� MessageReceived 浜嬩欢瑙﹀彂銆�
+ /// </remarks>
public class RobotMessageHandler : IRobotMessageRouter
{
+ /// <summary>
+ /// Socket 瀹㈡埛绔綉鍏虫帴鍙�
+ /// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬鍚戝鎴风鍙戦�佸搷搴旀秷鎭��
+ /// </remarks>
private readonly ISocketClientGateway _socketClientGateway;
+
+ /// <summary>
+ /// 鏈烘鎵嬬姸鎬佺鐞嗗櫒
+ /// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬璇诲彇鍜屾洿鏂版満鍣ㄤ汉鐨勭姸鎬併��
+ /// </remarks>
private readonly RobotStateManager _stateManager;
+
+ /// <summary>
+ /// 缂撳瓨鏈嶅姟
+ /// </summary>
+ /// <remarks>
+ /// 鐩存帴浣跨敤缂撳瓨鏈嶅姟妫�鏌ョ姸鎬佹槸鍚﹀瓨鍦ㄣ��
+ /// </remarks>
private readonly ICacheService _cache;
+
+ /// <summary>
+ /// 绠�鍗曞懡浠ゅ鐞嗗櫒
+ /// </summary>
+ /// <remarks>
+ /// 澶勭悊绠�鍗曠殑鐘舵�佹洿鏂板懡浠わ紝濡傝繍琛岀姸鎬併�佹ā寮忓垏鎹㈢瓑銆�
+ /// </remarks>
private readonly IRobotSimpleCommandHandler _simpleCommandHandler;
+
+ /// <summary>
+ /// 鍓嶇紑鍛戒护澶勭悊鍣�
+ /// </summary>
+ /// <remarks>
+ /// 澶勭悊甯﹀弬鏁扮殑鍓嶇紑鍛戒护锛屽 pickfinished锛堝彇璐у畬鎴愶級銆乸utfinished锛堟斁璐у畬鎴愶級銆�
+ /// </remarks>
private readonly IRobotPrefixCommandHandler _prefixCommandHandler;
+
+ /// <summary>
+ /// 鏃ュ織璁板綍鍣�
+ /// </summary>
private readonly ILogger<RobotJob> _logger;
+ /// <summary>
+ /// 鏋勯�犲嚱鏁�
+ /// </summary>
+ /// <param name="socketClientGateway">Socket 缃戝叧</param>
+ /// <param name="stateManager">鐘舵�佺鐞嗗櫒</param>
+ /// <param name="cache">缂撳瓨鏈嶅姟</param>
+ /// <param name="simpleCommandHandler">绠�鍗曞懡浠ゅ鐞嗗櫒</param>
+ /// <param name="prefixCommandHandler">鍓嶇紑鍛戒护澶勭悊鍣�</param>
+ /// <param name="logger">鏃ュ織璁板綍鍣�</param>
public RobotMessageHandler(
ISocketClientGateway socketClientGateway,
RobotStateManager stateManager,
@@ -35,32 +94,70 @@
}
/// <summary>
- /// 澶勭悊鎺ユ敹鍒扮殑娑堟伅銆備繚鎸佸師鏈夎涓猴細绠�鍗曞懡浠ゅ拰鍓嶇紑鍛戒护閮藉洖鍐欏師娑堟伅銆�
+ /// 澶勭悊鎺ユ敹鍒扮殑娑堟伅
/// </summary>
+ /// <remarks>
+ /// 澶勭悊娴佺▼锛�
+ /// 1. 璁板綍鏃ュ織锛堣褰曞師濮嬫秷鎭唴瀹癸級
+ /// 2. 楠岃瘉缂撳瓨涓槸鍚﹀瓨鍦ㄨ璁惧鐨勭姸鎬�
+ /// 3. 灏濊瘯鐢ㄧ畝鍗曞懡浠ゅ鐞嗗櫒澶勭悊锛堢姸鎬佹洿鏂扮被鍛戒护锛�
+ /// - 濡傛灉澶勭悊鎴愬姛锛屽洖鍐欏師娑堟伅骞舵洿鏂扮姸鎬�
+ /// 4. 濡傛灉涓嶆槸绠�鍗曞懡浠わ紝妫�鏌ユ槸鍚︽槸鍓嶇紑鍛戒护锛坧ickfinished/putfinished锛�
+ /// - 濡傛灉鏄紝璋冪敤鍓嶇紑鍛戒护澶勭悊鍣ㄥ鐞�
+ /// 5. 淇濇寔鍘熸湁琛屼负锛氱畝鍗曞懡浠ゅ拰鍓嶇紑鍛戒护閮藉洖鍐欏師娑堟伅
+ ///
+ /// 娉ㄦ剰锛氭鏂规硶鍙兘鍦� TCP 娑堟伅鎺ユ敹鐨勪笂涓嬫枃涓棰戠箒璋冪敤锛岄渶娉ㄦ剰鎬ц兘銆�
+ /// </remarks>
+ /// <param name="message">鍘熷娑堟伅瀛楃涓�</param>
+ /// <param name="isJson">娑堟伅鏄惁涓� JSON 鏍煎紡锛堝綋鍓嶆湭浣跨敤锛�</param>
+ /// <param name="client">TCP 瀹㈡埛绔繛鎺�</param>
+ /// <param name="state">鏈哄櫒浜哄綋鍓嶇姸鎬�</param>
+ /// <returns>鍝嶅簲娑堟伅锛屽鏋滄棤闇�鍥炲鍒欒繑鍥� null</returns>
public async Task<string?> HandleMessageReceivedAsync(string message, bool isJson, TcpClient client, RobotSocketState state)
{
+ // 璁板綍鎺ユ敹鍒扮殑娑堟伅鏃ュ織
_logger.LogInformation($"鎺ユ敹鍒板鎴风銆恵state.RobotCrane.DeviceName}銆戝彂閫佹秷鎭�恵message}銆�");
+ QuartzLogger.Error($"鎺ユ敹鍒板鎴风娑堟伅銆恵message}銆�", state.RobotCrane.DeviceName);
+
+ // 鏋勫缓缂撳瓨閿紝妫�鏌� Redis 涓槸鍚﹀瓨鍦ㄨ璁惧鐨勭姸鎬�
var cacheKey = $"{RedisPrefix.Code}:{RedisName.SocketDevices}:{client.Client.RemoteEndPoint}";
+
+ // 濡傛灉缂撳瓨涓笉瀛樺湪鎴栫姸鎬佷负 null锛屽拷鐣ユ娑堟伅
if (!_cache.TryGetValue(cacheKey, out RobotSocketState? cachedState) || cachedState == null)
{
return null;
}
+ // 浣跨敤缂撳瓨涓幏鍙栫殑鐘舵��
var activeState = cachedState;
+
+ // 灏嗘秷鎭浆鎹负灏忓啓锛堢敤浜庣畝鍗曞懡浠ゅ尮閰嶏級
string messageLower = message.ToLowerInvariant();
+ // 灏濊瘯鐢ㄧ畝鍗曞懡浠ゅ鐞嗗櫒澶勭悊
+ // 绠�鍗曞懡浠ゅ寘鎷細homing銆乭omed銆乺unning銆乸ausing銆乺unmode銆乧ontrolmode 绛�
if (await _simpleCommandHandler.HandleAsync(messageLower, activeState))
{
+ // 澶勭悊鎴愬姛鍚庯紝灏嗗師娑堟伅鍥炲啓鍒板鎴风锛堜繚鎸佸師鏈夎涓猴級
await _socketClientGateway.SendMessageAsync(client, message);
+ _logger.LogInformation($"鍙戦�佹秷鎭�恵message}銆�");
+ QuartzLogger.Error($"鍙戦�佹秷鎭細銆恵message}銆�", state.RobotCrane.DeviceName);
+
+ // 瀹夊叏鏇存柊鐘舵�佸埌 Redis
_stateManager.TryUpdateStateSafely(activeState.IPAddress, activeState);
return null;
}
+ // 濡傛灉涓嶆槸绠�鍗曞懡浠わ紝妫�鏌ユ槸鍚︽槸鍓嶇紑鍛戒护
+ // 鍓嶇紑鍛戒护鍖呮嫭锛歱ickfinished銆乸utfinished锛堝悗闈㈣窡閫楀彿鍒嗛殧鐨勪綅缃弬鏁帮級
if (_prefixCommandHandler.IsPrefixCommand(messageLower))
{
+ // 璋冪敤鍓嶇紑鍛戒护澶勭悊鍣�
+ // 鍓嶇紑鍛戒护澶勭悊鍣ㄤ細瑙f瀽浣嶇疆鍙傛暟骞舵洿鏂扮姸鎬�
await _prefixCommandHandler.HandleAsync(message, activeState, client);
}
+ // 榛樿杩斿洖 null锛屼笉浜х敓鍝嶅簲娑堟伅
return null;
}
}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotSocketState.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotSocketState.cs
index d4077a5..aa601be 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotSocketState.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotSocketState.cs
@@ -4,86 +4,164 @@
namespace WIDESEAWCS_Tasks
{
/// <summary>
- /// 鏈烘鎵婼ocket閫氫俊鐘舵��
+ /// 鏈烘鎵� Socket 閫氫俊鐘舵�佹暟鎹被
/// </summary>
+ /// <remarks>
+ /// 璇ョ被鐢ㄤ簬鍦� Redis 缂撳瓨涓瓨鍌ㄦ満姊版墜鐨勫疄鏃剁姸鎬侊紝鍖呮嫭浣嶇疆銆佷换鍔°�佹墜鑷傚璞$瓑淇℃伅銆�
+ /// 鎵�鏈夊睘鎬у潎璁捐涓虹嚎绋嬪畨鍏ㄦ洿鏂帮紝閫氳繃 <see cref="RobotStateManager"/> 鐨勭増鏈帶鍒舵満鍒舵潵闃叉骞跺彂瑕嗙洊銆�
+ /// </remarks>
public class RobotSocketState
{
+ /// <summary>
+ /// 鏈烘鎵嬬殑 IP 鍦板潃锛屼綔涓虹紦瀛橀敭鐨勫敮涓�鏍囪瘑
+ /// </summary>
public string IPAddress { get; set; } = string.Empty;
/// <summary>
- /// 鐗堟湰鍙凤紝鐢ㄤ簬闃叉骞跺彂鏇存柊鏃舵棫鍊艰鐩栨柊鍊�
- /// 姣忔淇敼鐘舵�佹椂閮藉簲璇ユ洿鏂版鍊�
+ /// 鐗堟湰鍙凤紝鐢ㄤ簬涔愯骞跺彂鎺у埗
/// </summary>
+ /// <remarks>
+ /// 姣忔淇敼鐘舵�佹椂鏇存柊涓� DateTime.UtcNow.Ticks銆�
+ /// <see cref="RobotStateManager"/> 浣跨敤姝ゅ瓧娈靛疄鐜颁箰瑙傞攣锛岄槻姝㈠苟鍙戞洿鏂版椂鏃у�艰鐩栨柊鍊笺��
+ /// </remarks>
public long Version { get; set; } = DateTime.UtcNow.Ticks;
/// <summary>
- /// 鏄惁宸茶闃呮秷鎭簨浠�
+ /// 鏄惁宸茶闃呮秷鎭簨浠舵爣蹇�
/// </summary>
+ /// <remarks>
+ /// 纭繚姣忎釜瀹㈡埛绔彧鍚姩涓�娆℃秷鎭鐞嗗惊鐜紝閬垮厤閲嶅璁㈤槄瀵艰嚧鐨勬秷鎭噸澶嶅鐞嗐��
+ /// </remarks>
public bool IsEventSubscribed { get; set; }
/// <summary>
/// 鏈烘鎵嬭繍琛屾ā寮�
/// </summary>
+ /// <remarks>
+ /// 1: 鎵嬪姩妯″紡
+ /// 2: 鑷姩妯″紡
+ /// 褰� RobotRunMode == 2 涓� RobotControlMode == 1 鏃讹紝绯荤粺杩涘叆鑷姩鎺у埗鐘舵�併��
+ /// </remarks>
public int? RobotRunMode { get; set; }
/// <summary>
/// 鏈烘鎵嬫帶鍒舵ā寮�
/// </summary>
+ /// <remarks>
+ /// 1: 瀹㈡埛绔帶鍒�
+ /// 2: 鏈煡/鍏朵粬
+ /// 涓� RobotRunMode 閰嶅悎鍒ゆ柇鏈哄櫒浜虹殑褰撳墠鎺у埗鐘舵�併��
+ /// </remarks>
public int? RobotControlMode { get; set; }
/// <summary>
- /// 鏈烘鎵嬫槸鍚︽姄鍙栫墿鏂欙紝0-鏃犵墿鏂欙紝1-鏈夌墿鏂�
+ /// 鏈烘鎵嬫墜鑷傛姄鍙栧璞$姸鎬�
/// </summary>
+ /// <remarks>
+ /// 0: 鏃犵墿鏂欙紙鎵嬭噦绌洪棽锛�
+ /// 1: 鏈夌墿鏂欙紙宸叉姄鍙栬揣鐗╋級
+ /// 鐢ㄤ簬鍒ゆ柇鏈哄櫒浜烘槸鍚﹀彲浠ユ墽琛屼笅涓�姝ュ姩浣溿��
+ /// </remarks>
public int? RobotArmObject { get; set; }
/// <summary>
- /// 鏈烘鎵嬭澶囦俊鎭�
+ /// 鏈烘鎵嬭澶囧熀纭�淇℃伅
/// </summary>
+ /// <remarks>
+ /// 鍖呭惈璁惧鐨� DeviceCode銆丏eviceName銆両PAddress 绛夊熀纭�淇℃伅銆�
+ /// 鍦ㄧ姸鎬佸垵濮嬪寲鏃朵粠 JobDetail.JobDataMap 鑾峰彇骞剁紦瀛樸��
+ /// </remarks>
public RobotCraneDevice? RobotCrane { get; set; }
/// <summary>
- /// 褰撳墠鍔ㄤ綔
+ /// 鏈烘鎵嬪綋鍓嶆鍦ㄦ墽琛岀殑鍔ㄤ綔
/// </summary>
+ /// <remarks>
+ /// 鍙兘鐨勫�硷細
+ /// - "Picking": 鍙栬揣涓�
+ /// - "Putting": 鏀捐揣涓�
+ /// - "PickFinished": 鍙栬揣瀹屾垚
+ /// - "PutFinished": 鏀捐揣瀹屾垚
+ /// - "AllPickFinished": 鍏ㄩ儴鍙栬揣瀹屾垚
+ /// - "AllPutFinished": 鍏ㄩ儴鏀捐揣瀹屾垚
+ /// </remarks>
public string? CurrentAction { get; set; }
/// <summary>
- /// 褰撳墠鐘舵��
+ /// 鏈烘鎵嬪綋鍓嶈繍琛岀姸鎬�
/// </summary>
+ /// <remarks>
+ /// 鍙兘鐨勫�硷細
+ /// - "Homing": 鍥為浂涓�
+ /// - "Homed": 宸插洖闆�
+ /// - "Running": 杩愯涓�
+ /// - "Pausing": 鏆傚仠涓�
+ /// - "Warming": 棰勭儹涓�
+ /// - "Emstoping": 鎬ュ仠涓�
+ /// </remarks>
public string? OperStatus { get; set; }
/// <summary>
- /// 鍙栬揣瀹屾垚浣嶇疆
+ /// 鏈�杩戜竴娆″彇璐у畬鎴愮殑浣嶇疆鏁扮粍
/// </summary>
+ /// <remarks>
+ /// 鏁扮粍涓殑姣忎釜鍏冪礌浠h〃涓�涓數姹犱綅缃紪鍙枫��
+ /// 鐢ㄤ簬璁板綍鍙栬揣鍔ㄤ綔娑夊強鐨勮揣浣嶏紝渚涘悗缁粍鐩�/鎷嗙洏鎿嶄綔浣跨敤銆�
+ /// </remarks>
public int[]? LastPickPositions { get; set; }
/// <summary>
- /// 鏀捐揣瀹屾垚浣嶇疆
+ /// 鏈�杩戜竴娆℃斁璐у畬鎴愮殑浣嶇疆鏁扮粍
/// </summary>
+ /// <remarks>
+ /// 鏁扮粍涓殑姣忎釜鍏冪礌浠h〃涓�涓數姹犱綅缃紪鍙枫��
+ /// 鐢ㄤ簬璁板綍鏀捐揣鍔ㄤ綔娑夊強鐨勮揣浣嶃��
+ /// </remarks>
public int[]? LastPutPositions { get; set; }
/// <summary>
- /// 鎶撳彇浣嶇疆鏉$爜
+ /// 鐢垫睜/璐т綅鏉$爜鍒楄〃
/// </summary>
+ /// <remarks>
+ /// 鍦ㄧ粍鐩樻搷浣滄椂鐢ㄤ簬璁板綍鐢熸垚鐨勬墭鐩樻潯鐮併��
+ /// 姣忎釜鏉$爜鏍煎紡涓� "TRAY" + 鏃ユ湡 + 鏃堕棿 + 闅忔満鏁般��
+ /// </remarks>
public List<string> CellBarcode { get; set; } = new List<string>();
/// <summary>
- /// 褰撳墠鎶撳彇浠诲姟
+ /// 鏈烘鎵嬪綋鍓嶆鍦ㄦ墽琛岀殑浠诲姟
/// </summary>
+ /// <remarks>
+ /// 褰撲换鍔′笅鍙戝埌鏈哄櫒浜哄悗锛岃瀛楁淇濆瓨浠诲姟璇︽儏銆�
+ /// 浠诲姟绫诲瀷鍖呮嫭锛氱粍鐩�(500)銆佹崲鐩�(510)銆佹媶鐩�(520)銆�
+ /// </remarks>
public Dt_RobotTask? CurrentTask { get; set; }
/// <summary>
- /// 鏄惁闇�瑕佹媶鐩�
+ /// 鏄惁闇�瑕佹墽琛屾媶鐩樹换鍔�
/// </summary>
+ /// <remarks>
+ /// 褰撲换鍔$被鍨嬩负 SplitPallet (520) 鏃惰涓� true銆�
+ /// 鎷嗙洏浠诲姟灏嗙數姹犱粠鎵樼洏涓婂彇涓嬪苟閫愪釜鏀剧疆鍒扮洰鏍囦綅缃��
+ /// </remarks>
public bool IsSplitPallet { get; set; }
/// <summary>
- /// 鏄惁闇�瑕佺粍鐩�
+ /// 鏄惁闇�瑕佹墽琛岀粍鐩樹换鍔�
/// </summary>
+ /// <remarks>
+ /// 褰撲换鍔$被鍨嬩负 GroupPallet (500) 鎴� ChangePallet (510) 鏃惰涓� true銆�
+ /// 缁勭洏浠诲姟灏嗗涓數姹犵粍鍚堝埌鍚屼竴涓墭鐩樹笂銆�
+ /// </remarks>
public bool IsGroupPallet { get; set; }
/// <summary>
- /// 浠诲姟鎬绘暟
+ /// 鏈哄櫒浜哄凡澶勭悊鐨勪换鍔℃�绘暟
/// </summary>
+ /// <remarks>
+ /// 绱璁板綍鏈哄櫒浜哄凡瀹屾垚澶勭悊鐨勭數姹�/璐х墿鏁伴噺銆�
+ /// 褰撹揪鍒� MaxTaskTotalNum (48) 鏃讹紝涓嶅啀涓嬪彂鏂颁换鍔°��
+ /// </remarks>
public int RobotTaskTotalNum { get; set; }
}
}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotStateManager.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotStateManager.cs
index 9500fd7..3bafe10 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotStateManager.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotStateManager.cs
@@ -6,74 +6,110 @@
namespace WIDESEAWCS_Tasks
{
/// <summary>
- /// 鏈烘鎵嬬姸鎬佺鐞嗗櫒 - 璐熻矗RobotSocketState鐨勫畨鍏ㄦ洿鏂板拰鍏嬮殕
+ /// 鏈烘鎵嬬姸鎬佺鐞嗗櫒 - 璐熻矗 RobotSocketState 鐨勭嚎绋嬪畨鍏ㄦ洿鏂板拰鍏嬮殕
/// </summary>
+ /// <remarks>
+ /// 鏍稿績鍔熻兘鏄�氳繃缂撳瓨鏈嶅姟锛圛CacheService锛夌鐞� Redis 涓殑鏈烘鎵嬬姸鎬併��
+ /// 鎻愪緵涔愯骞跺彂鎺у埗锛岄�氳繃鐗堟湰鍙凤紙Version锛夊瓧娈甸槻姝㈠苟鍙戞洿鏂版椂鐨勬暟鎹鐩栭棶棰樸��
+ /// </remarks>
public class RobotStateManager
{
+ /// <summary>
+ /// 缂撳瓨鏈嶅姟瀹炰緥锛岀敤浜庤鍐� Redis 涓殑鐘舵�佹暟鎹�
+ /// </summary>
private readonly ICacheService _cache;
+ /// <summary>
+ /// 鏋勯�犲嚱鏁�
+ /// </summary>
+ /// <param name="cache">缂撳瓨鏈嶅姟瀹炰緥锛堥�氬父涓� HybridCacheService锛�</param>
public RobotStateManager(ICacheService cache)
{
_cache = cache;
}
/// <summary>
- /// 瀹夊叏鏇存柊RobotSocketState缂撳瓨锛岄槻姝㈠苟鍙戣鐩�
+ /// 瀹夊叏鏇存柊 RobotSocketState 缂撳瓨锛岄槻姝㈠苟鍙戣鐩�
/// </summary>
- /// <param name="ipAddress">璁惧IP鍦板潃</param>
- /// <param name="updateAction">鏇存柊鐘舵�佺殑濮旀墭锛堜紶鍏ュ綋鍓嶇姸鎬侊紝杩斿洖淇敼鍚庣殑鏂扮姸鎬侊級</param>
- /// <returns>鏄惁鏇存柊鎴愬姛</returns>
+ /// <remarks>
+ /// 浣跨敤涔愯骞跺彂妯″紡锛氬厛璇诲彇褰撳墠鐗堟湰鍙凤紝鎵ц鏇存柊鏃舵鏌ョ増鏈槸鍚︿竴鑷淬��
+ /// 濡傛灉鐗堟湰涓嶅尮閰嶏紙璇存槑鏈夊叾浠栫嚎绋嬪凡鏇存柊锛夛紝鍒欐洿鏂板け璐ヨ繑鍥� false銆�
+ /// </remarks>
+ /// <param name="ipAddress">璁惧 IP 鍦板潃锛岀敤浜庢瀯寤虹紦瀛橀敭</param>
+ /// <param name="updateAction">鏇存柊鐘舵�佺殑濮旀墭鍑芥暟锛屼紶鍏ュ綋鍓嶇姸鎬佸壇鏈紝杩斿洖淇敼鍚庣殑鏂扮姸鎬�</param>
+ /// <returns>鏄惁鏇存柊鎴愬姛锛沠alse 琛ㄧず鐗堟湰鍐茬獊鎴栫姸鎬佷笉瀛樺湪</returns>
public bool TryUpdateStateSafely(string ipAddress, Func<RobotSocketState, RobotSocketState> updateAction)
{
+ // 鏋勫缓 Redis 缂撳瓨閿紝鏍煎紡锛歿RedisPrefix.Code}:{RedisName.SocketDevices}:{ipAddress}
var cacheKey = GetCacheKey(ipAddress);
+
+ // 浠庣紦瀛樿幏鍙栧綋鍓嶅瓨鍌ㄧ殑鐘舵��
var currentState = _cache.Get<RobotSocketState>(cacheKey);
+ // 濡傛灉缂撳瓨涓笉瀛樺湪璇ヨ澶囩殑鐘舵�侊紝鐩存帴杩斿洖 false锛堝簲鐢� GetOrCreateState 鍏堝垱寤猴級
if (currentState == null)
{
return false;
}
- // 浣跨敤褰撳墠瀛樺偍鐨勭増鏈彿浣滀负鏈熸湜鐗堟湰
+ // 璁板綍褰撳墠瀛樺偍鐨勭増鏈彿锛屼綔涓烘洿鏂版椂鐨勬湡鏈涚増鏈�
var expectedVersion = currentState.Version;
- // 鍒涘缓鐘舵�佸壇鏈繘琛屼慨鏀癸紙閬垮厤淇敼鍘熷璞″紩鐢級
+ // 鍒涘缓鐘舵�佺殑娣辨嫹璐濆壇鏈紝閬垮厤鐩存帴淇敼鍘熷璞″紩鐢�
+ // 杩欐牱鍙互纭繚鍦ㄥ绾跨▼鐜涓嬶紝姣忎釜绾跨▼鎿嶄綔鐨勬槸鐙珛鐨勭姸鎬佸壇鏈�
var stateCopy = CloneState(currentState);
+
+ // 鎵ц璋冪敤鑰呮彁渚涚殑鏇存柊閫昏緫锛屼紶鍏ュ壇鏈姸鎬侊紝鑾峰彇鏂扮殑鐘舵�佸璞�
var newState = updateAction(stateCopy);
+
+ // 灏嗘柊鐘舵�佺殑鐗堟湰鍙锋洿鏂颁负鏈�鏂扮殑鏃堕棿鎴筹紝琛ㄧず鏁版嵁宸叉洿鏂�
newState.Version = DateTime.UtcNow.Ticks;
+ // 璋冪敤缂撳瓨鏈嶅姟鐨勫畨鍏ㄦ洿鏂版柟娉曪紝浼犲叆鏈熸湜鐗堟湰鍜岀増鏈彁鍙栧櫒
+ // 濡傛灉褰撳墠鐗堟湰涓庢湡鏈涚増鏈笉涓�鑷达紙宸茶鍏朵粬绾跨▼鏇存柊锛夛紝鍒欐洿鏂板け璐�
return _cache.TrySafeUpdate(
cacheKey,
newState,
expectedVersion,
- s => s.Version
+ s => s.Version // 鎸囧畾鍝釜瀛楁浣滀负鐗堟湰鍙�
);
}
/// <summary>
- /// 瀹夊叏鏇存柊RobotSocketState缂撳瓨锛堢畝鍗曠増鏈級
+ /// 瀹夊叏鏇存柊 RobotSocketState 缂撳瓨鐨勯噸杞界増鏈紙鐩存帴浼犲叆鏂扮姸鎬侊級
/// </summary>
- /// <param name="ipAddress">璁惧IP鍦板潃</param>
- /// <param name="newState">鏂扮姸鎬侊紙浼氳鏇存柊Version瀛楁锛�</param>
- /// <returns>鏄惁鏇存柊鎴愬姛</returns>
+ /// <remarks>
+ /// 涓庝笂涓�涓噸杞界殑鍖哄埆锛氭鏂规硶鐩存帴鎺ユ敹瀹屾暣鐨勬柊鐘舵�佸璞★紝鑰屼笉鏄洿鏂板鎵樸��
+ /// 濡傛灉璁惧鐘舵�佷笉瀛樺湪浜庣紦瀛樹腑锛屽垯鐩存帴娣诲姞鏂扮姸鎬併��
+ /// </remarks>
+ /// <param name="ipAddress">璁惧 IP 鍦板潃锛岀敤浜庢瀯寤虹紦瀛橀敭</param>
+ /// <param name="newState">鏂扮姸鎬佸璞★紙鏂规硶鍐呴儴浼氭洿鏂板叾 Version 瀛楁锛�</param>
+ /// <returns>鏄惁鏇存柊鎴愬姛锛涙柊寤鸿缃负 true</returns>
public bool TryUpdateStateSafely(string ipAddress, RobotSocketState newState)
{
+ // 鏋勫缓 Redis 缂撳瓨閿�
var cacheKey = GetCacheKey(ipAddress);
+
+ // 浠庣紦瀛樿幏鍙栧綋鍓嶅瓨鍌ㄧ殑鐘舵��
var currentState = _cache.Get<RobotSocketState>(cacheKey);
+ // 濡傛灉褰撳墠涓嶅瓨鍦ㄨ璁惧鐨勭姸鎬�
if (currentState == null)
{
- // 褰撳墠涓嶅瓨鍦紝鐩存帴娣诲姞
+ // 涓烘柊鐘舵�佽缃増鏈彿锛堟椂闂存埑锛�
newState.Version = DateTime.UtcNow.Ticks;
+ // 鐩存帴娣诲姞鍒扮紦瀛�
_cache.AddObject(cacheKey, newState);
return true;
}
- // 浣跨敤褰撳墠瀛樺偍鐨勭増鏈彿浣滀负鏈熸湜鐗堟湰
+ // 褰撳墠瀛樺湪鐘舵�侊紝璁板綍鏈熸湜鐗堟湰鍙风敤浜庝箰瑙傞攣妫�鏌�
var expectedVersion = currentState.Version;
// 鏇存柊鏂扮姸鎬佺殑鐗堟湰鍙蜂负鏈�鏂版椂闂存埑
newState.Version = DateTime.UtcNow.Ticks;
+ // 灏濊瘯瀹夊叏鏇存柊锛屽鏋滅増鏈啿绐佸垯杩斿洖 false
return _cache.TrySafeUpdate(
cacheKey,
newState,
@@ -83,40 +119,64 @@
}
/// <summary>
- /// 鍏嬮殕RobotSocketState瀵硅薄锛堝垱寤烘繁鎷疯礉锛�
+ /// 鍏嬮殕 RobotSocketState 瀵硅薄锛堟繁鎷疯礉锛�
/// </summary>
+ /// <remarks>
+ /// 浣跨敤 JSON 搴忓垪鍖�/鍙嶅簭鍒楀寲瀹炵幇娣辨嫹璐濄��
+ /// 杩欐牱鍙互纭繚鏂板璞′笌鍘熷璞″畬鍏ㄧ嫭绔嬶紝淇敼鏂板璞′笉浼氬奖鍝嶅師瀵硅薄銆�
+ /// </remarks>
+ /// <param name="source">婧愮姸鎬佸璞�</param>
+ /// <returns>鏂扮殑鐘舵�佸璞★紝鏄簮瀵硅薄鐨勬繁鎷疯礉</returns>
public RobotSocketState CloneState(RobotSocketState source)
{
- // 浣跨敤搴忓垪鍖�/鍙嶅簭鍒楀寲杩涜娣辨嫹璐�
+ // 灏嗘簮瀵硅薄搴忓垪鍖栦负 JSON 瀛楃涓�
var json = JsonConvert.SerializeObject(source);
+ // 鍙嶅簭鍒楀寲涓烘柊鐨� RobotSocketState 瀵硅薄
+ // 濡傛灉鍙嶅簭鍒楀寲澶辫触锛堣繑鍥� null锛夛紝鍒涘缓涓�涓柊瀵硅薄骞跺鍒� IPAddress
return JsonConvert.DeserializeObject<RobotSocketState>(json) ?? new RobotSocketState { IPAddress = source.IPAddress };
}
/// <summary>
- /// 鑾峰彇Redis缂撳瓨閿�
+ /// 鑾峰彇 Redis 缂撳瓨閿�
/// </summary>
+ /// <remarks>
+ /// 缂撳瓨閿牸寮忥細{RedisPrefix.Code}:{RedisName.SocketDevices}:{ipAddress}
+ /// 渚嬪锛欳ode:SocketDevices:192.168.1.100
+ /// </remarks>
+ /// <param name="ipAddress">璁惧 IP 鍦板潃</param>
+ /// <returns>瀹屾暣鐨� Redis 缂撳瓨閿�</returns>
public static string GetCacheKey(string ipAddress)
{
return $"{RedisPrefix.Code}:{RedisName.SocketDevices}:{ipAddress}";
}
/// <summary>
- /// 浠庣紦瀛樿幏鍙栫姸鎬�
+ /// 浠庣紦瀛樿幏鍙栨満姊版墜鐘舵��
/// </summary>
+ /// <param name="ipAddress">璁惧 IP 鍦板潃</param>
+ /// <returns>濡傛灉瀛樺湪鍒欒繑鍥炵姸鎬佸璞★紝鍚﹀垯杩斿洖 null</returns>
public RobotSocketState? GetState(string ipAddress)
{
return _cache.Get<RobotSocketState>(GetCacheKey(ipAddress));
}
/// <summary>
- /// 鑾峰彇鎴栧垱寤虹姸鎬�
+ /// 鑾峰彇鎴栧垱寤烘満姊版墜鐘舵��
/// </summary>
+ /// <remarks>
+ /// 濡傛灉缂撳瓨涓凡瀛樺湪璇ヨ澶囩殑鐘舵�侊紝鐩存帴杩斿洖銆�
+ /// 濡傛灉涓嶅瓨鍦紝鍒欏垱寤烘柊鐨勭姸鎬佸璞″苟瀛樺叆缂撳瓨锛岀劧鍚庤繑鍥炪��
+ /// </remarks>
+ /// <param name="ipAddress">璁惧 IP 鍦板潃</param>
+ /// <param name="robotCrane">鏈哄櫒浜鸿澶囦俊鎭紝鐢ㄤ簬鍒濆鍖栨柊鐘舵��</param>
+ /// <returns>璇ヨ澶囩殑鐘舵�佸璞�</returns>
public RobotSocketState GetOrCreateState(string ipAddress, RobotCraneDevice robotCrane)
{
+ // 浣跨敤缂撳瓨鏈嶅姟鐨� GetOrAdd 鏂规硶锛屽伐鍘傚嚱鏁板湪缂撳瓨鏈懡涓椂鍒涘缓鏂扮姸鎬�
return _cache.GetOrAdd(GetCacheKey(ipAddress), _ => new RobotSocketState
{
- IPAddress = ipAddress,
- RobotCrane = robotCrane
+ IPAddress = ipAddress, // 璁剧疆 IP 鍦板潃浣滀负鏍囪瘑
+ RobotCrane = robotCrane // 淇濆瓨璁惧淇℃伅
});
}
}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs
index 724a3a8..1b0f476 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs
@@ -1,10 +1,11 @@
-锘縰sing Newtonsoft.Json;
+using Newtonsoft.Json;
using WIDESEA_Core;
using WIDESEAWCS_Common;
using WIDESEAWCS_Common.HttpEnum;
using WIDESEAWCS_Common.TaskEnum;
using WIDESEAWCS_Core;
using WIDESEAWCS_Core.Helper;
+using WIDESEAWCS_Core.LogHelper;
using WIDESEAWCS_DTO.Stock;
using WIDESEAWCS_DTO.TaskInfo;
using WIDESEAWCS_ITaskInfoService;
@@ -15,17 +16,65 @@
namespace WIDESEAWCS_Tasks
{
/// <summary>
- /// 鏈哄櫒浜轰换鍔″鐞嗗櫒锛氳礋璐d换鍔¤幏鍙栥�佷笅鍙戙�佸叆搴撲换鍔″洖浼犲強搴撳瓨 DTO 鏋勫缓銆�
+ /// 鏈哄櫒浜轰换鍔″鐞嗗櫒 - 璐熻矗浠诲姟鑾峰彇銆佷笅鍙戙�佸叆搴撲换鍔″洖浼犲強搴撳瓨 DTO 鏋勫缓
/// </summary>
+ /// <remarks>
+ /// 鏍稿績鑱岃矗锛�
+ /// 1. 浠庢暟鎹簱杞寰呭鐞嗙殑鏈哄櫒浜轰换鍔�
+ /// 2. 鍚戞満鍣ㄤ汉瀹㈡埛绔笅鍙戝彇璐ф寚浠わ紙Pickbattery锛�
+ /// 3. 澶勭悊鍏ュ簱浠诲姟鐨勫洖浼狅紙鎷嗙洏/缁勭洏/鎹㈢洏鍦烘櫙锛�
+ /// 4. 鏋勫缓搴撳瓨鍥炰紶 DTO 骞惰皟鐢� WMS 鎺ュ彛
+ ///
+ /// 閫氳繃缃戝叧璁块棶 Socket锛岄伩鍏嶄笟鍔″眰鐩存帴渚濊禆 TcpSocketServer銆�
+ /// </remarks>
public class RobotTaskProcessor
{
- // 閫氳繃缃戝叧璁块棶 Socket锛岄伩鍏嶄笟鍔″眰鐩存帴渚濊禆 TcpSocketServer銆�
+ /// <summary>
+ /// Socket 瀹㈡埛绔綉鍏虫帴鍙�
+ /// </summary>
+ /// <remarks>
+ /// 閫氳繃缃戝叧璁块棶 Socket锛岄伩鍏嶄笟鍔″眰鐩存帴渚濊禆 TcpSocketServer銆�
+ /// 鎻愪緵缁熶竴鐨勫鎴风閫氫俊鎺ュ彛銆�
+ /// </remarks>
private readonly ISocketClientGateway _socketClientGateway;
+
+ /// <summary>
+ /// 鏈烘鎵嬬姸鎬佺鐞嗗櫒
+ /// </summary>
private readonly RobotStateManager _stateManager;
+
+ /// <summary>
+ /// 鏈哄櫒浜轰换鍔℃湇鍔�
+ /// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬鏌ヨ銆佹洿鏂般�佸垹闄ゆ満鍣ㄤ汉浠诲姟璁板綍銆�
+ /// </remarks>
private readonly IRobotTaskService _robotTaskService;
+
+ /// <summary>
+ /// 閫氱敤浠诲姟鏈嶅姟
+ /// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬涓� WMS 绯荤粺浜や簰锛屾帴鏀朵换鍔°�佸鐞嗕换鍔$姸鎬佺瓑銆�
+ /// </remarks>
private readonly ITaskService _taskService;
+
+ /// <summary>
+ /// HTTP 瀹㈡埛绔府鍔╃被
+ /// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬璋冪敤 WMS 绯荤粺鐨� HTTP 鎺ュ彛銆�
+ /// </remarks>
private readonly HttpClientHelper _httpClientHelper;
+ /// <summary>
+ /// 鏋勯�犲嚱鏁�
+ /// </summary>
+ /// <param name="socketClientGateway">Socket 缃戝叧</param>
+ /// <param name="stateManager">鐘舵�佺鐞嗗櫒</param>
+ /// <param name="robotTaskService">鏈哄櫒浜轰换鍔℃湇鍔�</param>
+ /// <param name="taskService">閫氱敤浠诲姟鏈嶅姟</param>
+ /// <param name="httpClientHelper">HTTP 瀹㈡埛绔府鍔╃被</param>
public RobotTaskProcessor(
ISocketClientGateway socketClientGateway,
RobotStateManager stateManager,
@@ -41,34 +90,68 @@
}
/// <summary>
- /// 鎸夎澶囩紪鐮佽幏鍙栧綋鍓嶆満鍣ㄤ汉浠诲姟銆�
+ /// 鎸夎澶囩紪鐮佽幏鍙栧綋鍓嶆満鍣ㄤ汉鐨勫緟澶勭悊浠诲姟
/// </summary>
+ /// <remarks>
+ /// 浠庢暟鎹簱涓煡璇㈡寚瀹氳澶囩紪鐮佺殑寰呭鐞嗘満鍣ㄤ汉浠诲姟銆�
+ /// 鍙繑鍥炵姸鎬佷负"寰呭鐞�"鐨勪换鍔°��
+ /// </remarks>
+ /// <param name="robotCrane">鏈哄櫒浜鸿澶囦俊鎭紝鍖呭惈璁惧缂栫爜</param>
+ /// <returns>寰呭鐞嗙殑浠诲姟瀵硅薄锛屽鏋滄病鏈夊垯杩斿洖 null</returns>
public Dt_RobotTask? GetTask(RobotCraneDevice robotCrane)
{
return _robotTaskService.QueryRobotCraneTask(robotCrane.DeviceCode);
}
/// <summary>
- /// 鍒犻櫎鏈哄櫒浜轰换鍔°��
+ /// 鍒犻櫎鏈哄櫒浜轰换鍔�
/// </summary>
+ /// <remarks>
+ /// 褰撲换鍔″畬鎴愶紙鏃犺鏄垚鍔熻繕鏄け璐ワ級鏃惰皟鐢紝鍒犻櫎鏁版嵁搴撲腑鐨勪换鍔¤褰曘��
+ /// </remarks>
+ /// <param name="ID">瑕佸垹闄ょ殑浠诲姟 ID</param>
+ /// <returns>鍒犻櫎鏄惁鎴愬姛</returns>
public bool? DeleteTask(int ID)
{
return _robotTaskService.Repository.DeleteDataById(ID);
}
/// <summary>
- /// 涓嬪彂鍙栬揣鎸囦护锛圥ickbattery锛夊埌鏈哄櫒浜哄鎴风銆�
+ /// 涓嬪彂鍙栬揣鎸囦护锛圥ickbattery锛夊埌鏈哄櫒浜哄鎴风
/// </summary>
+ /// <remarks>
+ /// 鍙戦�佹牸寮忥細Pickbattery,{婧愬湴鍧�}
+ /// 渚嬪锛歅ickbattery,A01 琛ㄧず浠� A01 浣嶇疆鍙栬揣
+ ///
+ /// 涓嬪彂鎴愬姛鍚庯細
+ /// 1. 鏇存柊浠诲姟鐘舵�佷负"鏈哄櫒浜烘墽琛屼腑"
+ /// 2. 灏嗕换鍔″叧鑱斿埌鐘舵�佸璞�
+ /// 3. 瀹夊叏鏇存柊鐘舵�佸埌 Redis
+ /// 4. 鏇存柊浠诲姟璁板綍鍒版暟鎹簱
+ /// </remarks>
+ /// <param name="task">瑕佷笅鍙戠殑浠诲姟瀵硅薄</param>
+ /// <param name="state">鏈哄櫒浜哄綋鍓嶇姸鎬�</param>
public async Task SendSocketRobotPickAsync(Dt_RobotTask task, RobotSocketState state)
{
+ // 鏋勫缓鍙栬揣鎸囦护锛屾牸寮忥細Pickbattery,{婧愬湴鍧�}
string taskString = $"Pickbattery,{task.RobotSourceAddress}";
+
+ // 閫氳繃 Socket 缃戝叧鍙戦�佹寚浠ゅ埌鏈哄櫒浜哄鎴风
bool result = await _socketClientGateway.SendToClientAsync(state.IPAddress, taskString);
+
if (result)
{
+ // 鍙戦�佹垚鍔燂紝璁板綍鏃ュ織
+ QuartzLogger.Error($"涓嬪彂鍙栬揣鎸囦护锛屾寚浠�: {taskString}", state.RobotCrane.DeviceName);
+
+ // 鏇存柊浠诲姟鐘舵�佷负"鏈哄櫒浜烘墽琛屼腑"
task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode();
+
+ // 灏嗕换鍔″叧鑱斿埌鐘舵�佸璞�
state.CurrentTask = task;
- // 淇濇寔鍘熻涔夛細浠呭湪鐘舵�佸畨鍏ㄥ啓鍏ユ垚鍔熷悗鍐嶆洿鏂颁换鍔$姸鎬併��
+ // 淇濇寔鍘熻涔夛細浠呭湪鐘舵�佸畨鍏ㄥ啓鍏ユ垚鍔熷悗鍐嶆洿鏂颁换鍔$姸鎬�
+ // 杩欐牱鍙互纭繚鐘舵�佸拰浠诲姟璁板綍鐨勪竴鑷存��
if (_stateManager.TryUpdateStateSafely(state.IPAddress, state))
{
await _robotTaskService.UpdateRobotTaskAsync(task);
@@ -77,89 +160,142 @@
}
/// <summary>
- /// 澶勭悊鍏ュ簱浠诲姟鍥炰紶锛堟媶鐩�/缁勭洏/鎹㈢洏鍦烘櫙锛夈��
+ /// 澶勭悊鍏ュ簱浠诲姟鍥炰紶锛堟媶鐩�/缁勭洏/鎹㈢洏鍦烘櫙锛�
/// </summary>
+ /// <remarks>
+ /// 褰撳彇璐у畬鎴愶紙AllPickFinished锛夋垨鏀捐揣瀹屾垚锛圓llPutFinished锛夋椂璋冪敤姝ゆ柟娉曘��
+ /// 鏍规嵁浠诲姟绫诲瀷鍜屽湴鍧�鏉ユ簮鍐冲畾濡備綍鍥炰紶缁� WMS銆�
+ ///
+ /// 澶勭悊閫昏緫锛�
+ /// 1. 鏍规嵁 useSourceAddress 鍐冲畾浣跨敤婧愬湴鍧�杩樻槸鐩爣鍦板潃
+ /// 2. 鏍规嵁浠诲姟绫诲瀷锛堢粍鐩�/鎹㈢洏/鎷嗙洏锛夊喅瀹氫换鍔$被鍨嬶紙鍏ュ簱/绌烘墭鐩樺叆搴擄級
+ /// 3. 鏋勫缓 CreateTaskDto 骞惰皟鐢� WMS 鎺ュ彛鍒涘缓浠诲姟
+ /// 4. 鎺ユ敹 WMS 杩斿洖鐨勪换鍔′俊鎭�
+ /// 5. 鏇存柊杈撻�佺嚎鐨勭洰鏍囧湴鍧�銆佷换鍔″彿绛�
+ /// </remarks>
+ /// <param name="state">鏈哄櫒浜哄綋鍓嶇姸鎬�</param>
+ /// <param name="useSourceAddress">鏄惁浣跨敤婧愬湴鍧�锛坱rue 琛ㄧず鎷嗙洏/鎹㈢洏鍦烘櫙锛宖alse 琛ㄧず缁勭洏/鎹㈢洏鍦烘櫙锛�</param>
+ /// <returns>澶勭悊鏄惁鎴愬姛</returns>
public async Task<bool> HandleInboundTaskAsync(RobotSocketState state, bool useSourceAddress)
{
+ // 鑾峰彇褰撳墠鍏宠仈鐨勪换鍔�
var currentTask = state.CurrentTask;
if (currentTask == null)
{
return false;
}
+ // 鑾峰彇宸烽亾浠g爜
string roadway = currentTask.RobotSourceAddressLineCode;
+
+ // 鏍规嵁宸烽亾鍚嶇О鍒ゆ柇浠撳簱 ID
+ // ZYRB1 -> 1, HPRB001 -> 2, 鍏朵粬 -> 3
int warehouseId = currentTask.RobotRoadway == "ZYRB1" ? 1 : currentTask.RobotRoadway == "HPRB001" ? 2 : 3;
+ // 浠诲姟绫诲瀷锛�0 琛ㄧず鏈畾涔夛紝绋嶅悗鏍规嵁浠诲姟绫诲瀷璁剧疆锛�
int taskType = 0;
+
+ // 婧愬湴鍧�鍜岀洰鏍囧湴鍧�锛堝垵濮嬪寲锛�
string SourceAddress = currentTask.RobotTargetAddressLineCode;
string TargetAddress = currentTask.RobotSourceAddressLineCode;
+
+ // 鎵樼洏浠g爜锛堝垵濮嬪寲涓虹┖锛�
string PalletCode = string.Empty;
+
+ // 鑾峰彇浠诲姟绫诲瀷鐨勬灇涓惧��
var robotTaskType = (RobotTaskTypeEnum)currentTask.RobotTaskType;
+ // 鏍规嵁 useSourceAddress 鍐冲畾澶勭悊閫昏緫
if (useSourceAddress)
{
+ // 浣跨敤婧愬湴鍧�鐨勫満鏅細鎷嗙洏銆佹崲鐩橈紙鏀剧┖鎵樼洏锛�
switch (robotTaskType)
{
case RobotTaskTypeEnum.GroupPallet:
+ // 缁勭洏浠诲姟涓嶄娇鐢ㄦ簮鍦板潃锛岀洿鎺ヨ繑鍥� false
return false;
case RobotTaskTypeEnum.ChangePallet:
case RobotTaskTypeEnum.SplitPallet:
- taskType = TaskTypeEnum.InEmpty.GetHashCode();
- PalletCode = currentTask.RobotSourceAddressPalletCode;
+ // 鎹㈢洏/鎷嗙洏鍦烘櫙锛氭墭鐩橀渶瑕佸叆搴�
+ taskType = TaskTypeEnum.InEmpty.GetHashCode(); // 绌烘墭鐩樺叆搴�
+ PalletCode = currentTask.RobotSourceAddressPalletCode; // 浣跨敤婧愬湴鍧�鐨勬墭鐩樼爜
break;
}
}
else
{
+ // 浣跨敤鐩爣鍦板潃鐨勫満鏅細缁勭洏銆佹崲鐩橈紙鎴愬搧鍏ュ簱锛�
switch (robotTaskType)
{
case RobotTaskTypeEnum.ChangePallet:
case RobotTaskTypeEnum.GroupPallet:
- taskType = TaskTypeEnum.Inbound.GetHashCode();
- PalletCode = currentTask.RobotTargetAddressPalletCode;
+ // 鎹㈢洏/缁勭洏鍦烘櫙锛氳揣鐗╅渶瑕佸叆搴�
+ taskType = TaskTypeEnum.Inbound.GetHashCode(); // 鎴愬搧鍏ュ簱
+ PalletCode = currentTask.RobotTargetAddressPalletCode; // 浣跨敤鐩爣鍦板潃鐨勬墭鐩樼爜
break;
case RobotTaskTypeEnum.SplitPallet:
+ // 鎷嗙洏浠诲姟涓嶄娇鐢ㄧ洰鏍囧湴鍧�
return true;
}
}
+ // 鏋勫缓鍒涘缓浠诲姟鐨� DTO
CreateTaskDto taskDto = new CreateTaskDto
{
- PalletCode = PalletCode,
- SourceAddress = SourceAddress ?? string.Empty,
- TargetAddress = TargetAddress ?? string.Empty,
- Roadway = roadway,
- WarehouseId = warehouseId,
- PalletType = 1,
- TaskType = taskType
+ PalletCode = PalletCode, // 鎵樼洏鏉$爜
+ SourceAddress = SourceAddress ?? string.Empty, // 婧愬湴鍧�
+ TargetAddress = TargetAddress ?? string.Empty, // 鐩爣鍦板潃
+ Roadway = roadway, // 宸烽亾
+ WarehouseId = warehouseId, // 浠撳簱 ID
+ PalletType = 1, // 鎵樼洏绫诲瀷锛堥粯璁や负1锛�
+ TaskType = taskType // 浠诲姟绫诲瀷锛堝叆搴�/绌烘墭鐩樺叆搴擄級
};
+ // 璋冪敤 WMS 鎺ュ彛鍒涘缓鍏ュ簱浠诲姟
var result = _httpClientHelper.Post<WebResponseContent>(nameof(ConfigKey.CreateTaskInboundAsync), taskDto.ToJson());
+
+ // 濡傛灉璋冪敤澶辫触鎴栬繑鍥為敊璇姸鎬�
if (!result.Data.Status && result.IsSuccess)
{
return false;
}
+ // 瑙f瀽 WMS 杩斿洖鐨勪换鍔′俊鎭�
WMSTaskDTO taskDTO = JsonConvert.DeserializeObject<WMSTaskDTO>(result.Data.Data.ToJson() ?? string.Empty) ?? new WMSTaskDTO();
+
+ // 璋冪敤浠诲姟鏈嶅姟鎺ユ敹 WMS 浠诲姟
var content = _taskService.ReceiveWMSTask(new List<WMSTaskDTO> { taskDTO });
if (!content.Status)
{
return false;
}
+ // 瑙f瀽杩斿洖鐨勪换鍔′俊鎭�
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));
+
if (device != null)
{
+ // 灏嗚澶囪浆鎹负杈撻�佺嚎绫诲瀷
CommonConveyorLine conveyorLine = (CommonConveyorLine)device;
+
+ // 璁剧疆杈撻�佺嚎鐨勭洰鏍囧湴鍧�
conveyorLine.SetValue(ConveyorLineDBNameNew.Target, taskInfo.NextAddress, sourceAddress);
+
+ // 璁剧疆杈撻�佺嚎鐨勪换鍔″彿
conveyorLine.SetValue(ConveyorLineDBNameNew.TaskNo, taskInfo.TaskNum, sourceAddress);
+
+ // 瑙﹀彂杈撻�佺嚎寮�濮嬫墽琛岋紙鍐欏叆 WCS_STB = 1锛�
conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_STB, 1, sourceAddress);
+ // 鏇存柊浠诲姟鐘舵�佸埌涓嬩竴闃舵
if (_taskService.UpdateTaskStatusToNext(taskInfo).Status)
{
return true;
@@ -170,23 +306,47 @@
}
/// <summary>
- /// 鏋勫缓搴撳瓨鍥炰紶 DTO銆�
+ /// 鏋勫缓搴撳瓨鍥炰紶 DTO
/// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬鎷嗙洏鍜岀粍鐩樻搷浣滄椂锛屽悜 WMS 鍥炰紶搴撳瓨淇℃伅銆�
+ /// DTO 鍖呭惈婧愯揣浣嶃�佺洰鏍囪揣浣嶃�佹墭鐩樼爜浠ュ強姣忎釜浣嶇疆鐨勭數姹犳潯鐮併��
+ /// </remarks>
+ /// <param name="state">鏈哄櫒浜哄綋鍓嶇姸鎬�</param>
+ /// <param name="positions">鐢垫睜浣嶇疆鏁扮粍</param>
+ /// <returns>鏋勫缓濂界殑搴撳瓨 DTO</returns>
public static StockDTO BuildStockDTO(RobotSocketState state, int[] positions)
{
return new StockDTO
{
+ // 婧愯緭閫佺嚎缂栧彿
SourceLineNo = state.CurrentTask.RobotSourceAddressLineCode,
+
+ // 婧愭墭鐩樺彿
SourcePalletNo = state.CurrentTask.RobotSourceAddressPalletCode,
+
+ // 鐩爣鎵樼洏鍙�
TargetPalletNo = state.CurrentTask.RobotTargetAddressPalletCode,
+
+ // 鐩爣杈撻�佺嚎缂栧彿
TargetLineNo = state.CurrentTask.RobotTargetAddressLineCode,
+
+ // 鐢垫睜浣嶇疆璇︽儏鍒楄〃
+ // 杩囨护鎺変綅缃负 0 鎴栬礋鏁扮殑鏃犳晥鏁版嵁
+ // 鎸変綅缃紪鍙锋帓搴�
+ // 涓烘瘡涓綅缃敓鎴愬搴旂殑搴撳瓨璇︽儏
Details = positions
- .Where(x => x > 0)
- .OrderBy(x => x)
+ .Where(x => x > 0) // 杩囨护鏃犳晥浣嶇疆
+ .OrderBy(x => x) // 鎸変綅缃帓搴�
.Select((x, idx) => new StockDetailDTO
{
+ // 鏁伴噺锛氬鏋滃凡鏈変换鍔℃�绘暟锛屼娇鐢ㄤ换鍔℃�绘暟+褰撳墠浣嶇疆鏁帮紱鍚﹀垯鍙娇鐢ㄥ綋鍓嶄綅缃暟
Quantity = state.RobotTaskTotalNum > 0 ? state.RobotTaskTotalNum + positions.Length : positions.Length,
+
+ // 閫氶亾/浣嶇疆缂栧彿
Channel = x,
+
+ // 鐢垫睜鏉$爜锛氬鏋滅姸鎬佷腑鏈夋潯鐮佸垪琛紝鍙栧搴斾綅缃殑鏉$爜锛涘惁鍒欎负绌�
CellBarcode = state.CellBarcode?.Count > 0 ? state.CellBarcode[x - 1] : ""
})
.ToList()
@@ -194,16 +354,33 @@
}
/// <summary>
- /// 璋冪敤鎷嗙洏 API銆�
+ /// 璋冪敤鎷嗙洏 API
/// </summary>
+ /// <remarks>
+ /// 褰撳彇璐у畬鎴愪笖闇�瑕佹媶鐩樻椂璋冪敤銆�
+ /// 灏嗙數姹犱粠鎵樼洏涓婂彇涓嬶紝閫愪釜鏀剧疆鍒扮洰鏍囦綅缃��
+ /// </remarks>
+ /// <param name="stockDTO">搴撳瓨 DTO锛屽寘鍚鎷嗙洏鐨勭數鑺俊鎭�</param>
+ /// <returns>HTTP 鍝嶅簲缁撴灉</returns>
public HttpResponseResult<WebResponseContent> PostSplitPalletAsync(StockDTO stockDTO)
{
return _httpClientHelper.Post<WebResponseContent>(nameof(ConfigKey.SplitPalletAsync), stockDTO.ToJson());
}
/// <summary>
- /// 璋冪敤缁勭洏/鎹㈢洏 API銆�
+ /// 璋冪敤缁勭洏/鎹㈢洏 API
/// </summary>
+ /// <remarks>
+ /// 褰撴斁璐у畬鎴愪笖闇�瑕佺粍鐩樻垨鎹㈢洏鏃惰皟鐢ㄣ��
+ /// 灏嗗涓數姹犵粍鍚堝埌鍚屼竴涓墭鐩樹笂銆�
+ ///
+ /// configKey 鍙傛暟鍐冲畾璋冪敤鍝釜 API锛�
+ /// - GroupPalletAsync: 缁勭洏鎺ュ彛
+ /// - ChangePalletAsync: 鎹㈢洏鎺ュ彛
+ /// </remarks>
+ /// <param name="configKey">閰嶇疆閿悕锛屽喅瀹氳皟鐢ㄥ摢涓� API</param>
+ /// <param name="stockDTO">搴撳瓨 DTO锛屽寘鍚缁勭洏鐨勭數鑺俊鎭�</param>
+ /// <returns>HTTP 鍝嶅簲缁撴灉</returns>
public HttpResponseResult<WebResponseContent> PostGroupPalletAsync(string configKey, StockDTO stockDTO)
{
return _httpClientHelper.Post<WebResponseContent>(configKey, stockDTO.ToJson());
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotPrefixCommandHandler.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotPrefixCommandHandler.cs
index 56ea0ab..ae82c68 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotPrefixCommandHandler.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotPrefixCommandHandler.cs
@@ -1,4 +1,4 @@
-锘縰sing System.Net.Sockets;
+using System.Net.Sockets;
using WIDESEAWCS_Common.HttpEnum;
using WIDESEAWCS_Common.TaskEnum;
using WIDESEAWCS_DTO.TaskInfo;
@@ -9,15 +9,61 @@
namespace WIDESEAWCS_Tasks.Workflow
{
/// <summary>
- /// 鍓嶇紑鍛戒护澶勭悊锛氳縼绉诲師 RobotMessageHandler 鐨� pickfinished/putfinished 鍒嗘敮銆�
+ /// 鍓嶇紑鍛戒护澶勭悊鍣�
/// </summary>
+ /// <remarks>
+ /// 杩佺Щ鍘� RobotMessageHandler 鐨� pickfinished/putfinished 鍒嗘敮銆�
+ ///
+ /// 鍓嶇紑鍛戒护鏄寚浠ョ壒瀹氬墠缂�寮�澶寸殑鍛戒护锛屽悗闈㈣窡闅忛�楀彿鍒嗛殧鐨勫弬鏁般��
+ /// 鏍煎紡锛歿鍓嶇紑},{鍙傛暟1},{鍙傛暟2},...
+ ///
+ /// 褰撳墠鏀寔鐨勫墠缂�鍛戒护锛�
+ /// - pickfinished: 鍙栬揣瀹屾垚锛屽悗闈㈣窡闅忓畬鎴愮殑浣嶇疆缂栧彿鍒楄〃
+ /// - putfinished: 鏀捐揣瀹屾垚锛屽悗闈㈣窡闅忓畬鎴愮殑浣嶇疆缂栧彿鍒楄〃
+ ///
+ /// 杩欎簺鍛戒护閫氬父鍖呭惈鍙栬揣鎴栨斁璐х殑浣嶇疆淇℃伅锛岄渶瑕佽В鏋愬苟鏇存柊鐘舵�併��
+ /// </remarks>
public class RobotPrefixCommandHandler : IRobotPrefixCommandHandler
{
+ /// <summary>
+ /// 鏈哄櫒浜轰换鍔℃湇鍔�
+ /// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬鏌ヨ鍜屾洿鏂颁换鍔¤褰曘��
+ /// </remarks>
private readonly IRobotTaskService _robotTaskService;
+
+ /// <summary>
+ /// 浠诲姟澶勭悊鍣�
+ /// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬澶勭悊鍙栬揣/鏀捐揣瀹屾垚鏃剁殑涓氬姟閫昏緫锛屽璋冪敤鎷嗙洏/缁勭洏 API銆�
+ /// </remarks>
private readonly RobotTaskProcessor _taskProcessor;
+
+ /// <summary>
+ /// 鐘舵�佺鐞嗗櫒
+ /// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬瀹夊叏鏇存柊鏈哄櫒浜虹殑鐘舵�併��
+ /// </remarks>
private readonly RobotStateManager _stateManager;
+
+ /// <summary>
+ /// Socket 缃戝叧
+ /// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬鍚戝鎴风鍙戦�佸搷搴旀秷鎭��
+ /// </remarks>
private readonly ISocketClientGateway _socketClientGateway;
+ /// <summary>
+ /// 鏋勯�犲嚱鏁�
+ /// </summary>
+ /// <param name="robotTaskService">浠诲姟鏈嶅姟</param>
+ /// <param name="taskProcessor">浠诲姟澶勭悊鍣�</param>
+ /// <param name="stateManager">鐘舵�佺鐞嗗櫒</param>
+ /// <param name="socketClientGateway">Socket 缃戝叧</param>
public RobotPrefixCommandHandler(
IRobotTaskService robotTaskService,
RobotTaskProcessor taskProcessor,
@@ -30,69 +76,137 @@
_socketClientGateway = socketClientGateway;
}
+ /// <summary>
+ /// 妫�鏌ユ秷鎭槸鍚︿负鍓嶇紑鍛戒护
+ /// </summary>
+ /// <remarks>
+ /// 鍓嶇紑鍛戒护蹇呴』浠� "pickfinished" 鎴� "putfinished" 寮�澶达紙涓嶅尯鍒嗗ぇ灏忓啓锛夈��
+ /// </remarks>
+ /// <param name="message">娑堟伅鍐呭锛堝皬鍐欏舰寮忥級</param>
+ /// <returns>濡傛灉鏄寚缂�鍛戒护杩斿洖 true</returns>
public bool IsPrefixCommand(string message)
{
+ // 妫�鏌ユ秷鎭槸鍚︿互 pickfinished 鎴� putfinished 寮�澶�
return message.StartsWith("pickfinished") || message.StartsWith("putfinished");
}
+ /// <summary>
+ /// 澶勭悊鍓嶇紑鍛戒护
+ /// </summary>
+ /// <remarks>
+ /// 澶勭悊娴佺▼锛�
+ /// 1. 瑙f瀽娑堟伅锛屾彁鍙栦綅缃弬鏁�
+ /// 2. 鏌ヨ褰撳墠浠诲姟
+ /// 3. 鏍规嵁鍛戒护绫诲瀷璋冪敤鐩稿簲鐨勫鐞嗘柟娉�
+ /// 4. 鍥炲啓鍘熸秷鎭埌瀹㈡埛绔�
+ ///
+ /// 娑堟伅鏍煎紡锛歿鍛戒护鍓嶇紑},{浣嶇疆1},{浣嶇疆2},...
+ /// 绀轰緥锛歱ickfinished,1,2,3 琛ㄧず鍙栬揣瀹屾垚锛屼綅缃� 1銆�2銆�3 鐨勮揣鐗╁凡鍙栬蛋
+ /// </remarks>
+ /// <param name="message">鍘熷娑堟伅鍐呭</param>
+ /// <param name="state">鏈哄櫒浜哄綋鍓嶇姸鎬�</param>
+ /// <param name="client">TCP 瀹㈡埛绔繛鎺ワ紝鐢ㄤ簬鍙戦�佸搷搴�</param>
public async Task HandleAsync(string message, RobotSocketState state, TcpClient client)
{
try
{
+ // 鎸夐�楀彿鍒嗛殧娑堟伅锛屾彁鍙栧懡浠ゅ拰鍙傛暟
+ // 渚嬪锛歱ickfinished,1,2,3 -> ["pickfinished", "1", "2", "3"]
var parts = message.Split(',');
+
+ // 妫�鏌ユ秷鎭牸寮忔槸鍚︽湁鏁堬細鑷冲皯瑕佹湁鍛戒护鍓嶇紑锛屼笖鐘舵�佷腑鏈夊綋鍓嶄换鍔�
if (parts.Length < 1 || state.CurrentTask == null)
{
return;
}
+ // 鎻愬彇鍛戒护鍓嶇紑骞惰浆鎹负灏忓啓
var cmd = parts[0].ToLowerInvariant();
+
+ // 瑙f瀽浣嶇疆鍙傛暟锛堣烦杩囧懡浠ゅ墠缂�锛屽鐞嗗悗闈㈢殑鏁板瓧锛�
+ // 杩囨护鎺夋棤娉曡В鏋愪负鏁板瓧鎴栧�间负 0 鐨勪綅缃�
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)
+ .Select(p => int.TryParse(p, out int value) ? value : (int?)null) // 灏濊瘯瑙f瀽涓烘暣鏁�
+ .Where(v => v.HasValue && v.Value != 0) // 杩囨护鎺� null 鍜� 0
+ .Select(v => v!.Value) // 鎻愬彇鍊硷紙宸茬煡闈� null锛�
.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}");
}
}
+ /// <summary>
+ /// 澶勭悊鍙栬揣瀹屾垚锛坧ickfinished锛夊懡浠�
+ /// </summary>
+ /// <remarks>
+ /// 澶勭悊閫昏緫锛�
+ /// 1. 濡傛灉鏄媶鐩樹换鍔★紝鏋勫缓搴撳瓨 DTO 骞惰皟鐢ㄦ媶鐩� API
+ /// 2. 鏇存柊褰撳墠鍔ㄤ綔涓�"鍙栬揣瀹屾垚"
+ /// 3. 璁板綍鍙栬揣瀹屾垚鐨勪綅缃�
+ /// 4. 鏇存柊浠诲姟鐘舵�佷负"鏈哄櫒浜哄彇璐у畬鎴�"
+ /// 5. 瀹夊叏鏇存柊鐘舵�佸埌 Redis
+ /// </remarks>
+ /// <param name="state">鏈哄櫒浜哄綋鍓嶇姸鎬�</param>
+ /// <param name="positions">鍙栬揣瀹屾垚鐨勪綅缃紪鍙锋暟缁�</param>
+ /// <param name="task">鏈哄櫒浜轰换鍔¤褰�</param>
private async Task HandlePickFinishedAsync(RobotSocketState state, int[] positions, Dt_RobotTask? task)
{
+ // 濡傛灉鏄媶鐩樹换鍔�
if (state.IsSplitPallet)
{
+ // 鏋勫缓搴撳瓨 DTO锛屽寘鍚綅缃俊鎭拰鎵樼洏鏉$爜
var stockDTO = RobotTaskProcessor.BuildStockDTO(state, positions);
+
+ // 璁板綍鍙栬揣瀹屾垚鐨勪綅缃�
state.LastPickPositions = positions;
+ // 璋冪敤鎷嗙洏 API
var result = _taskProcessor.PostSplitPalletAsync(stockDTO);
+
+ // 濡傛灉 API 璋冪敤鎴愬姛
if (result.Data.Status && result.IsSuccess)
{
+ // 鏇存柊褰撳墠鍔ㄤ綔涓�"鍙栬揣瀹屾垚"
state.CurrentAction = "PickFinished";
}
}
else
{
+ // 闈炴媶鐩樹换鍔★紝鐩存帴鏇存柊鍔ㄤ綔
state.CurrentAction = "PickFinished";
}
+ // 璁板綍鍙栬揣瀹屾垚鐨勪綅缃紙鏃犺鏄惁鎷嗙洏閮借褰曪級
state.LastPickPositions = positions;
+
+ // 濡傛灉浠诲姟瀛樺湪
if (task != null)
{
+ // 鏇存柊浠诲姟鐘舵�佷负"鏈哄櫒浜哄彇璐у畬鎴�"
task.RobotTaskState = TaskRobotStatusEnum.RobotPickFinish.GetHashCode();
+
+ // 瀹夊叏鏇存柊鐘舵�佸埌 Redis锛岀‘淇濇洿鏂版垚鍔熷悗鍐嶆洿鏂版暟鎹簱
if (_stateManager.TryUpdateStateSafely(state.IPAddress, state))
{
await _robotTaskService.Repository.UpdateDataAsync(task);
@@ -100,34 +214,70 @@
}
}
+ /// <summary>
+ /// 澶勭悊鏀捐揣瀹屾垚锛坧utfinished锛夊懡浠�
+ /// </summary>
+ /// <remarks>
+ /// 澶勭悊閫昏緫锛�
+ /// 1. 濡傛灉鏄粍鐩樹换鍔★紝鏋勫缓搴撳瓨 DTO 骞惰皟鐢ㄧ粍鐩�/鎹㈢洏 API
+ /// 2. 濡傛灉缁勭洏鎴愬姛锛屽鍔犱换鍔¤鏁�
+ /// 3. 鏇存柊褰撳墠鍔ㄤ綔涓�"鏀捐揣瀹屾垚"
+ /// 4. 鏇存柊浠诲姟鐘舵�佷负"鏈哄櫒浜烘斁璐у畬鎴�"
+ /// 5. 瀹夊叏鏇存柊鐘舵�佸埌 Redis
+ /// </remarks>
+ /// <param name="state">鏈哄櫒浜哄綋鍓嶇姸鎬�</param>
+ /// <param name="positions">鏀捐揣瀹屾垚鐨勪綅缃紪鍙锋暟缁�</param>
+ /// <param name="task">鏈哄櫒浜轰换鍔¤褰�</param>
private async Task HandlePutFinishedAsync(RobotSocketState state, int[] positions, Dt_RobotTask? task)
{
+ // 鍋囪鏀捐揣鎴愬姛锛堝鏋滃悗缁� API 璋冪敤澶辫触涔熶笉鍥為��璁℃暟锛�
bool putSuccess = true;
+
+ // 濡傛灉鏄粍鐩樹换鍔★紙鍖呭惈鎹㈢洏锛�
if (state.IsGroupPallet)
{
+ // 璁板綍鏀捐揣瀹屾垚鐨勪綅缃�
state.LastPutPositions = positions;
+
+ // 鏋勫缓搴撳瓨 DTO
var stockDTO = RobotTaskProcessor.BuildStockDTO(state, positions);
+
+ // 鏍规嵁浠诲姟绫诲瀷鍐冲畾璋冪敤鍝釜 API
+ // 鎹㈢洏浠诲姟璋冪敤 ChangePalletAsync锛岀粍鐩樹换鍔¤皟鐢� GroupPalletAsync
var configKey = state.CurrentTask?.RobotTaskType == RobotTaskTypeEnum.ChangePallet.GetHashCode()
? nameof(ConfigKey.ChangePalletAsync)
: nameof(ConfigKey.GroupPalletAsync);
+ // 璋冪敤缁勭洏/鎹㈢洏 API
var result = _taskProcessor.PostGroupPalletAsync(configKey, stockDTO);
+
+ // 妫�鏌� API 杩斿洖鐘舵��
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();
+
+ // 瀹夊叏鏇存柊鐘舵�佸埌 Redis
if (_stateManager.TryUpdateStateSafely(state.IPAddress, state))
{
await _robotTaskService.Repository.UpdateDataAsync(task);
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotSimpleCommandHandler.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotSimpleCommandHandler.cs
index 1869396..c8887de 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotSimpleCommandHandler.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotSimpleCommandHandler.cs
@@ -1,54 +1,121 @@
-锘縰sing WIDESEAWCS_Common.TaskEnum;
+using WIDESEAWCS_Common.TaskEnum;
using WIDESEAWCS_Tasks.Workflow.Abstractions;
namespace WIDESEAWCS_Tasks.Workflow
{
/// <summary>
- /// 绠�鍗曞懡浠ゅ鐞嗭細浠呰縼绉诲師 RobotMessageHandler 涓殑鍛戒护鍒嗘敮锛屼笉鏀瑰彉涓氬姟璇箟銆�
+ /// 绠�鍗曞懡浠ゅ鐞嗗櫒
/// </summary>
+ /// <remarks>
+ /// 杩佺Щ鍘� RobotMessageHandler 涓殑绠�鍗曞懡浠ゅ垎鏀紝涓嶆敼鍙樹笟鍔¤涔夈��
+ ///
+ /// 绠�鍗曞懡浠ゆ槸鎸囦笉闇�瑕侀澶栧弬鏁扮殑鐘舵�佹洿鏂板懡浠わ紝濡傝繍琛岀姸鎬併�佹ā寮忓垏鎹㈢瓑銆�
+ /// 涓庡墠缂�鍛戒护锛堥渶瑕佽В鏋愪綅缃弬鏁帮級鐩稿銆�
+ ///
+ /// 澶勭悊瀹屾垚鍚庤繑鍥� true锛涙棤娉曡瘑鍒殑鍛戒护杩斿洖 false銆�
+ /// </remarks>
public class RobotSimpleCommandHandler : IRobotSimpleCommandHandler
{
+ /// <summary>
+ /// 鏈哄櫒浜轰换鍔″鐞嗗櫒
+ /// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬澶勭悊 allpickfinished 鍜� allputfinished 鍛戒护鏃讹紝
+ /// 璋冪敤浠诲姟鍏ュ簱鍜屽垹闄ら�昏緫銆�
+ /// </remarks>
private readonly RobotTaskProcessor _taskProcessor;
+ /// <summary>
+ /// 鏋勯�犲嚱鏁�
+ /// </summary>
+ /// <param name="taskProcessor">浠诲姟澶勭悊鍣ㄥ疄渚�</param>
public RobotSimpleCommandHandler(RobotTaskProcessor taskProcessor)
{
_taskProcessor = taskProcessor;
}
+ /// <summary>
+ /// 澶勭悊绠�鍗曞懡浠�
+ /// </summary>
+ /// <remarks>
+ /// 鏍规嵁娑堟伅鍐呭鏇存柊鏈哄櫒浜虹殑杩愯鐘舵�併�佹ā寮忋�佹墜鑷傚璞$瓑灞炴�с��
+ /// 鏌愪簺鍛戒护锛堝 allpickfinished銆乤llputfinished锛変細瑙﹀彂瀹為檯鐨勪笟鍔¢�昏緫銆�
+ ///
+ /// 鍛戒护鍒楄〃锛�
+ /// - homing: 鍥為浂涓�
+ /// - homed: 宸插洖闆�
+ /// - running: 杩愯涓�
+ /// - pausing: 鏆傚仠涓�
+ /// - warming: 棰勭儹涓�
+ /// - emstoping: 鎬ュ仠涓�
+ /// - picking: 鍙栬揣涓�
+ /// - puting: 鏀捐揣涓�
+ /// - runmode,1: 杩愯妯″紡鍒囨崲鍒版墜鍔�
+ /// - runmode,2: 杩愯妯″紡鍒囨崲鍒拌嚜鍔�
+ /// - controlmode,1: 鎺у埗妯″紡鍒囨崲鍒板鎴风鎺у埗
+ /// - controlmode,2: 鎺у埗妯″紡鍒囨崲鍒板叾浠�
+ /// - armobject,1: 鎵嬭噦鏈夌墿鏂�
+ /// - armobject,0: 鎵嬭噦鏃犵墿鏂�
+ /// - allpickfinished: 鍏ㄩ儴鍙栬揣瀹屾垚
+ /// - allputfinished: 鍏ㄩ儴鏀捐揣瀹屾垚
+ /// </remarks>
+ /// <param name="message">娑堟伅鍐呭锛堝皬鍐欏舰寮忥級</param>
+ /// <param name="state">鏈哄櫒浜哄綋鍓嶇姸鎬侊紙浼氳淇敼锛�</param>
+ /// <returns>鏄惁鎴愬姛澶勭悊锛涙棤娉曡瘑鍒殑鍛戒护杩斿洖 false</returns>
public async Task<bool> HandleAsync(string message, RobotSocketState state)
{
+ // 浣跨敤 switch 琛ㄨ揪寮忚繘琛屾ā寮忓尮閰嶏紝鎻愰珮鍙鎬у拰鎬ц兘
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)
{
+ // 娌℃湁浠诲姟鍏宠仈锛岃繑鍥� false
return false;
}
+ // 鍒ゆ柇浠诲姟绫诲瀷
var robotTaskType = (RobotTaskTypeEnum)currentTask.RobotTaskType;
+
+ // 鍙湁鎷嗙洏鎴栨崲鐩樹换鍔¢渶瑕佸鐞嗗叆搴�
if (robotTaskType == RobotTaskTypeEnum.SplitPallet || robotTaskType == RobotTaskTypeEnum.ChangePallet)
{
+ // 澶勭悊鍏ュ簱浠诲姟鍥炰紶
+ // useSourceAddress: true 琛ㄧず浣跨敤婧愬湴鍧�锛堟媶鐩�/鎹㈢洏鍦烘櫙锛�
if (await _taskProcessor.HandleInboundTaskAsync(state, useSourceAddress: true))
{
+ // 鍏ュ簱鎴愬姛锛屽垹闄や换鍔¤褰�
_taskProcessor.DeleteTask(currentTask.RobotTaskId);
return true;
}
@@ -56,70 +123,101 @@
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)
{
+ // 澶勭悊鍏ュ簱浠诲姟鍥炰紶
+ // useSourceAddress: false 琛ㄧず浣跨敤鐩爣鍦板潃锛堢粍鐩�/鎹㈢洏鍦烘櫙锛�
if (await _taskProcessor.HandleInboundTaskAsync(state, useSourceAddress: false))
{
+ // 鍏ュ簱鎴愬姛锛屽垹闄や换鍔¤褰�
_taskProcessor.DeleteTask(currentTask.RobotTaskId);
- state.CurrentTask = null;
- state.RobotTaskTotalNum = 0;
- state.CellBarcode = new List<string>();
+
+ // 娓呯悊鐘舵�侊紝涓轰笅涓�涓换鍔″仛鍑嗗
+ 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;
+ // ==================== 妯″紡鍒囨崲鍛戒护 ====================
+
+ // 杩愯妯″紡鍒囨崲涓烘墜鍔紙妯″紡1锛�
case "runmode,1":
state.RobotRunMode = 1;
return true;
+ // 杩愯妯″紡鍒囨崲涓鸿嚜鍔紙妯″紡2锛�
case "runmode,2":
state.RobotRunMode = 2;
return true;
+ // 鎺у埗妯″紡鍒囨崲涓哄鎴风鎺у埗锛堟ā寮�1锛�
case "controlmode,1":
state.RobotControlMode = 1;
return true;
+ // 鎺у埗妯″紡鍒囨崲涓哄叾浠栵紙妯″紡2锛�
case "controlmode,2":
state.RobotControlMode = 2;
return true;
+ // ==================== 鎵嬭噦瀵硅薄鍛戒护 ====================
+
+ // 鎵嬭噦鏈夌墿鏂欙紙鎶撳彇鍒拌揣鐗╋級
case "armobject,1":
state.RobotArmObject = 1;
return true;
+ // 鎵嬭噦鏃犵墿鏂欙紙鎵嬭噦绌洪棽锛�
case "armobject,0":
state.RobotArmObject = 0;
return true;
+ // ==================== 榛樿鎯呭喌 ====================
+
+ // 鏃犳硶璇嗗埆鐨勫懡浠わ紝杩斿洖 false
default:
return false;
}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotWorkflowOrchestrator.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotWorkflowOrchestrator.cs
index 190a8d6..1f03443 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotWorkflowOrchestrator.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotWorkflowOrchestrator.cs
@@ -1,5 +1,6 @@
using WIDESEA_Core;
using WIDESEAWCS_Common.TaskEnum;
+using WIDESEAWCS_Core.LogHelper;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
using WIDESEAWCS_Tasks.Workflow.Abstractions;
@@ -7,15 +8,62 @@
namespace WIDESEAWCS_Tasks.Workflow
{
/// <summary>
- /// RobotJob 流程编排器:迁移原 RobotJob 状态机分支,降低 Job 类复杂度。
+ /// 鏈哄櫒浜轰换鍔$紪鎺掑櫒 - 璐熻矗 RobotJob 涓殑鐘舵�佹満娴佽浆鍜屾墽琛屾楠ょ紪鎺�
/// </summary>
+ /// <remarks>
+ /// 杩佺Щ鍘� RobotJob 鐘舵�佹満娴佽浆鏀寔锛岄檷浣� Job 灞傜殑澶嶆潅搴︺��
+ ///
+ /// 鏍稿績鑱岃矗锛�
+ /// 1. 鏍规嵁鏈哄櫒浜哄綋鍓嶇姸鎬佸拰浠诲姟鐘舵�侊紝鍐冲畾涓嬩竴姝ュ姩浣�
+ /// 2. 澶勭悊鍙栬揣瀹屾垚鍚庣殑鏀捐揣鎸囦护涓嬪彂
+ /// 3. 澶勭悊鏀捐揣瀹屾垚鍚庣殑鍙栬揣鎸囦护涓嬪彂锛堢粍鐩樺満鏅級
+ ///
+ /// 鐘舵�佹満娴佽浆瑙勫垯锛�
+ /// - 鏉′欢锛歊obotRunMode == 2锛堣嚜鍔ㄦā寮忥級涓� RobotControlMode == 1锛堝鎴风鎺у埗锛変笖 OperStatus != "Running"
+ /// - 鍙栬揣瀹屾垚 -> 鏀捐揣锛歅ickFinished + RobotArmObject == 1 + RobotPickFinish -> 鍙戦�� Putbattery
+ /// - 鏀捐揣瀹屾垚 -> 鍙栬揣锛歅utFinished + Homed + RobotArmObject == 0 -> 鍙戦�� Pickbattery锛堢粍鐩�/鎹㈢洏鍦烘櫙锛�
+ /// </remarks>
public class RobotWorkflowOrchestrator : IRobotWorkflowOrchestrator
{
+ /// <summary>
+ /// 鏈烘鎵嬬姸鎬佺鐞嗗櫒
+ /// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬璇诲彇鍜屾洿鏂版満鍣ㄤ汉鐨勭姸鎬併��
+ /// </remarks>
private readonly RobotStateManager _stateManager;
+
+ /// <summary>
+ /// 鏈烘鎵嬪鎴风绠$悊鍣�
+ /// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬鍚戞満鍣ㄤ汉瀹㈡埛绔彂閫佹寚浠ゃ��
+ /// </remarks>
private readonly RobotClientManager _clientManager;
+
+ /// <summary>
+ /// 浠诲姟澶勭悊鍣�
+ /// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬鎵ц浠诲姟鐩稿叧鐨勪笟鍔¢�昏緫锛屽鍙戦�佸彇璐ф寚浠ゃ��
+ /// </remarks>
private readonly RobotTaskProcessor _taskProcessor;
+
+ /// <summary>
+ /// 鏈哄櫒浜轰换鍔℃湇鍔�
+ /// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬鏇存柊浠诲姟鐘舵�併��
+ /// </remarks>
private readonly IRobotTaskService _robotTaskService;
+ /// <summary>
+ /// 鏋勯�犲嚱鏁�
+ /// </summary>
+ /// <param name="stateManager">鐘舵�佺鐞嗗櫒</param>
+ /// <param name="clientManager">瀹㈡埛绔鐞嗗櫒</param>
+ /// <param name="taskProcessor">浠诲姟澶勭悊鍣�</param>
+ /// <param name="robotTaskService">浠诲姟鏈嶅姟</param>
public RobotWorkflowOrchestrator(
RobotStateManager stateManager,
RobotClientManager clientManager,
@@ -28,81 +76,173 @@
_robotTaskService = robotTaskService;
}
+ /// <summary>
+ /// 鎵ц浠诲姟缂栨帓娴佺▼
+ /// </summary>
+ /// <remarks>
+ /// 鏍规嵁鏈哄櫒浜哄綋鍓嶇姸鎬佸拰浠诲姟鐘舵�侊紝鍐冲畾鏄惁涓嬪彂鎸囦护銆�
+ ///
+ /// 鎵ц鏉′欢锛�
+ /// 1. 鏈哄櫒浜哄浜庤嚜鍔ㄦā寮忥紙RobotRunMode == 2锛�
+ /// 2. 鏈哄櫒浜哄浜庡鎴风鎺у埗妯″紡锛圧obotControlMode == 1锛�
+ /// 3. 鏈哄櫒浜轰笉鍦ㄨ繍琛屼腑锛圤perStatus != "Running"锛�
+ ///
+ /// 娴佽浆閫昏緫锛�
+ /// - 鍙栬揣瀹屾垚涓旀墜鑷傛湁璐� -> 鍙戦�佹斁璐ф寚浠わ紙Putbattery锛�
+ /// - 鏀捐揣瀹屾垚涓旀墜鑷傛棤璐� -> 鍙戦�佸彇璐ф寚浠わ紙Pickbattery锛�
+ /// </remarks>
+ /// <param name="latestState">鏈哄櫒浜烘渶鏂扮姸鎬�</param>
+ /// <param name="task">寰呮墽琛岀殑鏈哄櫒浜轰换鍔�</param>
+ /// <param name="ipAddress">鏈哄櫒浜� IP 鍦板潃</param>
public async Task ExecuteAsync(RobotSocketState latestState, Dt_RobotTask task, string ipAddress)
{
- // 保持原有分支判定条件不变,确保行为一致。
+ // 鎸夊師鏂规鍒嗘敮鍒ゆ柇锛岀‘淇濋�昏緫涓�鑷�
+ // 妫�鏌ユ槸鍚︽弧瓒宠嚜鍔ㄦ帶鍒舵潯浠讹細
+ // 1. 杩愯妯″紡涓鸿嚜鍔紙2锛�
+ // 2. 鎺у埗妯″紡涓哄鎴风鎺у埗锛�1锛�
+ // 3. 杩愯鐘舵�佷笉鏄� Running锛堣鏄庡凡瀹屾垚褰撳墠鍔ㄤ綔锛�
if (latestState.RobotRunMode == 2 && latestState.RobotControlMode == 1 && latestState.OperStatus != "Running")
{
+ // ========== 鍙栬揣瀹屾垚鍚庣殑鏀捐揣澶勭悊 ==========
+ // 鏉′欢锛�
+ // - 褰撳墠鍔ㄤ綔鏄� PickFinished 鎴� AllPickFinished锛堝彇璐у畬鎴愶級
+ // - 鎵嬭噦涓婃湁鐗╂枡锛圧obotArmObject == 1锛�
+ // - 浠诲姟鐘舵�佷负 RobotPickFinish锛堝凡璁板綍鍙栬揣瀹屾垚锛�
if ((latestState.CurrentAction == "PickFinished" || latestState.CurrentAction == "AllPickFinished")
&& latestState.RobotArmObject == 1
&& task.RobotTaskState == TaskRobotStatusEnum.RobotPickFinish.GetHashCode())
{
+ // 鍙戦�佹斁璐ф寚浠�
await HandlePickFinishedStateAsync(task, ipAddress);
}
+ // ========== 鏀捐揣瀹屾垚鍚庣殑鍙栬揣澶勭悊 ==========
+ // 鏉′欢锛�
+ // - 褰撳墠鍔ㄤ綔鏄� PutFinished銆丄llPutFinished 鎴� null锛堟斁璐у畬鎴愶級
+ // - 杩愯鐘舵�佷负 Homed锛堝凡褰掍綅锛�
+ // - 鎵嬭噦涓婃棤鐗╂枡锛圧obotArmObject == 0锛�
+ // - 浠诲姟鐘舵�佷负 RobotPutFinish 鎴栦笉鏄� RobotExecuting
else if ((latestState.CurrentAction == "PutFinished" || latestState.CurrentAction == "AllPutFinished" || latestState.CurrentAction == null)
&& latestState.OperStatus == "Homed"
&& latestState.RobotArmObject == 0
&& (task.RobotTaskState == TaskRobotStatusEnum.RobotPutFinish.GetHashCode()
|| task.RobotTaskState != TaskRobotStatusEnum.RobotExecuting.GetHashCode()))
{
+ // 鍙戦�佸彇璐ф寚浠�
await HandlePutFinishedStateAsync(task, ipAddress);
}
}
}
+ /// <summary>
+ /// 澶勭悊鍙栬揣瀹屾垚鍚庣殑鏀捐揣鎸囦护
+ /// </summary>
+ /// <remarks>
+ /// 褰撳彇璐у畬鎴愬悗锛屽悜鏈哄櫒浜哄彂閫佹斁璐ф寚浠わ紙Putbattery锛夈��
+ /// 鏈哄櫒浜烘敹鍒版寚浠ゅ悗浼氬皢璐х墿鏀剧疆鍒扮洰鏍囧湴鍧�銆�
+ ///
+ /// 鎸囦护鏍煎紡锛歅utbattery,{鐩爣鍦板潃}
+ /// 渚嬪锛歅utbattery,B01 琛ㄧず灏嗚揣鐗╂斁缃埌 B01 浣嶇疆
+ /// </remarks>
+ /// <param name="task">褰撳墠浠诲姟</param>
+ /// <param name="ipAddress">鏈哄櫒浜� IP 鍦板潃</param>
private async Task HandlePickFinishedStateAsync(Dt_RobotTask task, string ipAddress)
{
+ // 鏋勫缓鏀捐揣鎸囦护锛屾牸寮忥細Putbattery,{鐩爣鍦板潃}
string taskString = $"Putbattery,{task.RobotTargetAddress}";
+
+ // 閫氳繃瀹㈡埛绔鐞嗗櫒鍙戦�佹寚浠ゅ埌鏈哄櫒浜�
bool result = await _clientManager.SendToClientAsync(ipAddress, taskString);
+
if (result)
{
+ // 鍙戦�佹垚鍔燂紝璁板綍鏃ュ織
+ QuartzLogger.Error($"涓嬪彂鏀捐揣鎸囦护锛屾寚锟�?: {taskString}", task.RobotRoadway);
+
+ // 鏇存柊浠诲姟鐘舵�佷负"鏈哄櫒浜烘墽琛屼腑"
task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode();
+ // 鑾峰彇鏈�鏂扮姸鎬佸苟鏇存柊浠诲姟鍏宠仈
var stateToUpdate = _stateManager.GetState(ipAddress);
if (stateToUpdate != null)
{
stateToUpdate.CurrentTask = task;
+
+ // 瀹夊叏鏇存柊鐘舵�佸埌 Redis
if (_stateManager.TryUpdateStateSafely(ipAddress, stateToUpdate))
{
+ // 鐘舵�佹洿鏂版垚鍔熷悗锛屾洿鏂颁换鍔¤褰�
await _robotTaskService.UpdateRobotTaskAsync(task);
}
}
}
}
+ /// <summary>
+ /// 澶勭悊鏀捐揣瀹屾垚鍚庣殑鍙栬揣鎸囦护
+ /// </summary>
+ /// <remarks>
+ /// 褰撴斁璐у畬鎴愬悗锛屾牴鎹换鍔$被鍨嬪喅瀹氫笅涓�姝ワ細
+ ///
+ /// 1. 濡傛灉涓嶆槸鎷嗙洏涔熶笉鏄粍鐩橈紙鏅�氫换鍔★級锛�
+ /// - 鐩存帴鍙戦�佸彇璐ф寚浠�
+ ///
+ /// 2. 濡傛灉鏄粍鐩樻垨鎹㈢洏浠诲姟锛�
+ /// - 鐢熸垚鏂扮殑鎵樼洏鏉$爜
+ /// - 灏嗘潯鐮佹坊鍔犲埌鐘舵�佷腑
+ /// - 鍙戦�佸彇璐ф寚浠�
+ ///
+ /// 缁勭洏浠诲姟鐨勬潯鐮佺敤浜庢爣璇嗘柊鐢熸垚鐨勬墭鐩橈紝
+ /// 鍚庣画鏀捐揣鏃朵細鐢ㄥ埌杩欎簺鏉$爜淇℃伅銆�
+ /// </remarks>
+ /// <param name="task">褰撳墠浠诲姟</param>
+ /// <param name="ipAddress">鏈哄櫒浜� IP 鍦板潃</param>
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);
+
+ // 璁板綍鏃ュ織
+ QuartzLogger.Error($"取锟斤拷锟斤拷锟斤拷锟叫撅拷牛锟斤拷锟叫�: {trayBarcode1}+{trayBarcode2}", stateForUpdate.RobotCrane.DeviceName);
+
+ // 鍙戦�佸彇璐ф寚浠�
await _taskProcessor.SendSocketRobotPickAsync(task, stateForUpdate);
}
}
else
{
+ // 闈炵粍鐩樹换鍔★紝鐩存帴鍙戦�佸彇璐ф寚浠�
await _taskProcessor.SendSocketRobotPickAsync(task, stateForUpdate);
}
}
}
}
-
-
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/SocketClientGateway.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/SocketClientGateway.cs
index 96879a3..feef4f1 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/SocketClientGateway.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/SocketClientGateway.cs
@@ -5,36 +5,69 @@
namespace WIDESEAWCS_Tasks.SocketServer
{
/// <summary>
- /// TcpSocketServer 的适配器实现,保持底层行为不变,仅做访问收口。
+ /// TcpSocketServer 鐨勭綉鍏冲疄鐜�
/// </summary>
+ /// <remarks>
+ /// 瀹炵幇 ISocketClientGateway 鎺ュ彛锛屽皢搴曞眰 TCP 閫氫俊缁嗚妭灏佽銆�
+ /// 浣夸笟鍔″眰涓嶇洿鎺ヤ緷璧� TcpSocketServer锛屼究浜庡崟鍏冩祴璇曞拰鏇挎崲瀹炵幇銆�
+ /// </remarks>
public class SocketClientGateway : ISocketClientGateway
{
+ /// <summary>
+ /// TCP Socket 鏈嶅姟鍣ㄥ疄渚�
+ /// </summary>
private readonly TcpSocketServer _tcpSocket;
+ /// <summary>
+ /// 鏋勯�犲嚱鏁�
+ /// </summary>
+ /// <param name="tcpSocket">TCP Socket 鏈嶅姟鍣ㄥ疄渚�</param>
public SocketClientGateway(TcpSocketServer tcpSocket)
{
_tcpSocket = tcpSocket;
}
+ /// <summary>
+ /// 寮傛鍙戦�佹秷鎭埌鎸囧畾瀹㈡埛绔�
+ /// </summary>
+ /// <param name="clientId">鐩爣瀹㈡埛绔� ID</param>
+ /// <param name="message">娑堟伅鍐呭</param>
+ /// <returns>鍙戦�佹槸鍚︽垚鍔�</returns>
public Task<bool> SendToClientAsync(string clientId, string message)
{
return _tcpSocket.SendToClientAsync(clientId, message);
}
+ /// <summary>
+ /// 閫氳繃 TcpClient 鍙戦�佹秷鎭�
+ /// </summary>
+ /// <param name="client">TCP 瀹㈡埛绔繛鎺�</param>
+ /// <param name="message">娑堟伅鍐呭</param>
public Task SendMessageAsync(TcpClient client, string message)
{
return _tcpSocket.SendMessageAsync(client, message);
}
+ /// <summary>
+ /// 鑾峰彇鎵�鏈夊凡杩炴帴瀹㈡埛绔� ID
+ /// </summary>
+ /// <returns>瀹㈡埛绔� ID 鍒楄〃</returns>
public IReadOnlyList<string> GetClientIds()
{
return _tcpSocket.GetClientIds();
}
+ /// <summary>
+ /// 澶勭悊瀹㈡埛绔繛鎺ョ殑娑堟伅寰幆
+ /// </summary>
+ /// <param name="client">TCP 瀹㈡埛绔繛鎺�</param>
+ /// <param name="clientId">瀹㈡埛绔� ID</param>
+ /// <param name="cancellationToken">鍙栨秷浠ょ墝</param>
+ /// <param name="robotCrane">鏈哄櫒浜虹姸鎬�</param>
+ /// <returns>浠诲姟</returns>
public Task HandleClientAsync(TcpClient client, string clientId, CancellationToken cancellationToken, RobotSocketState robotCrane)
{
return _tcpSocket.HandleClientAsync(client, clientId, cancellationToken, robotCrane);
}
}
}
-
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/SocketServerHostedService.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/SocketServerHostedService.cs
index 987ddbe..e1298a7 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/SocketServerHostedService.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/SocketServerHostedService.cs
@@ -4,29 +4,60 @@
namespace WIDESEAWCS_Tasks.SocketServer
{
/// <summary>
- /// Socket服务端托管服务
+ /// Socket 鏈嶅姟鍣ㄥ悗鍙颁富鏈烘湇鍔�
/// </summary>
+ /// <remarks>
+ /// 瀹炵幇 IHostedService 鎺ュ彛锛屼綔涓� ASP.NET Core 鐨勫悗鍙版湇鍔¤繍琛屻��
+ /// 璐熻矗鍦ㄥ簲鐢ㄥ惎鍔ㄦ椂鍚姩 Socket 鏈嶅姟鍣紝鍋滄鏃跺叧闂湇鍔″櫒銆�
+ /// </remarks>
public class SocketServerHostedService : IHostedService
{
+ /// <summary>
+ /// TCP Socket 鏈嶅姟鍣ㄥ疄渚�
+ /// </summary>
private readonly TcpSocketServer _server;
+
+ /// <summary>
+ /// Socket 鏈嶅姟鍣ㄩ厤缃�夐」
+ /// </summary>
private readonly SocketServerOptions _options;
+ /// <summary>
+ /// 鏋勯�犲嚱鏁�
+ /// </summary>
+ /// <param name="server">TCP Socket 鏈嶅姟鍣ㄥ疄渚�</param>
+ /// <param name="options">閰嶇疆閫夐」</param>
public SocketServerHostedService(TcpSocketServer server, IOptions<SocketServerOptions> options)
{
_server = server;
_options = options.Value;
}
+ /// <summary>
+ /// 鍚姩 Socket 鏈嶅姟鍣�
+ /// </summary>
+ /// <remarks>
+ /// 濡傛灉閰嶇疆涓湇鍔″櫒琚鐢紙Enabled=false锛夛紝鍒欎笉鍚姩銆�
+ /// </remarks>
+ /// <param name="cancellationToken">鍙栨秷浠ょ墝</param>
+ /// <returns>鍚姩浠诲姟</returns>
public Task StartAsync(CancellationToken cancellationToken)
{
+ // 妫�鏌ユ湇鍔″櫒鏄惁鍚敤
if (!_options.Enabled)
{
return Task.CompletedTask;
}
+ // 鍚姩鏈嶅姟鍣�
return _server.StartAsync(cancellationToken);
}
+ /// <summary>
+ /// 鍋滄 Socket 鏈嶅姟鍣�
+ /// </summary>
+ /// <param name="cancellationToken">鍙栨秷浠ょ墝</param>
+ /// <returns>鍋滄浠诲姟</returns>
public Task StopAsync(CancellationToken cancellationToken)
{
return _server.StopAsync(cancellationToken);
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/SocketServerOptions.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/SocketServerOptions.cs
index 2844c0c..8ce9dc2 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/SocketServerOptions.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/SocketServerOptions.cs
@@ -3,63 +3,106 @@
namespace WIDESEAWCS_Tasks.SocketServer
{
/// <summary>
- /// Socket服务端配置
+ /// Socket 鏈嶅姟鍣ㄩ厤缃�夐」
/// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬閰嶇疆 TCP Socket 鏈嶅姟鍣ㄧ殑杩愯鍙傛暟銆�
+ /// 閰嶇疆閫氳繃 appsettings.json 鐨� SocketServer 鑺傜偣鍔犺浇銆�
+ /// </remarks>
public class SocketServerOptions : IConfigurableOptions
{
/// <summary>
- /// 是否启用
+ /// 鏄惁鍚敤 Socket 鏈嶅姟鍣�
/// </summary>
+ /// <remarks>
+ /// 璁剧疆涓� false 鏃讹紝鏈嶅姟鍣ㄤ笉浼氬惎鍔ㄣ��
+ /// </remarks>
public bool Enabled { get; set; } = true;
/// <summary>
- /// 监听端口
+ /// 鏈嶅姟鍣ㄧ洃鍚鍙�
/// </summary>
+ /// <remarks>
+ /// TCP 瀹㈡埛绔繛鎺ュ埌姝ょ鍙c��
+ /// 榛樿涓� 2000銆�
+ /// </remarks>
public int Port { get; set; } = 2000;
/// <summary>
- /// 监听地址
+ /// 鐩戝惉鍦板潃
/// </summary>
+ /// <remarks>
+ /// 鏈嶅姟鍣ㄧ粦瀹氬埌姝ゅ湴鍧�銆�
+ /// 0.0.0.0 琛ㄧず鐩戝惉鎵�鏈夌綉缁滄帴鍙c��
+ /// </remarks>
public string IpAddress { get; set; } = "0.0.0.0";
/// <summary>
- /// 连接队列长度
+ /// 杩炴帴闃熷垪闀垮害
/// </summary>
+ /// <remarks>
+ /// 绛夊緟鎺ュ彈鐨勮繛鎺ラ槦鍒楁渶澶ч暱搴︺��
+ /// </remarks>
public int Backlog { get; set; } = 1000;
/// <summary>
- /// 文本编码名称(例如: utf-8, gbk)
+ /// 瀛楃缂栫爜鍚嶇О
/// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬娑堟伅鐨勫瓧绗︾紪鐮侊紝濡� utf-8銆乬bk銆�
+ /// </remarks>
public string EncodingName { get; set; } = "utf-8";
/// <summary>
- /// 是否自动检测编码(尝试 UTF-8 后回退到 GBK)
+ /// 鏄惁鑷姩妫�娴嬬紪鐮侊紙閽堝 GBK 瀹㈡埛绔級
/// </summary>
+ /// <remarks>
+ /// 褰撹缃负 true 鏃讹紝浼氳嚜鍔ㄦ娴嬪鎴风娑堟伅鐨勭紪鐮併��
+ /// 濡傛灉娑堟伅鏄� UTF-8 鏍煎紡鍒欑敤 UTF-8 瑙g爜锛屽惁鍒欏皾璇� GBK 瑙g爜銆�
+ /// </remarks>
public bool AutoDetectEncoding { get; set; } = true;
/// <summary>
- /// 客户端空闲超时时间(秒),超过则断开
+ /// 瀹㈡埛绔┖闂茶秴鏃舵椂闂达紙绉掞級
/// </summary>
+ /// <remarks>
+ /// 濡傛灉瀹㈡埛绔湪姝ゆ椂闂村唴娌℃湁娲诲姩锛屾柇寮�杩炴帴銆�
+ /// 璁剧疆涓� 0 琛ㄧず涓嶅惎鐢ㄨ秴鏃躲��
+ /// </remarks>
public int IdleTimeoutSeconds { get; set; } = 300;
/// <summary>
- /// 是否启用心跳检查
+ /// 鏄惁鍚敤蹇冭烦妫�娴�
/// </summary>
+ /// <remarks>
+ /// 鍚敤鍚庯紝浼氬湪杩炴帴绌洪棽鏃跺彂閫佸績璺虫帰娴嬨��
+ /// </remarks>
public bool EnableHeartbeat { get; set; } = true;
/// <summary>
- /// 日志文件路径(相对于程序运行目录)
+ /// 鏃ュ織鏂囦欢璺緞
/// </summary>
+ /// <remarks>
+ /// 鏃ュ織鏂囦欢鐨勭浉瀵硅矾寰勶紝鐩稿浜庡簲鐢ㄧ▼搴忕洰褰曘��
+ /// </remarks>
public string LogFilePath { get; set; } = "socketserver.log";
/// <summary>
- /// 消息头标记
+ /// 娑堟伅澶存爣璇�
/// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬甯цВ鏋愮殑娑堟伅澶淬��
+ /// 鎺ユ敹娑堟伅鏃舵煡鎵炬澶存爣璇嗐��
+ /// </remarks>
public string MessageHeader { get; set; } = "<START>";
/// <summary>
- /// 消息尾标记
+ /// 娑堟伅灏炬爣璇�
/// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬甯цВ鏋愮殑娑堟伅灏俱��
+ /// 鎺ユ敹娑堟伅鏃舵煡鎵炬灏炬爣璇嗐��
+ /// </remarks>
public string MessageFooter { get; set; } = "<END>";
}
}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Clients.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Clients.cs
index 5eb8fee..8f7e705 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Clients.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Clients.cs
@@ -10,10 +10,13 @@
public partial class TcpSocketServer
{
/// <summary>
- /// 检索当前在服务中注册的所有客户端标识符的只读列表。
+ /// 鑾峰彇鎵�鏈夊凡杩炴帴瀹㈡埛绔� ID 鍒楄〃
/// </summary>
- /// <remarks>返回的列表表示调用时刻客户端ID的快照。后续对客户端集合的更改不会影响返回的列表。此方法是线程安全的。</remarks>
- /// <returns>包含客户端ID的<see cref="IReadOnlyList{String}"/>。如果没有客户端注册,列表将为空。</returns>
+ /// <remarks>
+ /// 杩斿洖褰撳墠鍦ㄦ湇鍔″櫒娉ㄥ唽鐨勫鎴风鏍囪瘑鍒楄〃銆�
+ /// 杩欐槸涓�涓彧璇诲垪琛ㄧ殑蹇収锛岀嚎绋嬪畨鍏ㄣ��
+ /// </remarks>
+ /// <returns>瀹㈡埛绔� ID 鍒楄〃</returns>
public IReadOnlyList<string> GetClientIds()
{
lock (_syncRoot)
@@ -23,11 +26,13 @@
}
/// <summary>
- /// 检索与指定设备标识符关联的客户端标识符。
+ /// 鏍规嵁璁惧 ID 鑾峰彇瀹㈡埛绔� ID
/// </summary>
- /// <remarks>此方法是线程安全的。如果未找到设备标识符,方法将返回null而不是抛出异常。</remarks>
- /// <param name="deviceId">要检索客户端标识符的设备的唯一标识符。不能为null。</param>
- /// <returns>与指定设备标识符关联的客户端标识符,如果不存在关联则返回null。</returns>
+ /// <remarks>
+ /// 鍦ㄨ澶囩粦瀹氳〃涓煡鎵惧搴旂殑瀹㈡埛绔� ID銆�
+ /// </remarks>
+ /// <param name="deviceId">璁惧鍞竴鏍囪瘑</param>
+ /// <returns>瀹㈡埛绔� ID锛屽鏋滄湭鎵惧埌鍒欒繑鍥� null</returns>
public string? GetClientIdByDevice(string deviceId)
{
lock (_syncRoot)
@@ -37,13 +42,15 @@
}
/// <summary>
- /// 异步向指定设备发送消息。
+ /// 寮傛鍚戞寚瀹氳澶囧彂閫佹秷鎭�
/// </summary>
- /// <remarks>如果指定设备未注册或无法找到,则返回 <see langword="false"/>。</remarks>
- /// <param name="deviceId">目标设备的唯一标识符。不能为null或空。</param>
- /// <param name="message">要发送给设备的消息。不能为null。</param>
- /// <returns>表示异步操作的任务。如果消息成功发送,任务结果为 <see langword="true"/>;
- /// 否则为 <see langword="false"/>。</returns>
+ /// <remarks>
+ /// 閫氳繃璁惧 ID 鏌ユ壘瀵瑰簲鐨勫鎴风杩炴帴锛岀劧鍚庡彂閫佹秷鎭��
+ /// 濡傛灉璁惧鏈敞鍐屾垨杩炴帴涓嶅瓨鍦紝杩斿洖 false銆�
+ /// </remarks>
+ /// <param name="deviceId">鐩爣璁惧鍞竴鏍囪瘑</param>
+ /// <param name="message">瑕佸彂閫佺殑娑堟伅</param>
+ /// <returns>鍙戦�佹槸鍚︽垚鍔�</returns>
public Task<bool> SendToDeviceAsync(string deviceId, string message)
{
var clientId = GetClientIdByDevice(deviceId);
@@ -52,15 +59,16 @@
}
/// <summary>
- /// 通过TCP连接异步向指定客户端发送带帧的文本消息。
+ /// 寮傛鍚戞寚瀹氬鎴风鍙戦�佹秷鎭�
/// </summary>
- /// <remarks>如果客户端未连接或不存在,此方法将返回 <see langword="false"/> 且不发送消息。
- /// 消息将优先使用客户端首选的文本编码进行编码;否则使用默认编码。
- /// 此方法对于向不同客户端的并发调用是线程安全的。</remarks>
- /// <param name="clientId">要发送消息到的客户端的唯一标识符。必须对应已连接的客户端。</param>
- /// <param name="message">要发送给客户端的文本消息。不能为null。</param>
- /// <returns>表示异步操作的任务。如果消息成功发送,任务结果为 <see langword="true"/>;
- /// 否则,如果客户端未连接或不存在,结果为 <see langword="false"/>。</returns>
+ /// <remarks>
+ /// 浣跨敤甯ф牸寮忓彂閫佹秷鎭紙娣诲姞澶村熬鏍囪瘑锛夈��
+ /// 姣忎釜瀹㈡埛绔殑鍙戦�佹搷浣滄槸浜掓枼鐨勶紙閫氳繃淇″彿閲忓疄鐜帮級銆�
+ /// 濡傛灉瀹㈡埛绔湭杩炴帴鎴栦笉瀛樺湪锛屽彂閫佸け璐ヨ繑鍥� false銆�
+ /// </remarks>
+ /// <param name="clientId">鐩爣瀹㈡埛绔� ID</param>
+ /// <param name="message">瑕佸彂閫佺殑娑堟伅</param>
+ /// <returns>鍙戦�佹槸鍚︽垚鍔�</returns>
public async Task<bool> SendToClientAsync(string clientId, string message)
{
TcpClient? client;
@@ -80,9 +88,11 @@
enc ??= _textEncoding;
+ // 鑾峰彇瀹㈡埛绔彂閫侀攣
if (sem != null) await sem.WaitAsync();
try
{
+ // 鍙戦�佹秷鎭�
var ns = client.GetStream();
var framedMessage = BuildFramedMessage(message);
var data = enc.GetBytes(framedMessage);
@@ -96,12 +106,13 @@
}
/// <summary>
- /// 异步向所有已连接的客户端发送指定的消息。
+ /// 寮傛骞挎挱娑堟伅鍒版墍鏈夊鎴风
/// </summary>
- /// <remarks>如果向某个客户端发送消息时发生错误,异常将被抑制并继续向其他客户端广播。
- /// 当所有发送操作完成后,此方法结束。</remarks>
- /// <param name="message">要广播给所有客户端的消息。不能为null。</param>
- /// <returns>表示异步广播操作的任务。</returns>
+ /// <remarks>
+ /// 灏嗘秷鎭彂閫佺粰鎵�鏈夊凡杩炴帴鐨勫鎴风銆�
+ /// 濡傛灉鏌愪釜瀹㈡埛绔彂閫佸け璐ワ紝涓嶅奖鍝嶅叾浠栧鎴风鐨勫彂閫併��
+ /// </remarks>
+ /// <param name="message">瑕佸箍鎾殑娑堟伅</param>
public async Task BroadcastAsync(string message)
{
List<TcpClient> clients;
@@ -110,6 +121,7 @@
clients = _clients.Values.ToList();
}
+ // 骞惰鍙戦�佹秷鎭埌鎵�鏈夊鎴风
await Task.WhenAll(clients.Select(c => Task.Run(async () =>
{
try { await SendMessageAsync(c, message); } catch { }
@@ -117,13 +129,14 @@
}
/// <summary>
- /// 通过网络流异步向指定的TCP客户端发送带帧的文本消息。
+ /// 閫氳繃 NetworkStream 鍙戦�佹秷鎭�
/// </summary>
- /// <remarks>如果客户端为null或未连接,此方法将立即返回而不发送消息。
- /// 消息将使用配置的文本编码进行编码并添加帧头后通过网络流发送。</remarks>
- /// <param name="client">要发送消息到的TCP客户端。必须处于连接状态;否则方法不执行任何操作。</param>
- /// <param name="message">要发送给客户端的文本消息。消息在传输前将被编码并添加帧头。</param>
- /// <returns>表示异步发送操作的任务。</returns>
+ /// <remarks>
+ /// 鐩存帴浣跨敤 TcpClient 鐨� NetworkStream 鍙戦�佹秷鎭��
+ /// 娑堟伅浼氭坊鍔犲抚澶村抚灏俱��
+ /// </remarks>
+ /// <param name="client">TCP 瀹㈡埛绔�</param>
+ /// <param name="message">娑堟伅鍐呭</param>
public async Task SendMessageAsync(TcpClient client, string message)
{
if (client == null || !client.Connected)
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Dispose.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Dispose.cs
index 539df48..94c439e 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Dispose.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Dispose.cs
@@ -6,17 +6,28 @@
public partial class TcpSocketServer
{
/// <summary>
- /// 释放服务器使用的所有资源并停止监听传入连接。
+ /// 閲婃斁鏈嶅姟鍣ㄨ祫婧�
/// </summary>
- /// <remarks>当不再需要服务器时调用此方法,以确保所有相关资源(如网络监听器和同步原语)被正确释放。
- /// 调用 <see cref="Dispose"/> 后,服务器无法重新启动或再次使用。</remarks>
+ /// <remarks>
+ /// 鍋滄鐩戝惉銆佸彇娑堟墍鏈夊鎴风浠诲姟銆佸叧闂洃鍚櫒銆侀噴鏀句俊鍙烽噺銆�
+ /// 璋冪敤姝ゆ柟娉曞悗锛屾湇鍔″櫒鏃犳硶鍐嶆浣跨敤銆�
+ /// </remarks>
public void Dispose()
{
+ // 鍙栨秷鎵�鏈夋搷浣�
_cts?.Cancel();
+
+ // 鍋滄鐩戝惉鍣�
_listener?.Stop();
+
+ // 閲婃斁鍙栨秷浠ょ墝婧�
_cts?.Dispose();
+
+ // 閲婃斁鎵�鏈夊鎴风淇″彿閲�
foreach (var sem in _clientLocks.Values) { try { sem.Dispose(); } catch { } }
_clientLocks.Clear();
+
+ // 璁板綍鍋滄鏃ュ織
Log($"[{DateTime.Now}] TcpSocketServer stopped");
}
}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Messaging.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Messaging.cs
index a0b8a91..571cd45 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Messaging.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Messaging.cs
@@ -8,16 +8,21 @@
public partial class TcpSocketServer
{
/// <summary>
- /// 异步处理与已连接的TCP客户端的通信,处理机器人起重机会话中的传入消息和客户端状态更新。
+ /// 澶勭悊瀹㈡埛绔繛鎺ョ殑娑堟伅寰幆
/// </summary>
- /// <remarks>此方法管理客户端连接的生命周期,包括读取消息、更新客户端状态和调用相关事件。
- /// 当处理结束时,客户端和相关的网络资源将被释放。如果启用心跳或空闲超时选项,
- /// 将应用额外的取消逻辑。事件调用期间的异常将被捕获并抑制,以确保会话处理的鲁棒性。</remarks>
- /// <param name="client">表示要处理的远程连接的TCP客户端。方法完成后将释放此对象。</param>
- /// <param name="clientId">已连接客户端的唯一标识符。用于在整个会话中跟踪和更新客户端状态。</param>
- /// <param name="cancellationToken">可用于取消客户端处理操作的取消令牌。如果请求取消,方法将立即终止处理。</param>
- /// <param name="robotCrane">表示与客户端关联的机器人起重机的当前状态对象。用于为消息处理和事件调用提供上下文。</param>
- /// <returns>表示处理客户端连接的异步操作的任务。当客户端断开连接或请求取消时任务完成。</returns>
+ /// <remarks>
+ /// 鎸佺画鎺ユ敹瀹㈡埛绔秷鎭紝鐩村埌杩炴帴鏂紑鎴栧彇娑堛��
+ /// 澶勭悊娴佺▼锛�
+ /// 1. 鎺ユ敹娑堟伅锛堝抚瑙f瀽锛�
+ /// 2. 鏇存柊瀹㈡埛绔姸鎬侊紙娲昏穬鏃堕棿銆佺紪鐮侊級
+ /// 3. 澶勭悊璁惧娉ㄥ唽
+ /// 4. 瑙﹀彂 MessageReceived 浜嬩欢
+ /// 杩炴帴鏂紑鏃舵竻鐞嗚祫婧愬苟瑙﹀彂 RobotReceived 浜嬩欢銆�
+ /// </remarks>
+ /// <param name="client">TCP 瀹㈡埛绔繛鎺�</param>
+ /// <param name="clientId">瀹㈡埛绔敮涓�鏍囪瘑</param>
+ /// <param name="cancellationToken">鍙栨秷浠ょ墝</param>
+ /// <param name="robotCrane">鏈哄櫒浜虹姸鎬�</param>
public async Task HandleClientAsync(TcpClient client, string clientId, CancellationToken cancellationToken, RobotSocketState robotCrane)
{
using (client)
@@ -28,19 +33,21 @@
CancellationTokenSource? localCts = null;
if (_options.EnableHeartbeat || _options.IdleTimeoutSeconds > 0)
{
+ // 鍒涘缓閾炬帴鐨勫彇娑堜护鐗屾簮
localCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
}
try
{
+ // 娑堟伅鎺ユ敹寰幆
while (!cancellationToken.IsCancellationRequested && client.Connected)
{
string? message;
try
{
var ct = localCts?.Token ?? cancellationToken;
+ // 鎺ユ敹瀹屾暣娑堟伅锛堝抚瑙f瀽锛�
message = await ReceiveFullMessageAsync(networkStream, _textEncoding, ct);
- //message = await reader.ReadLineAsync().WaitAsync(ct);
}
catch (OperationCanceledException)
{
@@ -52,20 +59,23 @@
break;
}
+ // 鏇存柊瀹㈡埛绔姸鎬�
UpdateClientStatus(clientId, message);
string messageLower = message.ToLowerInvariant();
+ // 澶勭悊娉ㄥ唽娑堟伅
if (TryHandleRegister(messageLower, message, clientId, networkStream, cancellationToken))
{
continue;
}
+ // 瑙﹀彂娑堟伅鎺ユ敹浜嬩欢
if (MessageReceived != null)
{
try
{
- // 判断是否为 JSON 格式
+ // 鍒ゆ柇鏄惁涓� JSON 鏍煎紡
bool isJsonFormat = TryParseJsonSilent(message);
_ = MessageReceived.Invoke(message, isJsonFormat, client, robotCrane);
}
@@ -75,6 +85,7 @@
}
finally
{
+ // 娓呯悊璧勬簮
try { localCts?.Cancel(); localCts?.Dispose(); } catch { }
RemoveClient(clientId);
try { _ = RobotReceived.Invoke(clientId); } catch { }
@@ -83,17 +94,18 @@
}
/// <summary>
- /// 尝试处理来自客户端的设备注册请求。返回一个值指示该消息是否被作为注册请求处理。
+ /// 澶勭悊璁惧娉ㄥ唽娑堟伅
/// </summary>
- /// <remarks>如果消息是有效的注册请求且包含非空的设备标识符,
- /// 则将设备绑定到客户端并发送确认信息。此方法不会因无效消息而抛出异常;
- /// 它仅返回 false。</remarks>
- /// <param name="messageLower">客户端消息的小写版本,用于判断消息是否为注册请求。</param>
- /// <param name="message">包含注册命令和设备标识符的原始客户端消息。</param>
- /// <param name="clientId">发送注册请求的客户端的唯一标识符。</param>
- /// <param name="client">与客户端通信的TCP客户端连接。</param>
- /// <param name="cancellationToken">可用于取消注册操作的取消令牌。</param>
- /// <returns>如果消息被识别并作为注册请求处理,则返回 true;否则返回 false。</returns>
+ /// <remarks>
+ /// 娉ㄥ唽娑堟伅鏍煎紡锛歳egister,{deviceId}
+ /// 灏嗚澶� ID 缁戝畾鍒板綋鍓嶅鎴风 ID銆�
+ /// </remarks>
+ /// <param name="messageLower">娑堟伅灏忓啓鐗堟湰</param>
+ /// <param name="message">鍘熷娑堟伅</param>
+ /// <param name="clientId">瀹㈡埛绔� ID</param>
+ /// <param name="networkStream">缃戠粶娴�</param>
+ /// <param name="cancellationToken">鍙栨秷浠ょ墝</param>
+ /// <returns>鏄惁澶勭悊浜嗘敞鍐屾秷鎭�</returns>
private bool TryHandleRegister(string messageLower, string message, string clientId, NetworkStream networkStream, CancellationToken cancellationToken)
{
if (!messageLower.StartsWith("register,"))
@@ -101,14 +113,17 @@
return false;
}
+ // 鎻愬彇璁惧 ID
string deviceId = message.Substring("register,".Length).Trim();
if (!string.IsNullOrEmpty(deviceId))
{
lock (_syncRoot)
{
+ // 缁戝畾璁惧鍒板鎴风
_deviceBindings[deviceId] = clientId;
}
+ // 鍥炲娉ㄥ唽鎴愬姛
_ = WriteToClientAsync(clientId, networkStream, $"Registered,{deviceId}", cancellationToken);
}
@@ -116,20 +131,27 @@
}
/// <summary>
- /// 更新客户端状态
+ /// 鏇存柊瀹㈡埛绔姸鎬�
/// </summary>
- /// <param name="clientId"></param>
- /// <param name="message"></param>
+ /// <remarks>
+ /// 鏇存柊鏈�鍚庢椿璺冩椂闂村拰瀛楃缂栫爜銆�
+ /// 濡傛灉寮�鍚簡鑷姩缂栫爜妫�娴嬶紝鏍规嵁娑堟伅鍐呭鍒ゆ柇鏄� UTF-8 杩樻槸 GBK銆�
+ /// </remarks>
+ /// <param name="clientId">瀹㈡埛绔� ID</param>
+ /// <param name="message">鏈�鏂版帴鏀剁殑娑堟伅</param>
private void UpdateClientStatus(string clientId, string message)
{
lock (_syncRoot)
{
+ // 鏇存柊鏈�鍚庢椿璺冩椂闂�
_clientLastActive[clientId] = DateTime.Now;
+ // 濡傛灉杩樻病鏈夎褰曠紪鐮�
if (!_clientEncodings.ContainsKey(clientId))
{
if (_options.AutoDetectEncoding && _autoDetectedGb2312 != null)
{
+ // 鑷姩妫�娴嬬紪鐮侊細JSON 鎴� UTF-8 瀛楄妭鐗瑰緛鍒欑敤 UTF-8锛屽惁鍒欑敤 GBK
bool isUtf8 = TryParseJsonSilent(message) || IsLikelyUtf8(_textEncoding.GetBytes(message));
_clientEncodings[clientId] = isUtf8 ? _textEncoding : _autoDetectedGb2312;
}
@@ -142,13 +164,11 @@
}
/// <summary>
- /// 写入消息到客户端
+ /// 寮傛鍙戦�佹秷鎭埌瀹㈡埛绔�
/// </summary>
- /// <param name="clientId"></param>
- /// <param name="networkStream"></param>
- /// <param name="message"></param>
- /// <param name="cancellationToken"></param>
- /// <returns></returns>
+ /// <remarks>
+ /// 鍐呴儴鏂规硶锛屼笉浣跨敤甯ф牸寮忥紝鐩存帴鍙戦�佸師濮嬫秷鎭��
+ /// </remarks>
private async Task WriteToClientAsync(string clientId, NetworkStream networkStream, string message, CancellationToken cancellationToken)
{
SemaphoreSlim? sem = null;
@@ -175,10 +195,13 @@
}
/// <summary>
- /// 添加消息帧头尾
+ /// 鏋勫缓甯ф秷鎭�
/// </summary>
- /// <param name="message"></param>
- /// <returns></returns>
+ /// <remarks>
+ /// 鍦ㄦ秷鎭墠鍚庢坊鍔犲ご灏炬爣璇嗐��
+ /// </remarks>
+ /// <param name="message">鍘熷娑堟伅</param>
+ /// <returns>甯﹀抚鏍囪瘑鐨勬秷鎭�</returns>
private string BuildFramedMessage(string message)
{
var header = _options.MessageHeader ?? string.Empty;
@@ -187,10 +210,14 @@
}
/// <summary>
- /// JSON格式尝试解析(静默失败)
+ /// 闈欓粯灏濊瘯瑙f瀽 JSON
/// </summary>
- /// <param name="message"></param>
- /// <returns></returns>
+ /// <remarks>
+ /// 鍒ゆ柇娑堟伅鏄惁浠� { 鎴� [ 寮�澶达紝濡傛灉鏄垯灏濊瘯瑙f瀽銆�
+ /// 瑙f瀽澶辫触涓嶆姏寮傚父銆�
+ /// </remarks>
+ /// <param name="message">娑堟伅鍐呭</param>
+ /// <returns>鏄惁鏄湁鏁堢殑 JSON 鏍煎紡</returns>
private static bool TryParseJsonSilent(string message)
{
if (string.IsNullOrWhiteSpace(message)) return false;
@@ -200,30 +227,35 @@
}
/// <summary>
- /// utf-8 可能性检测
+ /// 鍒ゆ柇瀛楄妭鏁扮粍鏄惁涓� UTF-8 缂栫爜
/// </summary>
- /// <param name="data"></param>
- /// <returns></returns>
+ /// <remarks>
+ /// 閫氳繃妫�鏌ュ瓧鑺傚簭鍒楁槸鍚︾鍚� UTF-8 澶氬瓧鑺傚瓧绗︾殑缂栫爜瑙勫垯銆�
+ /// </remarks>
+ /// <param name="data">瀛楄妭鏁扮粍</param>
+ /// <returns>鏄惁鍙兘鏄� UTF-8 缂栫爜</returns>
private static bool IsLikelyUtf8(byte[] data)
{
int i = 0;
while (i < data.Length)
{
byte b = data[i];
- if (b <= 0x7F) { i++; continue; }
- if (b >= 0xC2 && b <= 0xDF)
+ if (b <= 0x7F) { i++; continue; } // ASCII 瀛楃
+
+ // 妫�鏌ュ瀛楄妭瀛楃
+ if (b >= 0xC2 && b <= 0xDF) // 2瀛楄妭瀛楃
{
if (i + 1 >= data.Length) return false;
if ((data[i + 1] & 0xC0) != 0x80) return false;
i += 2; continue;
}
- if (b >= 0xE0 && b <= 0xEF)
+ if (b >= 0xE0 && b <= 0xEF) // 3瀛楄妭瀛楃
{
if (i + 2 >= data.Length) return false;
if ((data[i + 1] & 0xC0) != 0x80 || (data[i + 2] & 0xC0) != 0x80) return false;
i += 3; continue;
}
- if (b >= 0xF0 && b <= 0xF4)
+ if (b >= 0xF0 && b <= 0xF4) // 4瀛楄妭瀛楃
{
if (i + 3 >= data.Length) return false;
if ((data[i + 1] & 0xC0) != 0x80 || (data[i + 2] & 0xC0) != 0x80 || (data[i + 3] & 0xC0) != 0x80) return false;
@@ -235,12 +267,16 @@
}
/// <summary>
- /// 读取完整消息
+ /// 鎺ユ敹瀹屾暣娑堟伅锛堝抚瑙f瀽锛�
/// </summary>
- /// <param name="networkStream">字节流</param>
- /// <param name="encoding">编码格式</param>
- /// <param name="cancellationToken"></param>
- /// <returns></returns>
+ /// <remarks>
+ /// 鏍规嵁閰嶇疆鐨勫ご灏炬爣璇嗚В鏋愭秷鎭��
+ /// 濡傛灉鏈厤缃ご灏撅紝鍒欎竴鐩磋鍒版暟鎹笉鍙敤銆�
+ /// </remarks>
+ /// <param name="networkStream">缃戠粶娴�</param>
+ /// <param name="encoding">瀛楃缂栫爜</param>
+ /// <param name="cancellationToken">鍙栨秷浠ょ墝</param>
+ /// <returns>鎺ユ敹鍒扮殑娑堟伅</returns>
private async Task<string?> ReceiveFullMessageAsync(NetworkStream networkStream, Encoding encoding, CancellationToken cancellationToken)
{
var header = _options.MessageHeader ?? string.Empty;
@@ -251,15 +287,18 @@
while (true)
{
+ // 璇诲彇鏁版嵁
int bytesRead = await networkStream.ReadAsync(buffer.AsMemory(0, buffer.Length), cancellationToken);
if (bytesRead <= 0)
{
if (builder.Length == 0) return null;
+ // 鏃犲ご灏鹃厤缃椂锛岃繑鍥炲凡鏈夋暟鎹�
return string.IsNullOrEmpty(header) && string.IsNullOrEmpty(footer) ? builder.ToString() : null;
}
builder.Append(encoding.GetString(buffer, 0, bytesRead));
+ // 濡傛灉娌℃湁閰嶇疆澶村熬锛屼笖鏁版嵁涓嶅彲鐢紝杩斿洖宸叉湁鏁版嵁
if (string.IsNullOrEmpty(header) && string.IsNullOrEmpty(footer))
{
if (!networkStream.DataAvailable)
@@ -269,6 +308,7 @@
continue;
}
+ // 鏌ユ壘甯уご
var data = builder.ToString();
var headerIndex = string.IsNullOrEmpty(header) ? 0 : data.IndexOf(header, StringComparison.Ordinal);
if (headerIndex < 0)
@@ -276,6 +316,7 @@
continue;
}
+ // 鎻愬彇甯у唴瀹�
var startIndex = headerIndex + header.Length;
var footerIndex = string.IsNullOrEmpty(footer) ? data.Length : data.IndexOf(footer, startIndex, StringComparison.Ordinal);
if (footerIndex >= 0)
@@ -287,4 +328,4 @@
return builder.ToString();
}
}
-}
\ No newline at end of file
+}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Server.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Server.cs
index fcd5764..7bb845e 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Server.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Server.cs
@@ -1,24 +1,24 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Net;
using System.Net.Sockets;
+using System.Text;
using System.Text.Json;
-using System.Threading;
-using System.Threading.Tasks;
-using WIDESEAWCS_Core.Helper;
+using System.IO;
+using WIDESEAWCS_Core.LogHelper;
namespace WIDESEAWCS_Tasks.SocketServer
{
public partial class TcpSocketServer
{
/// <summary>
- /// 异步启动TCP服务器,使其开始接受传入的客户端连接。
+ /// 寮傛鍚姩 TCP Socket 鏈嶅姟鍣�
/// </summary>
- /// <remarks>如果服务器已在运行或通过配置禁用,此方法将立即返回而不启动服务器。
- /// 后续的客户端监控和接受操作在后台任务中运行。此方法不会阻塞调用线程。</remarks>
- /// <param name="cancellationToken">可用于请求取消服务器启动及后续后台操作的取消令牌。</param>
- /// <returns>表示异步启动操作的任务。当服务器开始监听连接时任务完成。</returns>
+ /// <remarks>
+ /// 鍒涘缓 TCP 鐩戝惉鍣ㄥ苟寮�濮嬫帴鍙楀鎴风杩炴帴銆�
+ /// 濡傛灉鏈嶅姟鍣ㄥ凡鍦ㄨ繍琛屾垨琚鐢紝鐩存帴杩斿洖銆�
+ /// 鍚姩鍚庡惎鍔ㄦ帴鍙楀惊鐜拰瀹㈡埛绔洃鎺т换鍔°��
+ /// </remarks>
+ /// <param name="cancellationToken">鍙栨秷浠ょ墝</param>
+ /// <returns>鍚姩浠诲姟</returns>
public Task StartAsync(CancellationToken cancellationToken)
{
if (IsRunning || !_options.Enabled)
@@ -26,30 +26,36 @@
return Task.CompletedTask;
}
+ // 瑙f瀽鐩戝惉鍦板潃
IPAddress ipAddress = IPAddress.Any;
if (IPAddress.TryParse(_options.IpAddress, out IPAddress? parsedAddress))
{
ipAddress = parsedAddress;
}
+ // 鍒涘缓鐩戝惉鍣�
_listener = new TcpListener(ipAddress, _options.Port);
_listener.Start(_options.Backlog);
_cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
IsRunning = true;
+ // 鍚姩鎺ュ彈瀹㈡埛绔繛鎺ュ惊鐜�
_ = AcceptLoopAsync(_cts.Token);
+
+ // 鍚姩瀹㈡埛绔洃鎺т换鍔★紙妫�鏌ョ┖闂茶秴鏃讹級
_monitorTask = Task.Run(() => MonitorClientsAsync(_cts.Token));
return Task.CompletedTask;
}
- //// <summary>
- /// 异步停止服务器并等待所有活动客户端连接完成。
+ /// <summary>
+ /// 寮傛鍋滄 TCP Socket 鏈嶅姟鍣�
/// </summary>
- /// <remarks>如果服务器未运行,此方法将立即返回而不执行任何操作。
- /// 此方法确保所有客户端任务完成后才将服务器标记为已停止。</remarks>
- /// <param name="cancellationToken">可用于在完成前取消停止操作的取消令牌。</param>
- /// <returns>表示异步停止操作的任务。</returns>
+ /// <remarks>
+ /// 鍋滄鎺ュ彈鏂拌繛鎺ワ紝绛夊緟鎵�鏈夊鎴风浠诲姟瀹屾垚銆�
+ /// </remarks>
+ /// <param name="cancellationToken">鍙栨秷浠ょ墝</param>
+ /// <returns>鍋滄浠诲姟</returns>
public async Task StopAsync(CancellationToken cancellationToken)
{
if (!IsRunning)
@@ -57,9 +63,13 @@
return;
}
+ // 鍙戦�佸彇娑堜俊鍙�
_cts?.Cancel();
+
+ // 鍋滄鐩戝惉
_listener?.Stop();
+ // 绛夊緟鎵�鏈夊鎴风浠诲姟瀹屾垚
Task[] tasks;
lock (_syncRoot)
{
@@ -75,12 +85,16 @@
}
/// <summary>
- /// 持续接受传入的TCP客户端连接,直到请求取消。
+ /// 寮傛鎺ュ彈瀹㈡埛绔繛鎺ョ殑涓诲惊鐜�
/// </summary>
- /// <remarks>此方法旨在后台运行以处理新的客户端连接。
- /// 如果监听器被释放或通过提供的令牌请求取消,循环将退出。</remarks>
- /// <param name="cancellationToken">可用于请求取消接受循环的令牌。当请求取消时,循环将立即终止。</param>
- /// <returns>表示异步接受循环操作的任务。</returns>
+ /// <summary>
+ /// 寮傛鎺ュ彈瀹㈡埛绔繛鎺ョ殑涓诲惊鐜�
+ /// </summary>
+ /// <remarks>
+ /// 鍦ㄥ悗鍙扮嚎绋嬩腑鎸佺画鎺ュ彈鏂扮殑瀹㈡埛绔繛鎺ャ��
+ /// 褰撴湁鏂拌繛鎺ユ椂锛屽皢鍏舵坊鍔犲埌瀹㈡埛绔瓧鍏稿苟鍚姩娑堟伅澶勭悊浠诲姟銆�
+ /// </remarks>
+ /// <param name="cancellationToken">鍙栨秷浠ょ墝</param>
private async Task AcceptLoopAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
@@ -88,31 +102,23 @@
TcpClient? client = null;
try
{
+ // 绛夊緟瀹㈡埛绔繛鎺�
client = await _listener!.AcceptTcpClientAsync().WaitAsync(cancellationToken);
- ConsoleHelper.WriteSuccessLine($"客户端上线:{client.Client.RemoteEndPoint.ToString()}");
+ QuartzLogger.Info($"瀹㈡埛绔繛鎺�:{client.Client.RemoteEndPoint.ToString()}");
}
- catch (OperationCanceledException)
- {
- break;
- }
- catch (ObjectDisposedException)
- {
- break;
- }
+ catch (OperationCanceledException) { break; }
+ catch (ObjectDisposedException) { break; }
catch
{
- if (cancellationToken.IsCancellationRequested)
- {
- break;
- }
+ if (cancellationToken.IsCancellationRequested) break;
}
- if (client == null)
- {
- continue;
- }
+ if (client == null) continue;
+ // 鐢熸垚瀹㈡埛绔� ID锛堜娇鐢ㄨ繙绋嬬鐐瑰湴鍧�锛�
string clientId = GetClientId(client);
+
+ // 娣诲姞鍒板鎴风瀛楀吀
lock (_syncRoot)
{
_clients[clientId] = client;
@@ -122,30 +128,41 @@
}
/// <summary>
- /// 从内部集合中移除指定标识符的客户端,并释放相关资源。
+ /// 绉婚櫎瀹㈡埛绔繛鎺�
/// </summary>
- /// <remarks>此方法关闭客户端连接,释放任何关联的锁,并移除对客户端的所有引用,
- /// 包括设备绑定和编码信息。通过对内部同步对象加锁确保线程安全。</remarks>
- /// <param name="clientId">要移除的客户端的唯一标识符。不能为null或空。</param>
+ /// <remarks>
+ /// 鍏抽棴瀹㈡埛绔繛鎺ュ苟娓呯悊鐩稿叧璧勬簮锛�
+ /// - 鍏抽棴 TcpClient
+ /// - 閲婃斁淇″彿閲�
+ /// - 绉婚櫎娲昏穬鏃堕棿鍜岀紪鐮佽褰�
+ /// - 绉婚櫎璁惧缁戝畾
+ /// </remarks>
+ /// <param name="clientId">瑕佺Щ闄ょ殑瀹㈡埛绔敮涓�鏍囪瘑</param>
private void RemoveClient(string clientId)
{
lock (_syncRoot)
{
+ // 鍏抽棴骞剁Щ闄ゅ鎴风杩炴帴
if (_clients.TryGetValue(clientId, out var client))
{
try { client.Close(); } catch { }
_clients.Remove(clientId);
}
+ // 閲婃斁淇″彿閲�
if (_clientLocks.TryGetValue(clientId, out var sem))
{
_clientLocks.Remove(clientId);
sem.Dispose();
}
+ // 绉婚櫎娲昏穬鏃堕棿璁板綍
_clientLastActive.Remove(clientId);
+
+ // 绉婚櫎缂栫爜璁板綍
_clientEncodings.Remove(clientId);
+ // 绉婚櫎璁惧缁戝畾
var deviceIds = _deviceBindings.Where(kv => kv.Value == clientId).Select(kv => kv.Key).ToList();
foreach (var deviceId in deviceIds)
{
@@ -155,12 +172,13 @@
}
/// <summary>
- /// 异步监控已连接的客户端,并断开超过配置超时时间闲置的客户端连接。
+ /// 寮傛鐩戞帶瀹㈡埛绔┖闂茶秴鏃�
/// </summary>
- /// <remarks>此方法持续检查闲置客户端,如果其不活动时间超过指定的空闲超时,则断开连接。
- /// 监控循环将持续运行,直到通过提供的令牌请求取消。</remarks>
- /// <param name="cancellationToken">可用于请求终止监控循环的取消令牌。</param>
- /// <returns>表示异步监控操作的任务。</returns>
+ /// <remarks>
+ /// 瀹氭湡妫�鏌ユ墍鏈夊鎴风鐨勬渶鍚庢椿璺冩椂闂达紝
+ /// 濡傛灉瓒呰繃绌洪棽瓒呮椂鏃堕棿锛屾柇寮�璇ュ鎴风杩炴帴銆�
+ /// </remarks>
+ /// <param name="cancellationToken">鍙栨秷浠ょ墝</param>
private async Task MonitorClientsAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
@@ -172,6 +190,7 @@
{
foreach (var kv in _clientLastActive)
{
+ // 妫�鏌ユ槸鍚﹁秴杩囩┖闂茶秴鏃�
if (_options.IdleTimeoutSeconds > 0 && DateTime.Now - kv.Value > TimeSpan.FromSeconds(_options.IdleTimeoutSeconds))
{
toRemove.Add(kv.Key);
@@ -179,27 +198,29 @@
}
}
+ // 鏂紑瓒呮椂鐨勫鎴风
foreach (var cid in toRemove)
{
RemoveClient(cid);
Log($"[{DateTime.Now}] TcpSocketServer disconnect idle client {cid}");
}
}
- catch
- {
- }
+ catch { }
+ // 姣忕妫�鏌ヤ竴娆�
try { await Task.Delay(1000, cancellationToken); } catch { }
}
}
/// <summary>
- /// 基于远程终端点获取指定TCP客户端的唯一标识符字符串。
+ /// 鑾峰彇瀹㈡埛绔敮涓�鏍囪瘑
/// </summary>
- /// <remarks>返回的标识符适用于在日志记录或跟踪场景中区分客户端。
- /// 如果客户端的远程终端点不可用,将生成GUID以确保唯一性。</remarks>
- /// <param name="client">要获取标识符的TCP客户端。不能为null。</param>
- /// <returns>表示客户端远程终端点的字符串(如果可用);否则为生成的新GUID字符串。</returns>
+ /// <remarks>
+ /// 浣跨敤瀹㈡埛绔殑杩滅▼绔偣鍦板潃浣滀负鏍囪瘑銆�
+ /// 濡傛灉杩滅▼绔偣涓嶅彲鐢紝鐢熸垚闅忔満 GUID銆�
+ /// </remarks>
+ /// <param name="client">TCP 瀹㈡埛绔�</param>
+ /// <returns>瀹㈡埛绔爣璇嗗瓧绗︿覆</returns>
public static string GetClientId(TcpClient client)
{
return client.Client.RemoteEndPoint?.ToString() ?? Guid.NewGuid().ToString();
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.cs
index 4d8789b..e08d58f 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.cs
@@ -11,115 +11,191 @@
namespace WIDESEAWCS_Tasks.SocketServer
{
/// <summary>
- /// TCP Socket服务端(基于行协议,按换行符分割消息)
+ /// TCP Socket 鏈嶅姟鍣� - 鏍稿績绫�
/// </summary>
+ /// <remarks>
+ /// 鏍稿績鑱岃矗锛�
+ /// 1. 鎺ュ彈瀹㈡埛绔� TCP 杩炴帴
+ /// 2. 绠$悊瀹㈡埛绔繛鎺ョ姸鎬�
+ /// 3. 鎺ユ敹鍜屽彂閫佹秷鎭�
+ /// 4. 澶勭悊璁惧娉ㄥ唽
+ /// 5. 娑堟伅甯цВ鏋愶紙鏀寔澶村熬鏍囪瘑锛�
+ ///
+ /// 鏈嶅姟鍣ㄤ娇鐢ㄤ互涓嬫暟鎹粨鏋勭鐞嗗鎴风锛�
+ /// - _clients: 瀹㈡埛绔� ID 鍒� TcpClient 鐨勬槧灏�
+ /// - _clientLocks: 瀹㈡埛绔� ID 鍒颁俊鍙烽噺鐨勬槧灏勶紙淇濊瘉姣忎釜瀹㈡埛绔殑鍙戦�佷簰鏂ワ級
+ /// - _deviceBindings: 璁惧 ID 鍒板鎴风 ID 鐨勬槧灏�
+ /// - _clientEncodings: 瀹㈡埛绔� ID 鍒扮紪鐮佺殑鏄犲皠锛堟敮鎸佽嚜鍔ㄧ紪鐮佹娴嬶級
+ /// - _clientLastActive: 瀹㈡埛绔� ID 鍒版渶鍚庢椿璺冩椂闂寸殑鏄犲皠
+ /// </remarks>
public partial class TcpSocketServer : IDisposable
{
+ /// <summary>
+ /// 鏈嶅姟鍣ㄩ厤缃�夐」
+ /// </summary>
private readonly SocketServerOptions _options;
/// <summary>
- /// 提供一个可用于同步对包含实例的访问的对象。
+ /// 鍚屾鏍瑰璞★紝鐢ㄤ簬绾跨▼鍚屾
/// </summary>
- /// <remarks>在对实例实现线程安全操作时,可将此对象用作锁定目标。此模式通常用于避免死锁并确保一致的同步。</remarks>
- public readonly object _syncRoot = new();
+ /// <remarks>
+ /// 鍦ㄥ绾跨▼璁块棶鍏变韩鏁版嵁缁撴瀯鏃朵娇鐢ㄦ瀵硅薄杩涜鍚屾銆�
+ /// 閲囩敤淇濆畧绛栫暐锛岀‘淇濈嚎绋嬪畨鍏ㄣ��
+ /// </remarks>
+ public readonly object _syncRoot = new object();
+ /// <summary>
+ /// TCP 鐩戝惉鍣�
+ /// </summary>
private TcpListener? _listener;
/// <summary>
- /// 表示用于发出进行中操作的取消请求的取消令牌源。
+ /// 鍙栨秷浠ょ墝婧�
/// </summary>
- /// <remarks>如果当前没有活动的取消机制,此字段可能为null。使用此令牌源取消支持取消的任务或操作。</remarks>
+ /// <remarks>
+ /// 鐢ㄤ簬璇锋眰鍋滄鏈嶅姟鍣ㄧ殑杩愯銆�
+ /// </remarks>
public CancellationTokenSource? _cts;
/// <summary>
- /// 提供表示活动客户端操作的任务列表。
+ /// 瀹㈡埛绔换鍔″垪琛�
/// </summary>
- /// <remarks>此字段用于内部跟踪异步客户端活动。它是只读的,不应在包含类外部直接修改。</remarks>
+ /// <remarks>
+ /// 璁板綍鎵�鏈夋椿璺冨鎴风鐨勫鐞嗕换鍔°��
+ /// </remarks>
public readonly List<Task> _clientTasks = new();
/// <summary>
- /// 提供从客户端标识符到其关联的TCP客户端连接的映射。
+ /// 瀹㈡埛绔繛鎺ュ瓧鍏�
/// </summary>
- /// <remarks>此字典允许通过唯一字符串标识符访问活动的TCP客户端。在多线程场景中,对集合的修改应小心进行以避免并发问题。</remarks>
+ /// <remarks>
+ /// Key: 瀹㈡埛绔� ID锛堥�氬父鏄� IP:Port锛�
+ /// Value: TcpClient 杩炴帴瀵硅薄
+ /// </remarks>
public readonly Dictionary<string, TcpClient> _clients = new();
/// <summary>
- /// 提供从设备标识符到其对应绑定值的映射。
+ /// 璁惧缁戝畾瀛楀吀
/// </summary>
- /// <remarks>此字段是只读的,用于包含类内部使用。应通过指定的方法或属性对字典进行修改以确保一致性。</remarks>
+ /// <remarks>
+ /// Key: 璁惧 ID
+ /// Value: 瀹㈡埛绔� ID
+ /// 鐢ㄤ簬閫氳繃璁惧 ID 鎵惧埌瀵瑰簲鐨勫鎴风杩炴帴銆�
+ /// </remarks>
public readonly Dictionary<string, string> _deviceBindings = new();
/// <summary>
- /// 提供从客户端标识符到其关联锁的映射,用于同步对客户端特定资源的访问。
+ /// 瀹㈡埛绔攣瀛楀吀
/// </summary>
- /// <remarks>字典中的每个条目将一个唯一的客户端ID与一个<see cref="SemaphoreSlim"/>实例关联,实现每个客户端的线程安全操作。此集合用于内部协调并发访问,不应直接修改。</remarks>
+ /// <remarks>
+ /// 姣忎釜瀹㈡埛绔竴涓� SemaphoreSlim锛岀‘淇濆悓涓�瀹㈡埛绔殑鍙戦�佹搷浣滀簰鏂ャ��
+ /// </remarks>
public readonly Dictionary<string, SemaphoreSlim> _clientLocks = new();
/// <summary>
- /// 提供从客户端标识符到其关联文本编码的映射。
+ /// 瀹㈡埛绔紪鐮佸瓧鍏�
/// </summary>
- /// <remarks>此字典用于内部跟踪已连接客户端的编码偏好。键表示客户端标识符,值指定用于文本操作的对应<see cref="System.Text.Encoding"/>。</remarks>
+ /// <remarks>
+ /// 璁板綍姣忎釜瀹㈡埛绔娇鐢ㄧ殑瀛楃缂栫爜銆�
+ /// 鏀寔鑷姩妫�娴嬶細UTF-8 鎴� GBK銆�
+ /// </remarks>
public readonly Dictionary<string, Encoding> _clientEncodings = new();
/// <summary>
- /// 存储每个客户端最后活动的时间戳,以客户端标识符为键。
+ /// 瀹㈡埛绔渶鍚庢椿璺冩椂闂村瓧鍏�
/// </summary>
- /// <remarks>此字段用于内部跟踪客户端活动。字典将客户端标识符映射到对应的最后活动时间(UTC)。直接修改此集合可能影响客户端会话管理逻辑。</remarks>
+ /// <remarks>
+ /// 璁板綍姣忎釜瀹㈡埛绔渶鍚庝竴娆℃椿鍔ㄧ殑鏃堕棿銆�
+ /// 鐢ㄤ簬绌洪棽瓒呮椂妫�娴嬨��
+ /// </remarks>
public readonly Dictionary<string, DateTime> _clientLastActive = new();
/// <summary>
- /// 指定包含类型中字符数据使用的文本编码。
+ /// 榛樿鏂囨湰缂栫爜
/// </summary>
- /// <remarks>使用此字段确定处理字符数据时如何编码或解码文本。编码影响字节如何被解释为字符,反之亦然。常见的编码包括UTF8、ASCII和Unicode。</remarks>
public readonly Encoding _textEncoding;
/// <summary>
- /// 表示自动检测到的GB2312编码(如果可用)。
+ /// 鑷姩妫�娴嬬殑 GBK 缂栫爜
/// </summary>
- /// <remarks>通常从输入数据确定编码时设置此字段。如果检测失败或未执行检测,值可能为null。</remarks>
public readonly Encoding? _autoDetectedGb2312;
+ /// <summary>
+ /// 鏃ュ織鏂囦欢璺緞
+ /// </summary>
private readonly string _logFile;
+ /// <summary>
+ /// 瀹㈡埛绔洃鎺т换鍔�
+ /// </summary>
private Task? _monitorTask;
/// <summary>
- /// 使用指定的服务器选项初始化 TcpSocketServer 类的新实例。
+ /// 鏈嶅姟鍣ㄦ槸鍚︽鍦ㄨ繍琛�
/// </summary>
- /// <remarks>如果启用了 AutoDetectEncoding 选项,服务器将默认使用 UTF-8 编码,
- /// 并尝试支持 GBK 编码进行自动检测。如果编码检测失败或提供了无效的编码名称,
- /// 将回退使用 UTF-8 编码。日志文件路径由 LogFilePath 选项决定,
- /// 如果未指定,则默认为应用程序基目录下的 'socketserver.log' 文件。</remarks>
- /// <param name="options">套接字服务器的配置选项。不能为 null。提供编码设置、日志文件路径和自动检测行为等配置。</param>
+ public bool IsRunning { get; private set; }
+
+ /// <summary>
+ /// 娑堟伅鎺ユ敹浜嬩欢
+ /// </summary>
+ /// <remarks>
+ /// 褰撴湇鍔″櫒鎺ユ敹鍒版秷鎭椂瑙﹀彂銆�
+ /// 鍙傛暟锛氭秷鎭唴瀹广�佹槸鍚� JSON 鏍煎紡銆乀CP 瀹㈡埛绔�佹満鍣ㄤ汉鐘舵��
+ /// </remarks>
+ public event Func<string, bool, TcpClient, RobotSocketState, Task<string?>>? MessageReceived;
+
+ /// <summary>
+ /// 鏈哄櫒浜鸿繛鎺ユ柇寮�浜嬩欢
+ /// </summary>
+ /// <remarks>
+ /// 褰撴満鍣ㄤ汉瀹㈡埛绔柇寮�杩炴帴鏃惰Е鍙戙��
+ /// 鍙傛暟锛氬鎴风 ID
+ /// </remarks>
+ public event Func<string, Task<string?>>? RobotReceived;
+
+ /// <summary>
+ /// 鏋勯�犲嚱鏁�
+ /// </summary>
+ /// <remarks>
+ /// 浣跨敤鎸囧畾鐨勯厤缃�夐」鍒濆鍖� TcpSocketServer 瀹炰緥銆�
+ /// 閰嶇疆椤瑰寘鎷細绔彛銆佸瓧绗︾紪鐮併�佽嚜鍔ㄧ紪鐮佹娴嬨�佹棩蹇楁枃浠惰矾寰勭瓑銆�
+ /// </remarks>
+ /// <param name="options">Socket 鏈嶅姟鍣ㄩ厤缃�夐」</param>
public TcpSocketServer(IOptions<SocketServerOptions> options)
{
_options = options.Value;
+
+ // 閰嶇疆瀛楃缂栫爜
if (_options.AutoDetectEncoding)
{
+ // 鑷姩妫�娴嬬紪鐮佹ā寮忥細榛樿 UTF-8锛屼篃鏀寔 GBK
_textEncoding = Encoding.UTF8;
try { _autoDetectedGb2312 = Encoding.GetEncoding("GBK"); } catch { _autoDetectedGb2312 = null; }
}
else
{
+ // 鎸囧畾缂栫爜妯″紡
try { _textEncoding = Encoding.GetEncoding(_options.EncodingName ?? "utf-8"); }
catch { _textEncoding = Encoding.UTF8; }
_autoDetectedGb2312 = null;
}
+ // 閰嶇疆鏃ュ織鏂囦欢璺緞
_logFile = Path.Combine(AppContext.BaseDirectory ?? ".", _options.LogFilePath ?? "socketserver.log");
Log($"[{DateTime.Now}] TcpSocketServer starting");
}
- public bool IsRunning { get; private set; }
-
- public event Func<string, bool, TcpClient, RobotSocketState, Task<string?>>? MessageReceived;
-
- public event Func<string, Task<string?>>? RobotReceived;
-
+ /// <summary>
+ /// 璁板綍鏃ュ織
+ /// </summary>
+ /// <remarks>
+ /// 灏嗘秷鎭緭鍑哄埌鎺у埗鍙板苟鍐欏叆鏃ュ織鏂囦欢銆�
+ /// </remarks>
+ /// <param name="message">鏃ュ織娑堟伅</param>
private void Log(string message)
{
Console.WriteLine(message);
try { File.AppendAllText(_logFile, message + Environment.NewLine); } catch { }
}
}
-}
\ No newline at end of file
+}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/CommonStackerCraneJob.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/CommonStackerCraneJob.cs
index 98a7aef..5ee565e 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/CommonStackerCraneJob.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/CommonStackerCraneJob.cs
@@ -1,4 +1,4 @@
-锘縰sing Quartz;
+using Quartz;
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
@@ -15,17 +15,77 @@
namespace WIDESEAWCS_Tasks
{
+ /// <summary>
+ /// 鍫嗗灈鏈轰换鍔′綔涓氾紙Quartz Job锛�- 鏍稿績璋冨害閫昏緫
+ /// </summary>
+ /// <remarks>
+ /// Quartz 瀹氭椂浠诲姟锛岃礋璐e爢鍨涙満鐨勪换鍔¤皟搴︺��
+ /// 浣跨敤 [DisallowConcurrentExecution] 绂佹骞跺彂鎵ц锛岀‘淇濆悓涓�鍫嗗灈鏈虹殑浠诲姟涓茶澶勭悊銆�
+ ///
+ /// 鏍稿績鑱岃矗锛�
+ /// 1. 浠庨厤缃枃浠跺姞杞藉懡浠ょ被鍨嬫槧灏�
+ /// 2. 妫�鏌ュ爢鍨涙満浠诲姟瀹屾垚鐘舵��
+ /// 3. 閫夋嫨鍚堥�傜殑浠诲姟锛堝鎵樼粰 StackerCraneTaskSelector锛�
+ /// 4. 鏋勫缓鍛戒护瀵硅薄锛堝鎵樼粰 StackerCraneCommandBuilder锛�
+ /// 5. 鍙戦�佸懡浠ゅ埌鍫嗗灈鏈�
+ /// 6. 澶勭悊浠诲姟瀹屾垚浜嬩欢
+ ///
+ /// 鏋舵瀯璁捐锛�
+ /// - StackerCraneTaskSelector锛氳礋璐i�夋嫨鍚堥�傜殑浠诲姟
+ /// - StackerCraneCommandBuilder锛氳礋璐e皢浠诲姟杞崲涓哄懡浠ゅ璞�
+ /// - CommonStackerCraneJob锛氳礋璐h皟搴︽祦绋嬫帶鍒�
+ /// </remarks>
[DisallowConcurrentExecution]
public class CommonStackerCraneJob : IJob
{
+ /// <summary>
+ /// 浠诲姟鏈嶅姟
+ /// </summary>
private readonly ITaskService _taskService;
+
+ /// <summary>
+ /// 浠诲姟鎵ц鏄庣粏鏈嶅姟
+ /// </summary>
private readonly ITaskExecuteDetailService _taskExecuteDetailService;
+
+ /// <summary>
+ /// 浠诲姟浠撳偍
+ /// </summary>
private readonly ITaskRepository _taskRepository;
+ /// <summary>
+ /// 鍫嗗灈鏈哄懡浠ら厤缃�
+ /// </summary>
+ /// <remarks>
+ /// 鍖呭惈宸烽亾涓庡懡浠ょ被鍨嬬殑鏄犲皠鍏崇郴銆�
+ /// 浠� JSON 鏂囦欢鍔犺浇銆�
+ /// </remarks>
private readonly StackerCraneCommandConfig _config;
+
+ /// <summary>
+ /// 鍫嗗灈鏈轰换鍔¢�夋嫨鍣�
+ /// </summary>
+ /// <remarks>
+ /// 璐熻矗閫夋嫨鍚堥�傜殑浠诲姟杩涜鎵ц銆�
+ /// </remarks>
private readonly StackerCraneTaskSelector _taskSelector;
+
+ /// <summary>
+ /// 鍫嗗灈鏈哄懡浠ゆ瀯寤哄櫒
+ /// </summary>
+ /// <remarks>
+ /// 璐熻矗灏嗕换鍔¤浆鎹负鍫嗗灈鏈哄彲鎵ц鐨勫懡浠ゅ璞°��
+ /// </remarks>
private readonly StackerCraneCommandBuilder _commandBuilder;
+ /// <summary>
+ /// 鏋勯�犲嚱鏁�
+ /// </summary>
+ /// <param name="taskService">浠诲姟鏈嶅姟</param>
+ /// <param name="taskExecuteDetailService">浠诲姟鎵ц鏄庣粏鏈嶅姟</param>
+ /// <param name="taskRepository">浠诲姟浠撳偍</param>
+ /// <param name="routerService">璺敱鏈嶅姟</param>
+ /// <param name="httpClientHelper">HTTP 瀹㈡埛绔府鍔╃被</param>
public CommonStackerCraneJob(
ITaskService taskService,
ITaskExecuteDetailService taskExecuteDetailService,
@@ -37,82 +97,120 @@
_taskExecuteDetailService = taskExecuteDetailService;
_taskRepository = taskRepository;
+ // 鍔犺浇閰嶇疆鏂囦欢
_config = LoadConfig();
+
+ // 鍒濆鍖栦换鍔¢�夋嫨鍣�
_taskSelector = new StackerCraneTaskSelector(taskService, routerService, httpClientHelper);
+
+ // 鍒濆鍖栧懡浠ゆ瀯寤哄櫒
_commandBuilder = new StackerCraneCommandBuilder(taskService, routerService, _config);
}
/// <summary>
- /// 鍔犺浇閰嶇疆锛堜紭鍏堢骇锛氶厤缃枃浠� > 榛樿閰嶇疆锛�
+ /// 鍔犺浇閰嶇疆鏂囦欢锛堜紭鍏堢骇锛氶厤缃枃浠� > 榛樿閰嶇疆锛�
/// </summary>
+ /// <remarks>
+ /// 浠庡簲鐢ㄧ▼搴忕洰褰曚笅鐨� StackerCraneJob/stackercrane-command-config.json 璇诲彇閰嶇疆銆�
+ /// 濡傛灉鏂囦欢涓嶅瓨鍦ㄦ垨瑙f瀽澶辫触锛屼娇鐢ㄩ粯璁ら厤缃��
+ /// </remarks>
+ /// <returns>鍫嗗灈鏈哄懡浠ら厤缃�</returns>
private static StackerCraneCommandConfig LoadConfig()
{
try
{
+ // 鏋勯�犻厤缃枃浠惰矾寰�
string configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "StackerCraneJob", "stackercrane-command-config.json");
if (File.Exists(configPath))
{
+ // 璇诲彇骞惰В鏋� JSON 閰嶇疆
string json = File.ReadAllText(configPath);
return System.Text.Json.JsonSerializer.Deserialize<StackerCraneCommandConfig>(json) ?? new StackerCraneCommandConfig();
}
}
catch (Exception ex)
{
+ // 閰嶇疆鍔犺浇澶辫触锛屼娇鐢ㄩ粯璁ら厤缃�
Console.WriteLine($"閰嶇疆鍔犺浇澶辫触: {ex.Message}锛屼娇鐢ㄩ粯璁ら厤缃�");
}
return new StackerCraneCommandConfig();
}
+ /// <summary>
+ /// Quartz Job 鐨勬墽琛屽叆鍙�
+ /// </summary>
+ /// <remarks>
+ /// 鎵ц娴佺▼锛�
+ /// 1. 浠� JobDataMap 鑾峰彇鍫嗗灈鏈鸿澶囦俊鎭�
+ /// 2. 璁㈤槄浠诲姟瀹屾垚浜嬩欢锛堜粎绗竴娆℃墽琛屾椂锛�
+ /// 3. 妫�鏌ュ爢鍨涙満浠诲姟瀹屾垚鐘舵��
+ /// 4. 妫�鏌ユ槸鍚﹀彲浠ュ彂閫佷换鍔�
+ /// 5. 閫夋嫨浠诲姟
+ /// 6. 鏋勫缓鍛戒护
+ /// 7. 鍙戦�佸懡浠�
+ /// 8. 鏇存柊浠诲姟鐘舵�佸拰鍫嗗灈鏈虹姸鎬�
+ /// </remarks>
+ /// <param name="context">Quartz 浣滀笟鎵ц涓婁笅鏂�</param>
public Task Execute(IJobExecutionContext context)
{
try
{
- //Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " CommonStackerCraneJob Start");
-
+ // 浠� JobDataMap 鑾峰彇鍫嗗灈鏈鸿澶囧弬鏁�
bool flag = context.JobDetail.JobDataMap.TryGetValue("JobParams", out object? value);
if (!flag || value is not IStackerCrane commonStackerCrane)
{
+ // 鍙傛暟鏃犳晥锛岀洿鎺ヨ繑鍥�
return Task.CompletedTask;
}
- // 璁㈤槄涓�娆′换鍔″畬鎴愪簨浠躲��
+ // ========== 璁㈤槄浠诲姟瀹屾垚浜嬩欢锛堝叏灞�鍙闃呬竴娆★級 ==========
if (!commonStackerCrane.IsEventSubscribed)
{
+ // 缁戝畾浠诲姟瀹屾垚浜嬩欢澶勭悊鏂规硶
commonStackerCrane.StackerCraneTaskCompletedEventHandler += CommonStackerCrane_StackerCraneTaskCompletedEventHandler;
}
+ // ========== 妫�鏌ュ爢鍨涙満浠诲姟瀹屾垚鐘舵�� ==========
commonStackerCrane.CheckStackerCraneTaskCompleted();
+ // ========== 妫�鏌ユ槸鍚﹀彲浠ュ彂閫佹柊浠诲姟 ==========
if (!commonStackerCrane.IsCanSendTask(commonStackerCrane.Communicator, commonStackerCrane.DeviceProDTOs, commonStackerCrane.DeviceProtocolDetailDTOs))
{
+ // 鍫嗗灈鏈轰笉鍙敤锛堝姝e湪鎵ц涓婁竴浠诲姟锛夛紝鐩存帴杩斿洖
return Task.CompletedTask;
}
-
- // 浠诲姟閫夋嫨涓嬫矇鍒颁笓鐢ㄩ�夋嫨鍣ㄣ��
+ // ========== 閫夋嫨浠诲姟 ==========
+ // 浠诲姟閫夋嫨涓嬫矇鍒颁笓鐢ㄩ�夋嫨鍣�
Dt_Task? task = _taskSelector.SelectTask(commonStackerCrane);
if (task == null)
{
+ // 娌℃湁鍙敤浠诲姟
return Task.CompletedTask;
}
- // 鍛戒护鏋勫缓涓嬫矇鍒颁笓鐢ㄦ瀯寤哄櫒銆�
+ // ========== 鏋勫缓鍛戒护 ==========
+ // 鍛戒护鏋勫缓涓嬫矇鍒颁笓鐢ㄦ瀯寤哄櫒
object? stackerCraneTaskCommand = _commandBuilder.ConvertToStackerCraneTaskCommand(task);
if (stackerCraneTaskCommand == null)
{
+ // 鍛戒护鏋勫缓澶辫触
return Task.CompletedTask;
}
+ // ========== 鍙戦�佸懡浠� ==========
bool sendFlag = SendStackerCraneCommand(commonStackerCrane, stackerCraneTaskCommand);
if (sendFlag)
{
+ // 鍙戦�佹垚鍔燂紝鏇存柊鐘舵��
commonStackerCrane.LastTaskType = task.TaskType;
_taskService.UpdateTaskStatusToNext(task.TaskNum);
}
}
catch (Exception ex)
{
+ // 璁板綍寮傚父
Console.WriteLine($"CommonStackerCraneJob Error: {ex.Message}");
}
@@ -120,25 +218,52 @@
}
/// <summary>
- /// 浠诲姟瀹屾垚浜嬩欢璁㈤槄鐨勬柟娉�
+ /// 鍫嗗灈鏈轰换鍔″畬鎴愪簨浠跺鐞�
/// </summary>
+ /// <remarks>
+ /// 褰撳爢鍨涙満鎶ュ憡浠诲姟瀹屾垚鏃惰皟鐢ㄦ鏂规硶銆�
+ /// 澶勭悊锛�
+ /// 1. 璁板綍鏃ュ織
+ /// 2. 鏇存柊浠诲姟鐘舵�佷负瀹屾垚
+ /// 3. 娓呴櫎鍫嗗灈鏈虹殑浣滀笟鎸囦护
+ /// </remarks>
+ /// <param name="sender">浜嬩欢鍙戦�佽�咃紙鍫嗗灈鏈鸿澶囷級</param>
+ /// <param name="e">浜嬩欢鍙傛暟锛屽寘鍚畬鎴愮殑浠诲姟鍙�</param>
private void CommonStackerCrane_StackerCraneTaskCompletedEventHandler(object? sender, StackerCraneTaskCompletedEventArgs e)
{
- CommonStackerCrane? commonStackerCrane = sender as CommonStackerCrane;
- if (commonStackerCrane != null)
+ IStackerCrane? stackerCrane = sender as IStackerCrane;
+ if (stackerCrane != null)
{
+ // 璁板綍鏃ュ織
Console.Out.WriteLine("TaskCompleted" + e.TaskNum);
+
+ // 鏇存柊浠诲姟鐘舵�佷负瀹屾垚
_taskService.StackCraneTaskCompleted(e.TaskNum);
- commonStackerCrane.SetValue(StackerCraneDBName.WorkAction, 2);
+
+ // 娓呴櫎鍫嗗灈鏈虹殑浣滀笟鎸囦护锛堣缃负 2锛岃〃绀虹┖闂诧級
+ stackerCrane.SetValue(StackerCraneDBName.WorkAction, 2);
}
}
+ /// <summary>
+ /// 鍙戦�佸爢鍨涙満鍛戒护
+ /// </summary>
+ /// <remarks>
+ /// 鏍规嵁鍛戒护绫诲瀷璋冪敤鐩稿簲鐨勫彂閫佹柟娉曘��
+ /// 鏀寔鏍囧噯鍛戒护鍜屾垚鍨嬪懡浠や袱绉嶆牸寮忋��
+ /// </remarks>
+ /// <param name="commonStackerCrane">鍫嗗灈鏈鸿澶囧璞�</param>
+ /// <param name="command">鍛戒护瀵硅薄</param>
+ /// <returns>鍙戦�佹槸鍚︽垚鍔�</returns>
private static bool SendStackerCraneCommand(IStackerCrane commonStackerCrane, object command)
{
return command switch
{
+ // 鎴愬瀷鍛戒护锛堝 HC 宸烽亾锛�
FormationStackerCraneTaskCommand formationCommand => commonStackerCrane.SendCommand(formationCommand),
+ // 鏍囧噯鍛戒护锛堝 GW銆丆W 宸烽亾锛�
StackerCraneTaskCommand standardCommand => commonStackerCrane.SendCommand(standardCommand),
+ // 鏈煡鍛戒护绫诲瀷
_ => false
};
}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneCommandBuilder.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneCommandBuilder.cs
index 1b00df1..d7b8602 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneCommandBuilder.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneCommandBuilder.cs
@@ -1,4 +1,4 @@
-锘縰sing System;
+using System;
using System.Diagnostics.CodeAnalysis;
using WIDESEAWCS_Common.TaskEnum;
using WIDESEAWCS_ITaskInfoService;
@@ -10,14 +10,40 @@
namespace WIDESEAWCS_Tasks
{
/// <summary>
- /// 鍫嗗灈鏈哄懡浠ゆ瀯寤哄櫒锛氬皝瑁呬换鍔″埌鍛戒护瀵硅薄鐨勮浆鎹笌鍦板潃瑙f瀽銆�
+ /// 鍫嗗灈鏈哄懡浠ゆ瀯寤哄櫒 - 灏佽浠诲姟鍒板懡浠ゅ璞$殑杞崲涓庡湴鍧�瑙f瀽
/// </summary>
+ /// <remarks>
+ /// 鏍稿績鑱岃矗锛�
+ /// 1. 鏍规嵁宸烽亾绫诲瀷閫夋嫨鍛戒护鏍煎紡锛堟爣鍑嗗懡浠�/鎴愬瀷鍛戒护锛�
+ /// 2. 鏋勫缓鍏ュ簱銆佸嚭搴撱�佺Щ搴撲换鍔$殑鍛戒护瀵硅薄
+ /// 3. 瑙f瀽浠诲姟鍦板潃锛堣-鍒�-灞傛牸寮忥級鍒板懡浠ゅ潗鏍�
+ /// 4. 鏌ヨ璺敱淇℃伅锛岀‘瀹氬爢鍨涙満鐨勫彇璐�/鏀捐揣绔欏彴
+ ///
+ /// 鍦板潃鏍煎紡绾﹀畾锛氬湴鍧�瀛楃涓叉牸寮忎负 "琛�-鍒�-灞�"锛屼緥濡� "1-2-3" 琛ㄧず绗�1琛屻�佺2鍒椼�佺3灞傘��
+ /// </remarks>
public class StackerCraneCommandBuilder
{
+ /// <summary>
+ /// 浠诲姟鏈嶅姟
+ /// </summary>
private readonly ITaskService _taskService;
+
+ /// <summary>
+ /// 璺敱鏈嶅姟
+ /// </summary>
private readonly IRouterService _routerService;
+
+ /// <summary>
+ /// 鍫嗗灈鏈哄懡浠ら厤缃�
+ /// </summary>
private readonly StackerCraneCommandConfig _config;
+ /// <summary>
+ /// 鏋勯�犲嚱鏁�
+ /// </summary>
+ /// <param name="taskService">浠诲姟鏈嶅姟</param>
+ /// <param name="routerService">璺敱鏈嶅姟</param>
+ /// <param name="config">鍛戒护閰嶇疆</param>
public StackerCraneCommandBuilder(
ITaskService taskService,
IRouterService routerService,
@@ -28,16 +54,36 @@
_config = config;
}
+ /// <summary>
+ /// 灏嗕换鍔¤浆鎹负鍫嗗灈鏈哄懡浠�
+ /// </summary>
+ /// <remarks>
+ /// 鏍规嵁宸烽亾绫诲瀷閫夋嫨涓嶅悓鐨勫懡浠ゆ瀯寤虹瓥鐣ャ��
+ /// </remarks>
+ /// <param name="task">浠诲姟瀵硅薄</param>
+ /// <returns>鍫嗗灈鏈哄懡浠ゅ璞★紝杞崲澶辫触杩斿洖 null</returns>
public object? ConvertToStackerCraneTaskCommand([NotNull] Dt_Task task)
{
+ // 鏍规嵁宸烽亾鑾峰彇鍛戒护绫诲瀷
string commandType = GetCommandType(task.Roadway);
+
+ // 鏍规嵁鍛戒护绫诲瀷璋冪敤鐩稿簲鐨勬瀯寤烘柟娉�
return commandType switch
{
- "Formation" => BuildCommand(task, CreateFormationCommand(task)),
- _ => BuildCommand(task, CreateStandardCommand(task))
+ "Formation" => BuildCommand(task, CreateFormationCommand(task)), // 鎴愬瀷鍛戒护
+ _ => BuildCommand(task, CreateStandardCommand(task)) // 鏍囧噯鍛戒护
};
}
+ /// <summary>
+ /// 鏍规嵁宸烽亾鑾峰彇鍛戒护绫诲瀷
+ /// </summary>
+ /// <remarks>
+ /// 閬嶅巻閰嶇疆涓殑鏄犲皠鍏崇郴锛屾壘鍒板尮閰嶇殑鍛戒护绫诲瀷銆�
+ /// 濡傛灉涓嶅尮閰嶄换浣曟槧灏勶紝杩斿洖榛樿鍛戒护绫诲瀷銆�
+ /// </remarks>
+ /// <param name="roadway">宸烽亾缂栫爜</param>
+ /// <returns>鍛戒护绫诲瀷锛圫tandard 鎴� Formation锛�</returns>
private string GetCommandType(string roadway)
{
foreach (var mapping in _config.RoadwayCommandMapping)
@@ -51,45 +97,88 @@
return _config.DefaultCommandType;
}
+ /// <summary>
+ /// 鍒涘缓鏍囧噯鍛戒护
+ /// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬鏍囧噯鍫嗗灈鏈猴紙GW銆丆W 寮�澶村贩閬擄級銆�
+ /// </remarks>
+ /// <param name="task">浠诲姟瀵硅薄</param>
+ /// <returns>鏍囧噯鍛戒护瀵硅薄</returns>
private static StackerCraneTaskCommand CreateStandardCommand(Dt_Task task)
{
return new StackerCraneTaskCommand
{
- TaskNum = task.TaskNum,
- WorkType = 1,
- WorkAction = 1
+ TaskNum = task.TaskNum, // 浠诲姟鍙�
+ WorkType = 1, // 浣滀笟绫诲瀷
+ WorkAction = 1 // 浣滀笟鎸囦护锛氬紑濮嬫墽琛�
};
}
+ /// <summary>
+ /// 鍒涘缓鎴愬瀷鍛戒护
+ /// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬鎴愬瀷鍫嗗灈鏈猴紙HC 寮�澶村贩閬擄級銆�
+ /// 鍖呭惈鏉$爜瀛楁锛岀敤浜庣數姹犺拷婧��
+ /// </remarks>
+ /// <param name="task">浠诲姟瀵硅薄</param>
+ /// <returns>鎴愬瀷鍛戒护瀵硅薄</returns>
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
+ Barcode = task.PalletCode, // 鎵樼洏鏉$爜
+ TaskNum = task.TaskNum, // 浠诲姟鍙�
+ WorkType = 1, // 浣滀笟绫诲瀷
+ WorkAction = 1, // 浣滀笟鎸囦护锛氬紑濮嬫墽琛�
+ FireAlarm = 0, // 鐏锛氭甯�
+ HeartBeat = 0, // 蹇冭烦
+ FieldName = string.Empty // 淇濈暀瀛楁
};
}
+ /// <summary>
+ /// 鏋勫缓鍛戒护锛堥�氱敤锛�
+ /// </summary>
+ /// <remarks>
+ /// 鏍规嵁浠诲姟绫诲瀷锛堝叆搴�/鍑哄簱/绉诲簱锛夎皟鐢ㄧ浉搴旂殑鏋勫缓鏂规硶銆�
+ /// </remarks>
+ /// <typeparam name="T">鍛戒护绫诲瀷</typeparam>
+ /// <param name="task">浠诲姟瀵硅薄</param>
+ /// <param name="command">鍒濆鍛戒护瀵硅薄</param>
+ /// <returns>濉厖濂界殑鍛戒护瀵硅薄</returns>
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
+ TaskTypeGroup.InboundGroup => BuildInboundCommand(task, command), // 鍏ュ簱
+ TaskTypeGroup.OutbondGroup => BuildOutboundCommand(task, command), // 鍑哄簱
+ TaskTypeGroup.RelocationGroup => BuildRelocationCommand(task, command), // 绉诲簱
+ _ => command // 鏈煡绫诲瀷锛岃繑鍥炲師鍛戒护
};
}
+ /// <summary>
+ /// 鏋勫缓鍏ュ簱鍛戒护
+ /// </summary>
+ /// <remarks>
+ /// 鍏ュ簱浠诲姟闇�瑕侊細
+ /// 1. 鏌ヨ鍫嗗灈鏈哄彇璐х珯鍙帮紙鏍规嵁褰撳墠鍦板潃鍜屼换鍔$被鍨嬶級
+ /// 2. 璁剧疆璧峰鍧愭爣锛堟潵鑷珯鍙帮級
+ /// 3. 瑙f瀽鐩爣鍦板潃锛岃缃粓鐐瑰潗鏍�
+ /// </remarks>
+ /// <typeparam name="T">鍛戒护绫诲瀷</typeparam>
+ /// <param name="task">浠诲姟瀵硅薄</param>
+ /// <param name="command">鍛戒护瀵硅薄</param>
+ /// <returns>濉厖濂界殑鍛戒护瀵硅薄</returns>
private T? BuildInboundCommand<T>(Dt_Task task, T command) where T : class
{
+ // 纭畾浠诲姟绫诲瀷锛堢┖鎵樼洏鐢ㄧ壒娈婄被鍨� 100锛�
int taskType = 0;
if (task.TaskType == (int)TaskOutboundTypeEnum.OutEmpty)
{
@@ -97,23 +186,29 @@
}
else
taskType = task.TaskType;
+
+ // 鏌ヨ鍫嗗灈鏈哄彇璐х珯鍙拌矾鐢�
Dt_Router? router = _routerService.QueryNextRoute(task.CurrentAddress, task.Roadway, 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));
+ // 瑙f瀽鐩爣鍦板潃锛堝簱浣嶅湴鍧�锛�
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);
@@ -121,8 +216,22 @@
return command;
}
+ /// <summary>
+ /// 鏋勫缓鍑哄簱鍛戒护
+ /// </summary>
+ /// <remarks>
+ /// 鍑哄簱浠诲姟闇�瑕侊細
+ /// 1. 鏌ヨ鍫嗗灈鏈烘斁璐х珯鍙帮紙鏍规嵁鐩爣鍦板潃鍜屼换鍔$被鍨嬶級
+ /// 2. 璁剧疆缁堢偣鍧愭爣锛堟潵鑷珯鍙帮級
+ /// 3. 瑙f瀽璧峰鍦板潃锛岃缃捣鐐瑰潗鏍�
+ /// </remarks>
+ /// <typeparam name="T">鍛戒护绫诲瀷</typeparam>
+ /// <param name="task">浠诲姟瀵硅薄</param>
+ /// <param name="command">鍛戒护瀵硅薄</param>
+ /// <returns>濉厖濂界殑鍛戒护瀵硅薄</returns>
private T? BuildOutboundCommand<T>(Dt_Task task, T command) where T : class
{
+ // 纭畾浠诲姟绫诲瀷
int taskType = 0;
if (task.TaskType == (int)TaskOutboundTypeEnum.OutEmpty)
{
@@ -131,6 +240,7 @@
else
taskType = task.TaskType;
+ // 鏌ヨ鍫嗗灈鏈烘斁璐х珯鍙拌矾鐢�
Dt_Router? router = _routerService.QueryNextRoute(task.Roadway, task.TargetAddress, taskType);
if (router == null)
{
@@ -138,16 +248,19 @@
return null;
}
+ // 璁剧疆缁堢偣鍧愭爣锛堟潵鑷矾鐢遍厤缃級
SetCommandProperty(command, "EndRow", Convert.ToInt16(router.SrmRow));
SetCommandProperty(command, "EndColumn", Convert.ToInt16(router.SrmColumn));
SetCommandProperty(command, "EndLayer", Convert.ToInt16(router.SrmLayer));
+ // 瑙f瀽璧峰鍦板潃锛堝簱浣嶅湴鍧�锛�
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);
@@ -155,24 +268,41 @@
return command;
}
+ /// <summary>
+ /// 鏋勫缓绉诲簱鍛戒护
+ /// </summary>
+ /// <remarks>
+ /// 绉诲簱浠诲姟闇�瑕侊細
+ /// 1. 瑙f瀽鐩爣鍦板潃锛堟柊鐨勫簱浣嶏級
+ /// 2. 瑙f瀽璧峰鍦板潃锛堝師鏉ョ殑搴撲綅锛�
+ /// 3. 璁剧疆璧锋鍧愭爣
+ /// </remarks>
+ /// <typeparam name="T">鍛戒护绫诲瀷</typeparam>
+ /// <param name="task">浠诲姟瀵硅薄</param>
+ /// <param name="command">鍛戒护瀵硅薄</param>
+ /// <returns>濉厖濂界殑鍛戒护瀵硅薄</returns>
private T? BuildRelocationCommand<T>(Dt_Task task, T command) where T : class
{
+ // 瑙f瀽鐩爣鍦板潃
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);
+ // 瑙f瀽璧峰鍦板潃
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);
@@ -180,22 +310,45 @@
return command;
}
+ /// <summary>
+ /// 璁剧疆鍛戒护灞炴�у�硷紙閫氳繃鍙嶅皠锛�
+ /// </summary>
+ /// <remarks>
+ /// 浣跨敤鍙嶅皠鍔ㄦ�佽缃懡浠ゅ璞$殑灞炴�у�笺��
+ /// </remarks>
+ /// <typeparam name="T">鍛戒护绫诲瀷</typeparam>
+ /// <param name="command">鍛戒护瀵硅薄</param>
+ /// <param name="propertyName">灞炴�у悕绉�</param>
+ /// <param name="value">灞炴�у��</param>
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>
+ /// 瑙f瀽鍦板潃瀛楃涓�
+ /// </summary>
+ /// <remarks>
+ /// 鍦板潃鏍煎紡锛氳-鍒�-灞傦紝渚嬪 "1-2-3" 琛ㄧず绗�1琛屻�佺2鍒椼�佺3灞傘��
+ /// </remarks>
+ /// <param name="address">鍦板潃瀛楃涓�</param>
+ /// <param name="row">瑙f瀽鍑虹殑琛屽潗鏍�</param>
+ /// <param name="column">瑙f瀽鍑虹殑鍒楀潗鏍�</param>
+ /// <param name="layer">瑙f瀽鍑虹殑灞傚潗鏍�</param>
+ /// <returns>瑙f瀽鎴愬姛杩斿洖 true</returns>
private static bool TryParseAddress(string address, out short row, out short column, out short layer)
{
row = column = layer = 0;
- string[] parts = address.Split("-");
+ // 鎸� "-" 鍒嗛殧鍦板潃
+ string[] parts = address.Split('-');
if (parts.Length != 3)
{
return false;
}
+ // 瑙f瀽鍚勯儴鍒嗕负 short 绫诲瀷
return short.TryParse(parts[0], out row)
&& short.TryParse(parts[1], out column)
&& short.TryParse(parts[2], out layer);
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneCommandConfig.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneCommandConfig.cs
index 0d0de4b..e68ecd7 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneCommandConfig.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneCommandConfig.cs
@@ -5,21 +5,39 @@
/// <summary>
/// 鍫嗗灈鏈哄懡浠ら厤缃�
/// </summary>
+ /// <remarks>
+ /// 瀹氫箟鍫嗗灈鏈哄懡浠ょ被鍨嬩笌宸烽亾鐨勬槧灏勫叧绯汇��
+ /// 鏍规嵁宸烽亾锛圧oadway锛夌殑涓嶅悓锛屽爢鍨涙満鍙兘浣跨敤涓嶅悓鐨勫懡浠ゆ牸寮忋��
+ /// 閰嶇疆鍙互閫氳繃 JSON 鏂囦欢鍔ㄦ�佸姞杞姐��
+ /// </remarks>
public class StackerCraneCommandConfig
{
/// <summary>
- /// Roadway 鍏抽敭瀛楀埌鍛戒护绫诲瀷鐨勬槧灏�
+ /// 宸烽亾鍏抽敭瀛楀埌鍛戒护绫诲瀷鐨勬槧灏勫瓧鍏�
/// </summary>
+ /// <remarks>
+ /// Key: 宸烽亾缂栫爜鐨勫叧閿瓧锛堝 HC銆丟W銆丆W锛�
+ /// Value: 鍛戒护绫诲瀷锛堝 Formation銆丼tandard锛�
+ ///
+ /// 鏄犲皠瑙勫垯锛�
+ /// - HC 寮�澶� -> Formation锛堟垚鍨嬪爢鍨涙満鍛戒护锛�
+ /// - GW 寮�澶� -> Standard锛堟爣鍑嗗爢鍨涙満鍛戒护锛�
+ /// - CW 寮�澶� -> Standard锛堟爣鍑嗗爢鍨涙満鍛戒护锛�
+ /// </remarks>
public Dictionary<string, string> RoadwayCommandMapping { get; set; } = new()
{
- { "HC", "Formation" },
- { "GW", "Standard" },
- { "CW", "Standard" }
+ { "HC", "Formation" }, // 鎴愬瀷鍫嗗灈鏈�
+ { "GW", "Standard" }, // 鏍囧噯鍫嗗灈鏈�
+ { "CW", "Standard" } // 鏍囧噯鍫嗗灈鏈�
};
/// <summary>
/// 榛樿鍛戒护绫诲瀷
/// </summary>
+ /// <remarks>
+ /// 褰撳贩閬撶紪鐮佷笉鍖归厤浠讳綍鏄犲皠瑙勫垯鏃朵娇鐢ㄧ殑榛樿鍛戒护绫诲瀷銆�
+ /// 榛樿涓� Standard锛堟爣鍑嗗懡浠ゆ牸寮忥級銆�
+ /// </remarks>
public string DefaultCommandType { get; set; } = "Standard";
}
}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneDBName.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneDBName.cs
index daa4604..c463416 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneDBName.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneDBName.cs
@@ -1,85 +1,109 @@
-锘�#region << 鐗� 鏈� 娉� 閲� >>
-/*----------------------------------------------------------------
- * 鍛藉悕绌洪棿锛歐IDESEAWCS_Tasks.StackerCraneJob
- * 鍒涘缓鑰咃細鑳$搴�
- * 鍒涘缓鏃堕棿锛�2024/8/2 16:13:36
- * 鐗堟湰锛歏1.0.0
- * 鎻忚堪锛�
- *
- * ----------------------------------------------------------------
- * 淇敼浜猴細
- * 淇敼鏃堕棿锛�
- * 鐗堟湰锛歏1.0.1
- * 淇敼璇存槑锛�
- *
- *----------------------------------------------------------------*/
-#endregion << 鐗� 鏈� 娉� 閲� >>
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
namespace WIDESEAWCS_Tasks.StackerCraneJob
{
+ /// <summary>
+ /// 鍫嗗灈鏈� PLC 瀵勫瓨鍣ㄥ悕绉版灇涓�
+ /// </summary>
+ /// <remarks>
+ /// 瀹氫箟鍫嗗灈鏈轰笌 WCS 閫氫俊鏃朵娇鐢ㄧ殑 PLC 瀵勫瓨鍣ㄥ湴鍧�鍚嶇О銆�
+ /// 鍖呭惈浠诲姟鍙枫�佷綔涓氱被鍨嬨�佽捣姝綅缃瓑淇℃伅銆�
+ /// </remarks>
public enum StackerCraneDBName
{
/// <summary>
/// 浠诲姟鍙�
/// </summary>
+ /// <remarks>
+ /// WCS 鍒嗛厤鐨勪换鍔″敮涓�鏍囪瘑鍙枫��
+ /// 鐢ㄤ簬 WCS 鍜屽爢鍨涙満涔嬮棿寤虹珛浠诲姟瀵瑰簲鐨勫叧鑱斻��
+ /// </remarks>
TaskNum,
/// <summary>
/// 浣滀笟绫诲瀷
/// </summary>
+ /// <remarks>
+ /// 鏍囪瘑浠诲姟鐨勭被鍨嬨��
+ /// </remarks>
WorkType,
/// <summary>
/// 鎵樼洏绫诲瀷
/// </summary>
+ /// <remarks>
+ /// 鏍囪瘑鎵樼洏鐨勮鏍肩被鍨嬨��
+ /// </remarks>
TrayType,
/// <summary>
/// 璧峰琛�
/// </summary>
+ /// <remarks>
+ /// 浠诲姟鐨勮捣濮嬩綅缃�-琛屽潗鏍囥��
+ /// 鐢ㄤ簬纭畾搴撲綅鍦ㄨ揣鏋朵腑鐨勮浣嶇疆銆�
+ /// </remarks>
StartRow,
/// <summary>
/// 璧峰鍒�
/// </summary>
+ /// <remarks>
+ /// 浠诲姟鐨勮捣濮嬩綅缃�-鍒楀潗鏍囥��
+ /// 鐢ㄤ簬纭畾搴撲綅鍦ㄨ揣鏋朵腑鐨勫垪浣嶇疆銆�
+ /// </remarks>
StartColumn,
/// <summary>
/// 璧峰灞�
/// </summary>
+ /// <remarks>
+ /// 浠诲姟鐨勮捣濮嬩綅缃�-灞傚潗鏍囥��
+ /// 鐢ㄤ簬纭畾搴撲綅鍦ㄨ揣鏋朵腑鐨勫眰浣嶇疆銆�
+ /// </remarks>
StartLayer,
/// <summary>
/// 鐩爣琛�
/// </summary>
+ /// <remarks>
+ /// 浠诲姟鐨勭洰鏍囦綅缃�-琛屽潗鏍囥��
+ /// 鍏ュ簱鏃惰〃绀鸿揣鐗╁瓨鏀剧殑琛屼綅缃紝鍑哄簱鏃惰〃绀鸿揣鐗╂潵婧愮殑琛屼綅缃��
+ /// </remarks>
EndRow,
/// <summary>
/// 鐩爣鍒�
/// </summary>
+ /// <remarks>
+ /// 浠诲姟鐨勭洰鏍囦綅缃�-鍒楀潗鏍囥��
+ /// 鍏ュ簱鏃惰〃绀鸿揣鐗╁瓨鏀剧殑鍒椾綅缃紝鍑哄簱鏃惰〃绀鸿揣鐗╂潵婧愮殑鍒椾綅缃��
+ /// </remarks>
EndColumn,
/// <summary>
/// 鐩爣灞�
/// </summary>
+ /// <remarks>
+ /// 浠诲姟鐨勭洰鏍囦綅缃�-灞傚潗鏍囥��
+ /// 鍏ュ簱鏃惰〃绀鸿揣鐗╁瓨鏀剧殑灞備綅缃紝鍑哄簱鏃惰〃绀鸿揣鐗╂潵婧愮殑灞備綅缃��
+ /// </remarks>
EndLayer,
/// <summary>
/// 浣滀笟鎸囦护
/// </summary>
+ /// <remarks>
+ /// 鎺у埗鍫嗗灈鏈虹殑鍔ㄤ綔锛�
+ /// - 1: 寮�濮嬫墽琛屼换鍔�
+ /// - 2: 浠诲姟瀹屾垚/鍋滄
+ /// </remarks>
WorkAction,
- ///// <summary>
- ///// 鎵樼洏鍙�
- ///// </summary>
- //Barcode,
-
+ /// <summary>
+ /// 褰撳墠浠诲姟鍙�
+ /// </summary>
+ /// <remarks>
+ /// 鍫嗗灈鏈哄綋鍓嶆鍦ㄦ墽琛岀殑浠诲姟鍙枫��
+ /// </remarks>
CurrentTaskNum
}
}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneTaskCommand.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneTaskCommand.cs
index 15c31c5..7534971 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneTaskCommand.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneTaskCommand.cs
@@ -1,83 +1,108 @@
-锘�#region << 鐗� 鏈� 娉� 閲� >>
-/*----------------------------------------------------------------
- * 鍛藉悕绌洪棿锛歐IDESEAWCS_Tasks.StackerCraneJob
- * 鍒涘缓鑰咃細鑳$搴�
- * 鍒涘缓鏃堕棿锛�2024/8/2 16:13:36
- * 鐗堟湰锛歏1.0.0
- * 鎻忚堪锛�
- *
- * ----------------------------------------------------------------
- * 淇敼浜猴細
- * 淇敼鏃堕棿锛�
- * 鐗堟湰锛歏1.0.1
- * 淇敼璇存槑锛�
- *
- *----------------------------------------------------------------*/
-#endregion << 鐗� 鏈� 娉� 閲� >>
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using WIDESEAWCS_Model.Models;
using WIDESEAWCS_QuartzJob.DeviceBase;
namespace WIDESEAWCS_Tasks.StackerCraneJob
{
+ /// <summary>
+ /// 鍫嗗灈鏈轰换鍔″懡浠ゆ暟鎹被
+ /// </summary>
+ /// <remarks>
+ /// 缁ф壙鑷� DeviceCommand锛岀敤浜庝笌鍫嗗灈鏈鸿繘琛� PLC 閫氫俊銆�
+ /// 鍖呭惈浠诲姟鍙枫�佷綔涓氱被鍨嬨�佽捣姝㈣鍒楀眰鍧愭爣绛夊瓧娈点��
+ /// 鏍囧噯鍛戒护鏍煎紡锛岀敤浜庡ぇ澶氭暟宸烽亾鐨勫爢鍨涙満銆�
+ /// </remarks>
public class StackerCraneTaskCommand : DeviceCommand
{
- #region <Public Menber>
/// <summary>
/// 浣滀笟鍛戒护
/// </summary>
+ /// <remarks>
+ /// 鎺у埗鍫嗗灈鏈虹殑鍔ㄤ綔锛�
+ /// - 1: 寮�濮嬫墽琛屼换鍔�
+ /// - 2: 浠诲姟瀹屾垚/鍋滄
+ /// </remarks>
public short WorkAction { get; set; }
+
/// <summary>
/// 浠诲姟鍙�
/// </summary>
+ /// <remarks>
+ /// WCS 鍒嗛厤鐨勪换鍔″敮涓�鏍囪瘑鍙枫��
+ /// </remarks>
public int TaskNum { get; set; }
/// <summary>
/// 浣滀笟绫诲瀷
/// </summary>
+ /// <remarks>
+ /// 鏍囪瘑浠诲姟鐨勭被鍨嬨��
+ /// </remarks>
public short WorkType { get; set; }
/// <summary>
- /// 鏃犳晥瀛楁
+ /// 鏃犳晥瀛楁锛堜繚鐣欏瓧娈碉級
/// </summary>
+ /// <remarks>
+ /// 鍘嗗彶閬楃暀瀛楁锛岀洰鍓嶄笉鍐嶄娇鐢ㄣ��
+ /// </remarks>
[DataLength(6)]
public string FieldName { get; set; } = "";
/// <summary>
/// 璧峰琛�
/// </summary>
+ /// <remarks>
+ /// 浠诲姟鐨勮捣濮嬩綅缃�-琛屽潗鏍囷紙璐ф灦琛屽彿锛夈��
+ /// 鍏ュ簱鏃惰〃绀鸿揣鐗╂潵鑷摢涓綅缃��
+ /// </remarks>
public short StartRow { get; set; }
/// <summary>
/// 璧峰鍒�
/// </summary>
+ /// <remarks>
+ /// 浠诲姟鐨勮捣濮嬩綅缃�-鍒楀潗鏍囷紙璐ф灦鍒楀彿锛夈��
+ /// 鍏ュ簱鏃惰〃绀鸿揣鐗╂潵鑷摢涓綅缃��
+ /// </remarks>
public short StartColumn { get; set; }
/// <summary>
/// 璧峰灞�
/// </summary>
+ /// <remarks>
+ /// 浠诲姟鐨勮捣濮嬩綅缃�-灞傚潗鏍囷紙璐ф灦灞傚彿锛夈��
+ /// 鍏ュ簱鏃惰〃绀鸿揣鐗╂潵鑷摢涓綅缃��
+ /// </remarks>
public short StartLayer { get; set; }
/// <summary>
/// 鐩爣琛�
/// </summary>
+ /// <remarks>
+ /// 浠诲姟鐨勭洰鏍囦綅缃�-琛屽潗鏍囷紙璐ф灦琛屽彿锛夈��
+ /// 鍏ュ簱鏃惰〃绀鸿揣鐗╁瓨鏀惧埌鍝釜浣嶇疆銆�
+ /// 鍑哄簱鏃惰〃绀鸿揣鐗╀粠鍝釜浣嶇疆鍙栧嚭銆�
+ /// </remarks>
public short EndRow { get; set; }
/// <summary>
/// 鐩爣鍒�
/// </summary>
+ /// <remarks>
+ /// 浠诲姟鐨勭洰鏍囦綅缃�-鍒楀潗鏍囷紙璐ф灦鍒楀彿锛夈��
+ /// 鍏ュ簱鏃惰〃绀鸿揣鐗╁瓨鏀惧埌鍝釜浣嶇疆銆�
+ /// 鍑哄簱鏃惰〃绀鸿揣鐗╀粠鍝釜浣嶇疆鍙栧嚭銆�
+ /// </remarks>
public short EndColumn { get; set; }
/// <summary>
/// 鐩爣灞�
/// </summary>
+ /// <remarks>
+ /// 浠诲姟鐨勭洰鏍囦綅缃�-灞傚潗鏍囷紙璐ф灦灞傚彿锛夈��
+ /// 鍏ュ簱鏃惰〃绀鸿揣鐗╁瓨鏀惧埌鍝釜浣嶇疆銆�
+ /// 鍑哄簱鏃惰〃绀鸿揣鐗╀粠鍝釜浣嶇疆鍙栧嚭銆�
+ /// </remarks>
public short EndLayer { get; set; }
-
- #endregion <Public Menber>
}
}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneTaskSelector.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneTaskSelector.cs
index 605fcc6..caa4de8 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneTaskSelector.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneTaskSelector.cs
@@ -1,4 +1,4 @@
-锘縰sing Newtonsoft.Json;
+using Newtonsoft.Json;
using System.Diagnostics.CodeAnalysis;
using WIDESEA_Core;
using WIDESEAWCS_Common.HttpEnum;
@@ -13,19 +13,57 @@
namespace WIDESEAWCS_Tasks
{
/// <summary>
- /// 鍫嗗灈鏈轰换鍔¢�夋嫨鍣細灏佽浠诲姟鎸戦�変笌绔欏彴鍙敤鎬у垽鏂��
+ /// 鍫嗗灈鏈轰换鍔¢�夋嫨鍣� - 灏佽浠诲姟鎸戦�変笌绔欏彴鍙敤鎬у垽鏂�
/// </summary>
+ /// <remarks>
+ /// 鏍稿績鑱岃矗锛�
+ /// 1. 鏍规嵁鍫嗗灈鏈轰笂涓�浠诲姟绫诲瀷閫夋嫨涓嬩竴涓悎閫傜殑浠诲姟
+ /// 2. 瀵瑰嚭搴撲换鍔¤繘琛岀Щ搴撴鏌ワ紙WMS 鍒ゆ柇锛�
+ /// 3. 鍒ゆ柇鍑哄簱绔欏彴鏄惁鍙敤锛堟槸鍚﹁鍗犵敤锛�
+ /// 4. 灏濊瘯閫夋嫨澶囬�夊嚭搴撶珯鍙�
+ ///
+ /// 浠诲姟閫夋嫨绛栫暐锛�
+ /// - 濡傛灉涓婁竴浠诲姟鏄嚭搴擄紝浼樺厛閫夋嫨鍏ュ簱浠诲姟
+ /// - 濡傛灉涓婁竴浠诲姟鏄叆搴擄紝浼樺厛閫夋嫨鍑哄簱浠诲姟
+ /// - 瀵逛簬鍑哄簱浠诲姟锛屽厛妫�鏌ユ槸鍚﹂渶瑕佺Щ搴�
+ /// </remarks>
public class StackerCraneTaskSelector
{
+ /// <summary>
+ /// 浠诲姟鏈嶅姟
+ /// </summary>
private readonly ITaskService _taskService;
+
+ /// <summary>
+ /// 璺敱鏈嶅姟
+ /// </summary>
private readonly IRouterService _routerService;
+
+ /// <summary>
+ /// 绉诲簱妫�鏌ュ鎵樺嚱鏁�
+ /// </summary>
+ /// <remarks>
+ /// 鐢ㄤ簬璋冪敤 WMS 鍒ゆ柇鍑哄簱浠诲姟鏄惁闇�瑕佸厛鎵ц绉诲簱銆�
+ /// </remarks>
private readonly Func<int, Dt_Task?> _transferCheck;
+ /// <summary>
+ /// 鏋勯�犲嚱鏁帮紙浣跨敤 HTTP 瀹㈡埛绔府鍔╃被锛�
+ /// </summary>
+ /// <param name="taskService">浠诲姟鏈嶅姟</param>
+ /// <param name="routerService">璺敱鏈嶅姟</param>
+ /// <param name="httpClientHelper">HTTP 瀹㈡埛绔府鍔╃被</param>
public StackerCraneTaskSelector(ITaskService taskService, IRouterService routerService, HttpClientHelper httpClientHelper)
: this(taskService, routerService, taskNum => QueryTransferTask(httpClientHelper, taskNum))
{
}
+ /// <summary>
+ /// 鏋勯�犲嚱鏁帮紙浣跨敤濮旀墭鍑芥暟锛�
+ /// </summary>
+ /// <param name="taskService">浠诲姟鏈嶅姟</param>
+ /// <param name="routerService">璺敱鏈嶅姟</param>
+ /// <param name="transferCheck">绉诲簱妫�鏌ュ嚱鏁�</param>
public StackerCraneTaskSelector(ITaskService taskService, IRouterService routerService, Func<int, Dt_Task?> transferCheck)
{
_taskService = taskService;
@@ -33,44 +71,69 @@
_transferCheck = transferCheck;
}
+ /// <summary>
+ /// 閫夋嫨鍚堥�傜殑浠诲姟
+ /// </summary>
+ /// <remarks>
+ /// 鏍规嵁鍫嗗灈鏈虹殑涓婁竴浠诲姟绫诲瀷鍜屽綋鍓嶇姸鎬侊紝閫夋嫨涓嬩竴涓簲璇ユ墽琛岀殑浠诲姟銆�
+ ///
+ /// 閫夋嫨绛栫暐锛�
+ /// 1. 濡傛灉娌℃湁涓婁竴浠诲姟绫诲瀷锛屾煡璇㈡櫘閫氫换鍔�
+ /// 2. 濡傛灉涓婁竴浠诲姟鏄嚭搴擄紝浼樺厛鏌ュ叆搴撲换鍔★紝鍐嶆煡鍑哄簱浠诲姟
+ /// 3. 濡傛灉涓婁竴浠诲姟鏄叆搴擄紝浼樺厛鏌ュ嚭搴撲换鍔�
+ /// 4. 瀵逛簬鍑哄簱浠诲姟锛岄渶瑕佸垽鏂珯鍙版槸鍚﹀彲鐢�
+ /// </remarks>
+ /// <param name="commonStackerCrane">鍫嗗灈鏈鸿澶囧璞�</param>
+ /// <returns>閫変腑鐨勪换鍔★紝濡傛灉娌℃湁鍙敤浠诲姟杩斿洖 null</returns>
public Dt_Task? SelectTask(IStackerCrane commonStackerCrane)
{
Dt_Task? candidateTask;
+
+ // 鏍规嵁涓婁竴浠诲姟绫诲瀷鍐冲畾鏌ヨ绛栫暐
if (commonStackerCrane.LastTaskType == null)
{
+ // 娌℃湁涓婁竴浠诲姟绫诲瀷锛屾煡璇㈡櫘閫氫换鍔�
candidateTask = _taskService.QueryStackerCraneTask(commonStackerCrane.DeviceCode);
}
else if (commonStackerCrane.LastTaskType.GetValueOrDefault().GetTaskTypeGroup() == TaskTypeGroup.OutbondGroup)
{
+ // 涓婁竴浠诲姟鏄嚭搴擄紝浼樺厛鏌ュ叆搴撲换鍔�
candidateTask = _taskService.QueryStackerCraneInTask(commonStackerCrane.DeviceCode);
+ // 濡傛灉娌℃湁鍏ュ簱浠诲姟锛屽啀鏌ヤ竴涓嬪嚭搴撲换鍔�
candidateTask ??= _taskService.QueryStackerCraneOutTask(commonStackerCrane.DeviceCode);
}
else
{
+ // 涓婁竴浠诲姟鏄叆搴擄紙闈炲嚭搴擄級锛屼紭鍏堟煡鍑哄簱浠诲姟
candidateTask = _taskService.QueryStackerCraneOutTask(commonStackerCrane.DeviceCode);
}
+ // 濡傛灉娌℃湁鍊欓�変换鍔★紝杩斿洖 null
if (candidateTask == null)
{
return null;
}
+ // 濡傛灉涓嶆槸鍑哄簱浠诲姟锛岀洿鎺ヨ繑鍥�
if (candidateTask.TaskType.GetTaskTypeGroup() != TaskTypeGroup.OutbondGroup)
{
return candidateTask;
}
+ // 灏濊瘯閫夋嫨鍑哄簱浠诲姟锛堝彲鑳介渶瑕佺Щ搴撴鏌ュ拰绔欏彴鍙敤鎬у垽鏂級
Dt_Task? selectedTask = TrySelectOutboundTask(candidateTask);
if (selectedTask != null)
{
return selectedTask;
}
+ // 鏌ユ壘鍏朵粬鍙敤鐨勫嚭搴撶珯鍙�
var otherOutStationCodes = _routerService
.QueryNextRoutes(commonStackerCrane.DeviceCode, candidateTask.NextAddress, candidateTask.TaskType)
.Select(x => x.ChildPosi)
.ToList();
+ // 鏌ヨ鍏朵粬绔欏彴鐨勫嚭搴撲换鍔�
var tasks = _taskService.QueryStackerCraneOutTasks(commonStackerCrane.DeviceCode, otherOutStationCodes);
foreach (var alternativeTask in tasks)
{
@@ -81,89 +144,147 @@
}
}
+ // 娌℃湁鍙敤鍑哄簱浠诲姟锛屽皾璇曡繑鍥炲叆搴撲换鍔�
return _taskService.QueryStackerCraneInTask(commonStackerCrane.DeviceCode);
}
+ /// <summary>
+ /// 灏濊瘯閫夋嫨鍑哄簱浠诲姟
+ /// </summary>
+ /// <remarks>
+ /// 瀵瑰�欓�夊嚭搴撲换鍔¤繘琛岋細
+ /// 1. 绉诲簱妫�鏌ワ紙璋冪敤 WMS 鍒ゆ柇鏄惁闇�瑕佺Щ搴擄級
+ /// 2. 绔欏彴鍙敤鎬у垽鏂�
+ ///
+ /// 濡傛灉浠诲姟琚垽瀹氫负闇�瑕佺Щ搴擄紝鍒欒繑鍥炵Щ搴撳悗鐨勪换鍔°��
+ /// </remarks>
+ /// <param name="outboundTask">鍊欓�夊嚭搴撲换鍔�</param>
+ /// <returns>鍙�変腑鐨勪换鍔★紝鎴� null锛堢珯鍙颁笉鍙敤锛�</returns>
private Dt_Task? TrySelectOutboundTask(Dt_Task outboundTask)
{
- // 鍙鏄嚭搴撲换鍔★紝蹇呴』鍏堣皟鐢╓MS鍒ゆ柇鏄惁闇�瑕佺Щ搴撱��
+ // 瀵逛簬鎵�鏈夊嚭搴撲换鍔★紝蹇呴』鍏堣皟鐢� WMS 鍒ゆ柇鏄惁闇�瑕佺Щ搴�
var taskAfterTransferCheck = _transferCheck(outboundTask.TaskNum) ?? outboundTask;
var taskGroup = taskAfterTransferCheck.TaskType.GetTaskTypeGroup();
+ // 濡傛灉鏄Щ搴撲换鍔℃垨鍑哄簱浠诲姟锛屽皾璇曚粠 WMS 娣诲姞浠诲姟
if (taskGroup == TaskTypeGroup.RelocationGroup || taskGroup == TaskTypeGroup.OutbondGroup)
{
TryAddTaskFromWms(taskAfterTransferCheck);
}
+ // 濡傛灉鏄Щ搴撲换鍔★紝鐩存帴杩斿洖
if (taskGroup == TaskTypeGroup.RelocationGroup)
{
return taskAfterTransferCheck;
}
+ // 濡傛灉涓嶆槸鍑哄簱浠诲姟锛岃繑鍥炲師浠诲姟
if (taskGroup != TaskTypeGroup.OutbondGroup)
{
return taskAfterTransferCheck;
}
+ // 鍒ゆ柇鍑哄簱绔欏彴鏄惁鍙敤
return IsOutTaskStationAvailable(taskAfterTransferCheck) ? taskAfterTransferCheck : null;
}
+ /// <summary>
+ /// 璋冪敤 WMS 妫�鏌ョЩ搴�
+ /// </summary>
+ /// <remarks>
+ /// 閫氳繃 HTTP 璇锋眰璋冪敤 WMS 鐨勭Щ搴撴鏌ユ帴鍙c��
+ /// </remarks>
+ /// <param name="httpClientHelper">HTTP 瀹㈡埛绔府鍔╃被</param>
+ /// <param name="taskNum">浠诲姟鍙�</param>
+ /// <returns>濡傛灉闇�瑕佺Щ搴撹繑鍥炵Щ搴撲换鍔★紝鍚﹀垯杩斿洖 null</returns>
private static Dt_Task? QueryTransferTask(HttpClientHelper httpClientHelper, int taskNum)
{
+ // 璋冪敤 WMS 鐨勭Щ搴撴鏌ユ帴鍙�
var response = httpClientHelper.Post<WebResponseContent>(
nameof(ConfigKey.TransferCheck),
taskNum.ToString());
+ // 妫�鏌ュ搷搴旀槸鍚︽垚鍔�
if (response == null || !response.IsSuccess || response.Data == null || !response.Data.Status || response.Data.Data == null)
{
return null;
}
+ // 瑙f瀽杩斿洖鐨勪换鍔℃暟鎹�
var taskJson = response.Data.Data.ToString();
return string.IsNullOrWhiteSpace(taskJson) ? null : JsonConvert.DeserializeObject<Dt_Task>(taskJson);
}
+ /// <summary>
+ /// 灏濊瘯浠� WMS 娣诲姞浠诲姟
+ /// </summary>
+ /// <remarks>
+ /// 濡傛灉浠诲姟涓嶅瓨鍦ㄤ簬鏈湴鏁版嵁搴擄紝浠� WMS 杩斿洖鐨勬暟鎹坊鍔犲埌鏈湴銆�
+ /// </remarks>
+ /// <param name="task">浠诲姟瀵硅薄</param>
private void TryAddTaskFromWms(Dt_Task task)
{
+ // 妫�鏌ヤ换鍔″彿鏄惁鏈夋晥
if (task.TaskNum <= 0)
{
return;
}
+ // 妫�鏌ヤ换鍔℃槸鍚﹀凡瀛樺湪
var existingTask = _taskService.QueryByTaskNum(task.TaskNum);
if (existingTask != null)
{
return;
}
+ // 娣诲姞鍒版湰鍦版暟鎹簱
_taskService.AddData(task);
}
+ /// <summary>
+ /// 鍒ゆ柇鍑哄簱绔欏彴鏄惁鍙敤
+ /// </summary>
+ /// <remarks>
+ /// 妫�鏌ョ洰鏍囩珯鍙板搴旂殑杈撻�佺嚎鏄惁琚崰鐢ㄣ��
+ /// 濡傛灉绔欏彴涓婃湁璐х墿锛屽垯璇ョ珯鍙颁笉鍙敤銆�
+ /// </remarks>
+ /// <param name="task">鍑哄簱浠诲姟</param>
+ /// <returns>绔欏彴鍙敤杩斿洖 true</returns>
private bool IsOutTaskStationAvailable([NotNull] Dt_Task task)
{
+ // 纭畾浠诲姟绫诲瀷
int taskType = 0;
if (task.TaskType == (int)TaskOutboundTypeEnum.OutEmpty)
{
+ // 绌烘墭鐩樺嚭搴�
taskType = 100;
}
else
taskType = task.TaskType;
+
+ // 鏌ヨ绔欏彴璺敱淇℃伅
Dt_Router? router = _routerService.QueryNextRoute(task.Roadway, task.NextAddress, 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);
}
}
-}
\ No newline at end of file
+}
--
Gitblit v1.9.3