wanshenmean
2026-01-30 c75f62bad64e5b03c9cda9ba97c68aba7f09dd3d
添加化成分容堆垛机调度,优化路由查找
已添加8个文件
已删除1个文件
已修改12个文件
2319 ■■■■ 文件已修改
.gitignore 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/VITE_MIGRATION_SUMMARY.md 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/package.json 2 ●●● 补丁 | 查看 | 原始文档 | 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 120 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/Service/DispatchInfoService.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/Service/IRouterService.cs 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/Service/RouterService.cs 304 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/StackerCrane/Common/CommonStackerCrane.cs 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/StackerCrane/Enum/FormationStackerCraneStatus.cs 171 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/StackerCrane/Spec/SpeFormationStackerCrane.cs 445 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/StackerCrane/Spec/SpeStackerCrane.cs 276 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/appsettings.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/FormationStackerCraneJob/FormationCommonStackerCraneJob.cs 308 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/FormationStackerCraneJob/FormationStackerCraneDBName.cs 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/FormationStackerCraneJob/FormationStackerCraneTaskCommand.cs 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/CommonStackerCraneJob.cs 329 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目资料/开发计划/副本陕煤项目开发计划260126.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
项目资料/高常温堆垛机/堆垛机通信协议.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
项目资料/高常温堆垛机/模拟PLC程序.zip 补丁 | 查看 | 原始文档 | blame | 历史
.gitignore
@@ -290,3 +290,109 @@
Code/WMS/WIDESEA_WMSServer/.vs/WIDESEA_WMSServer/v18/DocumentLayout.json
Code/WMS/WIDESEA_WMSClient/yarn.lock
Code/WMS/WIDESEA_WMSClient/package-lock.json
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/06BDA642C34D304DA3CFBDE5A90B0214/02CEDD732D62C94D758FE67FF83D72CF
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/06BDA642C34D304DA3CFBDE5A90B0214/12313E989BCBD9B17CB5E1F4659BB227
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/06BDA642C34D304DA3CFBDE5A90B0214/20D96583D4E59CBF251955AB3AEE224B
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/06BDA642C34D304DA3CFBDE5A90B0214/326CB4F0763C1328A2FE9B1DEAEEE459
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/06BDA642C34D304DA3CFBDE5A90B0214/41A179025C25D2BE7AF7B6AA41CD697F
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/06BDA642C34D304DA3CFBDE5A90B0214/43DB506154C7EA149D631F3446B3F79A
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/06BDA642C34D304DA3CFBDE5A90B0214/6B3C476AA39528929C43BD504073D1A2
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/06BDA642C34D304DA3CFBDE5A90B0214/6D201C9A521C3192EE982AA13020805C
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/06BDA642C34D304DA3CFBDE5A90B0214/7A3359239D3444727928DAAA9D7F188C
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/06BDA642C34D304DA3CFBDE5A90B0214/966BBA8CF0A87B2A233AEA0A2F0794E9
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/06BDA642C34D304DA3CFBDE5A90B0214/CE1DA310E2C206A7C790F1012746725B
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/06BDA642C34D304DA3CFBDE5A90B0214/EF8B82096B9438E8EF04EF866ACC6365
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/0E757001EF822C40B0B7B32A7191F50B/37BA68D3A4AFE9B4B3E8D1F219796E43
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/0E757001EF822C40B0B7B32A7191F50B/66639773C1563C8E4A66750CD9E91A42
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/0E757001EF822C40B0B7B32A7191F50B/6B3C476AA39528929C43BD504073D1A2
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/0E757001EF822C40B0B7B32A7191F50B/6D201C9A521C3192EE982AA13020805C
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/0E757001EF822C40B0B7B32A7191F50B/74A5F8BAFAF84678F3DBAD19D2DCA4C4
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/0E757001EF822C40B0B7B32A7191F50B/7A3359239D3444727928DAAA9D7F188C
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/0E757001EF822C40B0B7B32A7191F50B/8C3792853AD604BFAF1D21B2148608F2
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/11C2B2745D388A49AA91E9482D590508/24ED6D94AF6C551D6278FB0AF2C07239
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/11C2B2745D388A49AA91E9482D590508/4A7264DE6B88B1CE5DB63F2CF21810E0
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/11C2B2745D388A49AA91E9482D590508/4ACFB47A91BDB6CAF109C5CB5A821941
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/11C2B2745D388A49AA91E9482D590508/6B3C476AA39528929C43BD504073D1A2
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/11C2B2745D388A49AA91E9482D590508/8E82010873955D36B8637A8AA7594D6D
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/11C2B2745D388A49AA91E9482D590508/AA217133559F0488EC77AF4AAD1A97D6
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/11C2B2745D388A49AA91E9482D590508/B2744ACFBBD6298BD34CEDA635CC7928
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/11C2B2745D388A49AA91E9482D590508/C3FEC06F7A6EB2FD6A2B504485FA7BE9
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/11C2B2745D388A49AA91E9482D590508/ECA5E1DC5AE3AF113B2740CF9FFEA730
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/2D60A5D08D551142A9CDCC8410949C3F/1FFCC3BC333CC2F8F80C3FD42D9C35A7
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/2D60A5D08D551142A9CDCC8410949C3F/4B405FB24AE2E7CDA196C2E13DCCD94D
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/2D60A5D08D551142A9CDCC8410949C3F/6B3C476AA39528929C43BD504073D1A2
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/2D60A5D08D551142A9CDCC8410949C3F/6C7A603CDFCA37C8B230FBCE1A6EF3C9
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/2D60A5D08D551142A9CDCC8410949C3F/6D201C9A521C3192EE982AA13020805C
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/2D60A5D08D551142A9CDCC8410949C3F/723DEEBD0A5EE2AED06E8CCF18EDC349
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/2D60A5D08D551142A9CDCC8410949C3F/A780863F90E89FAFD4F108245B9FC59C
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/2D60A5D08D551142A9CDCC8410949C3F/B6F47F9752892BB8AC3789633BF0B198
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/2D60A5D08D551142A9CDCC8410949C3F/BC33C2D906CA58D53FBC0BD60B91818F
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/2D60A5D08D551142A9CDCC8410949C3F/ED961C09A796FFDF0F5BABA1939DA716
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/40A6D24721279A47A0317205F33DE3FD/288D1FB098610EAA981381CF1D3425C1
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/40A6D24721279A47A0317205F33DE3FD/6B3C476AA39528929C43BD504073D1A2
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/40A6D24721279A47A0317205F33DE3FD/6D201C9A521C3192EE982AA13020805C
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/40A6D24721279A47A0317205F33DE3FD/74A5F8BAFAF84678F3DBAD19D2DCA4C4
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/40A6D24721279A47A0317205F33DE3FD/7A3359239D3444727928DAAA9D7F188C
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/40A6D24721279A47A0317205F33DE3FD/8C3792853AD604BFAF1D21B2148608F2
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/40A6D24721279A47A0317205F33DE3FD/A592E03ACCDEC4FECC773C76FCACA72D
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/7029E49609C0524D965C26823A27EEA1/50FD06FB30A9DB2E369D237EB6D8D8F8
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/7029E49609C0524D965C26823A27EEA1/5CFB13DBA9FE67985336DC2F4C226534
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/7029E49609C0524D965C26823A27EEA1/6B3C476AA39528929C43BD504073D1A2
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/7029E49609C0524D965C26823A27EEA1/6D201C9A521C3192EE982AA13020805C
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/7029E49609C0524D965C26823A27EEA1/74A5F8BAFAF84678F3DBAD19D2DCA4C4
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/7029E49609C0524D965C26823A27EEA1/7A3359239D3444727928DAAA9D7F188C
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/7029E49609C0524D965C26823A27EEA1/8C3792853AD604BFAF1D21B2148608F2
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/80B3BD5CD7AB2A41AFD5BD70193EC06E/55DE31DE47C1715E5B9151AE24AC74CC
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/80B3BD5CD7AB2A41AFD5BD70193EC06E/6B3C476AA39528929C43BD504073D1A2
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/80B3BD5CD7AB2A41AFD5BD70193EC06E/6D201C9A521C3192EE982AA13020805C
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/80B3BD5CD7AB2A41AFD5BD70193EC06E/7A3359239D3444727928DAAA9D7F188C
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/80B3BD5CD7AB2A41AFD5BD70193EC06E/8C3792853AD604BFAF1D21B2148608F2
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/80B3BD5CD7AB2A41AFD5BD70193EC06E/F8448AD0D92C292C4B1EBD43C11F2870
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/80B3BD5CD7AB2A41AFD5BD70193EC06E/FD23F73719A7512E845F2F1D8B1BF111
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/BAE14F88C2A85A4FA1EA674CFD09AB3E/40F7E57802DACE2B3BBB65B9A371BB17
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/BAE14F88C2A85A4FA1EA674CFD09AB3E/6B3C476AA39528929C43BD504073D1A2
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/BAE14F88C2A85A4FA1EA674CFD09AB3E/6D201C9A521C3192EE982AA13020805C
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/BAE14F88C2A85A4FA1EA674CFD09AB3E/74A5F8BAFAF84678F3DBAD19D2DCA4C4
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/BAE14F88C2A85A4FA1EA674CFD09AB3E/7A3359239D3444727928DAAA9D7F188C
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/BAE14F88C2A85A4FA1EA674CFD09AB3E/7CA1586DEF629651EE20C80BA9F5498F
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/BAE14F88C2A85A4FA1EA674CFD09AB3E/8C3792853AD604BFAF1D21B2148608F2
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/EAC09E9546070C4397A0096536CDEAD9/464529942B4DF0909C04E15C72912D32
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/EAC09E9546070C4397A0096536CDEAD9/6B3C476AA39528929C43BD504073D1A2
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/EAC09E9546070C4397A0096536CDEAD9/6D201C9A521C3192EE982AA13020805C
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/EAC09E9546070C4397A0096536CDEAD9/7A3359239D3444727928DAAA9D7F188C
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/EAC09E9546070C4397A0096536CDEAD9/811D807D4744CFA93E159B8F74F0F8BC
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/EAC09E9546070C4397A0096536CDEAD9/8C3792853AD604BFAF1D21B2148608F2
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/F3CC65965247224EAA3ECED76422C6C8/6B3C476AA39528929C43BD504073D1A2
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/F3CC65965247224EAA3ECED76422C6C8/6D201C9A521C3192EE982AA13020805C
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/F3CC65965247224EAA3ECED76422C6C8/7A3359239D3444727928DAAA9D7F188C
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/F3CC65965247224EAA3ECED76422C6C8/8C3792853AD604BFAF1D21B2148608F2
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/F3CC65965247224EAA3ECED76422C6C8/C81B0E8985C68630D39D9C4831A88D3D
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/F3CC65965247224EAA3ECED76422C6C8/CE83D9037361C8282E2811065127E2F6
Code/WCS/WIDESEAWCS_Server/.vs/CopilotSnapshots/48A4EA5F3CA61E408736097ACF7047F6/state.mpack
Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/copilot-chat/bef6627e/sessions/5feaa448-a63c-401e-8736-097acf7047f6
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/CodeChunks.db-shm
Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/CopilotIndices/18.0.988.22099/CodeChunks.db-wal
Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/CopilotIndices/18.0.988.22099/SemanticSymbols.db
Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/CopilotIndices/18.0.988.22099/SemanticSymbols.db-shm
Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/CopilotIndices/18.0.988.22099/SemanticSymbols.db-wal
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/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/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/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/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/CopilotIndices/18.0.988.22099/SemanticSymbols.db
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/CodeChunks.db
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_Client/VITE_MIGRATION_SUMMARY.md
@@ -89,7 +89,7 @@
### å¼€å‘环境
```bash
yarn dev
yarn serve
# Vite dev server è¿è¡ŒäºŽ http://localhost:8080/
# æ”¯æŒ HMR (Hot Module Replacement)
```
Code/WCS/WIDESEAWCS_Client/package.json
@@ -3,7 +3,7 @@
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "vite",
    "serve": "vite",
    "build": "vite build",
    "preview": "vite preview",
    "lint": "eslint --ext .js,.vue src"
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,12 +3,28 @@
  "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\\spestackercrane.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\\spestackercrane.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\\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:{487FA45B-EA1A-4ACA-BB5B-0F6708F462C0}|WIDESEAWCS_Server\\WIDESEAWCS_Server.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_server\\appsettings.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}",
      "RelativeMoniker": "D:0:0:{487FA45B-EA1A-4ACA-BB5B-0F6708F462C0}|WIDESEAWCS_Server\\WIDESEAWCS_Server.csproj|solutionrelative:wideseawcs_server\\appsettings.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}"
      "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\\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\\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\\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}"
    }
  ],
  "DocumentGroupContainers": [
@@ -18,7 +34,7 @@
      "DocumentGroups": [
        {
          "DockedWidth": 200,
          "SelectedChildIndex": 2,
          "SelectedChildIndex": 6,
          "Children": [
            {
              "$type": "Bookmark",
@@ -29,33 +45,85 @@
              "Name": "ST:0:0:{40ea2e6b-2121-4bb8-a43e-c83c04b51041}"
            },
            {
              "$type": "Document",
              "DocumentIndex": 0,
              "Title": "SpeStackerCrane.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_QuartzJob\\StackerCrane\\Spec\\SpeStackerCrane.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_QuartzJob\\StackerCrane\\Spec\\SpeStackerCrane.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_QuartzJob\\StackerCrane\\Spec\\SpeStackerCrane.cs",
              "RelativeToolTip": "WIDESEAWCS_QuartzJob\\StackerCrane\\Spec\\SpeStackerCrane.cs",
              "ViewState": "AgIAAAAAAAAAAAAAAAAAAJQAAAARAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-01-28T05:55:14.804Z",
              "EditorCaption": ""
            },
            {
              "$type": "Bookmark",
              "Name": "ST:0:0:{aa2115a1-9712-457b-9047-dbb71ca2cdd2}"
            },
            {
              "$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==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-01-29T09:17:51.622Z",
              "EditorCaption": ""
            },
            {
              "$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==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-01-29T09:13:24.922Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 1,
              "Title": "appsettings.json",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Server\\appsettings.json",
              "RelativeDocumentMoniker": "WIDESEAWCS_Server\\appsettings.json",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Server\\appsettings.json",
              "RelativeToolTip": "WIDESEAWCS_Server\\appsettings.json",
              "ViewState": "AgIAAAYAAAAAAAAAAAAAABQAAAAAAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001642|",
              "WhenOpened": "2026-01-26T08:07:36.508Z",
              "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==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-01-29T03:35:09.011Z",
              "EditorCaption": ""
            },
            {
              "$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,
              "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==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-01-29T03:12:40.035Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 5,
              "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==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-01-29T02:59:35.12Z",
              "EditorCaption": ""
            }
          ]
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/Service/DispatchInfoService.cs
@@ -64,7 +64,7 @@
                Creater = a.Creater,
                CycleHasRunTimes = 0,
                EndTime = a.EndTime,
                Id = b.Id,
                Id = a.Id,
                IntervalSecond = a.IntervalSecond,
                Modifier = a.Modifier,
                ModifyDate = a.ModifyDate,
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/Service/IRouterService.cs
@@ -33,6 +33,39 @@
        List<Dt_Router> QueryNextRoutes(string startPosi, string endPosi, int routeType);
        /// <summary>
        /// æ ¹æ®èµ·ç‚¹èŽ·å–ä¸‹ä¸€ä¸ªå•ä¸ªå­èŠ‚ç‚¹è·¯ç”±
        /// </summary>
        /// <param name="startPosi">起点/当前位置</param>
        /// <returns>返回下一个路由节点,如果没有则返回null</returns>
        public Dt_Router QueryNextRoute(string startPosi);
        /// <summary>
        /// æ ¹æ®èµ·ç‚¹å’Œè·¯ç”±ç±»åž‹èŽ·å–ä¸‹ä¸€ä¸ªå•ä¸ªå­èŠ‚ç‚¹è·¯ç”±
        /// </summary>
        /// <param name="startPosi">起点/当前位置</param>
        /// <param name="routeType">路由类型</param>
        /// <returns>返回下一个路由节点,如果没有则返回null</returns>
        public Dt_Router QueryNextRoute(string startPosi, int routeType);
        /// <summary>
        /// æ ¹æ®èµ·ç‚¹ã€ç»ˆç‚¹æ–¹å‘和路由类型获取下一个单个子节点路由(智能选择朝向终点的路由)
        /// </summary>
        /// <param name="startPosi">起点/当前位置</param>
        /// <param name="endPosi">终点位置(用于方向判断)</param>
        /// <param name="routeType">路由类型</param>
        /// <returns>返回下一个路由节点,优先返回朝向终点的路由,如果没有则返回null</returns>
        public Dt_Router QueryNextRoute(string startPosi, string endPosi, int routeType);
        /// <summary>
        /// èŽ·å–ä»Žèµ·ç‚¹åˆ°ç»ˆç‚¹çš„å®Œæ•´è·¯å¾„ï¼ˆæŒ‰é¡ºåºè¿”å›žæ¯ä¸ªå­èŠ‚ç‚¹è·¯ç”±ï¼‰
        /// </summary>
        /// <param name="startPosi">起点位置</param>
        /// <param name="endPosi">终点位置</param>
        /// <param name="routeType">路由类型</param>
        /// <returns>返回有序的路由列表,如果找不到路径则返回空列表</returns>
        public List<Dt_Router> QueryRoutePath(string startPosi, string endPosi, int routeType);
        /// <summary>
        /// æ ¹æ®è®¾å¤‡ç¼–号获取对应的路由点位编号(输送线站台编号)信息
        /// </summary>
        /// <param name="deviceCode">设备编号</param>
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/Service/RouterService.cs
@@ -42,46 +42,23 @@
        /// <returns>返回路由实体集合。</returns>
        public List<Dt_Router> QueryNextRoutes(string startPosi, string endPosi)
        {
            //todo æ–¹æ³•需优化
            List<Dt_Router> routers = new List<Dt_Router>();
            try
            {
                //查询下一个路由信息
                List<Dt_Router> dt_Routers = BaseDal.QueryData(x => (x.NextPosi == endPosi || x.ChildPosi == endPosi), new Dictionary<string, OrderByType> { { nameof(Dt_Router.IsEnd), OrderByType.Desc } });
                if (dt_Routers.Count > 0)
                // ä¸€æ¬¡æ€§æŸ¥è¯¢æ‰€æœ‰è·¯ç”±æ•°æ®åˆ°å†…å­˜
                List<Dt_Router> allRouters = BaseDal.QueryData(x => true);
                // åœ¨å†…存中进行路径搜索
                routers = FindRoutesInMemory(startPosi, endPosi, allRouters, null);
                if (routers.Count == 0)
                {
                    foreach (var item in dt_Routers)
                    {
                        //如果下一个路由的起点和终点都匹配,则添加到路由列表中
                        if (item.StartPosi == startPosi && !routers.Any(x => x.Id == item.Id))
                        {
                            routers.Add(item);
                        }
                        else
                        {
                            //否则,递归查询下一个路由的起点
                            List<Dt_Router> tempRouters = QueryNextRoutes(startPosi, item.StartPosi);
                            foreach (var router in tempRouters)
                            {
                                //如果下一个路由的起点和终点都匹配,则添加到路由列表中
                                if (router.StartPosi == startPosi && !routers.Any(x => x.Id == router.Id))
                                {
                                    routers.Add(router);
                                }
                            }
                        }
                    }
                }
                else
                {
                    //如果查询不到下一个路由信息,则抛出异常
                    throw new Exception($"该路径未配置或配置错误,请检查设备路由信息,起点:【{startPosi}】,终点:【{endPosi}】");
                }
            }
            catch (Exception ex)
            {
                //throw new Exception(ex.Message);
                //记录错误信息
                // è®°å½•错误信息
            }
            return routers;
        }
@@ -95,48 +72,250 @@
        /// <returns>返回路由实体集合。</returns>
        public List<Dt_Router> QueryNextRoutes(string startPosi, string endPosi, int routeType)
        {
            //todo æ–¹æ³•需优化
            List<Dt_Router> routers = new List<Dt_Router>();
            try
            {
                //查询下一个路由信息
                List<Dt_Router> dt_Routers = BaseDal.QueryData(x => (x.NextPosi == endPosi || x.ChildPosi == endPosi) && x.InOutType == routeType, new Dictionary<string, OrderByType> { { nameof(Dt_Router.IsEnd), OrderByType.Desc } });
                if (dt_Routers.Count > 0)
                // ä¸€æ¬¡æ€§æŸ¥è¯¢æŒ‡å®šç±»åž‹çš„æ‰€æœ‰è·¯ç”±æ•°æ®åˆ°å†…å­˜
                List<Dt_Router> allRouters = BaseDal.QueryData(x => x.InOutType == routeType);
                // åœ¨å†…存中进行路径搜索
                routers = FindRoutesInMemory(startPosi, endPosi, allRouters, routeType);
                if (routers.Count == 0)
                {
                    foreach (var item in dt_Routers)
                    {
                        //如果下一个路由的起点和终点都匹配,则添加到路由列表中
                        if (item.StartPosi == startPosi && !routers.Any(x => x.Id == item.Id))
                        {
                            routers.Add(item);
                        }
                        else
                        {
                            //否则,递归查询下一个路由的起点
                            List<Dt_Router> tempRouters = QueryNextRoutes(startPosi, item.StartPosi, routeType);
                            foreach (var router in tempRouters)
                            {
                                //如果下一个路由的起点和终点都匹配,则添加到路由列表中
                                if (router.StartPosi == startPosi && !routers.Any(x => x.Id == router.Id))
                                {
                                    routers.Add(router);
                                }
                            }
                        }
                    }
                }
                else
                {
                    //如果查询不到下一个路由信息,则抛出异常
                    throw new Exception($"该路径未配置或配置错误,请检查设备路由信息,起点:【{startPosi}】,终点:【{endPosi}】");
                }
            }
            catch (Exception ex)
            {
                //throw new Exception(ex.Message);
                //记录错误信息
                // è®°å½•错误信息
            }
            return routers;
        }
        /// <summary>
        /// åœ¨å†…存中查找从起点到终点的所有路由
        /// </summary>
        /// <param name="startPosi">起点位置</param>
        /// <param name="endPosi">终点位置</param>
        /// <param name="allRouters">所有路由数据</param>
        /// <param name="routeType">路由类型(可选)</param>
        /// <returns>符合条件的路由列表</returns>
        private List<Dt_Router> FindRoutesInMemory(string startPosi, string endPosi, List<Dt_Router> allRouters, int? routeType)
        {
            List<Dt_Router> result = new List<Dt_Router>();
            HashSet<int> addedIds = new HashSet<int>();
            // æž„建索引:以 NextPosi å’Œ ChildPosi ä¸ºé”®çš„字典,加速查找
            var routersByNext = allRouters
                .GroupBy(r => r.NextPosi)
                .ToDictionary(g => g.Key, g => g.ToList());
            var routersByChild = allRouters
                .GroupBy(r => r.ChildPosi)
                .ToDictionary(g => g.Key, g => g.ToList());
            // æ‰¾åˆ°æ‰€æœ‰æŒ‡å‘终点的路由(终点的父节点)
            List<Dt_Router> endRouters = new List<Dt_Router>();
            if (routersByNext.ContainsKey(endPosi))
                endRouters.AddRange(routersByNext[endPosi]);
            if (routersByChild.ContainsKey(endPosi))
                endRouters.AddRange(routersByChild[endPosi].Where(r => !endRouters.Any(e => e.Id == r.Id)));
            // æŒ‰ IsEnd é™åºæŽ’序
            endRouters = endRouters.OrderByDescending(r => r.IsEnd).ToList();
            // ä½¿ç”¨é˜Ÿåˆ—进行广度优先搜索
            Queue<(Dt_Router router, List<Dt_Router> path)> queue = new Queue<(Dt_Router, List<Dt_Router>)>();
            // å°†æ‰€æœ‰ç»ˆç‚¹è·¯ç”±åŠ å…¥é˜Ÿåˆ—
            foreach (var endRouter in endRouters)
            {
                queue.Enqueue((endRouter, new List<Dt_Router> { endRouter }));
            }
            // å¹¿åº¦ä¼˜å…ˆæœç´¢
            while (queue.Count > 0)
            {
                var (currentRouter, currentPath) = queue.Dequeue();
                // å¦‚果当前路由的起点就是目标起点,找到完整路径
                if (currentRouter.StartPosi == startPosi)
                {
                    // å°†è·¯å¾„中的所有路由添加到结果中(去重)
                    foreach (var router in currentPath)
                    {
                        if (!addedIds.Contains(router.Id))
                        {
                            result.Add(router);
                            addedIds.Add(router.Id);
                        }
                    }
                    continue;
                }
                // æŸ¥æ‰¾å½“前路由起点的父路由
                List<Dt_Router> parentRouters = new List<Dt_Router>();
                if (routersByNext.ContainsKey(currentRouter.StartPosi))
                    parentRouters.AddRange(routersByNext[currentRouter.StartPosi]);
                if (routersByChild.ContainsKey(currentRouter.StartPosi))
                    parentRouters.AddRange(routersByChild[currentRouter.StartPosi].Where(r => !parentRouters.Any(p => p.Id == r.Id)));
                // å°†çˆ¶è·¯ç”±åŠ å…¥é˜Ÿåˆ—
                foreach (var parentRouter in parentRouters)
                {
                    // é¿å…å¾ªçŽ¯å¼•ç”¨
                    if (!currentPath.Any(p => p.Id == parentRouter.Id))
                    {
                        var newPath = new List<Dt_Router>(currentPath) { parentRouter };
                        queue.Enqueue((parentRouter, newPath));
                    }
                }
            }
            return result;
        }
        /// <summary>
        /// æ ¹æ®èµ·ç‚¹èŽ·å–ä¸‹ä¸€ä¸ªå•ä¸ªå­èŠ‚ç‚¹è·¯ç”±
        /// </summary>
        /// <param name="startPosi">起点/当前位置</param>
        /// <returns>返回下一个路由节点,如果没有则返回null</returns>
        public Dt_Router QueryNextRoute(string startPosi)
        {
            try
            {
                // æŸ¥è¯¢ä»Žèµ·ç‚¹å‡ºå‘的所有路由
                List<Dt_Router> routes = BaseDal.QueryData(x => x.StartPosi == startPosi,
                    new Dictionary<string, OrderByType> { { nameof(Dt_Router.IsEnd), OrderByType.Desc } });
                // è¿”回第一个路由
                return routes.FirstOrDefault();
            }
            catch (Exception ex)
            {
                // è®°å½•错误信息
                return null;
            }
        }
        /// <summary>
        /// æ ¹æ®èµ·ç‚¹å’Œè·¯ç”±ç±»åž‹èŽ·å–ä¸‹ä¸€ä¸ªå•ä¸ªå­èŠ‚ç‚¹è·¯ç”±
        /// </summary>
        /// <param name="startPosi">起点/当前位置</param>
        /// <param name="routeType">路由类型</param>
        /// <returns>返回下一个路由节点,如果没有则返回null</returns>
        public Dt_Router QueryNextRoute(string startPosi, int routeType)
        {
            try
            {
                // æŸ¥è¯¢ä»Žèµ·ç‚¹å‡ºå‘的指定类型路由
                List<Dt_Router> routes = BaseDal.QueryData(x => x.StartPosi == startPosi && x.InOutType == routeType,
                    new Dictionary<string, OrderByType> { { nameof(Dt_Router.IsEnd), OrderByType.Desc } });
                // è¿”回第一个路由
                return routes.FirstOrDefault();
            }
            catch (Exception ex)
            {
                // è®°å½•错误信息
                return null;
            }
        }
        /// <summary>
        /// æ ¹æ®èµ·ç‚¹ã€ç»ˆç‚¹æ–¹å‘和路由类型获取下一个单个子节点路由(智能选择朝向终点的路由)
        /// </summary>
        /// <param name="startPosi">起点/当前位置</param>
        /// <param name="endPosi">终点位置(用于方向判断)</param>
        /// <param name="routeType">路由类型</param>
        /// <returns>返回下一个路由节点,优先返回朝向终点的路由,如果没有则返回null</returns>
        public Dt_Router QueryNextRoute(string startPosi, string endPosi, int routeType)
        {
            try
            {
                // æŸ¥è¯¢ä»Žèµ·ç‚¹å‡ºå‘的指定类型路由
                List<Dt_Router> routes = BaseDal.QueryData(x => x.StartPosi == startPosi && x.InOutType == routeType,
                    new Dictionary<string, OrderByType> { { nameof(Dt_Router.IsEnd), OrderByType.Desc } });
                if (routes.Count == 0)
                    return null;
                // ä¼˜å…ˆé€‰æ‹©ç›´æŽ¥æŒ‡å‘终点的路由
                Dt_Router directRoute = routes.FirstOrDefault(x => x.NextPosi == endPosi || x.ChildPosi == endPosi);
                if (directRoute != null)
                    return directRoute;
                // å¦‚果没有直接路由,使用查找算法找到朝向终点的路由
                List<Dt_Router> allRouters = BaseDal.QueryData(x => x.InOutType == routeType);
                foreach (var route in routes)
                {
                    // æ£€æŸ¥ä»Žè¿™ä¸ªè·¯ç”±çš„下一个位置是否能到达终点
                    var pathToEnd = FindRoutesInMemory(route.NextPosi, endPosi, allRouters, routeType);
                    if (pathToEnd.Count > 0)
                        return route;
                }
                // å¦‚果都不能到达终点,返回第一个路由
                return routes.FirstOrDefault();
            }
            catch (Exception ex)
            {
                // è®°å½•错误信息
                return null;
            }
        }
        /// <summary>
        /// èŽ·å–ä»Žèµ·ç‚¹åˆ°ç»ˆç‚¹çš„å®Œæ•´è·¯å¾„ï¼ˆæŒ‰é¡ºåºè¿”å›žæ¯ä¸ªå­èŠ‚ç‚¹è·¯ç”±ï¼‰
        /// </summary>
        /// <param name="startPosi">起点位置</param>
        /// <param name="endPosi">终点位置</param>
        /// <param name="routeType">路由类型</param>
        /// <returns>返回有序的路由列表,如果找不到路径则返回空列表</returns>
        public List<Dt_Router> QueryRoutePath(string startPosi, string endPosi, int routeType)
        {
            List<Dt_Router> path = new List<Dt_Router>();
            string currentPosi = startPosi;
            HashSet<string> visitedPositions = new HashSet<string>();
            try
            {
                while (currentPosi != endPosi)
                {
                    if (visitedPositions.Contains(currentPosi))
                    {
                        break;
                    }
                    visitedPositions.Add(currentPosi);
                    Dt_Router nextRoute = QueryNextRoute(currentPosi, endPosi, routeType);
                    if (nextRoute == null)
                    {
                        break;
                    }
                    path.Add(nextRoute);
                    currentPosi = nextRoute.NextPosi;
                    if (path.Count > 1000)
                    {
                        break;
                    }
                }
                if (currentPosi != endPosi)
                {
                    return new List<Dt_Router>();
                }
            }
            catch (Exception ex)
            {
                return new List<Dt_Router>();
            }
            return path;
        }
        /// <summary>
@@ -144,7 +323,6 @@
        /// </summary>
        /// <param name="deviceCode">设备编号</param>
        /// <returns>返回路由点位编号(输送线站台编号)集合</returns>
        // æ ¹æ®è®¾å¤‡ç¼–码查询所有位置
        public List<string> QueryAllPositions(string deviceCode)
        {
            // åˆ›å»ºä¸€ä¸ªå­—符串列表,用于存储所有位置
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/StackerCrane/Common/CommonStackerCrane.cs
@@ -1,4 +1,5 @@
#region << ç‰ˆ æœ¬ æ³¨ é‡Š >>
/*----------------------------------------------------------------
 * å‘½åç©ºé—´ï¼šWIDESEAWCS_QuartzJob
 * åˆ›å»ºè€…:胡童庆
@@ -11,21 +12,15 @@
 * ä¿®æ”¹æ—¶é—´ï¼š
 * ç‰ˆæœ¬ï¼šV1.0.1
 * ä¿®æ”¹è¯´æ˜Žï¼š
 *
 *
 *----------------------------------------------------------------*/
#endregion << ç‰ˆ æœ¬ æ³¨ é‡Š >>
using HslCommunication;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using WIDESEAWCS_Communicator;
using WIDESEAWCS_Core.Enums;
using WIDESEAWCS_QuartzJob.DeviceBase;
using WIDESEAWCS_QuartzJob.DTO;
using WIDESEAWCS_QuartzJob.StackerCrane;
@@ -40,26 +35,32 @@
    public class CommonStackerCrane : IStackerCrane
    {
        #region Private Member
        /// <summary>
        /// å †åž›æœºé€šè®¯å¯¹è±¡
        /// </summary>
        private BaseCommunicator _communicator;
        /// <summary>
        /// å †åž›æœºåè®®ä¿¡æ¯
        /// </summary>
        private readonly List<DeviceProDTO> _deviceProDTOs;
        /// <summary>
        /// å †åž›æœºåè®®æ˜Žç»†ä¿¡æ¯
        /// </summary>
        private readonly List<DeviceProtocolDetailDTO> _deviceProtocolDetailDTOs;
        /// <summary>
        /// è®¾å¤‡ç¼–号
        /// </summary>
        public readonly string _deviceCode;
        /// <summary>
        /// è®¾å¤‡åç§°
        /// </summary>
        public readonly string _deviceName;
        /// <summary>
        /// ä¸Šä¸€æ¬¡ä»»åŠ¡å·
        /// </summary>
@@ -70,9 +71,11 @@
        private bool _heartStatr = true;
        private bool _isConnected = true;
        #endregion Private Member
        #region Public Member
        /// <summary>
        /// å †åž›æœºé€šè®¯å¯¹è±¡
        /// </summary>
@@ -176,6 +179,7 @@
        #endregion
        #region Constructor Function
        /// <summary>
        /// æž„造函数
        /// </summary>
@@ -193,9 +197,11 @@
            _deviceName = deviceName;
            CheckConnect();
        }
        #endregion
        #region Private Method
        /// <summary>
        /// æ ¹æ®åè®®è¯»å–堆垛机状态
        /// </summary>
@@ -306,7 +312,6 @@
                    return StackerCraneStatus.Unkonw.ToString();
                }
            }
            //todo é€šè®¯æœªè¿žæŽ¥æ—¶æŠ›å‡ºå¼‚常
            return StackerCraneStatus.Unkonw.ToString();
        }
@@ -333,9 +338,11 @@
                }
            });
        }
        #endregion
        #region Public Method
        /// <summary>
        /// å‘送任务命令
        /// </summary>
