wanshenmean
2026-02-03 3f67529e0f492f30851f091fea7f97a01cb502e5
添加机器人/套接字任务支持;更新枚举与 .gitignore 文件

添加机器人支持与TCP套接字服务器实现,更新任务枚举及辅助逻辑,并忽略Copilot快照文件。

具体变更包括:
- 新增机器人相关任务文件:RobotCraneDevice.cs、RobotJob.cs。
- 新增SocketServer实现文件:TcpSocketServer(含.cs/.Server/.Clients/.Messaging/.Dispose相关文件)、SocketServerHostedService.cs、SocketServerOptions.cs。
- 更新TaskStatusEnum,引入TaskRobotStatusEnum(机器人生命周期状态)。
- 在TaskTypeEnum中新增RobotToManual,用于表示机器人任务。
- 更新TaskEnumHelper,以正确解析出站任务类型,并在计算下一个状态时处理TaskRobotStatusEnum。
- 调整RouterInOutType数值(In=200, Out=100)。
- 修改ITaskService接口(部分新增List<int>属性,用于出站任务类型)。
- 更新项目中的.vs/DocumentLayout.json条目(打开的文件/标记),并更新.gitignore以排除CopilotSnapshots及相关.vs Copilot索引文件。

这些变更增加了机器人任务支持和基于套接字的服务器框架,并更新了枚举/辅助逻辑以支持机器人工作流程。同时将.vs元数据和Copilot产物添加到.gitignore中,避免提交IDE/Copilot状态文件。
已添加14个文件
已重命名2个文件
已删除2个文件
已修改21个文件
1428 ■■■■■ 文件已修改
.gitignore 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/CopilotIndices/18.0.988.22099/CodeChunks.db 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/CopilotIndices/18.0.988.22099/SemanticSymbols.db 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/v18/DocumentLayout.json 228 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Common/TaskEnum/TaskEnumHelper.cs 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Common/TaskEnum/TaskStatusEnum.cs 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Common/TaskEnum/TaskTypeEnum.cs 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Core/Enums/RouterInOutType.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_ITaskInfoService/ITaskService.cs 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/QuartzNet/QuartzNetExtension.cs 133 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/Robot/RobotCraneDevice.cs 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/Program.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/appsettings.json 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/TaskService.cs 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineJob/CommonConveyorLineJob.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineJob/ConveyorLineTaskCommand.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/FormationStackerCraneJob/FormationStackerCraneTaskCommand.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotJob.cs 297 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/SocketServerHostedService.cs 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/SocketServerOptions.cs 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Clients.cs 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Dispose.cs 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Messaging.cs 175 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Server.cs 164 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.cs 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/.vs/WIDESEA_WMSServer/CopilotIndices/18.0.988.22099/CodeChunks.db 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/.vs/WIDESEA_WMSServer/CopilotIndices/18.0.988.22099/SemanticSymbols.db 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/.vs/WIDESEA_WMSServer/v18/DocumentLayout.json 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/appsettings.json 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目资料/设备协议/分容柜接口协议/化成分容柜接口协议1.1.xls 补丁 | 查看 | 原始文档 | blame | 历史
项目资料/设备协议/化成库堆垛机协议/PlcLink_堆垛机项目.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
项目资料/设备协议/拘束机对接协议/拘束机对接协议-汇川5U.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
项目资料/设备协议/机械手协议/交互流程表(1).xlsx 补丁 | 查看 | 原始文档 | blame | 历史
项目资料/设备协议/高常温堆垛机与输送线/WCS-输送线对接协议说明-V260202.docx 补丁 | 查看 | 原始文档 | blame | 历史
项目资料/设备协议/高常温堆垛机与输送线/WCS输送对接地址表.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
项目资料/设备协议/高常温堆垛机与输送线/堆垛机与上位机交互信息.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
项目资料/需要对接的设备信息/陕煤硬件设备对接.xls 补丁 | 查看 | 原始文档 | blame | 历史
项目资料/高常温堆垛机/堆垛机通信协议.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
项目资料/高常温堆垛机/模拟PLC程序.zip 补丁 | 查看 | 原始文档 | blame | 历史
.gitignore
@@ -396,3 +396,12 @@
Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/CopilotIndices/18.0.988.22099/CodeChunks.db
Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/CopilotIndices/18.0.988.22099/SemanticSymbols.db
Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/v18/DocumentLayout.json
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/0272CC30DEEFE244BD51E6AD406E11CA/2841A59D75EBB84B92E801077DB88BA4/2C0CE2B065FDC35419958E23E64252B6
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/0272CC30DEEFE244BD51E6AD406E11CA/2841A59D75EBB84B92E801077DB88BA4/48EC962EDBF70FC42B1C2628B04CD8DB
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/0272CC30DEEFE244BD51E6AD406E11CA/2841A59D75EBB84B92E801077DB88BA4/5E1AAAA5BACAE48E8D86545A7EFEB583
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/0272CC30DEEFE244BD51E6AD406E11CA/2841A59D75EBB84B92E801077DB88BA4/86F5B814633CDE33B1AD91E6EF4A0AD5
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/0272CC30DEEFE244BD51E6AD406E11CA/2841A59D75EBB84B92E801077DB88BA4/915864713A7919AB693EE494165DF029
/Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots
/Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/copilot-chat
/Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/CopilotIndices
/Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/CopilotIndices
Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/CopilotIndices/18.0.988.22099/CodeChunks.db
Binary files differ
Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/CopilotIndices/18.0.988.22099/SemanticSymbols.db
Binary files differ
Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/v18/DocumentLayout.json
@@ -3,28 +3,56 @@
  "WorkspaceRootPath": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\",
  "Documents": [
    {
      "AbsoluteMoniker": "D:0:0:{6236BFFF-173D-44A8-9FC3-7C001EA30347}|WIDESEAWCS_QuartzJob\\WIDESEAWCS_QuartzJob.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_quartzjob\\stackercrane\\spec\\speformationstackercrane.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{6236BFFF-173D-44A8-9FC3-7C001EA30347}|WIDESEAWCS_QuartzJob\\WIDESEAWCS_QuartzJob.csproj|solutionrelative:wideseawcs_quartzjob\\stackercrane\\spec\\speformationstackercrane.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
      "AbsoluteMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_tasks\\robotjob\\robotjob.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|solutionrelative:wideseawcs_tasks\\robotjob\\robotjob.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{6236BFFF-173D-44A8-9FC3-7C001EA30347}|WIDESEAWCS_QuartzJob\\WIDESEAWCS_QuartzJob.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_quartzjob\\stackercrane\\enum\\formationstackercranestatus.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{6236BFFF-173D-44A8-9FC3-7C001EA30347}|WIDESEAWCS_QuartzJob\\WIDESEAWCS_QuartzJob.csproj|solutionrelative:wideseawcs_quartzjob\\stackercrane\\enum\\formationstackercranestatus.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
      "AbsoluteMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_tasks\\socketserver\\tcpsocketserver.messaging.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|solutionrelative:wideseawcs_tasks\\socketserver\\tcpsocketserver.messaging.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_tasks\\formationstackercranejob\\formationstackercranedbname.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|solutionrelative:wideseawcs_tasks\\formationstackercranejob\\formationstackercranedbname.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
      "AbsoluteMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_tasks\\socketserver\\tcpsocketserver.server.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|solutionrelative:wideseawcs_tasks\\socketserver\\tcpsocketserver.server.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_tasks\\formationstackercranejob\\formationstackercranetaskcommand.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|solutionrelative:wideseawcs_tasks\\formationstackercranejob\\formationstackercranetaskcommand.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
      "AbsoluteMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_tasks\\socketserver\\tcpsocketserver.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|solutionrelative:wideseawcs_tasks\\socketserver\\tcpsocketserver.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_tasks\\socketserver\\tcpsocketserver.clients.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|solutionrelative:wideseawcs_tasks\\socketserver\\tcpsocketserver.clients.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{83F18A31-5983-4587-A0B2-414BF70E50B5}|WIDESEAWCS_TaskInfoService\\WIDESEAWCS_TaskInfoService.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_taskinfoservice\\taskservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{83F18A31-5983-4587-A0B2-414BF70E50B5}|WIDESEAWCS_TaskInfoService\\WIDESEAWCS_TaskInfoService.csproj|solutionrelative:wideseawcs_taskinfoservice\\taskservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{9FBC654C-51DE-422D-9E1E-6A38268DE1E2}|WIDESEAWCS_Common\\WIDESEAWCS_Common.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_common\\taskenum\\taskstatusenum.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{9FBC654C-51DE-422D-9E1E-6A38268DE1E2}|WIDESEAWCS_Common\\WIDESEAWCS_Common.csproj|solutionrelative:wideseawcs_common\\taskenum\\taskstatusenum.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{9FBC654C-51DE-422D-9E1E-6A38268DE1E2}|WIDESEAWCS_Common\\WIDESEAWCS_Common.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_common\\taskenum\\taskenumhelper.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{9FBC654C-51DE-422D-9E1E-6A38268DE1E2}|WIDESEAWCS_Common\\WIDESEAWCS_Common.csproj|solutionrelative:wideseawcs_common\\taskenum\\taskenumhelper.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{9FBC654C-51DE-422D-9E1E-6A38268DE1E2}|WIDESEAWCS_Common\\WIDESEAWCS_Common.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_common\\taskenum\\tasktypeenum.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{9FBC654C-51DE-422D-9E1E-6A38268DE1E2}|WIDESEAWCS_Common\\WIDESEAWCS_Common.csproj|solutionrelative:wideseawcs_common\\taskenum\\tasktypeenum.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_tasks\\stackercranejob\\commonstackercranejob.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|solutionrelative:wideseawcs_tasks\\stackercranejob\\commonstackercranejob.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{7279A2AE-8D1F-4E66-A73A-01AF7927A336}|WIDESEAWCS_ITaskInfoService\\WIDESEAWCS_ITaskInfoService.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_itaskinfoservice\\itaskservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{7279A2AE-8D1F-4E66-A73A-01AF7927A336}|WIDESEAWCS_ITaskInfoService\\WIDESEAWCS_ITaskInfoService.csproj|solutionrelative:wideseawcs_itaskinfoservice\\itaskservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_tasks\\formationstackercranejob\\formationcommonstackercranejob.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|solutionrelative:wideseawcs_tasks\\formationstackercranejob\\formationcommonstackercranejob.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_tasks\\stackercranejob\\commonstackercranejob.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|solutionrelative:wideseawcs_tasks\\stackercranejob\\commonstackercranejob.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
      "AbsoluteMoniker": "D:0:0:{6236BFFF-173D-44A8-9FC3-7C001EA30347}|WIDESEAWCS_QuartzJob\\WIDESEAWCS_QuartzJob.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_quartzjob\\quartznet\\quartznetextension.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{6236BFFF-173D-44A8-9FC3-7C001EA30347}|WIDESEAWCS_QuartzJob\\WIDESEAWCS_QuartzJob.csproj|solutionrelative:wideseawcs_quartzjob\\quartznet\\quartznetextension.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    }
  ],
  "DocumentGroupContainers": [
@@ -34,7 +62,7 @@
      "DocumentGroups": [
        {
          "DockedWidth": 200,
          "SelectedChildIndex": 6,
          "SelectedChildIndex": 11,
          "Children": [
            {
              "$type": "Bookmark",
@@ -50,80 +78,164 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 3,
              "Title": "FormationStackerCraneTaskCommand.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\FormationStackerCraneJob\\FormationStackerCraneTaskCommand.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Tasks\\FormationStackerCraneJob\\FormationStackerCraneTaskCommand.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\FormationStackerCraneJob\\FormationStackerCraneTaskCommand.cs",
              "RelativeToolTip": "WIDESEAWCS_Tasks\\FormationStackerCraneJob\\FormationStackerCraneTaskCommand.cs",
              "ViewState": "AgIAAEcAAAAAAAAAAAAiwDAAAAAVAAAAAAAAAA==",
              "DocumentIndex": 8,
              "Title": "TaskTypeEnum.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Common\\TaskEnum\\TaskTypeEnum.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Common\\TaskEnum\\TaskTypeEnum.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Common\\TaskEnum\\TaskTypeEnum.cs",
              "RelativeToolTip": "WIDESEAWCS_Common\\TaskEnum\\TaskTypeEnum.cs",
              "ViewState": "AgIAADYAAAAAAAAAAAAuwEwAAAAZAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-01-29T09:17:51.622Z",
              "EditorCaption": ""
              "WhenOpened": "2026-02-02T07:43:32.381Z"
            },
            {
              "$type": "Document",
              "DocumentIndex": 2,
              "Title": "FormationStackerCraneDBName.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\FormationStackerCraneJob\\FormationStackerCraneDBName.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Tasks\\FormationStackerCraneJob\\FormationStackerCraneDBName.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\FormationStackerCraneJob\\FormationStackerCraneDBName.cs",
              "RelativeToolTip": "WIDESEAWCS_Tasks\\FormationStackerCraneJob\\FormationStackerCraneDBName.cs",
              "ViewState": "AgIAAAAAAAAAAAAAAAAAAC4AAAAQAAAAAAAAAA==",
              "DocumentIndex": 7,
              "Title": "TaskEnumHelper.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Common\\TaskEnum\\TaskEnumHelper.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Common\\TaskEnum\\TaskEnumHelper.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Common\\TaskEnum\\TaskEnumHelper.cs",
              "RelativeToolTip": "WIDESEAWCS_Common\\TaskEnum\\TaskEnumHelper.cs",
              "ViewState": "AgIAABEAAAAAAAAAAAApwBgAAAAkAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-01-29T09:13:24.922Z",
              "EditorCaption": ""
              "WhenOpened": "2026-02-02T07:13:45.107Z"
            },
            {
              "$type": "Document",
              "DocumentIndex": 1,
              "Title": "FormationStackerCraneStatus.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_QuartzJob\\StackerCrane\\Enum\\FormationStackerCraneStatus.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_QuartzJob\\StackerCrane\\Enum\\FormationStackerCraneStatus.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_QuartzJob\\StackerCrane\\Enum\\FormationStackerCraneStatus.cs",
              "RelativeToolTip": "WIDESEAWCS_QuartzJob\\StackerCrane\\Enum\\FormationStackerCraneStatus.cs",
              "ViewState": "AgIAAEAAAAAAAAAAAAArwFQAAAAQAAAAAAAAAA==",
              "DocumentIndex": 5,
              "Title": "TaskService.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_TaskInfoService\\TaskService.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_TaskInfoService\\TaskService.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_TaskInfoService\\TaskService.cs",
              "RelativeToolTip": "WIDESEAWCS_TaskInfoService\\TaskService.cs",
              "ViewState": "AgIAAGECAAAAAAAAAAAvwHMCAADaAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-01-29T03:35:09.011Z",
              "EditorCaption": ""
              "WhenOpened": "2026-02-02T07:10:41.925Z"
            },
            {
              "$type": "Document",
              "DocumentIndex": 0,
              "Title": "SpeFormationStackerCrane.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_QuartzJob\\StackerCrane\\Spec\\SpeFormationStackerCrane.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_QuartzJob\\StackerCrane\\Spec\\SpeFormationStackerCrane.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_QuartzJob\\StackerCrane\\Spec\\SpeFormationStackerCrane.cs",
              "RelativeToolTip": "WIDESEAWCS_QuartzJob\\StackerCrane\\Spec\\SpeFormationStackerCrane.cs",
              "ViewState": "AgIAAN8AAAAAAAAAAAAnwPEAAAAUAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-01-29T03:12:53.832Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 4,
              "DocumentIndex": 11,
              "Title": "FormationCommonStackerCraneJob.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\FormationStackerCraneJob\\FormationCommonStackerCraneJob.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Tasks\\FormationStackerCraneJob\\FormationCommonStackerCraneJob.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\FormationStackerCraneJob\\FormationCommonStackerCraneJob.cs",
              "RelativeToolTip": "WIDESEAWCS_Tasks\\FormationStackerCraneJob\\FormationCommonStackerCraneJob.cs",
              "ViewState": "AgIAAEEAAAAAAAAAAAAqwFQAAAAVAAAAAAAAAA==",
              "ViewState": "AgIAAFkAAAAAAAAAAAAWwG0AAAAkAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-01-29T03:12:40.035Z",
              "EditorCaption": ""
              "WhenOpened": "2026-02-02T07:08:40.6Z"
            },
            {
              "$type": "Document",
              "DocumentIndex": 5,
              "DocumentIndex": 10,
              "Title": "ITaskService.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_ITaskInfoService\\ITaskService.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_ITaskInfoService\\ITaskService.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_ITaskInfoService\\ITaskService.cs",
              "RelativeToolTip": "WIDESEAWCS_ITaskInfoService\\ITaskService.cs",
              "ViewState": "AgIAACAAAAAAAAAAAAAjwDUAAAAnAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-02T07:08:06.875Z"
            },
            {
              "$type": "Document",
              "DocumentIndex": 6,
              "Title": "TaskStatusEnum.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Common\\TaskEnum\\TaskStatusEnum.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Common\\TaskEnum\\TaskStatusEnum.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Common\\TaskEnum\\TaskStatusEnum.cs",
              "RelativeToolTip": "WIDESEAWCS_Common\\TaskEnum\\TaskStatusEnum.cs",
              "ViewState": "AgIAAJYAAAAAAAAAAAAAwJ8AAAAWAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-02T07:05:39.323Z"
            },
            {
              "$type": "Document",
              "DocumentIndex": 9,
              "Title": "CommonStackerCraneJob.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\StackerCraneJob\\CommonStackerCraneJob.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Tasks\\StackerCraneJob\\CommonStackerCraneJob.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\StackerCraneJob\\CommonStackerCraneJob.cs",
              "RelativeToolTip": "WIDESEAWCS_Tasks\\StackerCraneJob\\CommonStackerCraneJob.cs",
              "ViewState": "AgIAAJ4AAAAAAAAAAAAkwLQAAAATAAAAAAAAAA==",
              "ViewState": "AgIAAGwAAAAAAAAAAAApwH4AAAA1AAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-01-29T02:59:35.12Z",
              "WhenOpened": "2026-02-02T06:13:24.454Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 12,
              "Title": "QuartzNetExtension.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_QuartzJob\\QuartzNet\\QuartzNetExtension.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_QuartzJob\\QuartzNet\\QuartzNetExtension.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_QuartzJob\\QuartzNet\\QuartzNetExtension.cs",
              "RelativeToolTip": "WIDESEAWCS_QuartzJob\\QuartzNet\\QuartzNetExtension.cs",
              "ViewState": "AgIAAHAAAAAAAAAAAAAAAIYAAABzAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-02T06:11:52.348Z"
            },
            {
              "$type": "Document",
              "DocumentIndex": 0,
              "Title": "RobotJob.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\RobotJob\\RobotJob.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Tasks\\RobotJob\\RobotJob.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\RobotJob\\RobotJob.cs",
              "RelativeToolTip": "WIDESEAWCS_Tasks\\RobotJob\\RobotJob.cs",
              "ViewState": "AgIAACMAAAAAAAAAAAAAwD4AAABRAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-02T03:52:06.502Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 1,
              "Title": "TcpSocketServer.Messaging.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Messaging.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Messaging.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Messaging.cs",
              "RelativeToolTip": "WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Messaging.cs",
              "ViewState": "AgIAACMAAAAAAAAAAAAkwD4AAAAQAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-02T03:38:35.325Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 3,
              "Title": "TcpSocketServer.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.cs",
              "RelativeToolTip": "WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.cs",
              "ViewState": "AgIAACQAAAAAAAAAAAAowDYAAAA/AAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-02T03:37:56.495Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 4,
              "Title": "TcpSocketServer.Clients.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Clients.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Clients.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Clients.cs",
              "RelativeToolTip": "WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Clients.cs",
              "ViewState": "AgIAAAAAAAAAAAAAAAAAAE0AAAAhAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-02T03:37:53.636Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 2,
              "Title": "TcpSocketServer.Server.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Server.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Server.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Server.cs",
              "RelativeToolTip": "WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Server.cs",
              "ViewState": "AgIAAFMAAAAAAAAAAAAQwGwAAAAAAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-02T03:37:49.526Z",
              "EditorCaption": ""
            }
          ]
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Common/TaskEnum/TaskEnumHelper.cs
@@ -32,7 +32,7 @@
                return TaskTypeGroup.OutbondGroup;
            }
            // å°è¯•将任务类型转换为TaskInStatusEnum枚举类型,如果成功,返回InboundGroup
            else if (!int.TryParse(Enum.Parse<TaskInStatusEnum>(taskTypeStr).ToString(), out result))
            else if (!int.TryParse(Enum.Parse<TaskOutboundTypeEnum>(taskTypeStr).ToString(), out result))
            {
                return TaskTypeGroup.InboundGroup;
            }
@@ -76,6 +76,12 @@
                // èŽ·å–TaskOutStatusEnum枚举类型的索引列表
                return type.GetEnumIndexList().Where(x => x > currentStatus && x < (int)TaskOutStatusEnum.OutFinish).OrderBy(x => x).FirstOrDefault();
            }
            // å¦‚æžœtype是TaskRobotStatusEnum枚举类型
            else if (type == typeof(TaskRobotStatusEnum))
            {
                // èŽ·å–TaskOutStatusEnum枚举类型的索引列表
                return type.GetEnumIndexList().Where(x => x > currentStatus && x < (int)TaskRobotStatusEnum.RobotFinish).OrderBy(x => x).FirstOrDefault();
            }
            // å¦‚果以上条件都不满足,抛出NotImplementedException异常
            else
            {
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Common/TaskEnum/TaskStatusEnum.cs
@@ -144,4 +144,43 @@
        [Description("出库任务异常")]
        OutException = 199,
    }
}
    public enum TaskRobotStatusEnum
    {
        /// <summary>
        /// æœºæ¢°æ‰‹æ–°å»ºä»»åŠ¡
        /// </summary>
        [Description("机械手新建任务")]
        RobotNew = 300,
        /// <summary>
        /// æœºæ¢°æ‰‹æ‰§è¡Œä¸­
        /// </summary>
        [Description("机械手执行中")]
        RobotExecuting = 310,
        /// <summary>
        /// æœºæ¢°æ‰‹å®Œæˆ
        /// </summary>
        [Description("机械手完成")]
        RobotFinish = 390,
        /// <summary>
        /// æœºæ¢°æ‰‹ä»»åŠ¡æŒ‚èµ·
        /// </summary>
        [Description("机械手任务挂起")]
        RobotPending = 397,
        /// <summary>
        /// æœºæ¢°æ‰‹ä»»åŠ¡å–æ¶ˆ
        /// </summary>
        [Description("机械手任务取消")]
        RobotCancel = 398,
        /// <summary>
        /// æœºæ¢°æ‰‹ä»»åС异叏
        /// </summary>
        [Description("机械手任务异常")]
        RobotException = 399,
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Common/TaskEnum/TaskTypeEnum.cs
@@ -71,6 +71,10 @@
    public enum TaskOtherTypeEnum
    {
        /// <summary>
        /// æœºæ¢°æ‰‹ä»»åŠ¡
        /// </summary>
        [Description("机械手")]
        RobotToManual = 400,
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Core/Enums/RouterInOutType.cs
@@ -13,12 +13,12 @@
        /// å…¥åº“路由
        /// </summary>
        [Description("入库路由")]
        In = 1,
        In = 200,
        /// <summary>
        /// å‡ºåº“路由
        /// </summary>
        [Description("出库路由")]
        Out = 2,
        Out = 100,
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_ITaskInfoService/ITaskService.cs
@@ -49,6 +49,11 @@
        public List<int> TaskOutboundTypes {  get; }
        /// <summary>
        /// æ‰€æœ‰å‡ºåº“任务类型
        /// </summary>
        public List<int> TaskRobotTypes { get; }
        /// <summary>
        /// æŽ¥æ”¶WMS任务信息
        /// </summary>
        /// <param name="taskDTOs">WMS任务对象集合</param>
@@ -103,6 +108,15 @@
        /// <returns>返回任务实体对象,可能为null</returns>
        Dt_Task QueryStackerCraneTask(string deviceNo, string currentAddress = "");
        /// <summary>
        /// æ ¹æ®è®¾å¤‡ç¼–号、当前地址按照优先级以及创建时间排序查询任务池新增的任务
        /// </summary>
        /// <param name="deviceNo">设备编号</param>
        /// <param name="currentAddress">当前地址</param>
        /// <returns>返回任务实体对象,可能为null</returns>
        Dt_Task QueryRobotCraneTask(string deviceNo,string currentAddress = "");
        /// <summary>
        /// æ ¹æ®è®¾å¤‡ç¼–号、当前地址按照优先级以及创建时间排序查询任务池入库类型的新增的任务
        /// </summary>
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/QuartzNet/QuartzNetExtension.cs
@@ -1,14 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using WIDESEAWCS_Core.Helper;
using System.Reflection;
using WIDESEAWCS_Core;
using WIDESEAWCS_Core.Helper;
using WIDESEAWCS_QuartzJob.DTO;
using Microsoft.Extensions.Logging;
using WIDESEAWCS_QuartzJob.QuartzExtensions;
using WIDESEAWCS_QuartzJob.Service;
namespace WIDESEAWCS_QuartzJob.QuartzNet
@@ -54,62 +47,67 @@
                    {
                        try
                        {
                            #region è¿žæŽ¥PLC
                            // åŠ è½½ç¨‹åºé›†
                            Assembly assembly = Assembly.Load($"WIDESEAWCS_Communicator");
                            // èŽ·å–ç±»åž‹
                            Type? type = assembly.GetType($"WIDESEAWCS_Communicator.{x.DevicePlcType}");
                            // åˆ›å»ºå®žä¾‹
                            object? obj = Activator.CreateInstance(type, new object[] { x.DeviceIp, x.DevicePort, x.DeviceName });
                            // è°ƒç”¨è¿žæŽ¥æ–¹æ³•
                            bool? connectResult = (bool)type.InvokeMember("Connect", BindingFlags.Default | BindingFlags.InvokeMethod, null, obj, new object[] { });
                            // åˆ¤æ–­è¿žæŽ¥ç»“æžœ
                            if (connectResult ?? false) ConsoleHelper.WriteSuccessLine(type.Name + x.DeviceCode + "连接成功"); else ConsoleHelper.WriteErrorLine(type.Name + x.DeviceCode + "连接失败");
                            #endregion
                            #region å®žä¾‹åŒ–设备对象
                            List<DeviceProDTO> devicePros = x.ProtocolList.Select(d => new DeviceProDTO
                            if (!x.DeviceName.Contains("机械手"))
                            {
                                // è®¾å¤‡å­ç¼–码
                                DeviceChildCode = d.DeviceChildCode,
                                // è®¾å¤‡æ•°æ®ç±»åž‹
                                DeviceDataType = d.DeviceProDataType,
                                // è®¾å¤‡ID
                                DeviceId = d.DeviceId,
                                // è®¾å¤‡åè®®ID
                                DeviceProId = d.Id,
                                // è®¾å¤‡åè®®æ•°æ®å—
                                DeviceProDataBlock = d.DeviceProDataBlock,
                                // è®¾å¤‡åè®®æ•°æ®é•¿åº¦
                                DeviceProDataLength = d.DeviceProDataLength,
                                // è®¾å¤‡åè®®åç§»é‡
                                DeviceProOffset = d.DeviceProOffset,
                                // è®¾å¤‡åè®®å‚数描述
                                DeviceProParamDes = d.DeviceProParamDes,
                                // è®¾å¤‡åè®®å‚数名称
                                DeviceProParamName = d.DeviceProParamName,
                                // è®¾å¤‡åè®®å‚数类型
                                DeviceProParamType = d.DeviceProParamType,
                                // è®¾å¤‡PLC类型
                                DevicePlcType = x.DevicePlcType
                            }).ToList();
                                #region è¿žæŽ¥PLC
                            // æ ¹æ®è®¾å¤‡ç±»åž‹èŽ·å–è®¾å¤‡åè®®è¯¦æƒ…
                            List<DeviceProtocolDetailDTO> deviceProtocolDetails = _deviceProtocolDetailService.GetDeviceProtocolDetailsByDeviceType(x.DeviceType);
                                // åŠ è½½ç¨‹åºé›†
                                Assembly assembly = Assembly.Load($"WIDESEAWCS_Communicator");
                                // èŽ·å–ç±»åž‹
                                Type? type = assembly.GetType($"WIDESEAWCS_Communicator.{x.DevicePlcType}");
                                // åˆ›å»ºå®žä¾‹
                                object? obj = Activator.CreateInstance(type, new object[] { x.DeviceIp, x.DevicePort, x.DeviceName });
                                // è°ƒç”¨è¿žæŽ¥æ–¹æ³•
                                bool? connectResult = (bool)type.InvokeMember("Connect", BindingFlags.Default | BindingFlags.InvokeMethod, null, obj, new object[] { });
                                // åˆ¤æ–­è¿žæŽ¥ç»“æžœ
                                if (connectResult ?? false) ConsoleHelper.WriteSuccessLine(type.Name + x.DeviceCode + "连接成功"); else ConsoleHelper.WriteErrorLine(type.Name + x.DeviceCode + "连接失败");
                            // åŠ è½½è®¾å¤‡ç¨‹åºé›†
                            Assembly assemblyDevice = Assembly.Load($"WIDESEAWCS_QuartzJob");
                            // èŽ·å–è®¾å¤‡ç±»åž‹å¯¹åº”çš„ç±»åž‹
                            Type typeDevice = assemblyDevice.GetType($"WIDESEAWCS_QuartzJob.{x.DeviceType}");
                            // åˆ›å»ºè®¾å¤‡å®žä¾‹
                            object deviceInstance = Activator.CreateInstance(typeDevice, new object[] { obj, devicePros, deviceProtocolDetails, x.DeviceCode, x.DeviceName });
                            #endregion
                                #endregion è¿žæŽ¥PLC
                            x.Device = (IDevice)deviceInstance;
                                #region å®žä¾‹åŒ–设备对象
                            Storage.Devices.Add((IDevice)deviceInstance);
                                List<DeviceProDTO> devicePros = x.ProtocolList.Select(d => new DeviceProDTO
                                {
                                    // è®¾å¤‡å­ç¼–码
                                    DeviceChildCode = d.DeviceChildCode,
                                    // è®¾å¤‡æ•°æ®ç±»åž‹
                                    DeviceDataType = d.DeviceProDataType,
                                    // è®¾å¤‡ID
                                    DeviceId = d.DeviceId,
                                    // è®¾å¤‡åè®®ID
                                    DeviceProId = d.Id,
                                    // è®¾å¤‡åè®®æ•°æ®å—
                                    DeviceProDataBlock = d.DeviceProDataBlock,
                                    // è®¾å¤‡åè®®æ•°æ®é•¿åº¦
                                    DeviceProDataLength = d.DeviceProDataLength,
                                    // è®¾å¤‡åè®®åç§»é‡
                                    DeviceProOffset = d.DeviceProOffset,
                                    // è®¾å¤‡åè®®å‚数描述
                                    DeviceProParamDes = d.DeviceProParamDes,
                                    // è®¾å¤‡åè®®å‚数名称
                                    DeviceProParamName = d.DeviceProParamName,
                                    // è®¾å¤‡åè®®å‚数类型
                                    DeviceProParamType = d.DeviceProParamType,
                                    // è®¾å¤‡PLC类型
                                    DevicePlcType = x.DevicePlcType
                                }).ToList();
                                // æ ¹æ®è®¾å¤‡ç±»åž‹èŽ·å–è®¾å¤‡åè®®è¯¦æƒ…
                                List<DeviceProtocolDetailDTO> deviceProtocolDetails = _deviceProtocolDetailService.GetDeviceProtocolDetailsByDeviceType(x.DeviceType);
                                // åŠ è½½è®¾å¤‡ç¨‹åºé›†
                                Assembly assemblyDevice = Assembly.Load($"WIDESEAWCS_QuartzJob");
                                // èŽ·å–è®¾å¤‡ç±»åž‹å¯¹åº”çš„ç±»åž‹
                                Type typeDevice = assemblyDevice.GetType($"WIDESEAWCS_QuartzJob.{x.DeviceType}");
                                // åˆ›å»ºè®¾å¤‡å®žä¾‹
                                object deviceInstance = Activator.CreateInstance(typeDevice, new object[] { obj, devicePros, deviceProtocolDetails, x.DeviceCode, x.DeviceName });
                                #endregion å®žä¾‹åŒ–设备对象
                                x.Device = (IDevice)deviceInstance;
                                Storage.Devices.Add((IDevice)deviceInstance);
                            }
                        }
                        catch (Exception ex)
                        {
@@ -123,8 +121,17 @@
                });
                for (int i = 0; i < dispatches.Count; i++)
                {
                    DeviceInfoDTO? deviceProInfo = deviceInfos.FirstOrDefault(x => x.Id == dispatches[i].Id);
                    dispatches[i].JobParams = deviceProInfo?.Device;
                    var targetDevice = deviceInfos.FirstOrDefault(x => x.Id == dispatches[i].Id);
                    if (targetDevice is null) continue;
                    // ä½¿ç”¨æ¨¡å¼åŒ¹é…
                    dispatches[i].JobParams = targetDevice switch
                    {
                        { DeviceName: var name } when name.Contains("机械手")
                            => new RobotCraneDevice { DeviceCode = targetDevice.DeviceCode, DeviceName = targetDevice.DeviceName, IPAddress = targetDevice.DeviceIp + ":" + targetDevice.DevicePort },
                        _ => targetDevice.Device
                    };
                    WebResponseContent responseContent = await _schedulerCenter.AddScheduleJobAsync(dispatches[i]);
                    if (responseContent.Status) ConsoleHelper.WriteSuccessLine(dispatches[i].Name + "调度服务添加成功"); else ConsoleHelper.WriteErrorLine(dispatches[i].Name + "调度服务添加失败");
                }
@@ -137,4 +144,4 @@
            }
        }
    }
}
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/Robot/RobotCraneDevice.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WIDESEAWCS_QuartzJob
{
    public class RobotCraneDevice
    {
        public string Device { get; set; }
        public string DeviceCode { get; set; }
        public string DeviceName { get; set; }
        public string IPAddress { get; set; }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/Program.cs
@@ -25,6 +25,7 @@
using WIDESEAWCS_QuartzJob.Seed;
using WIDESEAWCS_Server.Filter;
using WIDESEAWCS_Server.HostedService;
using WIDESEAWCS_Tasks.SocketServer;
using WIDESEAWCS_WCSServer.Filter;
var builder = WebApplication.CreateBuilder(args);
@@ -73,6 +74,8 @@
builder.Services.AddHttpContextSetup();
builder.Services.AddHostedService<QuartzJobHostedService>();//任务调度 å¯åŠ¨æœåŠ¡
builder.Services.AddSingleton<TcpSocketServer>();
builder.Services.AddHostedService<SocketServerHostedService>();
builder.Services.AddMvc(options =>
{
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/appsettings.json
@@ -44,5 +44,16 @@
  "PrintSql": true, //打印SQL语句
  "LogAOPEnable": true, //是否记录AOP日志
  "WebSocketEnable": true, //是否开启WebSocket服务
  "WebSocketPort": 9296 //WebSocket服务端口
  "WebSocketPort": 9296, //WebSocket服务端口
  "SocketServer": {
    "Enabled": true,
    "Port": 2000,
    "IpAddress": "0.0.0.0",
    "Backlog": 100,
    "EncodingName": "utf-8",
    "AutoDetectEncoding": true,
    "IdleTimeoutSeconds": 300,
    "EnableHeartbeat": true,
    "LogFilePath": "socketserver.log"
  }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/TaskService.cs
@@ -1,4 +1,5 @@
#region << ç‰ˆ æœ¬ æ³¨ é‡Š >>
/*----------------------------------------------------------------
 * å‘½åç©ºé—´ï¼šWIDESEAWCS_TaskInfoService
 * åˆ›å»ºè€…:胡童庆
@@ -11,8 +12,9 @@
 * ä¿®æ”¹æ—¶é—´ï¼š
 * ç‰ˆæœ¬ï¼šV1.0.1
 * ä¿®æ”¹è¯´æ˜Žï¼š
 *
 *
 *----------------------------------------------------------------*/
#endregion << ç‰ˆ æœ¬ æ³¨ é‡Š >>
using AutoMapper;
@@ -44,11 +46,14 @@
                {nameof(Dt_Task.CreateDate),OrderByType.Asc},
            };
        public Dictionary<string, OrderByType> TaskOrderBy { get { return _taskOrderBy; } set { _taskOrderBy = value; } }
        public Dictionary<string, OrderByType> TaskOrderBy
        { get { return _taskOrderBy; } set { _taskOrderBy = value; } }
        public List<int> TaskInboundTypes => typeof(TaskInboundTypeEnum).GetEnumIndexList();
        public List<int> TaskOutboundTypes => typeof(TaskOutboundTypeEnum).GetEnumIndexList();
        public List<int> TaskRobotTypes => typeof(TaskOtherTypeEnum).GetEnumIndexList();
        public TaskService(ITaskRepository BaseDal, IRouterService routerService, ITaskExecuteDetailService taskExecuteDetailService, ITaskExecuteDetailRepository taskExecuteDetailRepository, IMapper mapper) : base(BaseDal)
        {
@@ -359,6 +364,17 @@
                        task.TargetAddress = task.NextAddress;
                    }
                }
                else if (task.TaskType.GetTaskTypeGroup() == TaskTypeGroup.OtherGroup)
                {
                    if (task.TaskState >= (int)TaskRobotStatusEnum.RobotNew)
                    {
                        return content = WebResponseContent.Instance.Error($"该任务状态不可跳转到下一步,任务号:【{task.TaskNum}】,任务状态:【{task.TaskState}】");
                    }
                    int nextStatus = task.TaskState.GetNextNotCompletedStatus<TaskRobotStatusEnum>();
                    task.TaskState = nextStatus;
                }
                else
                {
                    throw new Exception($"任务类型错误,未找到该任务类型,任务号:【{task.TaskNum}】,任务类型:【{task.TaskType}】");
@@ -452,7 +468,7 @@
                }
                else if (task.TaskType.GetTaskTypeGroup() == TaskTypeGroup.InboundGroup && task.TaskState == (int)TaskInStatusEnum.SC_InExecuting)
                {
                    //todo
                    //todo
                    int nextStatus = task.TaskState.GetNextNotCompletedStatus<TaskInStatusEnum>();
                    task.TaskState = nextStatus;
                    task.ModifyDate = DateTime.Now;
@@ -481,7 +497,6 @@
                }
                else if (task.TaskType.GetTaskTypeGroup() == TaskTypeGroup.OtherGroup)
                {
                }
                else
                {
@@ -598,5 +613,19 @@
            BaseDal.DeleteAndMoveIntoHty(tasks, OperateTypeEnum.人工删除);
            return WebResponseContent.Instance.OK($"成功删除{tasks.Count}条数据");
        }
        /// <summary>
        /// æ ¹æ®è®¾å¤‡ç¼–号、当前地址按照优先级以及创建时间排序查询任务池新增的任务
        /// </summary>
        /// <param name="deviceNo">设备编号</param>
        /// <param name="currentAddress">当前地址</param>
        /// <returns>返回任务实体对象,可能为null</returns>
        public Dt_Task QueryRobotCraneTask(string deviceNo, string currentAddress = "")
        {
            if (string.IsNullOrEmpty(currentAddress))
                return BaseDal.QueryFirst(x => x.Roadway == deviceNo && (TaskRobotTypes.Contains(x.TaskType) && x.TaskState <= (int)TaskRobotStatusEnum.RobotExecuting), TaskOrderBy);
            else
                return BaseDal.QueryFirst(x => x.Roadway == deviceNo && TaskRobotTypes.Contains(x.TaskType) && x.CurrentAddress == currentAddress && x.TaskState <= (int)TaskRobotStatusEnum.RobotExecuting, TaskOrderBy);
        }
    }
}
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineJob/CommonConveyorLineJob.cs
@@ -63,11 +63,6 @@
                    List<Task> tasks = new List<Task>();
                    foreach (string childDeviceCode in childDeviceCodes)
                    {
                        int num = new Random().Next(1, 100);
                        if (num < 30)
                        {
                            throw new CommunicationException("错误测试", CommunicationErrorType.Unknown);
                        }
                        ConveyorLineTaskCommand command = conveyorLine.ReadCustomer<ConveyorLineTaskCommand>(childDeviceCode);
                        if (command != null)
                        {
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineJob/ConveyorLineTaskCommand.cs
@@ -28,7 +28,7 @@
    {
        public ushort InteractiveSignal { get; set; }
        [DataLength(25)]
        [DataLength(254)]
        public string Barcode { get; set; }
        public int TargetAddress { get; set; }
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/FormationStackerCraneJob/FormationStackerCraneTaskCommand.cs
@@ -89,7 +89,7 @@
        /// <summary>
        /// æ‰˜ç›˜å·
        /// </summary>
        [DataLength(26)]
        [DataLength(25)]
        public string Barcode { get; set; }
        #endregion <Public Menber>
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotJob.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,297 @@
using Quartz;
using System.Collections.Concurrent;
using System.Net.Sockets;
using WIDESEAWCS_Core.Helper;
using WIDESEAWCS_ITaskInfoRepository;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
using WIDESEAWCS_QuartzJob;
using WIDESEAWCS_QuartzJob.Service;
using WIDESEAWCS_Tasks.SocketServer;
namespace WIDESEAWCS_Tasks
{
    [DisallowConcurrentExecution]
    public class RobotJob : IJob
    {
        private readonly TcpSocketServer _TcpSocket;
        private static readonly ConcurrentDictionary<string, RobotSocketState> _socketStates = new();
        private static int _eventSubscribedFlag;
        private readonly ITaskService _taskService;
        private readonly ITaskExecuteDetailService _taskExecuteDetailService;
        private readonly ITaskRepository _taskRepository;
        private readonly IRouterService _routerService;
        public RobotJob(TcpSocketServer TcpSocket, ITaskService taskService, ITaskExecuteDetailService taskExecuteDetailService, ITaskRepository taskRepository, IRouterService routerService)
        {
            _TcpSocket = TcpSocket;
            _taskService = taskService;
            _taskExecuteDetailService = taskExecuteDetailService;
            _taskRepository = taskRepository;
            _routerService = routerService;
        }
        public Task Execute(IJobExecutionContext context)
        {
            bool flag = context.JobDetail.JobDataMap.TryGetValue("JobParams", out object? value);
            RobotCraneDevice robotCrane = (RobotCraneDevice?)value ?? new RobotCraneDevice();
            if (!flag || robotCrane.IsNullOrEmpty())
            {
                return Task.CompletedTask;
            }
            string ipAddress = robotCrane.IPAddress;
            // èŽ·å–æˆ–åˆ›å»ºçŠ¶æ€
            RobotSocketState state = _socketStates.GetOrAdd(ipAddress, _ => new RobotSocketState
            {
                IPAddress = ipAddress,
                RobotCrane = robotCrane
            });
            // æ›´æ–°è®¾å¤‡ä¿¡æ¯
            state.RobotCrane = robotCrane;
            // æ£€æŸ¥æ˜¯å¦æœ‰è¯¥å®¢æˆ·ç«¯è¿žæŽ¥
            var clientIds = _TcpSocket.GetClientIds();
            if (!clientIds.Contains(ipAddress))
            {
                return Task.CompletedTask;
            }
            // è®¢é˜…一次 message äº‹ä»¶ï¼ˆå…¨å±€ä¸€æ¬¡ï¼‰
            if (Interlocked.CompareExchange(ref _eventSubscribedFlag, 1, 0) == 0)
            {
                _TcpSocket.MessageReceived += _TcpSocket_MessageReceived;
                _TcpSocket.RobotReceived += _TcpSocket_RobotReceived;
            }
            if (!state.IsEventSubscribed)
            {
                _TcpSocket._clients.TryGetValue(ipAddress, out TcpClient client);
                Task clientTask = _TcpSocket.HandleClientAsync(client, robotCrane.IPAddress, _TcpSocket._cts.Token, state);
                state.IsEventSubscribed = true;
            }
            // èŽ·å–ä»»åŠ¡å¹¶ç¼“å­˜åˆ°çŠ¶æ€ä¸­
            Dt_Task? task = GetTask(robotCrane);
            if (task != null)
            {
                state.CurrentTask = task;
            }
            return Task.CompletedTask;
        }
        /// <summary>
        ///  äº‹ä»¶ï¼šå®¢æˆ·ç«¯æ–­å¼€è¿žæŽ¥æ—¶è§¦å‘
        /// </summary>
        /// <param name="clientId"></param>
        /// <returns></returns>
        private Task<string?> _TcpSocket_RobotReceived(string clientId)
        {
            _socketStates.TryRemove(clientId, out _);
            return Task.FromResult<string?>(null);
        }
        /// <summary>
        /// äº‹ä»¶ï¼šæ”¶åˆ°æ¶ˆæ¯æ—¶è§¦å‘
        /// </summary>
        /// <param name="message"></param>
        /// <param name="isJson"></param>
        /// <param name="client"></param>
        /// <param name="state"></param>
        /// <returns></returns>
        private async Task<string?> _TcpSocket_MessageReceived(string message, bool isJson, TcpClient client, RobotSocketState state)
        {
            string messageLower = message.ToLowerInvariant();
            if (IsSimpleCommand(messageLower, state))
            {
                return null;
            }
            if (IsPrefixCommand(messageLower))
            {
                try
                {
                    var parts = message.Split(',');
                    if (parts.Length >= 1)
                    {
                        var cmd = parts[0].ToLowerInvariant();
                        int[] positions = new int[4];
                        for (int i = 1; i <= 4 && i < parts.Length; i++)
                        {
                            int.TryParse(parts[i], out positions[i - 1]);
                        }
                        if (cmd.StartsWith("pickfinished"))
                        {
                            state.LastPickPositions = positions;
                            state.CurrentAction = "PickFinished";
                        }
                        else if (cmd.StartsWith("putfinished"))
                        {
                            state.LastPutPositions = positions;
                            state.CurrentAction = "PutFinished";
                        }
                    }
                }
                catch { }
                return null;
            }
            return null;
        }
        /// <summary>
        /// æœºæ¢°æ‰‹ç®€å•命令处理
        /// </summary>
        /// <param name="message"></param>
        /// <param name="state"></param>
        /// <returns></returns>
        private bool IsSimpleCommand(string message, RobotSocketState state)
        {
            switch (message)
            {
                case "homing":
                    state.CurrentAction = "Homing";
                    return true;
                case "homed":
                    state.CurrentAction = "Homed";
                    return true;
                case "picking":
                    state.CurrentAction = "Picking";
                    return true;
                case "puting":
                    state.CurrentAction = "Putting";
                    return true;
                case "allpickfinished":
                    state.CurrentAction = "AllPickFinished";
                    return true;
                case "allputfinished":
                    state.CurrentAction = "AllPutFinished";
                    return true;
                case "running":
                    state.OperStatus = "Running";
                    return true;
                case "pausing":
                    state.OperStatus = "Pausing";
                    return true;
                case "warming":
                    state.OperStatus = "Warming";
                    return true;
                case "emstoping":
                    state.OperStatus = "Emstoping";
                    return true;
                case "runmode,1":
                    state.RobotRunMode = 1;
                    return true;
                case "runmodemode,2":
                    state.RobotRunMode = 2;
                    return true;
                case "controlmode,1":
                    state.RobotControlMode = 1;
                    return true;
                case "controlmode,2":
                    state.RobotControlMode = 2;
                    return true;
                case "armobject,1":
                    state.RobotArmObject = 1;
                    return true;
                case "armobject,0":
                    state.RobotArmObject = 0;
                    return true;
                default:
                    return false;
            }
        }
        /// <summary>
        /// æœºæ¢°æ‰‹å‰ç¼€å‘½ä»¤å¤„理
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        private static bool IsPrefixCommand(string message)
        {
            return message.StartsWith("pickfinished") || message.StartsWith("putfinished");
        }
        private Dt_Task? GetTask(RobotCraneDevice robotCrane)
        {
            return _taskService.QueryRobotCraneTask(robotCrane.DeviceCode);
        }
    }
    public class RobotSocketState
    {
        public string IPAddress { get; set; } = string.Empty;
        /// <summary>
        /// æ˜¯å¦å·²è®¢é˜…消息事件
        /// </summary>
        public bool IsEventSubscribed { get; set; }
        /// <summary>
        /// æœºæ¢°æ‰‹è¿è¡Œæ¨¡å¼
        /// </summary>
        public int? RobotRunMode { get; set; }
        /// <summary>
        /// æœºæ¢°æ‰‹æŽ§åˆ¶æ¨¡å¼
        /// </summary>
        public int? RobotControlMode { get; set; }
        /// <summary>
        /// æœºæ¢°æ‰‹æŠ“取对象
        /// </summary>
        public int? RobotArmObject { get; set; }
        /// <summary>
        /// æœºæ¢°æ‰‹è®¾å¤‡ä¿¡æ¯
        /// </summary>
        public RobotCraneDevice? RobotCrane { get; set; }
        /// <summary>
        /// å½“前动作
        /// </summary>
        public string? CurrentAction { get; set; }
        /// <summary>
        /// å½“前状态
        /// </summary>
        public string? OperStatus { get; set; }
        /// <summary>
        /// å–货完成位置
        /// </summary>
        public int[]? LastPickPositions { get; set; }
        /// <summary>
        /// æ”¾è´§å®Œæˆä½ç½®
        /// </summary>
        public int[]? LastPutPositions { get; set; }
        /// <summary>
        /// å½“前抓取任务
        /// </summary>
        public Dt_Task? CurrentTask { get; set; }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/SocketServerHostedService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
namespace WIDESEAWCS_Tasks.SocketServer
{
    /// <summary>
    /// Socket服务端托管服务
    /// </summary>
    public class SocketServerHostedService : IHostedService
    {
        private readonly TcpSocketServer _server;
        private readonly SocketServerOptions _options;
        public SocketServerHostedService(TcpSocketServer server, IOptions<SocketServerOptions> options)
        {
            _server = server;
            _options = options.Value;
        }
        public Task StartAsync(CancellationToken cancellationToken)
        {
            if (!_options.Enabled)
            {
                return Task.CompletedTask;
            }
            return _server.StartAsync(cancellationToken);
        }
        public Task StopAsync(CancellationToken cancellationToken)
        {
            return _server.StopAsync(cancellationToken);
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/SocketServerOptions.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,55 @@
using WIDESEAWCS_Core.Core;
namespace WIDESEAWCS_Tasks.SocketServer
{
    /// <summary>
    /// Socket服务端配置
    /// </summary>
    public class SocketServerOptions : IConfigurableOptions
    {
        /// <summary>
        /// æ˜¯å¦å¯ç”¨
        /// </summary>
        public bool Enabled { get; set; } = true;
        /// <summary>
        /// ç›‘听端口
        /// </summary>
        public int Port { get; set; } = 2000;
        /// <summary>
        /// ç›‘听地址
        /// </summary>
        public string IpAddress { get; set; } = "0.0.0.0";
        /// <summary>
        /// è¿žæŽ¥é˜Ÿåˆ—长度
        /// </summary>
        public int Backlog { get; set; } = 100;
        /// <summary>
        /// æ–‡æœ¬ç¼–码名称(例如: utf-8, gbk)
        /// </summary>
        public string EncodingName { get; set; } = "utf-8";
        /// <summary>
        /// æ˜¯å¦è‡ªåŠ¨æ£€æµ‹ç¼–ç ï¼ˆå°è¯• UTF-8 åŽå›žé€€åˆ° GBK)
        /// </summary>
        public bool AutoDetectEncoding { get; set; } = true;
        /// <summary>
        /// å®¢æˆ·ç«¯ç©ºé—²è¶…时时间(秒),超过则断开
        /// </summary>
        public int IdleTimeoutSeconds { get; set; } = 300;
        /// <summary>
        /// æ˜¯å¦å¯ç”¨å¿ƒè·³æ£€æŸ¥
        /// </summary>
        public bool EnableHeartbeat { get; set; } = true;
        /// <summary>
        /// æ—¥å¿—文件路径(相对于程序运行目录)
        /// </summary>
        public string LogFilePath { get; set; } = "socketserver.log";
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Clients.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,94 @@
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace WIDESEAWCS_Tasks.SocketServer
{
    public partial class TcpSocketServer
    {
        public IReadOnlyList<string> GetClientIds()
        {
            lock (_syncRoot)
            {
                return _clients.Keys.ToList();
            }
        }
        public string? GetClientIdByDevice(string deviceId)
        {
            lock (_syncRoot)
            {
                return _deviceBindings.TryGetValue(deviceId, out var cid) ? cid : null;
            }
        }
        public Task<bool> SendToDeviceAsync(string deviceId, string message)
        {
            var clientId = GetClientIdByDevice(deviceId);
            if (clientId == null) return Task.FromResult(false);
            return SendToClientAsync(clientId, message);
        }
        public async Task<bool> SendToClientAsync(string clientId, string message)
        {
            TcpClient? client;
            SemaphoreSlim? sem = null;
            Encoding? enc = null;
            lock (_syncRoot)
            {
                _clients.TryGetValue(clientId, out client);
                _clientLocks.TryGetValue(clientId, out sem);
                _clientEncodings.TryGetValue(clientId, out enc);
            }
            if (client == null || !client.Connected)
            {
                return false;
            }
            enc ??= _textEncoding;
            if (sem != null) await sem.WaitAsync();
            try
            {
                var ns = client.GetStream();
                var data = enc.GetBytes((message ?? string.Empty) + "\n");
                await ns.WriteAsync(data, 0, data.Length);
            }
            finally
            {
                if (sem != null) sem.Release();
            }
            return true;
        }
        public async Task BroadcastAsync(string message)
        {
            List<TcpClient> clients;
            lock (_syncRoot)
            {
                clients = _clients.Values.ToList();
            }
            await Task.WhenAll(clients.Select(c => Task.Run(async () =>
            {
                try { await SendAsync(c, message); } catch { }
            })));
        }
        public static async Task SendAsync(TcpClient client, string message)
        {
            if (client == null || !client.Connected)
            {
                return;
            }
            NetworkStream stream = client.GetStream();
            var data = Encoding.UTF8.GetBytes((message ?? string.Empty) + "\n");
            await stream.WriteAsync(data, 0, data.Length);
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Dispose.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
using System;
using System.IO;
namespace WIDESEAWCS_Tasks.SocketServer
{
    public partial class TcpSocketServer
    {
        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");
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Messaging.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,175 @@
using HslCommunication.Core.IMessage;
using System;
using System.Net.Sockets;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using WIDESEAWCS_QuartzJob;
namespace WIDESEAWCS_Tasks.SocketServer
{
    public partial class TcpSocketServer
    {
        public async Task HandleClientAsync(TcpClient client, string clientId, CancellationToken cancellationToken, RobotSocketState robotCrane)
        {
            using (client)
            using (NetworkStream networkStream = client.GetStream())
            using (StreamReader reader = new(networkStream, _textEncoding, false, 1024, true))
            using (StreamWriter writer = new(networkStream, _textEncoding, 1024, true) { AutoFlush = true })
            {
                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;
                            message = await reader.ReadLineAsync().WaitAsync(ct);
                        }
                        catch (OperationCanceledException)
                        {
                            break;
                        }
                        if (message == null)
                        {
                            break;
                        }
                        UpdateClientStatus(clientId, message);
                        string messageLower = message.ToLowerInvariant();
                        if (TryHandleRegister(messageLower, message, clientId, networkStream, cancellationToken))
                        {
                            continue;
                        }
                        if (MessageReceived != null)
                        {
                            try { _ = MessageReceived.Invoke(message, false, client, robotCrane); } catch { }
                        }
                    }
                }
                finally
                {
                    try { localCts?.Cancel(); localCts?.Dispose(); } catch { }
                    RemoveClient(clientId);
                    try { _ = RobotReceived.Invoke(clientId); } catch { }
                }
            }
        }
        private bool TryHandleRegister(string messageLower, string message, string clientId, NetworkStream networkStream, CancellationToken cancellationToken)
        {
            if (!messageLower.StartsWith("register,"))
            {
                return false;
            }
            string deviceId = message.Substring("register,".Length).Trim();
            if (!string.IsNullOrEmpty(deviceId))
            {
                lock (_syncRoot)
                {
                    _deviceBindings[deviceId] = clientId;
                }
                _ = WriteToClientAsync(clientId, networkStream, $"Registered,{deviceId}", cancellationToken);
            }
            return true;
        }
        private void UpdateClientStatus(string clientId, string message)
        {
            lock (_syncRoot)
            {
                _clientLastActive[clientId] = DateTime.Now;
                if (!_clientEncodings.ContainsKey(clientId))
                {
                    if (_options.AutoDetectEncoding && _autoDetectedGb2312 != null)
                    {
                        bool isUtf8 = TryParseJsonSilent(message) || IsLikelyUtf8(_textEncoding.GetBytes(message));
                        _clientEncodings[clientId] = isUtf8 ? _textEncoding : _autoDetectedGb2312;
                    }
                    else
                    {
                        _clientEncodings[clientId] = _textEncoding;
                    }
                }
            }
        }
        private async Task WriteToClientAsync(string clientId, NetworkStream networkStream, string message, CancellationToken cancellationToken)
        {
            SemaphoreSlim? sem = null;
            Encoding? enc = null;
            lock (_syncRoot)
            {
                _clientLocks.TryGetValue(clientId, out sem);
                _clientEncodings.TryGetValue(clientId, out enc);
            }
            enc ??= _textEncoding;
            if (sem != null) await sem.WaitAsync(cancellationToken);
            try
            {
                var data = enc.GetBytes(message + "\n");
                await networkStream.WriteAsync(data, 0, data.Length, cancellationToken);
            }
            finally
            {
                if (sem != null) sem.Release();
            }
        }
        private static bool TryParseJsonSilent(string message)
        {
            if (string.IsNullOrWhiteSpace(message)) return false;
            char c = message.TrimStart()[0];
            if (c != '{' && c != '[') return false;
            try { JsonDocument.Parse(message); return true; } catch { return false; }
        }
        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 (i + 1 >= data.Length) return false;
                    if ((data[i + 1] & 0xC0) != 0x80) return false;
                    i += 2; continue;
                }
                if (b >= 0xE0 && b <= 0xEF)
                {
                    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 (i + 3 >= data.Length) return false;
                    if ((data[i + 1] & 0xC0) != 0x80 || (data[i + 2] & 0xC0) != 0x80 || (data[i + 3] & 0xC0) != 0x80) return false;
                    i += 4; continue;
                }
                return false;
            }
            return true;
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Server.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,164 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
namespace WIDESEAWCS_Tasks.SocketServer
{
    public partial class TcpSocketServer
    {
        public Task StartAsync(CancellationToken cancellationToken)
        {
            if (IsRunning || !_options.Enabled)
            {
                return Task.CompletedTask;
            }
            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;
        }
        public async Task StopAsync(CancellationToken cancellationToken)
        {
            if (!IsRunning)
            {
                return;
            }
            _cts?.Cancel();
            _listener?.Stop();
            Task[] tasks;
            lock (_syncRoot)
            {
                tasks = _clientTasks.ToArray();
            }
            if (tasks.Length > 0)
            {
                await Task.WhenAll(tasks);
            }
            IsRunning = false;
        }
        private async Task AcceptLoopAsync(CancellationToken cancellationToken)
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                TcpClient? client = null;
                try
                {
                    client = await _listener!.AcceptTcpClientAsync().WaitAsync(cancellationToken);
                }
                catch (OperationCanceledException)
                {
                    break;
                }
                catch (ObjectDisposedException)
                {
                    break;
                }
                catch
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        break;
                    }
                }
                if (client == null)
                {
                    continue;
                }
                string clientId = GetClientId(client);
                lock (_syncRoot)
                {
                    _clients[clientId] = client;
                    _clientLocks[clientId] = new SemaphoreSlim(1, 1);
                }
            }
        }
        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)
                {
                    _deviceBindings.Remove(deviceId);
                }
            }
        }
        private async Task MonitorClientsAsync(CancellationToken cancellationToken)
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                try
                {
                    List<string> toRemove = new();
                    lock (_syncRoot)
                    {
                        foreach (var kv in _clientLastActive)
                        {
                            if (_options.IdleTimeoutSeconds > 0 && DateTime.Now - kv.Value > TimeSpan.FromSeconds(_options.IdleTimeoutSeconds))
                            {
                                toRemove.Add(kv.Key);
                            }
                        }
                    }
                    foreach (var cid in toRemove)
                    {
                        RemoveClient(cid);
                        Log($"[{DateTime.Now}] TcpSocketServer disconnect idle client {cid}");
                    }
                }
                catch
                {
                }
                try { await Task.Delay(1000, cancellationToken); } catch { }
            }
        }
        public static string GetClientId(TcpClient client)
        {
            return client.Client.RemoteEndPoint?.ToString() ?? Guid.NewGuid().ToString();
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,63 @@
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using WIDESEAWCS_QuartzJob;
namespace WIDESEAWCS_Tasks.SocketServer
{
    /// <summary>
    /// TCP Socket服务端(基于行协议,按换行符分割消息)
    /// </summary>
    public partial class TcpSocketServer : IDisposable
    {
        private readonly SocketServerOptions _options;
        public readonly object _syncRoot = new();
        private TcpListener? _listener;
        public CancellationTokenSource? _cts;
        public readonly List<Task> _clientTasks = new();
        public readonly Dictionary<string, TcpClient> _clients = new();
        public readonly Dictionary<string, string> _deviceBindings = new();
        public readonly Dictionary<string, SemaphoreSlim> _clientLocks = new();
        public readonly Dictionary<string, Encoding> _clientEncodings = new();
        public readonly Dictionary<string, DateTime> _clientLastActive = new();
        public readonly Encoding _textEncoding;
        public readonly Encoding? _autoDetectedGb2312;
        private readonly string _logFile;
        private Task? _monitorTask;
        public TcpSocketServer(IOptions<SocketServerOptions> options)
        {
            _options = options.Value;
            if (_options.AutoDetectEncoding)
            {
                _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;
        private void Log(string message)
        {
            Console.WriteLine(message);
            try { File.AppendAllText(_logFile, message + Environment.NewLine); } catch { }
        }
    }
}
Code/WMS/WIDESEA_WMSServer/.vs/WIDESEA_WMSServer/CopilotIndices/18.0.988.22099/CodeChunks.db
Binary files differ
Code/WMS/WIDESEA_WMSServer/.vs/WIDESEA_WMSServer/CopilotIndices/18.0.988.22099/SemanticSymbols.db
Binary files differ
Code/WMS/WIDESEA_WMSServer/.vs/WIDESEA_WMSServer/v18/DocumentLayout.json
@@ -36,7 +36,7 @@
              "RelativeDocumentMoniker": "WIDESEA_WMSServer\\appsettings.json",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_WMSServer\\appsettings.json",
              "RelativeToolTip": "WIDESEA_WMSServer\\appsettings.json",
              "ViewState": "AgIAAAAAAAAAAAAAAAAAABwAAAAZAAAAAAAAAA==",
              "ViewState": "AgIAAAAAAAAAAAAAAAAAAB4AAAAZAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001642|",
              "WhenOpened": "2026-01-26T08:48:58.694Z",
              "EditorCaption": ""
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/appsettings.json
@@ -28,7 +28,7 @@
    "PrintSql": false, //打印SQL语句
    "ApiName": "WIDESEA",
    "ExpMinutes": 120,
    "DBSeedEnable": true,
    "DBSeedEnable": false,
    "PDAVersion": "4",
    "WebSocketPort": 9296
}
ÏîÄ¿×ÊÁÏ/É豸ЭÒé/·ÖÈݹñ½Ó¿ÚЭÒé/»¯³É·ÖÈݹñ½Ó¿ÚЭÒé1.1.xls
Binary files differ
ÏîÄ¿×ÊÁÏ/É豸ЭÒé/»¯³É¿â¶Ñ¶â»úЭÒé/PlcLink_¶Ñ¶â»úÏîÄ¿.xlsx
Binary files differ
ÏîÄ¿×ÊÁÏ/É豸ЭÒé/¾ÐÊø»ú¶Ô½ÓЭÒé/¾ÐÊø»ú¶Ô½ÓЭÒé-»ã´¨5U.xlsx
Binary files differ
ÏîÄ¿×ÊÁÏ/É豸ЭÒé/»úеÊÖЭÒé/½»»¥Á÷³Ì±í(1).xlsx
Binary files differ
ÏîÄ¿×ÊÁÏ/É豸ЭÒé/¸ß³£Î¶Ѷâ»úÓëÊäËÍÏß/WCS-ÊäËÍÏß¶Ô½ÓЭÒé˵Ã÷-V260202.docx
Binary files differ
ÏîÄ¿×ÊÁÏ/É豸ЭÒé/¸ß³£Î¶Ѷâ»úÓëÊäËÍÏß/WCSÊäËͶԽӵØÖ·±í.xlsx
Binary files differ
ÏîÄ¿×ÊÁÏ/É豸ЭÒé/¸ß³£Î¶Ѷâ»úÓëÊäËÍÏß/¶Ñ¶â»úÓëÉÏλ»ú½»»¥ÐÅÏ¢.xlsx
Binary files differ
ÏîÄ¿×ÊÁÏ/ÐèÒª¶Ô½ÓµÄÉ豸ÐÅÏ¢/ÉÂúӲ¼þÉ豸¶Ô½Ó.xls
Binary files differ
ÏîÄ¿×ÊÁÏ/¸ß³£Î¶Ѷâ»ú/¶Ñ¶â»úͨÐÅЭÒé.xlsx
Binary files differ
ÏîÄ¿×ÊÁÏ/¸ß³£Î¶Ѷâ»ú/Ä£ÄâPLC³ÌÐò.zip
Binary files differ