添加机器人/套接字任务支持;更新枚举与 .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个文件
| | |
| | | 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 |
| | |
| | | "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": [ |
| | |
| | | "DocumentGroups": [ |
| | | { |
| | | "DockedWidth": 200, |
| | | "SelectedChildIndex": 6, |
| | | "SelectedChildIndex": 11, |
| | | "Children": [ |
| | | { |
| | | "$type": "Bookmark", |
| | |
| | | }, |
| | | { |
| | | "$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": "" |
| | | } |
| | | ] |
| | |
| | | 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; |
| | | } |
| | |
| | | // è·å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 |
| | | { |
| | |
| | | [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, |
| | | } |
| | | } |
| | |
| | | |
| | | public enum TaskOtherTypeEnum |
| | | { |
| | | |
| | | /// <summary> |
| | | /// æºæ¢°æä»»å¡ |
| | | /// </summary> |
| | | [Description("æºæ¢°æ")] |
| | | RobotToManual = 400, |
| | | } |
| | | } |
| | |
| | | /// å
¥åºè·¯ç± |
| | | /// </summary> |
| | | [Description("å
¥åºè·¯ç±")] |
| | | In = 1, |
| | | In = 200, |
| | | |
| | | /// <summary> |
| | | /// åºåºè·¯ç± |
| | | /// </summary> |
| | | [Description("åºåºè·¯ç±")] |
| | | Out = 2, |
| | | Out = 100, |
| | | } |
| | | } |
| | |
| | | public List<int> TaskOutboundTypes { get; } |
| | | |
| | | /// <summary> |
| | | /// ææåºåºä»»å¡ç±»å |
| | | /// </summary> |
| | | public List<int> TaskRobotTypes { get; } |
| | | |
| | | /// <summary> |
| | | /// æ¥æ¶WMSä»»å¡ä¿¡æ¯ |
| | | /// </summary> |
| | | /// <param name="taskDTOs">WMSä»»å¡å¯¹è±¡éå</param> |
| | |
| | | /// <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> |
| | |
| | | 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 |
| | |
| | | { |
| | | 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) |
| | | { |
| | |
| | | }); |
| | | 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 + "è°åº¦æå¡æ·»å 失败"); |
| | | } |
| | |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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; } |
| | | } |
| | | } |
| | |
| | | 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); |
| | |
| | | builder.Services.AddHttpContextSetup(); |
| | | |
| | | builder.Services.AddHostedService<QuartzJobHostedService>();//ä»»å¡è°åº¦ å¯å¨æå¡ |
| | | builder.Services.AddSingleton<TcpSocketServer>(); |
| | | builder.Services.AddHostedService<SocketServerHostedService>(); |
| | | |
| | | builder.Services.AddMvc(options => |
| | | { |
| | |
| | | "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" |
| | | } |
| | | } |
| | |
| | | #region << ç æ¬ 注 é >> |
| | | |
| | | /*---------------------------------------------------------------- |
| | | * å½å空é´ï¼WIDESEAWCS_TaskInfoService |
| | | * å建è
ï¼è¡ç«¥åº |
| | |
| | | * ä¿®æ¹æ¶é´ï¼ |
| | | * çæ¬ï¼V1.0.1 |
| | | * ä¿®æ¹è¯´æï¼ |
| | | * |
| | | * |
| | | *----------------------------------------------------------------*/ |
| | | |
| | | #endregion << ç æ¬ 注 é >> |
| | | |
| | | using AutoMapper; |
| | |
| | | {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) |
| | | { |
| | |
| | | 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}ã"); |
| | |
| | | } |
| | | 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; |
| | |
| | | } |
| | | else if (task.TaskType.GetTaskTypeGroup() == TaskTypeGroup.OtherGroup) |
| | | { |
| | | |
| | | } |
| | | else |
| | | { |
| | |
| | | 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); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | 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) |
| | | { |
| | |
| | | { |
| | | public ushort InteractiveSignal { get; set; } |
| | | |
| | | [DataLength(25)] |
| | | [DataLength(254)] |
| | | public string Barcode { get; set; } |
| | | |
| | | public int TargetAddress { get; set; } |
| | |
| | | /// <summary> |
| | | /// æçå· |
| | | /// </summary> |
| | | [DataLength(26)] |
| | | [DataLength(25)] |
| | | public string Barcode { get; set; } |
| | | |
| | | #endregion <Public Menber> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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; } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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); |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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"; |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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); |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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"); |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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; |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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(); |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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 { } |
| | | } |
| | | } |
| | | } |
| | |
| | | "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": "" |
| | |
| | | "PrintSql": false, //æå°SQLè¯å¥ |
| | | "ApiName": "WIDESEA", |
| | | "ExpMinutes": 120, |
| | | "DBSeedEnable": true, |
| | | "DBSeedEnable": false, |
| | | "PDAVersion": "4", |
| | | "WebSocketPort": 9296 |
| | | } |