@@ -391,21 +398,27 @@
                                case TypeCode.Boolean:
                                    operateResult = Communicator.Wait(devicePro.DeviceProAddress, 500, 10 * 6000, Convert.ToBoolean(deviceProtocolDetail.ProtocalDetailValue));
                                    break;
                                case TypeCode.Byte:
                                    operateResult = Communicator.Wait(devicePro.DeviceProAddress, 500, 10 * 6000, Convert.ToByte(deviceProtocolDetail.ProtocalDetailValue));
                                    break;
                                case TypeCode.Int16:
                                    operateResult = Communicator.Wait(devicePro.DeviceProAddress, 500, 10 * 6000, Convert.ToInt16(deviceProtocolDetail.ProtocalDetailValue));
                                    break;
                                case TypeCode.Int32:
                                    operateResult = Communicator.Wait(devicePro.DeviceProAddress, 500, 10 * 6000, Convert.ToInt32(deviceProtocolDetail.ProtocalDetailValue));
                                    break;
                                case TypeCode.UInt16:
                                    operateResult = Communicator.Wait(devicePro.DeviceProAddress, 500, 10 * 6000, Convert.ToUInt16(deviceProtocolDetail.ProtocalDetailValue));
                                    break;
                                case TypeCode.UInt32:
                                    operateResult = Communicator.Wait(devicePro.DeviceProAddress, 500, 10 * 6000, Convert.ToUInt32(deviceProtocolDetail.ProtocalDetailValue));
                                    break;
                                default:
                                    break;
                            }
@@ -421,7 +434,6 @@
                }
                catch (Exception ex)
                {
                }
                finally
                {
@@ -450,7 +462,6 @@
        /// </summary>
        public void Heartbeat()
        {
        }
        /// <summary>
@@ -483,6 +494,7 @@
            // å‘Šè¯‰åžƒåœ¾å›žæ”¶å™¨ä¸å†è°ƒç”¨æ­¤å¯¹è±¡çš„终结器
            GC.SuppressFinalize(this);
        }
        #endregion
    }
}
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/StackerCrane/Enum/FormationStackerCraneStatus.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,171 @@
#region << ç‰ˆ æœ¬ æ³¨ é‡Š >>
/*----------------------------------------------------------------
 * å‘½åç©ºé—´ï¼šWIDESEAWCS_QuartzJob
 * åˆ›å»ºè€…:胡童庆
 * åˆ›å»ºæ—¶é—´ï¼š2024/8/2 16:13:36
 * ç‰ˆæœ¬ï¼šV1.0.0
 * æè¿°ï¼šå †åž›æœºçŠ¶æ€æžšä¸¾
 *
 * ----------------------------------------------------------------
 * ä¿®æ”¹äººï¼š
 * ä¿®æ”¹æ—¶é—´ï¼š
 * ç‰ˆæœ¬ï¼šV1.0.1
 * ä¿®æ”¹è¯´æ˜Žï¼š
 *
 *----------------------------------------------------------------*/
#endregion << ç‰ˆ æœ¬ æ³¨ é‡Š >>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WIDESEAWCS_QuartzJob.StackerCrane.Enum
{
    /// <summary>
    /// å †åž›æœºè¿è¡ŒçŠ¶æ€æžšä¸¾
    /// </summary>
    public enum FormationStackerCraneRunStatus
    {
        /// <summary>
        /// OFF
        /// </summary>
        [Description("OFF")]
        Off,
        /// <summary>
        /// å¾…机中
        /// </summary>
        [Description("待机中")]
        Standby,
        /// <summary>
        /// æ‰‹åŠ¨æ¨¡å¼
        /// </summary>
        [Description("手动模式")]
        ManualMode,
        /// <summary>
        /// åŽŸç‚¹å›žå½’ä¸­
        /// </summary>
        [Description("原点回归中")]
        ReturningToOrigin,
        /// <summary>
        /// è‡ªåŠ¨è¿è½¬ä¸­
        /// </summary>
        [Description("自动运转中")]
        AutoRunning,
        /// <summary>
        /// å¼‚常中
        /// </summary>
        [Description("异常中")]
        Abnormal,
        /// <summary>
        /// éžå¸¸åœæ­¢ä¸­
        /// </summary>
        [Description("非常停止中")]
        EmergencyStopping,
        /// <summary>
        /// æœªçŸ¥
        /// </summary>
        [Description("未知")]
        Unkonw
    }
    /// <summary>
    /// å †åž›æœºå·¥ä½œçŠ¶æ€æžšä¸¾
    /// </summary>
    public enum FormationStackerCraneOperationStatus
    {
        /// <summary>
        /// æ— 
        /// </summary>
        [Description("无")]
        None,
        /// <summary>
        /// ç©ºé—²ä¸­
        /// </summary>
        [Description("空闲中")]
        Idle,
        /// <summary>
        /// ç§»åЍ䏭
        /// </summary>
        [Description("移动中")]
        Moving,
        /// <summary>
        /// å–货中
        /// </summary>
        [Description("取货中")]
        PickingUp,
        /// <summary>
        /// æ”¾è´§ä¸­
        /// </summary>
        [Description("放货中")]
        PuttingDown,
        /// <summary>
        /// å¤„理中
        /// </summary>
        [Description("处理中")]
        Processing,
        /// <summary>
        /// å¼‚常中
        /// </summary>
        [Description("异常中")]
        Abnormal,
        /// <summary>
        /// éžå¸¸åœæ­¢ä¸­
        /// </summary>
        [Description("非常停止中")]
        EmergencyStopping,
        /// <summary>
        /// æœªçŸ¥
        /// </summary>
        [Description("未知")]
        Unkonw
    }
    /// <summary>
    /// å †åž›æœºä»»åŠ¡å®ŒæˆçŠ¶æ€æžšä¸¾
    /// </summary>
    public enum FormationStackerCraneTaskCompletionStatus
    {
        /// <summary>
        /// æ— 
        /// </summary>
        [Description("无")]
        None,
        /// <summary>
        /// æ­£å¸¸å®Œæˆ
        /// </summary>
        [Description("正常完成")]
        NormalCompleted,
        /// <summary>
        /// å¼‚常完成
        /// </summary>
        [Description("异常完成")]
        AbnormalCompleted,
        /// <summary>
        /// æœªçŸ¥
        /// </summary>
        [Description("未知")]
        Unkonw
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/StackerCrane/Spec/SpeFormationStackerCrane.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,445 @@
using HslCommunication;
using System.ComponentModel;
using System.Reflection;
using WIDESEAWCS_Communicator;
using WIDESEAWCS_QuartzJob.DeviceBase;
using WIDESEAWCS_QuartzJob.DTO;
using WIDESEAWCS_QuartzJob.StackerCrane;
using WIDESEAWCS_QuartzJob.StackerCrane.Enum;
namespace WIDESEAWCS_QuartzJob
{
    /// <summary>
    /// è‡ªå®šä¹‰å †åž›æœº
    /// </summary>
    [Description("化成分容堆垛机")]
    public class SpeFormationStackerCrane : IStackerCrane
    {
        #region Private Member
        /// <summary>
        /// å †åž›æœºé€šè®¯å¯¹è±¡
        /// </summary>
        private BaseCommunicator _communicator;
        /// <summary>
        /// å †åž›æœºåè®®ä¿¡æ¯
        /// </summary>
        private readonly List<DeviceProDTO> _deviceProDTOs;
        /// <summary>
        /// å †åž›æœºåè®®æ˜Žç»†ä¿¡æ¯
        /// </summary>
        private readonly List<DeviceProtocolDetailDTO> _deviceProtocolDetailDTOs;
        /// <summary>
        /// è®¾å¤‡ç¼–号
        /// </summary>
        public readonly string _deviceCode;
        /// <summary>
        /// è®¾å¤‡åç§°
        /// </summary>
        public readonly string _deviceName;
        /// <summary>
        /// ä¸Šä¸€æ¬¡ä»»åŠ¡å·
        /// </summary>
        private int _lastTaskNum;
        private bool _isChecked = false;
        private bool _heartStart = true;
        private bool _isConnected = true;
        #endregion Private Member
        #region Public Member
        /// <summary>
        /// é€šè®¯å¯¹è±¡
        /// </summary>
        public BaseCommunicator Communicator => _communicator;
        /// <summary>
        /// é€šè®¯åè®®ä¿¡æ¯
        /// </summary>
        public List<DeviceProDTO> DeviceProDTOs => _deviceProDTOs;
        /// <summary>
        /// é€šè®¯åè®®æ˜Žç»†ä¿¡æ¯
        /// </summary>
        public List<DeviceProtocolDetailDTO> DeviceProtocolDetailDTOs => _deviceProtocolDetailDTOs;
        public FormationStackerCraneTaskCompletionStatus StackerCraneCompletionValue => GetStackerCraneCompletionStatus();
        /// <summary>
        /// å †åž›æœºçŠ¶æ€
        /// </summary>
        public FormationStackerCraneRunStatus StackerCraneStatusValue => GetStackerCraneStatus();
        /// <summary>
        /// å †åž›æœºçŠ¶æ€ä¸­æ–‡è¯´æ˜Ž
        /// </summary>
        public string StackerCraneStatusDes => GetEnumDes(StackerCraneStatusValue);
        /// <summary>
        /// ä½œä¸šçŠ¶æ€
        /// </summary>
        public FormationStackerCraneOperationStatus StackerCraneWorkStatusValue => GetStackerCraneWorkStatus();
        /// <summary>
        /// ä½œä¸šçŠ¶æ€ä¸­æ–‡è¯´æ˜Ž
        /// </summary>
        public string StackerCraneWorkStatusDes => GetEnumDes(StackerCraneWorkStatusValue);
        /// <summary>
        /// ä¸Šä¸€æ¬¡æ‰§è¡Œçš„任务号
        /// </summary>
        public int LastTaskNum => _lastTaskNum;
        /// <summary>
        /// å½“前正在执行的任务号
        /// </summary>
        public int CurrentTaskNum => GetCurrentTaskNum();
        /// <summary>
        /// ä¸Šä¸€æ¬¡æ‰§è¡Œçš„任务类型
        /// </summary>
        public int? LastTaskType { get; set; } = null;
        /// <summary>
        /// è®¾å¤‡ç¼–号
        /// </summary>
        public string DeviceCode => _deviceCode;
        /// <summary>
        /// è®¾å¤‡åç§°
        /// </summary>
        public string DeviceName => _deviceName;
        /// <summary>
        /// è®¾å¤‡æ˜¯å¦å·²è¿žæŽ¥
        /// </summary>
        public bool IsConnected => Communicator.IsConnected && _isConnected;
        /// <summary>
        /// å †åž›æœºä»»åС命令
        /// </summary>
        public object StackerCraneTaskCommand { get; set; }
        /// <summary>
        /// å †åž›æœºä»»åŠ¡å®Œæˆäº‹ä»¶
        /// </summary>
        public event EventHandler<StackerCraneTaskCompletedEventArgs> StackerCraneTaskCompletedEventHandler;
        /// <summary>
        /// å †åž›æœºå®Œæˆäº‹ä»¶æ˜¯å¦å·²è®¢é˜…
        /// </summary>
        public bool IsEventSubscribed => StackerCraneTaskCompletedEventHandler != null;
        #endregion Public Member
        #region Constructor Function
        /// <summary>
        /// æž„造函数
        /// </summary>
        /// <param name="communicator">堆垛机通讯对象</param>
        /// <param name="deviceProDTOs">堆垛机协议信息</param>
        /// <param name="deviceProtocolDetailDTOs">堆垛机协议明细信息</param>
        /// <param name="deviceCode">设备编号</param>
        /// <param name="deviceName">设备名称</param>
        public SpeFormationStackerCrane(BaseCommunicator communicator, List<DeviceProDTO> deviceProDTOs, List<DeviceProtocolDetailDTO> deviceProtocolDetailDTOs, string deviceCode, string deviceName)
        {
            _communicator = communicator;
            _deviceProDTOs = deviceProDTOs;
            _deviceProtocolDetailDTOs = deviceProtocolDetailDTOs;
            _deviceCode = deviceCode;
            _deviceName = deviceName;
            CheckConnect();
        }
        #endregion Constructor Function
        #region Private Method
        private object GetStatus(string protocolParamType)
        {
            if (!Communicator.IsConnected)
            {
                return StackerCraneStatus.Unkonw.ToString();
            }
            List<DeviceProDTO> devicePros = _deviceProDTOs.Where(x => x.DeviceProParamType == protocolParamType).ToList();
            if (devicePros.Count == 0)
            {
                throw new Exception($"未获取到协议信息,协议参数类型:{protocolParamType}");
            }
            foreach (var devicePro in devicePros)
            {
                object readStatus = Communicator.ReadAsObj(devicePro.DeviceProAddress, devicePro.DeviceDataType);
                DeviceProtocolDetailDTO? deviceProtocolDetail = _deviceProtocolDetailDTOs.FirstOrDefault(
                    x => x.DeviceProParamName == devicePro.DeviceProParamType
                    && x.ProtocalDetailValue.Equals(readStatus.ToString()));
                if (deviceProtocolDetail != null)
                {
                    return Convert.ToInt32(readStatus);
                }
            }
            return StackerCraneStatus.Unkonw.ToString();
        }
        /// <summary>
        /// èŽ·å–å †åž›æœºè®¾å¤‡çŠ¶æ€
        /// </summary>
        /// <returns></returns>
        private FormationStackerCraneRunStatus GetStackerCraneStatus()
        {
            return (FormationStackerCraneRunStatus)GetStatus(nameof(FormationStackerCraneRunStatus));
        }
        /// <summary>
        /// èŽ·å–å †åž›æœºå·¥ä½œçŠ¶æ€
        /// </summary>
        /// <returns></returns>
        private FormationStackerCraneOperationStatus GetStackerCraneWorkStatus()
        {
            return (FormationStackerCraneOperationStatus)GetStatus(nameof(FormationStackerCraneOperationStatus));
        }
        private FormationStackerCraneTaskCompletionStatus GetStackerCraneCompletionStatus()
        {
            return (FormationStackerCraneTaskCompletionStatus)GetStatus(nameof(FormationStackerCraneTaskCompletionStatus));
        }
        /// <summary>
        /// èŽ·å–æžšä¸¾è¯´æ˜Ž
        /// </summary>
        /// <typeparam name="T">枚举泛型</typeparam>
        /// <param name="value"></param>
        /// <returns></returns>
        private string GetEnumDes<T>(T value) where T : Enum
        {
            FieldInfo? fieldInfo = typeof(T).GetField(value.ToString());
            if (fieldInfo != null)
            {
                DescriptionAttribute? descriptionAttribute = fieldInfo.GetCustomAttribute<DescriptionAttribute>();
                if (descriptionAttribute != null)
                {
                    return descriptionAttribute.Description;
                }
                return "未定义";
            }
            return "未知";
        }
        private int GetCurrentTaskNum()
        {
            DeviceProDTO? devicePro = _deviceProDTOs.FirstOrDefault(x => x.DeviceProParamName == nameof(CurrentTaskNum));
            return devicePro == null ? throw new Exception($"读取当前任务号错误,未获取到协议信息,请检查配置参数名称是否配置,且配置为{nameof(CurrentTaskNum)}") : (int)Communicator.ReadAsObj(devicePro.DeviceProAddress, devicePro.DeviceDataType);
        }
        private void CheckConnect()
        {
            Task.Run(() =>
            {
                while (_heartStart)
                {
                    try
                    {
                        DeviceProDTO? devicePro = _deviceProDTOs.FirstOrDefault();
                        if (devicePro == null)
                        {
                            _isConnected = false;
                        }
                        else
                        {
                            Communicator.ReadAsObj(devicePro.DeviceProAddress, devicePro.DeviceDataType);
                            _isConnected = true;
                        }
                    }
                    catch (Exception ex)
                    {
                        _isConnected = false;
                    }
                    Thread.Sleep(500);
                }
            });
        }
        #endregion Private Method
        #region Public Method
        /// <summary>
        /// èŽ·å–å †åž›æœºçŠ¶æ€
        /// </summary>
        public T GetStackerCraneStatus<T>() where T : notnull, Enum
        {
            return (T)GetStatus(typeof(T).Name);
        }
        /// <summary>
        /// é‡Šæ”¾å¯¹è±¡
        /// </summary>
        public void Dispose()
        {
            _heartStart = false;
            _communicator.Dispose();
            GC.SuppressFinalize(this);
        }
        /// <summary>
        /// æ ¹æ®å‚数名称读取堆垛机对应的数据。
        /// </summary>
        /// <typeparam name="TEnum">参数名称枚举类型。</typeparam>
        /// <typeparam name="TResult">读取结果的返回值类型。</typeparam>
        /// <param name="value">参数名称。</param>
        /// <returns>返回读取到的数据。</returns>
        /// <exception cref="Exception"></exception>
        public TResult GetValue<TEnum, TResult>(TEnum value) where TEnum : Enum
        {
            if (!IsConnected) throw new Exception($"通讯连接错误,请检查网络");
            DeviceProDTO? devicePro = _deviceProDTOs.FirstOrDefault(x => x.DeviceProParamName == value.ToString());
            return devicePro == null ? throw new Exception($"读取数据错误,未在协议信息里面找到参数{value.ToString()}") : (TResult)Communicator.ReadAsObj(devicePro.DeviceProAddress, devicePro.DeviceDataType);
        }
        /// <summary>
        /// å¿ƒè·³
        /// </summary>
        public void Heartbeat()
        {
        }
        /// <summary>
        /// å‘送任务命令
        /// </summary>
        /// <param name="command">任务命令</param>
        /// <returns></returns>
        public bool SendCommand<T>(T command) where T : IDataTransfer, new()
        {
            if (Communicator is SiemensS7)
            {
                if (!IsConnected) throw new Exception($"通讯连接错误,请检查网络");
                DeviceProDTO? devicePro = _deviceProDTOs.Where(x => x.DeviceProParamType == nameof(DeviceCommand)).OrderBy(x => x.DeviceProOffset).FirstOrDefault();
                if (devicePro == null)
                {
                    return false;
                }
                if (Communicator.WriteCustomer(devicePro.DeviceProAddress, command))
                {
                    StackerCraneTaskCommand = command;
                    //CheckStackerCraneTaskCompleted();
                    return true;
                }
                return false;
            }
            else
            {
                throw new Exception("暂不支持除西门子之外的PLC");
            }
        }
        /// <summary>
        /// ç›‘测堆垛机任务是否完成(防止任务完成事件监测超时,定义手动触发功能)
        /// </summary>
        public void CheckStackerCraneTaskCompleted()
        {
            if (_isChecked)
                return;
            Task.Run(() =>
            {
                _isChecked = true;
                try
                {
                    DeviceProDTO? devicePro = _deviceProDTOs.FirstOrDefault(x => x.DeviceProParamName == nameof(FormationStackerCraneTaskCompletionStatus));
                    if (devicePro == null)
                        return;
                    DeviceProtocolDetailDTO? deviceProtocolDetail = _deviceProtocolDetailDTOs.FirstOrDefault(
                        x => x.DeviceProParamName == devicePro.DeviceProParamName
                        && x.ProtocolDetailType == FormationStackerCraneTaskCompletionStatus.NormalCompleted.ToString());
                    if (deviceProtocolDetail == null)
                        return;
                    OperateResult<TimeSpan> operateResult = WaitForCompletion(devicePro, deviceProtocolDetail);
                    if (operateResult.IsSuccess)
                    {
                        int taskNum = CurrentTaskNum;
                        StackerCraneTaskCompletedEventArgs args = new(taskNum);
                        StackerCraneTaskCompletedEventHandler?.Invoke(this, args);
                        _lastTaskNum = taskNum;
                    }
                }
                catch (Exception ex)
                {
                    // TODO: æ·»åŠ æ—¥å¿—è®°å½•
                }
                finally
                {
                    _isChecked = false;
                }
            });
        }
        /// <summary>
        /// ç­‰å¾…任务完成信号
        /// </summary>
        private OperateResult<TimeSpan> WaitForCompletion(DeviceProDTO devicePro, DeviceProtocolDetailDTO deviceProtocolDetail)
        {
            TypeCode typeCode = SiemensDBDataType.GetTypeCode(devicePro.DeviceDataType);
            return typeCode switch
            {
                TypeCode.Boolean => Communicator.Wait(devicePro.DeviceProAddress, 500, 10 * 6000,
                    Convert.ToBoolean(deviceProtocolDetail.ProtocalDetailValue)),
                TypeCode.Byte => Communicator.Wait(devicePro.DeviceProAddress, 500, 10 * 6000,
                    Convert.ToByte(deviceProtocolDetail.ProtocalDetailValue)),
                TypeCode.Int16 => Communicator.Wait(devicePro.DeviceProAddress, 500, 10 * 6000,
                    Convert.ToInt16(deviceProtocolDetail.ProtocalDetailValue)),
                TypeCode.Int32 => Communicator.Wait(devicePro.DeviceProAddress, 500, 10 * 6000,
                    Convert.ToInt32(deviceProtocolDetail.ProtocalDetailValue)),
                TypeCode.UInt16 => Communicator.Wait(devicePro.DeviceProAddress, 500, 10 * 6000,
                    Convert.ToUInt16(deviceProtocolDetail.ProtocalDetailValue)),
                TypeCode.UInt32 => Communicator.Wait(devicePro.DeviceProAddress, 500, 10 * 6000,
                    Convert.ToUInt32(deviceProtocolDetail.ProtocalDetailValue)),
                _ => new OperateResult<TimeSpan>()
            };
        }
        /// <summary>
        /// æ ¹æ®å‚数名称写入堆垛机对应的数据。
        /// </summary>
        /// <typeparam name="TEnum">参数名称枚举类型。</typeparam>
        /// <typeparam name="TValue">要写入的数据类型。</typeparam>
        /// <param name="enum">参数名称。</param>
        /// <param name="value">要写入的数据。</param>
        /// <returns>返回写入成功或失败</returns>
        /// <exception cref="Exception"></exception>
        public bool SetValue<TEnum, TValue>(TEnum @enum, TValue value)
            where TEnum : Enum
            where TValue : notnull
        {
            if (!IsConnected) throw new Exception($"通讯连接错误,请检查网络");
            DeviceProDTO? devicePro = _deviceProDTOs.FirstOrDefault(x => x.DeviceProParamName == @enum.ToString());
            return devicePro == null ? throw new Exception($"写入数据错误,未在协议信息里面找到参数{value.ToString()}") : Communicator.WriteObj(devicePro.DeviceProAddress, devicePro.DeviceDataType, value);
        }
        #endregion Public Method
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/StackerCrane/Spec/SpeStackerCrane.cs
ÎļþÒÑɾ³ý
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/appsettings.json
@@ -38,8 +38,8 @@
  "ApiName": "WIDESEAWCS",
  "ExpMinutes": 120,
  "QuartzJobAutoStart": true,
  "DBSeedEnable": true,
  "QuartzDBSeedEnable": true,
  "DBSeedEnable": false,
  "QuartzDBSeedEnable": false,
  "LogDeubgEnable": false, //是否记录调试日志
  "PrintSql": true, //打印SQL语句
  "LogAOPEnable": true, //是否记录AOP日志
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/FormationStackerCraneJob/FormationCommonStackerCraneJob.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,308 @@
using Quartz;
using System.Diagnostics.CodeAnalysis;
using WIDESEAWCS_Common.TaskEnum;
using WIDESEAWCS_ITaskInfoRepository;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
using WIDESEAWCS_QuartzJob;
using WIDESEAWCS_QuartzJob.Models;
using WIDESEAWCS_QuartzJob.Service;
using WIDESEAWCS_QuartzJob.StackerCrane.Enum;
using WIDESEAWCS_Tasks.StackerCraneJob;
namespace WIDESEAWCS_Tasks
{
    [DisallowConcurrentExecution]
    public class FormationCommonStackerCraneJob : IJob
    {
        private readonly ITaskService _taskService;
        private readonly ITaskExecuteDetailService _taskExecuteDetailService;
        private readonly ITaskRepository _taskRepository;
        private readonly IRouterService _routerService;
        public FormationCommonStackerCraneJob(ITaskService taskService, ITaskExecuteDetailService taskExecuteDetailService, ITaskRepository taskRepository, IRouterService routerService)
        {
            _taskService = taskService;
            _taskExecuteDetailService = taskExecuteDetailService;
            _taskRepository = taskRepository;
            _routerService = routerService;
        }
        public Task Execute(IJobExecutionContext context)
        {
            try
            {
                Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " FormationCommonStackerCraneJob Start");
                bool flag = context.JobDetail.JobDataMap.TryGetValue("JobParams", out object? value);
                if (!flag || value is not SpeFormationStackerCrane commonStackerCrane)
                {
                    return Task.CompletedTask;
                }
                if (!commonStackerCrane.IsEventSubscribed)
                {
                    commonStackerCrane.StackerCraneTaskCompletedEventHandler += CommonStackerCrane_StackerCraneTaskCompletedEventHandler;
                }
                if (commonStackerCrane.StackerCraneStatusValue != FormationStackerCraneRunStatus.Standby)
                {
                    return Task.CompletedTask;
                }
                commonStackerCrane.CheckStackerCraneTaskCompleted();
                if (commonStackerCrane.StackerCraneWorkStatusValue == FormationStackerCraneOperationStatus.Idle)
                {
                    Dt_Task? task = GetTask(commonStackerCrane);
                    if (task != null)
                    {
                        FormationStackerCraneTaskCommand? stackerCraneTaskCommand = ConvertToStackerCraneTaskCommand(task);
                        if (stackerCraneTaskCommand != null)
                        {
                            bool sendFlag = commonStackerCrane.SendCommand(stackerCraneTaskCommand);
                            if (sendFlag)
                            {
                                commonStackerCrane.LastTaskType = task.TaskType;
                                _taskService.UpdateTaskStatusToNext(task.TaskNum);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"FormationCommonStackerCraneJob Error: {ex.Message}");
            }
            return Task.CompletedTask;
        }
        /// <summary>
        /// ä»»åŠ¡å®Œæˆäº‹ä»¶è®¢é˜…çš„æ–¹æ³•
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CommonStackerCrane_StackerCraneTaskCompletedEventHandler(object? sender, WIDESEAWCS_QuartzJob.StackerCrane.StackerCraneTaskCompletedEventArgs e)
        {
            SpeFormationStackerCrane? commonStackerCrane = sender as SpeFormationStackerCrane;
            if (commonStackerCrane != null)
            {
                if (commonStackerCrane.GetValue<FormationStackerCraneDBName, short>(FormationStackerCraneDBName.WorkType) != 7)
                {
                    Console.Out.WriteLine("TaskCompleted" + e.TaskNum);
                    _taskService.StackCraneTaskCompleted(e.TaskNum);
                    commonStackerCrane.SetValue(FormationStackerCraneDBName.WorkType, 4);
                }
            }
        }
        /// <summary>
        /// èŽ·å–ä»»åŠ¡
        /// </summary>
        /// <param name="commonStackerCrane">堆垛机对象</param>
        /// <returns></returns>
        private Dt_Task? GetTask(SpeFormationStackerCrane commonStackerCrane)
        {
            Dt_Task? task = null;
            if (commonStackerCrane.LastTaskType == null)
            {
                task = _taskService.QueryStackerCraneTask(commonStackerCrane.DeviceCode);
            }
            else
            {
                if (commonStackerCrane.LastTaskType.GetValueOrDefault().GetTaskTypeGroup() == TaskTypeGroup.OutbondGroup)
                {
                    task = _taskService.QueryStackerCraneInTask(commonStackerCrane.DeviceCode);
                    task ??= _taskService.QueryStackerCraneOutTask(commonStackerCrane.DeviceCode);
                }
                else
                {
                    task = _taskService.QueryStackerCraneOutTask(commonStackerCrane.DeviceCode);
                }
            }
            if (task != null && task.TaskType.GetTaskTypeGroup() == TaskTypeGroup.OutbondGroup)
            {
                if (IsOutTaskStationAvailable(task))
                {
                    return task;
                }
                List<string> otherOutStationCodes = _routerService.QueryNextRoutes(commonStackerCrane.DeviceCode, task.NextAddress, task.TaskType)
                    .Select(x => x.ChildPosi).ToList();
                List<Dt_Task> tasks = _taskService.QueryStackerCraneOutTasks(commonStackerCrane.DeviceCode, otherOutStationCodes);
                foreach (var alternativeTask in tasks)
                {
                    if (IsOutTaskStationAvailable(alternativeTask))
                    {
                        return alternativeTask;
                    }
                }
                task = _taskService.QueryStackerCraneInTask(commonStackerCrane.DeviceCode);
            }
            return task;
        }
        /// <summary>
        /// æ£€æŸ¥å‡ºåº“任务的出库站台是否可用
        /// </summary>
        /// <param name="task">任务实体</param>
        /// <returns>如果站台可用返回true,否则返回false</returns>
        private bool IsOutTaskStationAvailable([NotNull] Dt_Task task)
        {
            Dt_Router? router = _routerService.QueryNextRoute(task.Roadway, task.NextAddress, task.TaskType);
            if (router == null)
            {
                _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"未找到站台【{task.NextAddress}】信息,无法校验站台");
                return false;
            }
            IDevice? device = Storage.Devices.FirstOrDefault(x => x.DeviceCode == router.ChildPosiDeviceCode);
            if (device == null)
            {
                _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"未找到出库站台【{router.ChildPosiDeviceCode}】对应的通讯对象,无法判断出库站台是否被占用");
                return false;
            }
            CommonConveyorLine conveyorLine = (CommonConveyorLine)device;
            return conveyorLine.IsOccupied(router.ChildPosi);
        }
        /// <summary>
        /// ä»»åŠ¡å®žä½“è½¬æ¢æˆå‘½ä»¤Model
        /// </summary>
        /// <param name="task">任务实体</param>
        /// <returns></returns>
        public FormationStackerCraneTaskCommand? ConvertToStackerCraneTaskCommand([NotNull] Dt_Task task)
        {
            FormationStackerCraneTaskCommand stackerCraneTaskCommand = new FormationStackerCraneTaskCommand
            {
                Barcode = task.PalletCode,
                TaskNum = task.TaskNum,
                WorkType = 4,
                WorkAction = 1,
                FireAlarm = 0,
                FieldName = ""
            };
            TaskTypeGroup taskTypeGroup = task.TaskType.GetTaskTypeGroup();
            return taskTypeGroup switch
            {
                TaskTypeGroup.InboundGroup => BuildInboundCommand(task, stackerCraneTaskCommand),
                TaskTypeGroup.OutbondGroup => BuildOutboundCommand(task, stackerCraneTaskCommand),
                TaskTypeGroup.RelocationGroup => BuildRelocationCommand(task, stackerCraneTaskCommand),
                _ => stackerCraneTaskCommand
            };
        }
        /// <summary>
        /// æž„建入库命令
        /// </summary>
        private FormationStackerCraneTaskCommand? BuildInboundCommand(Dt_Task task, FormationStackerCraneTaskCommand command)
        {
            Dt_Router? router = _routerService.QueryNextRoute(task.CurrentAddress, task.Roadway, task.TaskType);
            if (router == null)
            {
                _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"未找到站台【{task.CurrentAddress}】信息,无法获取对应的堆垛机取货站台信息");
                return null;
            }
            command.StartRow = Convert.ToInt16(router.SrmRow);
            command.StartColumn = Convert.ToInt16(router.SrmColumn);
            command.StartLayer = Convert.ToInt16(router.SrmLayer);
            if (!TryParseAddress(task.NextAddress, out short endRow, out short endColumn, out short endLayer))
            {
                _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"入库任务终点错误,终点:【{task.NextAddress}】");
                return null;
            }
            command.EndRow = endRow;
            command.EndColumn = endColumn;
            command.EndLayer = endLayer;
            return command;
        }
        /// <summary>
        /// æž„建出库命令
        /// </summary>
        private FormationStackerCraneTaskCommand? BuildOutboundCommand(Dt_Task task, FormationStackerCraneTaskCommand command)
        {
            Dt_Router? router = _routerService.QueryNextRoute(task.Roadway, task.TargetAddress, task.TaskType);
            if (router == null)
            {
                _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"未找到站台【{task.TargetAddress}】信息,无法获取对应的堆垛机放货站台信息");
                return null;
            }
            command.EndRow = Convert.ToInt16(router.SrmRow);
            command.EndColumn = Convert.ToInt16(router.SrmColumn);
            command.EndLayer = Convert.ToInt16(router.SrmLayer);
            if (!TryParseAddress(task.CurrentAddress, out short startRow, out short startColumn, out short startLayer))
            {
                _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"出库任务起点错误,起点:【{task.CurrentAddress}】");
                return null;
            }
            command.StartRow = startRow;
            command.StartColumn = startColumn;
            command.StartLayer = startLayer;
            return command;
        }
        /// <summary>
        /// æž„建移库命令
        /// </summary>
        private FormationStackerCraneTaskCommand? BuildRelocationCommand(Dt_Task task, FormationStackerCraneTaskCommand command)
        {
            if (!TryParseAddress(task.NextAddress, out short endRow, out short endColumn, out short endLayer))
            {
                _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"移库任务终点错误,终点:【{task.NextAddress}】");
                return null;
            }
            command.EndRow = endRow;
            command.EndColumn = endColumn;
            command.EndLayer = endLayer;
            if (!TryParseAddress(task.CurrentAddress, out short startRow, out short startColumn, out short startLayer))
            {
                _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"移库任务起点错误,起点:【{task.CurrentAddress}】");
                return null;
            }
            command.StartRow = startRow;
            command.StartColumn = startColumn;
            command.StartLayer = startLayer;
            return command;
        }
        /// <summary>
        /// è§£æžåœ°å€å­—符串(格式:行-列-层)
        /// </summary>
        private bool TryParseAddress(string address, out short row, out short column, out short layer)
        {
            row = column = layer = 0;
            string[] parts = address.Split("-");
            if (parts.Length != 3)
            {
                return false;
            }
            return short.TryParse(parts[0], out row)
                && short.TryParse(parts[1], out column)
                && short.TryParse(parts[2], out layer);
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/FormationStackerCraneJob/FormationStackerCraneDBName.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,84 @@
#region << ç‰ˆ æœ¬ æ³¨ é‡Š >>
/*----------------------------------------------------------------
 * å‘½åç©ºé—´ï¼šWIDESEAWCS_Tasks.StackerCraneJob
 * åˆ›å»ºè€…:胡童庆
 * åˆ›å»ºæ—¶é—´ï¼š2024/8/2 16:13:36
 * ç‰ˆæœ¬ï¼šV1.0.0
 * æè¿°ï¼š
 *
 * ----------------------------------------------------------------
 * ä¿®æ”¹äººï¼š
 * ä¿®æ”¹æ—¶é—´ï¼š
 * ç‰ˆæœ¬ï¼šV1.0.1
 * ä¿®æ”¹è¯´æ˜Žï¼š
 *
 *----------------------------------------------------------------*/
#endregion << ç‰ˆ æœ¬ æ³¨ é‡Š >>
namespace WIDESEAWCS_Tasks.StackerCraneJob
{
    public enum FormationStackerCraneDBName
    {
        /// <summary>
        /// å¿ƒè·³
        /// </summary>
        HeartBeat,
        /// <summary>
        /// ä»»åŠ¡å·
        /// </summary>
        TaskNum,
        /// <summary>
        /// ä½œä¸šæŒ‡ä»¤
        /// </summary>
        WorkAction,
        /// <summary>
        /// æ¶ˆé˜²æŒ‡ä»¤
        /// </summary>
        FireAlarm,
        /// <summary>
        /// ä½œä¸šç±»åž‹
        /// </summary>
        WorkType,
        /// <summary>
        /// èµ·å§‹è¡Œ
        /// </summary>
        StartRow,
        /// <summary>
        /// èµ·å§‹åˆ—
        /// </summary>
        StartColumn,
        /// <summary>
        /// èµ·å§‹å±‚
        /// </summary>
        StartLayer,
        /// <summary>
        /// ç›®æ ‡è¡Œ
        /// </summary>
        EndRow,
        /// <summary>
        /// ç›®æ ‡åˆ—
        /// </summary>
        EndColumn,
        /// <summary>
        /// ç›®æ ‡å±‚
        /// </summary>
        EndLayer,
        /// <summary>
        /// æ‰˜ç›˜å·
        /// </summary>
        Barcode,
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/FormationStackerCraneJob/FormationStackerCraneTaskCommand.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,97 @@
#region << ç‰ˆ æœ¬ æ³¨ é‡Š >>
/*----------------------------------------------------------------
 * å‘½åç©ºé—´ï¼šWIDESEAWCS_Tasks.StackerCraneJob
 * åˆ›å»ºè€…:胡童庆
 * åˆ›å»ºæ—¶é—´ï¼š2024/8/2 16:13:36
 * ç‰ˆæœ¬ï¼šV1.0.0
 * æè¿°ï¼š
 *
 * ----------------------------------------------------------------
 * ä¿®æ”¹äººï¼š
 * ä¿®æ”¹æ—¶é—´ï¼š
 * ç‰ˆæœ¬ï¼šV1.0.1
 * ä¿®æ”¹è¯´æ˜Žï¼š
 *
 *----------------------------------------------------------------*/
#endregion << ç‰ˆ æœ¬ æ³¨ é‡Š >>
using WIDESEAWCS_QuartzJob.DeviceBase;
namespace WIDESEAWCS_Tasks.StackerCraneJob
{
    public class FormationStackerCraneTaskCommand : DeviceCommand
    {
        #region <Public Menber>
        /// <summary>
        /// å¿ƒè·³
        /// </summary>
        public short HeartBeat { get; set; }
        /// <summary>
        /// ä»»åŠ¡å·
        /// </summary>
        public int TaskNum { get; set; }
        /// <summary>
        /// æ— æ•ˆå­—段
        /// </summary>
        [DataLength(32)]
        public string FieldName { get; set; }
        /// <summary>
        /// ä½œä¸šæŒ‡ä»¤
        /// </summary>
        public short WorkAction { get; set; }
        /// <summary>
        /// æ¶ˆé˜²æŒ‡ä»¤
        /// </summary>
        public short FireAlarm { get; set; }
        /// <summary>
        /// ä½œä¸šç±»åž‹
        /// </summary>
        public short WorkType { get; set; }
        /// <summary>
        /// èµ·å§‹è¡Œ
        /// </summary>
        public short StartRow { get; set; }
        /// <summary>
        /// èµ·å§‹åˆ—
        /// </summary>
        public short StartColumn { get; set; }
        /// <summary>
        /// èµ·å§‹å±‚
        /// </summary>
        public short StartLayer { get; set; }
        /// <summary>
        /// ç›®æ ‡è¡Œ
        /// </summary>
        public short EndRow { get; set; }
        /// <summary>
        /// ç›®æ ‡åˆ—
        /// </summary>
        public short EndColumn { get; set; }
        /// <summary>
        /// ç›®æ ‡å±‚
        /// </summary>
        public short EndLayer { get; set; }
        /// <summary>
        /// æ‰˜ç›˜å·
        /// </summary>
        [DataLength(26)]
        public string Barcode { get; set; }
        #endregion <Public Menber>
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/CommonStackerCraneJob.cs
@@ -43,40 +43,40 @@
        {
            try
            {
                List<Dt_Task> tasks = _taskService.Repository.QueryData();
                Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " CommonStackerCraneJob Start");
                bool flag = context.JobDetail.JobDataMap.TryGetValue("JobParams", out object? value);
                if (flag && value != null && value is CommonStackerCrane commonStackerCrane)
                {
                    if (!commonStackerCrane.IsEventSubscribed)
                    {
                        commonStackerCrane.StackerCraneTaskCompletedEventHandler += CommonStackerCrane_StackerCraneTaskCompletedEventHandler;//订阅任务完成事件
                    }
                    if (commonStackerCrane.StackerCraneAutoStatusValue == StackerCraneAutoStatus.Automatic && commonStackerCrane.StackerCraneStatusValue == StackerCraneStatus.Normal)
                    {
                        commonStackerCrane.CheckStackerCraneTaskCompleted();//防止任务完成事件监测超时,再手动触发一次
                        if (commonStackerCrane.StackerCraneWorkStatusValue == StackerCraneWorkStatus.Standby)
                bool flag = context.JobDetail.JobDataMap.TryGetValue("JobParams", out object? value);
                if (!flag || value is not CommonStackerCrane commonStackerCrane)
                {
                    return Task.CompletedTask;
                }
                if (!commonStackerCrane.IsEventSubscribed)
                {
                    commonStackerCrane.StackerCraneTaskCompletedEventHandler += CommonStackerCrane_StackerCraneTaskCompletedEventHandler;
                }
                if (commonStackerCrane.StackerCraneAutoStatusValue != StackerCraneAutoStatus.Automatic
                    || commonStackerCrane.StackerCraneStatusValue != StackerCraneStatus.Normal)
                {
                    return Task.CompletedTask;
                }
                commonStackerCrane.CheckStackerCraneTaskCompleted();
                if (commonStackerCrane.StackerCraneWorkStatusValue == StackerCraneWorkStatus.Standby)
                {
                    Dt_Task? task = GetTask(commonStackerCrane);
                    if (task != null)
                    {
                        StackerCraneTaskCommand? stackerCraneTaskCommand = ConvertToStackerCraneTaskCommand(task);
                        if (stackerCraneTaskCommand != null)
                        {
                            Dt_Task? task = GetTask(commonStackerCrane);
                            if (task != null)
                            bool sendFlag = commonStackerCrane.SendCommand(stackerCraneTaskCommand);
                            if (sendFlag)
                            {
                                int num = new Random().Next(1, 100);
                                if (num < 30)
                                {
                                    throw new CommunicationException("错误测试", CommunicationErrorType.Unknown);
                                }
                                StackerCraneTaskCommand? stackerCraneTaskCommand = ConvertToStackerCraneTaskCommand(task);
                                if (stackerCraneTaskCommand != null)
                                {
                                    bool sendFlag = commonStackerCrane.SendCommand(stackerCraneTaskCommand);
                                    if (sendFlag)
                                    {
                                        commonStackerCrane.LastTaskType = task.TaskType;
                                        _taskService.UpdateTaskStatusToNext(task.TaskNum);
                                    }
                                }
                                commonStackerCrane.LastTaskType = task.TaskType;
                                _taskService.UpdateTaskStatusToNext(task.TaskNum);
                            }
                        }
                    }
@@ -84,6 +84,7 @@
            }
            catch (Exception ex)
            {
                Console.WriteLine($"CommonStackerCraneJob Error: {ex.Message}");
            }
            return Task.CompletedTask;
        }
@@ -114,7 +115,7 @@
        /// <returns></returns>
        private Dt_Task? GetTask(CommonStackerCrane commonStackerCrane)
        {
            Dt_Task task;
            Dt_Task? task = null;
            if (commonStackerCrane.LastTaskType == null)
            {
                task = _taskService.QueryStackerCraneTask(commonStackerCrane.DeviceCode);
@@ -124,10 +125,7 @@
                if (commonStackerCrane.LastTaskType.GetValueOrDefault().GetTaskTypeGroup() == TaskTypeGroup.OutbondGroup)
                {
                    task = _taskService.QueryStackerCraneInTask(commonStackerCrane.DeviceCode);
                    if (task == null)
                    {
                        task = _taskService.QueryStackerCraneOutTask(commonStackerCrane.DeviceCode);
                    }
                    task ??= _taskService.QueryStackerCraneOutTask(commonStackerCrane.DeviceCode);
                }
                else
                {
@@ -137,58 +135,50 @@
            if (task != null && task.TaskType.GetTaskTypeGroup() == TaskTypeGroup.OutbondGroup)
            {
                if (OutTaskStationIsOccupied(task) != null)
                if (IsOutTaskStationAvailable(task))
                {
                    return task;
                }
                else
                List<string> otherOutStationCodes = _routerService.QueryNextRoutes(commonStackerCrane.DeviceCode, task.NextAddress, task.TaskType)
                    .Select(x => x.ChildPosi).ToList();
                List<Dt_Task> tasks = _taskService.QueryStackerCraneOutTasks(commonStackerCrane.DeviceCode, otherOutStationCodes);
                foreach (var alternativeTask in tasks)
                {
                    List<string> otherOutStaionCodes = _routerService.QueryNextRoutes(commonStackerCrane.DeviceCode, task.NextAddress).Select(x => x.ChildPosi).ToList();
                    List<Dt_Task> tasks = _taskService.QueryStackerCraneOutTasks(commonStackerCrane.DeviceCode, otherOutStaionCodes);
                    foreach (var item in tasks)
                    if (IsOutTaskStationAvailable(alternativeTask))
                    {
                        if (OutTaskStationIsOccupied(task) != null)
                        {
                            return task;
                        }
                        return alternativeTask;
                    }
                    task = _taskService.QueryStackerCraneInTask(commonStackerCrane.DeviceCode);
                }
                task = _taskService.QueryStackerCraneInTask(commonStackerCrane.DeviceCode);
            }
            return task;
        }
        /// <summary>
        /// å‡ºåº“任务判断出库站台是否被占用
        /// å‡ºåº“任务判断出库站台是否可用
        /// </summary>
        /// <param name="task">任务实体</param>
        /// <returns>如果未被占用,返回传入的任务信息,否则,返回null</returns>
        private Dt_Task? OutTaskStationIsOccupied([NotNull] Dt_Task task)
        /// <returns>如果站台可用返回true,否则返回false</returns>
        private bool IsOutTaskStationAvailable([NotNull] Dt_Task task)
        {
            Dt_Router? router = _routerService.QueryNextRoutes(task.Roadway, task.NextAddress).FirstOrDefault();
            if (router != null)
            {
                IDevice? device = Storage.Devices.FirstOrDefault(x => x.DeviceCode == router.ChildPosiDeviceCode);
                if (device != null)
                {
                    CommonConveyorLine conveyorLine = (CommonConveyorLine)device;
                    if (conveyorLine.IsOccupied(router.ChildPosi))//出库站台未被占用
                    {
                        return task;
                    }
                }
                else
                {
                    _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"未找到出库站台【{router.ChildPosiDeviceCode}】对应的通讯对象,无法判断出库站台是否被占用");
                }
            }
            else
            Dt_Router? router = _routerService.QueryNextRoute(task.Roadway, task.NextAddress, task.TaskType);
            if (router == null)
            {
                _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"未找到站台【{task.NextAddress}】信息,无法校验站台");
                return false;
            }
            return null;
            IDevice? device = Storage.Devices.FirstOrDefault(x => x.DeviceCode == router.ChildPosiDeviceCode);
            if (device == null)
            {
                _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"未找到出库站台【{router.ChildPosiDeviceCode}】对应的通讯对象,无法判断出库站台是否被占用");
                return false;
            }
            CommonConveyorLine conveyorLine = (CommonConveyorLine)device;
            return conveyorLine.IsOccupied(router.ChildPosi);
        }
        /// <summary>
@@ -196,103 +186,128 @@
        /// </summary>
        /// <param name="task">任务实体</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public StackerCraneTaskCommand? ConvertToStackerCraneTaskCommand([NotNull] Dt_Task task)
        {
            StackerCraneTaskCommand stackerCraneTaskCommand = new StackerCraneTaskCommand();
            stackerCraneTaskCommand.Barcode = task.PalletCode;
            stackerCraneTaskCommand.TaskNum = task.TaskNum;
            stackerCraneTaskCommand.WorkType = 1;
            stackerCraneTaskCommand.TrayType = 0;
            if (task.TaskType.GetTaskTypeGroup() == TaskTypeGroup.InboundGroup)//判断是否是入库任务
            StackerCraneTaskCommand stackerCraneTaskCommand = new StackerCraneTaskCommand
            {
                List<Dt_Router> routers = _routerService.QueryNextRoutes(task.CurrentAddress, task.Roadway);
                if (routers.Count > 0)
                {
                    stackerCraneTaskCommand.StartRow = Convert.ToInt16(routers.FirstOrDefault().SrmRow);
                    stackerCraneTaskCommand.StartColumn = Convert.ToInt16(routers.FirstOrDefault().SrmColumn);
                    stackerCraneTaskCommand.StartLayer = Convert.ToInt16(routers.FirstOrDefault().SrmLayer);
                Barcode = task.PalletCode,
                TaskNum = task.TaskNum,
                WorkType = 1,
                TrayType = 0
            };
                    string[] targetCodes = task.NextAddress.Split("-");
                    if (targetCodes.Length == 3)
                    {
                        stackerCraneTaskCommand.EndRow = Convert.ToInt16(targetCodes[0]);
                        stackerCraneTaskCommand.EndColumn = Convert.ToInt16(targetCodes[1]);
                        stackerCraneTaskCommand.EndLayer = Convert.ToInt16(targetCodes[2]);
                    }
                    else
                    {
                        //数据配置错误
                        _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"入库任务终点错误,起点:【{task.NextAddress}】");
                        return null;
                    }
                }
                else
                {
                    _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"未找到站台【{task.NextAddress}】信息,无法获取对应的堆垛机取货站台信息");
                    return null;
                }
            }
            else if (task.TaskType.GetTaskTypeGroup() == TaskTypeGroup.OutbondGroup)
            TaskTypeGroup taskTypeGroup = task.TaskType.GetTaskTypeGroup();
            return taskTypeGroup switch
            {
                List<Dt_Router> routers = _routerService.QueryNextRoutes(task.Roadway, task.TargetAddress);
                if (routers.Count > 0)
                {
                    stackerCraneTaskCommand.EndRow = Convert.ToInt16(routers.FirstOrDefault().SrmRow);
                    stackerCraneTaskCommand.EndColumn = Convert.ToInt16(routers.FirstOrDefault().SrmColumn);
                    stackerCraneTaskCommand.EndLayer = Convert.ToInt16(routers.FirstOrDefault().SrmLayer);
                TaskTypeGroup.InboundGroup => BuildInboundCommand(task, stackerCraneTaskCommand),
                TaskTypeGroup.OutbondGroup => BuildOutboundCommand(task, stackerCraneTaskCommand),
                TaskTypeGroup.RelocationGroup => BuildRelocationCommand(task, stackerCraneTaskCommand),
                _ => stackerCraneTaskCommand
            };
        }
                    string[] sourceCodes = task.CurrentAddress.Split("-");
                    if (sourceCodes.Length == 3)
                    {
                        stackerCraneTaskCommand.StartRow = Convert.ToInt16(sourceCodes[0]);
                        stackerCraneTaskCommand.StartColumn = Convert.ToInt16(sourceCodes[1]);
                        stackerCraneTaskCommand.StartLayer = Convert.ToInt16(sourceCodes[2]);
                    }
                    else
                    {
                        //数据配置错误
                        _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"出库任务起点错误,起点:【{task.CurrentAddress}】");
                        return null;
                    }
                }
                else
                {
                    _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"未找到站台【{task.NextAddress}】信息,无法获取对应的堆垛机放货站台信息");
                    return null;
                }
            }
            else if (task.TaskType.GetTaskTypeGroup() == TaskTypeGroup.RelocationGroup)
        /// <summary>
        /// æž„建入库命令
        /// </summary>
        private StackerCraneTaskCommand? BuildInboundCommand(Dt_Task task, StackerCraneTaskCommand command)
        {
            Dt_Router? router = _routerService.QueryNextRoute(task.CurrentAddress, task.Roadway, task.TaskType);
            if (router == null)
            {
                string[] targetCodes = task.NextAddress.Split("-");
                if (targetCodes.Length == 3)
                {
                    stackerCraneTaskCommand.EndRow = Convert.ToInt16(targetCodes[0]);
                    stackerCraneTaskCommand.EndColumn = Convert.ToInt16(targetCodes[1]);
                    stackerCraneTaskCommand.EndLayer = Convert.ToInt16(targetCodes[2]);
                }
                else
                {
                    //数据配置错误
                    _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"移库任务终点错误,起点:【{task.NextAddress}】");
                    return null;
                }
                string[] sourceCodes = task.CurrentAddress.Split("-");
                if (sourceCodes.Length == 3)
                {
                    stackerCraneTaskCommand.StartRow = Convert.ToInt16(sourceCodes[0]);
                    stackerCraneTaskCommand.StartColumn = Convert.ToInt16(sourceCodes[1]);
                    stackerCraneTaskCommand.StartLayer = Convert.ToInt16(sourceCodes[2]);
                }
                else
                {
                    //数据配置错误
                    _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"移库任务起点错误,起点:【{task.CurrentAddress}】");
                    return null;
                }
                _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"未找到站台【{task.CurrentAddress}】信息,无法获取对应的堆垛机取货站台信息");
                return null;
            }
            return stackerCraneTaskCommand;
            command.StartRow = Convert.ToInt16(router.SrmRow);
            command.StartColumn = Convert.ToInt16(router.SrmColumn);
            command.StartLayer = Convert.ToInt16(router.SrmLayer);
            if (!TryParseAddress(task.NextAddress, out short endRow, out short endColumn, out short endLayer))
            {
                _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"入库任务终点错误,终点:【{task.NextAddress}】");
                return null;
            }
            command.EndRow = endRow;
            command.EndColumn = endColumn;
            command.EndLayer = endLayer;
            return command;
        }
        /// <summary>
        /// æž„建出库命令
        /// </summary>
        private StackerCraneTaskCommand? BuildOutboundCommand(Dt_Task task, StackerCraneTaskCommand command)
        {
            Dt_Router? router = _routerService.QueryNextRoute(task.Roadway, task.TargetAddress, task.TaskType);
            if (router == null)
            {
                _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"未找到站台【{task.TargetAddress}】信息,无法获取对应的堆垛机放货站台信息");
                return null;
            }
            command.EndRow = Convert.ToInt16(router.SrmRow);
            command.EndColumn = Convert.ToInt16(router.SrmColumn);
            command.EndLayer = Convert.ToInt16(router.SrmLayer);
            if (!TryParseAddress(task.CurrentAddress, out short startRow, out short startColumn, out short startLayer))
            {
                _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"出库任务起点错误,起点:【{task.CurrentAddress}】");
                return null;
            }
            command.StartRow = startRow;
            command.StartColumn = startColumn;
            command.StartLayer = startLayer;
            return command;
        }
        /// <summary>
        /// æž„建移库命令
        /// </summary>
        private StackerCraneTaskCommand? BuildRelocationCommand(Dt_Task task, StackerCraneTaskCommand command)
        {
            if (!TryParseAddress(task.NextAddress, out short endRow, out short endColumn, out short endLayer))
            {
                _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"移库任务终点错误,终点:【{task.NextAddress}】");
                return null;
            }
            command.EndRow = endRow;
            command.EndColumn = endColumn;
            command.EndLayer = endLayer;
            if (!TryParseAddress(task.CurrentAddress, out short startRow, out short startColumn, out short startLayer))
            {
                _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"移库任务起点错误,起点:【{task.CurrentAddress}】");
                return null;
            }
            command.StartRow = startRow;
            command.StartColumn = startColumn;
            command.StartLayer = startLayer;
            return command;
        }
        /// <summary>
        /// è§£æžåœ°å€å­—符串(格式:行-列-层)
        /// </summary>
        private bool TryParseAddress(string address, out short row, out short column, out short layer)
        {
            row = column = layer = 0;
            string[] parts = address.Split("-");
            if (parts.Length != 3)
            {
                return false;
            }
            return short.TryParse(parts[0], out row)
                && short.TryParse(parts[1], out column)
                && short.TryParse(parts[2], out layer);
        }
    }
}
ÏîÄ¿×ÊÁÏ/¿ª·¢¼Æ»®/¸±±¾ÉÂúÏîÄ¿¿ª·¢¼Æ»®260126.xlsx
Binary files differ
ÏîÄ¿×ÊÁÏ/¸ß³£Î¶Ѷâ»ú/¶Ñ¶â»úͨÐÅЭÒé.xlsx
Binary files differ
ÏîÄ¿×ÊÁÏ/¸ß³£Î¶Ѷâ»ú/Ä£ÄâPLC³ÌÐò.zip
Binary files differ