From 442a4c8ae2f17c65dc9e542cf711565d73dd5a46 Mon Sep 17 00:00:00 2001 From: chenx <1366659369@qq.com> Date: Mon, 27 Jul 2026 15:56:05 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=20MCS=20=E6=A1=8C=E9=9D=A2?= =?UTF-8?q?=E4=BB=A3=E7=90=86=E4=B8=8E=E5=BC=80=E7=82=BC=E6=9C=BA=E5=8A=A8?= =?UTF-8?q?=E4=BD=9C=E5=90=8C=E6=AD=A5=EF=BC=8C=E4=BF=AE=E5=A4=8D=E5=8E=9F?= =?UTF-8?q?=E6=96=99=E7=9B=B8=E5=85=B3=E8=8F=9C=E5=8D=95=E6=9D=83=E9=99=90?= =?UTF-8?q?=EF=BC=8C=E6=A1=8C=E9=9D=A2=E7=AB=AF=E6=96=B0=E5=A2=9E=E4=B8=8A?= =?UTF-8?q?=E8=BE=85=E6=9C=BA=20MES=20=E8=8F=9C=E5=8D=95=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- doc/MCS_SQLServer_表结构说明.docx | Bin 0 -> 104535 bytes doc/MCS_SQLServer_表结构说明.md | 3550 +++++++++++++++++ doc/MCS_SQLServer_表结构说明.xlsx | Bin 0 -> 201507 bytes .../org/jeecg/config/shiro/ShiroConfig.java | 4 + .../jeecg-module-xslmes/pom.xml | 6 + ...AutoSmallMaterialDemandPlanController.java | 13 +- ...nualSmallMaterialDemandPlanController.java | 13 +- .../MesXslMillActionController.java | 60 + ...MesXslRawMaterialDemandPlanController.java | 15 +- .../MesXslDeviceAgentAnonController.java | 113 + .../xslmes/entity/MesXslMillAction.java | 86 + .../xslmes/mapper/MesXslMillActionMapper.java | 6 + .../mcs/access/DesktopAgentMcsDataAccess.java | 85 + .../mcs/access/DirectJdbcMcsDataAccess.java | 159 + .../xslmes/mcs/access/IMcsDataAccess.java | 31 + .../mcs/access/McsDataAccessFacade.java | 65 + .../mcs/access/MesToMcsPushService.java | 30 + .../access/proxy/AgentProxyConnection.java | 290 ++ .../access/proxy/AgentProxyDataSource.java | 68 + .../proxy/AgentProxyPreparedStatement.java | 565 +++ .../mcs/access/proxy/AgentProxyStatement.java | 249 ++ .../mcs/access/proxy/MapListResultSet.java | 1240 ++++++ .../controller/McsToMesActMillController.java | 59 + .../controller/MesToMcsPushController.java | 41 + .../MesXslMcsDbConfigController.java | 13 + .../mcs/datasource/McsDataSourceManager.java | 33 + .../xslmes/mcs/entity/McsToMesActMill.java | 76 + .../xslmes/mcs/entity/MesXslMcsDbConfig.java | 8 + .../mcs/mapper/McsToMesActMillMapper.java | 10 + .../mcs/service/IMcsToMesActMillService.java | 10 + .../impl/McsToMesActMillServiceImpl.java | 16 + .../impl/MesXslMcsDbConfigServiceImpl.java | 44 +- .../xslmes/mcs/sync/GenericMcsSyncEngine.java | 31 +- .../service/IMesXslMillActionService.java | 6 + .../impl/MesXslMillActionServiceImpl.java | 10 + ...MesXslMixingProductionPlanServiceImpl.java | 99 +- .../device/sync/agent/DeviceAgentCommand.java | 34 + .../sync/agent/DeviceAgentCommandService.java | 166 + .../device/sync/agent/DeviceAgentResult.java | 30 + .../device/sync/agent/DeviceAgentSession.java | 22 + .../agent/DeviceAgentSessionRegistry.java | 121 + .../controller/DeviceWebSocketController.java | 98 + ...3.9.2_164__mes_xsl_mcs_db_config_agent.sql | 4 + ....9.2_165__mes_xsl_mill_action_act_mill.sql | 105 + ...V3.9.2_166__hide_mcs_act_mill_web_menu.sql | 14 + ..._restore_raw_material_inspect_std_menu.sql | 74 + ....2_168__restore_raw_material_log_menus.sql | 123 + ....9.2_169__fix_misparented_menu_buttons.sql | 149 + ..._grant_raw_material_inspect_role_perms.sql | 17 + jeecgboot-vue3/package.json | 2 +- .../system/appconfig/McsDbConfig.data.ts | 31 +- .../system/appconfig/McsDbConfigModal.vue | 6 +- .../appconfig/ThirdAppMcsDbConfigForm.vue | 35 +- .../MesXslAutoSmallMaterialDemandPlan.data.ts | 15 +- .../MesXslAutoSmallMaterialDemandPlanList.vue | 20 +- ...esXslManualSmallMaterialDemandPlan.data.ts | 15 +- ...esXslManualSmallMaterialDemandPlanList.vue | 20 +- .../mesXslMillAction/MesXslMillAction.api.ts | 13 + .../mesXslMillAction/MesXslMillAction.data.ts | 33 + .../mesXslMillAction/MesXslMillActionList.vue | 81 + .../components/MesXslMillActionModal.vue | 30 + .../MesXslRawMaterialDemandPlan.data.ts | 11 +- .../MesXslRawMaterialDemandPlanList.vue | 20 +- .../mcsToMesActMill/McsToMesActMill.api.ts | 13 + .../mcsToMesActMill/McsToMesActMill.data.ts | 29 + .../components/McsToMesActMillModal.vue | 30 + .../views/xslmesMcs/mcsToMesActMill/index.vue | 71 + .../Core/Services/IEquipmentDbStompSender.cs | 11 + .../SeedData/SysConfigSeedData.cs | 2 +- .../YY.Admin.Core/SeedData/SysMenuSeedData.cs | 15 + .../SeedData/SysTenantMenuSeedData.cs | 9 + .../Configuration/appsettings.json | 10 + .../Service/Auth/SysAuthService.cs | 16 +- .../Service/Config/SysConfigService.cs | 32 +- .../EquipmentDb/EquipmentDbCommandHandler.cs | 486 +++ .../EquipmentDbConnectionFactory.cs | 51 + .../EquipmentDb/EquipmentDbSettingsStore.cs | 110 + .../EquipmentDb/EquipmentDbWhitelist.cs | 78 + .../Service/EquipmentDb/McsActMillService.cs | 125 + .../YY.Admin.Services.csproj | 1 + .../Hubs/StompWebSocketService.cs | 82 +- .../YY.Admin/Module/NavigationExtensions.cs | 11 + yy-admin-master/YY.Admin/Module/SyncModule.cs | 21 + .../YY.Admin/ViewModels/Base/BaseViewModel.cs | 42 +- .../ViewModels/Control/MenuTreeViewModel.cs | 27 + .../Dialogs/ServerSettingsDialogViewModel.cs | 75 +- .../McsActMill/McsActMillListViewModel.cs | 100 + .../UpperMes/BomValidationListViewModel.cs | 13 + .../EquipmentDowntimeListViewModel.cs | 13 + .../UpperMes/FormulaMaintainListViewModel.cs | 13 + .../UpperMes/PlanManageListViewModel.cs | 13 + .../UpperMes/ProductionLogListViewModel.cs | 13 + .../UpperMes/ReportQueryListViewModel.cs | 13 + .../UpperMes/ShiftHandoverListViewModel.cs | 13 + .../Dialogs/ServerSettingsDialogView.xaml | 14 + .../Dialogs/ServerSettingsDialogView.xaml.cs | 19 + .../Views/McsActMill/McsActMillListView.xaml | 100 + .../McsActMill/McsActMillListView.xaml.cs | 11 + .../Views/UpperMes/BomValidationListView.xaml | 15 + .../UpperMes/BomValidationListView.xaml.cs | 11 + .../UpperMes/EquipmentDowntimeListView.xaml | 15 + .../EquipmentDowntimeListView.xaml.cs | 11 + .../UpperMes/FormulaMaintainListView.xaml | 15 + .../UpperMes/FormulaMaintainListView.xaml.cs | 11 + .../Views/UpperMes/PlanManageListView.xaml | 15 + .../Views/UpperMes/PlanManageListView.xaml.cs | 11 + .../Views/UpperMes/ProductionLogListView.xaml | 15 + .../UpperMes/ProductionLogListView.xaml.cs | 11 + .../Views/UpperMes/ReportQueryListView.xaml | 15 + .../UpperMes/ReportQueryListView.xaml.cs | 11 + .../Views/UpperMes/ShiftHandoverListView.xaml | 15 + .../UpperMes/ShiftHandoverListView.xaml.cs | 11 + yy-admin-master/YY.Admin/menu_fix.csx | 3 +- 113 files changed, 10169 insertions(+), 91 deletions(-) create mode 100644 doc/MCS_SQLServer_表结构说明.docx create mode 100644 doc/MCS_SQLServer_表结构说明.md create mode 100644 doc/MCS_SQLServer_表结构说明.xlsx create mode 100644 jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslMillActionController.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/deviceagent/MesXslDeviceAgentAnonController.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/entity/MesXslMillAction.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mapper/MesXslMillActionMapper.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/DesktopAgentMcsDataAccess.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/DirectJdbcMcsDataAccess.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/IMcsDataAccess.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/McsDataAccessFacade.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/MesToMcsPushService.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/proxy/AgentProxyConnection.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/proxy/AgentProxyDataSource.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/proxy/AgentProxyPreparedStatement.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/proxy/AgentProxyStatement.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/proxy/MapListResultSet.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/controller/McsToMesActMillController.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/controller/MesToMcsPushController.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/entity/McsToMesActMill.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/mapper/McsToMesActMillMapper.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/service/IMcsToMesActMillService.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/service/impl/McsToMesActMillServiceImpl.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslMillActionService.java create mode 100644 jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMillActionServiceImpl.java create mode 100644 jeecg-boot/jeecg-module-device-sync/src/main/java/org/jeecg/modules/device/sync/agent/DeviceAgentCommand.java create mode 100644 jeecg-boot/jeecg-module-device-sync/src/main/java/org/jeecg/modules/device/sync/agent/DeviceAgentCommandService.java create mode 100644 jeecg-boot/jeecg-module-device-sync/src/main/java/org/jeecg/modules/device/sync/agent/DeviceAgentResult.java create mode 100644 jeecg-boot/jeecg-module-device-sync/src/main/java/org/jeecg/modules/device/sync/agent/DeviceAgentSession.java create mode 100644 jeecg-boot/jeecg-module-device-sync/src/main/java/org/jeecg/modules/device/sync/agent/DeviceAgentSessionRegistry.java create mode 100644 jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_164__mes_xsl_mcs_db_config_agent.sql create mode 100644 jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_165__mes_xsl_mill_action_act_mill.sql create mode 100644 jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_166__hide_mcs_act_mill_web_menu.sql create mode 100644 jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_167__restore_raw_material_inspect_std_menu.sql create mode 100644 jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_168__restore_raw_material_log_menus.sql create mode 100644 jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_169__fix_misparented_menu_buttons.sql create mode 100644 jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_170__grant_raw_material_inspect_role_perms.sql create mode 100644 jeecgboot-vue3/src/views/xslmes/mesXslMillAction/MesXslMillAction.api.ts create mode 100644 jeecgboot-vue3/src/views/xslmes/mesXslMillAction/MesXslMillAction.data.ts create mode 100644 jeecgboot-vue3/src/views/xslmes/mesXslMillAction/MesXslMillActionList.vue create mode 100644 jeecgboot-vue3/src/views/xslmes/mesXslMillAction/components/MesXslMillActionModal.vue create mode 100644 jeecgboot-vue3/src/views/xslmesMcs/mcsToMesActMill/McsToMesActMill.api.ts create mode 100644 jeecgboot-vue3/src/views/xslmesMcs/mcsToMesActMill/McsToMesActMill.data.ts create mode 100644 jeecgboot-vue3/src/views/xslmesMcs/mcsToMesActMill/components/McsToMesActMillModal.vue create mode 100644 jeecgboot-vue3/src/views/xslmesMcs/mcsToMesActMill/index.vue create mode 100644 yy-admin-master/YY.Admin.Core/Core/Services/IEquipmentDbStompSender.cs create mode 100644 yy-admin-master/YY.Admin.Services/Service/EquipmentDb/EquipmentDbCommandHandler.cs create mode 100644 yy-admin-master/YY.Admin.Services/Service/EquipmentDb/EquipmentDbConnectionFactory.cs create mode 100644 yy-admin-master/YY.Admin.Services/Service/EquipmentDb/EquipmentDbSettingsStore.cs create mode 100644 yy-admin-master/YY.Admin.Services/Service/EquipmentDb/EquipmentDbWhitelist.cs create mode 100644 yy-admin-master/YY.Admin.Services/Service/EquipmentDb/McsActMillService.cs create mode 100644 yy-admin-master/YY.Admin/ViewModels/McsActMill/McsActMillListViewModel.cs create mode 100644 yy-admin-master/YY.Admin/ViewModels/UpperMes/BomValidationListViewModel.cs create mode 100644 yy-admin-master/YY.Admin/ViewModels/UpperMes/EquipmentDowntimeListViewModel.cs create mode 100644 yy-admin-master/YY.Admin/ViewModels/UpperMes/FormulaMaintainListViewModel.cs create mode 100644 yy-admin-master/YY.Admin/ViewModels/UpperMes/PlanManageListViewModel.cs create mode 100644 yy-admin-master/YY.Admin/ViewModels/UpperMes/ProductionLogListViewModel.cs create mode 100644 yy-admin-master/YY.Admin/ViewModels/UpperMes/ReportQueryListViewModel.cs create mode 100644 yy-admin-master/YY.Admin/ViewModels/UpperMes/ShiftHandoverListViewModel.cs create mode 100644 yy-admin-master/YY.Admin/Views/McsActMill/McsActMillListView.xaml create mode 100644 yy-admin-master/YY.Admin/Views/McsActMill/McsActMillListView.xaml.cs create mode 100644 yy-admin-master/YY.Admin/Views/UpperMes/BomValidationListView.xaml create mode 100644 yy-admin-master/YY.Admin/Views/UpperMes/BomValidationListView.xaml.cs create mode 100644 yy-admin-master/YY.Admin/Views/UpperMes/EquipmentDowntimeListView.xaml create mode 100644 yy-admin-master/YY.Admin/Views/UpperMes/EquipmentDowntimeListView.xaml.cs create mode 100644 yy-admin-master/YY.Admin/Views/UpperMes/FormulaMaintainListView.xaml create mode 100644 yy-admin-master/YY.Admin/Views/UpperMes/FormulaMaintainListView.xaml.cs create mode 100644 yy-admin-master/YY.Admin/Views/UpperMes/PlanManageListView.xaml create mode 100644 yy-admin-master/YY.Admin/Views/UpperMes/PlanManageListView.xaml.cs create mode 100644 yy-admin-master/YY.Admin/Views/UpperMes/ProductionLogListView.xaml create mode 100644 yy-admin-master/YY.Admin/Views/UpperMes/ProductionLogListView.xaml.cs create mode 100644 yy-admin-master/YY.Admin/Views/UpperMes/ReportQueryListView.xaml create mode 100644 yy-admin-master/YY.Admin/Views/UpperMes/ReportQueryListView.xaml.cs create mode 100644 yy-admin-master/YY.Admin/Views/UpperMes/ShiftHandoverListView.xaml create mode 100644 yy-admin-master/YY.Admin/Views/UpperMes/ShiftHandoverListView.xaml.cs diff --git a/doc/MCS_SQLServer_表结构说明.docx b/doc/MCS_SQLServer_表结构说明.docx new file mode 100644 index 0000000000000000000000000000000000000000..f2dc572c819adc3e2948d68e80f6802d42166d9d GIT binary patch literal 104535 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 z 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

lJDJU!hMh^UQF;`blqJq5?om;;pFDDm5(>K?c2g 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%0A6E*W^ue#Xe9F@Vv?pxJ70p0RfEt-xc#o{(Im=P z@4#$f$0*D(X)4;EIogxUj63>pzp#B&Gmaab+o&lQ&K|8^Ss8%JaHfG*qTO+gU9O;B zk}O5Hnfc>&_u2RX=J%Nsw6i|w0K#J9OY6yolrQST6IR>BRl`~D=Pq;U`@|5RT=Ez& z2V;W3No)2P3U7^biFFdN8CG2o|!8UovTCt+LPfp)q z;It71Q(gR6C&Vm&Y-BB9$RuW$t0iVP|LF@0+F@HEB2Bl(ZP@Z>v=E%;IhG6agOFv( z<*rJLsYiWWsLgM^R$OU5a)&)#jqJqsP;rPv)(j};BxS+R;vQ$n;`VL}#Sik+K~-3u z8Pd<;j)h;BuzQNp%*&U}&Fx5?WBC!zQC`e$!*55vq=D!S@@bC|YI8wd4??l%EFor6 z_%9;MgkPy#tCS)B4m_oZG$+>PnPaK%9yw%l;|DG4l8mJa67%D<^1;6Tq4wBs{*B5zF2(Pf@?AAlC3teE7X(XjodLud@0dy`tddk4-zPfL zNV^K;&%l4vmt)vZzeX5!x(V|jICztAf0tr%P{%_2=%pZSQQo*Pb@px%{M)2XOD0Tgsnh=`Y_ z2#wx1&Our9PU4)uy0t7Qy7u4Q4YT+%df(GQ47a%P8zFOpK9%y0qK?^`f`T6Tm2xxj zpu>jO@xKOB~X=i*A0_o7;k2!jLcw?=u{XnNvB+!YiL30cU_O z{P!6|U}t!N&1Qrb?RnaKbni*A^D=%cYIu`NgBN-7dZcb`Y#wwUF|%`nN0tRumzu9DNo~2zvna`{vtPP?qx@zyl6PX9n{-Z>ib%P{ zx*tVFu@PvrI?WL^NscOAGe$S1GP^x52Ol>~XY6E4`G`aW8*3#kChikHU*5LFCnY5r zk<+>eec54Ly1lF_S4=fmyOv_9RPTLSROxiyeiB;L{FL*(#us$kUIWyRWLzT}On6fW zkT&dflVcp5rQzfL9;UcYe-9HrFiclgKPXqXp4snD%?i9Or6zaQoff~H)JcgWCG@;{ z_wj0*!GGF#K_3{X9LN9HKxtyLsq;jJ#m_DyPyhRSXcxmqDhM`G7jQCdCg&!y@MzNT z$_G|$`T3OOZi~Tq+O{g#zfN<5TmBv_@~d1*Lv9(7=McX|zqn6m`o6+=ue#F|=P1ed zqLf@QP?Kp<(hc7w$AJF#w@C}3Tpb0mxqCx@a~Mz`j3$=&VcM;1o;Y0~31u|@xLENV zaE*Z`RO{J1@j-dy^LeC%ebi63ntjy2zlmMZe?hp9=ZV?fEF1BT7Y+W6lBg0`1rM^} z1=>1yId9M>^?X%bt>k~FR46=6#eFt2_S-53^7V9Tt>O5q{Ef`~XQ}OyQg^4FWyBxt z<>H)XqHRwFh;m!wpE6{|$)Rbp9TUi-E|i-J(Dm2SaD4f)L5*Zg2d_2Im-*DZdieDl zM&cwb7CB^d(yn#CcC2tA-A`*&9Wj=wZ+z(#+wDJv#N`95*G2@MRlHEq z#Bev~&Y07GS@mnCd=^wW$`FL1hagUam-C_1ROXR5#XTu+&6)(V{A>BzG#(nIKprINOkpfng3NwvNEewK&$3Y4V zl1ow1m6{RSg2?fvtWT4=SBX}ih1Z7!eR(f%Y3;}tRZfM-j$XJ%X-q1QEZfCFi`Oai zAD4%RLi;g+7EHXy9!?=X)AlR0JAa{4b~=i!I)$39-&mI=yxsMb0?(%NIiy>WeYsw(hYeR_9-^C`vH&;NppA z%tcz%Ai>L43x=y1+zuJ{{o-VCHfG(&dy@FPZSu8F32wNhz*MyG(8)#O&q)`0K7FCe zxv{w=9L;Go1_Xw6V!||k_{8L|c)QBES&lVk>~t@B=JK>Vq8}HvY^oto8SusPm{JcG zDZ;ZWnE5wX)9b12cjLCRRa+}7Vz=`^nz5gctjIiipXMXQgRFGqG2?)iSjV^g0)crJ z@yZ2TcM476SE;olD&r?x5PlnVjf~Bg*&mh^{f95Ry0}B_zzKF}R~p7?B_RnsG!68oYf#y=s^??aJCq}qTz%vtmgGA> z+tY9ibTeT4uc!S8q^v^s!T~nV>5R}V8iYF4ywqM#>*5`LOwh&qWHaq)lGD1*BP`$p zGHDY(V@zFmU0|C%2YCZ;=^aMYCFIh+@Z`;r2k_+irA;6?DR^x>hJ7H^DFrWXh$%x6 z*lc0Pn`iG!H8L0W{^1IVOtE%2Vw^k-M?&qYM1cXdZyEo>?&3e)FMDmrwLHPrQ!nGgvZ_87t z{y)Or0xFJWZ5PJf-QC?SxVu9j1b6o!3GM`U26uNSxVt;S-GaO1A9nUW_nhzl&b_PG zYGzH<`^ei&byanBw<*c@@x;!*PLd5j*2rYMmCk>^-FduijNgSEc_!zx4_vQ#x617w z*atsaA3ZA_LDP4(ES*j2-Nl>sM4IW$FtZhA)wP@5@I<5!(-gNNm96bZ=z2s7A})I} zI~%U;n+4o7L5LxLq$ybZjEx=MN1zaNg}fBdDJ$rQQbUrjZrL<79@pCf?5>jJ@}huR zF4@q`6rA7SMp@9f8OBgl2hbqU*8xiX&~lPTpye2F7`m}lskF$jk`lsMvA#|B`~Y_9 zAmliJ;fQI7a;-NRg1u;ndrSyPfhq>j^C7Z~{RUxEa(e-kTnI^<*0yL2sPJ;Kx2PAC`lDm)~gh>hwqAUo=fz-db zU|Q#4=tdhigq7|!>y2abDw?bo7i$5MsG#tPJUWbC;?4V0iZWwD%T?OD_Mbc4=I6#B zCB+L0XAJ~=J2=>z7r@uz3s9oYOPgB$b)HvV{!_=zXYxz`;x5*H4FPHCVn^Q$@2_}o z8|-XO&f~>;o#j%(uT@FuB;!YP_s$^Ww0D|G)MDeh*H;?4S-s|CR4G)MbyiCJKc(wc z&MS2_14azmu!g&Y8&DYDKEa3&BV?QS_KuyF>3yWmS1~BsXdvUrH7h?}B;sn2nZS9? zJWLh6LCF4~OUay(&C0A13~1ZOsCaIr2#>G7gB0gLl$gaMO1WGNCIy&*D1c+Q8R$Q^ zmg4w<3A|Qj)IaF026i(XgGLAVaTtQJ4#HKl*C1)=hz{3i0g=azfJC_zaD(S*=gq1q2 z>8-?PwdSh?T&&^?S)T-^If5+kgxy+Y_4pKw^-2*qJ;p6R-MjK*7FnLbhg#r;7q+kf zf~yK-ImST%P&C1zOBWj|K@vY@qV*O5^Pz<`rTSkF=Z=>-xc*Jms8HHKt2(aLRqJg1 z#0g%R;_N4ElxYqOg=+&2M138A1pt%-QJEmg>+Ga-n*r|T=Eq&5BuAvi>xx(rmnB}_ z2D*Y;Xd$ReQQ2<6EXRJqh;sa5dZDZSfvzK9p~E+{(9d$^y=UFnWBf!VWjGj1pjn7M z=m?x=<#hbe;m-0^NVMoV`V*eDY+9?~o`AES3AV?SrjlEd_Cxnok%FXoibSM?*H z2tno&VzCRkO7^6v!YDR|J0ef;_0=jNk!}!rG-z-fm|lXyU~jpTfrv~5-n&ZBbt`Cb zxl^EKSO|h$V*$c2DVEAOphDAPtLV27)E9}F95}R_48hf+1&In$-WXjK<#P2Tz#rL# zKV&cb!*O09Hj{O#1i6p8v-CPzFgCDDQ0hgt-;FT~>=N|AKZFsjWENAq`&B^!y4^v| zSp%`HCZH6g)R1jVb+;jb%}WTj4US${ayNOK!`5AgKw6}=qV-D81sG6|9#BsgJW!AO z-+CaK9WZxTahQR6E<|r!>|MO=!o?k4BSEWqr)zGOi?@D`v4aHt}4>$@-2to7&UjYi`FTWaZ8<)DC2N zKZ;)QI>siHorPaT`Rpq^8FL~svpY+huTYMn`_@(4Y9Dcg)z5YGzfzR~PL?BD|Q@Iv~869+_4 zoAVvzF!U>`CL zV>&gX?_`U?2B^ahoZOiqlR8B`TVRfskvZ~Ld+4t%oJoF=td|Y-Swp6hNJ-#Ju3r7> z*hGq(@QeDP^D?*=wt?Gqo^HW^V?DV{hD&6itnQJyZ^(M<4v^}{-1Lij26nwO9omti zCfvI3{D;Czxpz@F$UCIIDdAG0Y@p{RcOkC<2qj8cJARE?T{sKsCHNCz+mf8g>ALMN zUI<5bI>53Dfv0TPY$B!OAva7VY5<@mFjXyyu++E z-J6_Ewr>t{o%YaizL0L(G|kBjWJzqc0J2Qy0$D)e|FFPc$Lmj&!tWnQ?f|IMG~8sy zl%O5t#wLX+QLcer>Vt(*x_UiB#;TrG-Co9`u?w;X`|ZK(bdOuvg`fvsF8U$D9&x)~ z2EZqIKiGaMH3~NI_7!0rLuO&{e*~43U=_Ofml+mA?6FC>ydm%nN>(AnE=E=%^hDt? zC4_6|0GjmfKP0&Yf0IbyOFyeFW<0jA?1wPJ0|kNXA3V%1kCl~4g}qa#9J~NR`qQti zi7?$JkokTEY&t&oyzC~Yob9J)VJmdK8v%beBL2sS0bXz{BJzoD=JiS2OTDkNcRCHE z0MlE#{>eLw$2-e~!aECgBvq!`P7YBkn8$T?vH#9^uhH>$?&xrOY9zvYk&9*lLvKP1 z`19>leH>m++KbX36#GS>&6NA*xoDw}VbgLbivF;F4valT*WU-G&99!xRX26x_S);g z*bBEXTTv16nu(5^`#kQ8${Z{gOSoWn@PIPS(oi!-eNG{VZ^lNT8J)B=v`B2P~r1SlUN7>)`i&?ShoIkH+8sz&1$@pp>Evns;`w^)^R z>9f_dlugxYl@|9x79%iD%p@DW#~CH zE;`;6m0*QHzk8N!DNjSwY!y}Bp49H%4m{9q6 z+@qv>vS>Pd5_zfCqqG>9b%9e{i3$9+xE9EBa@Cu`ty$*Ax~zF%v)&e%rHp?d_rO~n zNZdimPWeJ3uG}+u5vWfc8LiR%{GNxGK=mVT#3Up~`-mOeRZD#peqn`hv*Vz!Z)7aca_CcjbsT)NEwv*`Y$4AKXlk=N|3v`u% z@+FPdL-2$PwZ$h_?JG~?7oc&@Eoy$KQfo+Hg;E>Yhug;BEd1si$bBSWBBvVVl^$Rl z96JKDpyeL+bdzYeq+o?G#ULnOS`{87L=NZpyNj*d6k&xL(*I=SebUnEExsZXNHX_d zNsd}y9MWQi(iUtmDz^WfC@D~%WyAXsxZr}h_S+FY_wt9KzI6){>YC)LfuHm(eLu7P zU^7MT)Rs*EAL7XooHF#uVz945hrcHppg46Oj5VimxMSN`Ul~*AvR_>Gt6Z!dgeQPLiB`ByP z)XF8|;J-=cxr+uvjbcXz(?sy$s+1cS1>qblcr_DWp)}e973I83gyJwyNcnRT%U95W zezD4lT!g0)J(rhC>VPjdiax1;|KRgdFjEx{<%4JA1bQ%PTk@z+N1k1*YiXSdVD zJWO&a?Y=ukLBpVfXJ$N6c2qJBZAuOVKlOt*dAc3Zx0=yC#mZWTyixPf36k?-V!3N< zr17!+cM5^L4uA0u{>A&@o%cBG5AV~5zj;5@hS=SrPa16pZ8j4GHOI2!Dr~`#@>I49 zK7kLK!q#Csd@g6<-E3Y(7@Us8fr+|@jS8@?LnEN(u_GgBg8H?DKhFcweTj4zg*-UX z7eXJn*?jkOh)j@7Ii}a;SktrXyQ9ZWAeAgb#j{S$gH)L=>5#tJ9Om$i%}FwV51NFB zi8v~xksDwB|Iezu>Av5YrB~+{e|rGW;r;FR{sSlj7V(#memP$XWbK2{d~%Eljn{S; zO}5Umy56|Op08`!h`Fs%pQZ!!w2!Ba^%#SX>$MOPtxq%Sf%E@2O>tzOvFr#HUGnVt z1y^lvFLNV_Zd5g@u*Ej%M#tTzl>>e}#sscd#={cxq9?{5<2YP0Lk$AoFn89>LL}OP z>4opIXbb&F%}artSu^bIJDPm}ORbFXA$nGSGl=X;_JpS&vnd^fh+|d!ag>Een2bz0O54NP~kYYkN-6CyPlk<%N5r1z%r?*xJa6PV%foH!Ho*MC)z#8Oq*r@(-;9}= zmTSWqnF8N`vEkhQv!+r$#Tn zjgXWxoDHcf_1C)9+?=@ArX40vBLecVU+rACT2?xSk0II>M88@^Z%KKY*Co|B9r;&g z@d@c7Qae>*de>Fai%OT0IAN-Y=dzwEzaH7L1E)H>q)oru+w|7@S#qU`FwbFmm2Q?q_hMk8&4VLWhyfGLe3VQPK%}3 zm$^hnIR{%gCx9&s0uzP~MV+L$YkiXwc%2Sxu#_oB)CAIdvzNI+q1d1|8?0jjUnIy| z81k{<#}gDL|0L`n!hxVp2wVYtt2ak#Hkb&cMz%lj@*I4WhOTIk10G@kh|Mja;C;Ip&pgbE^EvxvHL zW=||4=4TxBu8)r-#Z&ObQzCo89U?(Pc*_B_-$K5f{1uDr_^vq{PQ6;S7AZ<2FVLtf#DZoZb*p;Bl>M^($&^XqM2iaN zii=kCf=JTmFo5$G!a+W^@umXeL+kxy0miycqELwvmMdkMGx2cL{N^FbzPl^iP#du+ zuOL-;jxZUvVdl;loafE2{sfykjA3=ZG@XZbrHxW5C{MC`chi;cUquJ9^&{?pA-6 zgU}`s4G)Ph;rq zh+5|Z#rHQL*tCCW1w{VjjrE5YgL?>w3B-V*t#4KGJ1JHUdB*r;?^sHtYVkW0Qxxvb z;Z*+HgCRymtbbq{W|LCRZB)Az7xun59MQXDkw1`&y7 zu-x?3Tc)eJ6vT-(?vGIzQNLHbe*gZ@rPl@?{=Hkg8oPNV|P?%j-n$#HTpvH%qPT{ONO_wXt(f zy@B}U`O%YC-*#d6)y;?M$NAd; zjQ8#({g&C~RYLiFNY=ewTKWF7OWnFa;pC#DvF({j&k6n6 z+hpqi3Lzhlmzmk)hf(_`9z3)$>4m;MdF7qegGcVXS?lHJ=;c4t(ay}zM{Mn1KOTDBFK*tx5?{~1W|+2n z&@at*v;e9)(>LT7Hhv!%zq#$}JtZ*BKL2>UdycNNh8JoZMeIEy4)S{f9%$$JC4uAt zyL*29{19Tc-)%~b_ihUgDYvb%>{D9C=P$t3;+1U}yj zYM%=cP3A7%kM3TFjBi`lzBFf@$3OoNI|qK@P& zVnQk950%+p_(jelARE|G=Oz;BGJuQ5#jQFJzsg>5em}G3T3sY)tZK2*zh3I#GJ9SL zJhN7Ns$$#^@#%cbl?wrE%)ZiD{@joxYn+|sWq)w($naU7$RdWuWpl% zn=$@u^L}?Vvm{oZDD8C>L~n_OZ5s$4uv}+pJ;P+gls;oNfnIU&sX8_|jFhzA0jP;hP~k(HPcu6NP#j z4>#lRO7|wy@XEf^Nesg9@IhOa{q)MB(dW;X5$!18?o0y?~@ z-cW6l6do#UhS`*Sh-Gx8t}S>HZ&c43vf{u12KBw~qCw=2P_re?Rc@#bVXD=ML6c_4 zyHq_Pv}B_}bkz-$VD zm2O}h<_JNfTFJXqydZc=qd}yOP&a7=zzBeadBDO1BG9PXKZQVqzY9ORO8J)kf^pCw z2CY0M@6zyuXa*L31V+*cfPDr=mYx171akjfsI-Q9-Ej8SytrAvZOZcQzYRKL z(#;eSAQAHE+6Uvvf^@Z-i!Dya@Bb!)$ld*aMSf7bCs9$;xNUGL#NY7C-W2L82A&o(D7o!ptWS5)}Bf5Y!H4kIDDC+yP;5k5tz>tqYP zA8sND^iitgSLrZ-P_iR;CqwOy?;v+C7xI6M3^r_^X2F2pJF=xwq7?O5aidZ4?7)?i zc-?jcuCu=zzLPZAPcS~dDt;yHRp0Y09+q^4ZzK1-SntU7O7cG%m?u!>;2Z=%g-ovL zxs}7)|F+cd{q;6`eBj!6rKi;0v6INqIS#MZ{<|J7ZvHsy+K6lPb^r11WAq2PE8f*F zdUb8a@jApW^~>knN)U{8$ZmHW9I4~L8$Pr2$o&qU_KwtQ1^UUXcY;HHGH^P3$p0ks z#M6$7)8{%!*KrlPeGxqJ+*_7Bi$xP8(b}+qrQDef)My((X0{?8Qg<(XztTeQD_!&ZqB=}bf55=SdxGGn|~5} zS%Ej?h(GGXk*C+k)2eXdB{9`t2e5uVbS~!W`LIO`<{dZ|qDb3GFLsAzOl<|J9n;P5 zs0SG``^5(8gFSl9vtSP|xrbm6NcYzsv?jHrMay1QHnXf#n$XhwExGkW*}l)~iPiX7 zt9I)#{j`O*qiQ8_;;p64=O;V~EyyQ7cEZ}nv*DV_o1!rIZu6&N8wW8BoYCvIzqE#% zPJyTybN!guEb{z1+LmV>JT2RtP#Y6>?qW}B@S*2R>5T!kS*HHo#b``Ya!o{Hr}mqN zw6s32{PK^%Z@0VT3Ho9O3uytQ6ivEVgHBxdLGdv^sqrzrzT{|XZSmPh=QkrBv$^(x z>j?SsPt^b_Q}&5&OX2HX{3azNGcvQ3P3o}*b1bX-!N;ud4ZqAv%>HE4a}8?jz*mQK zZO$yr^ZfqnDanCLlc1rn1T|Vp2Z9JQ%346Kgqr$uf;hj{^;nCadN9P&fG87qRHYy8|(Lg1L(^>v2(uF1o1pe8`Ix zu0C%OU+A-}lrdZC9z_~tpys(el#vtkyNlnPJC^K@70!!J&+y>4uQYIjReH649b_R{ zdR{T7ccvp6e2JV)TYJs;FhQusEyRnr*E`-M#5WOAZtp!;8-jEsMg2(I`KYq$D8oF8 zyFzK_P51#&Y?mZ=YMMrbSR+<{_9Qz^tm=^V8&Rw>bVOE}Sdfed#8Fo&!uuzF*9kxF z{MX@{3H`ko;*B^5^!}W!0mPy0VGfOSm~HpViwphR0JhpLAHv8dZIqD5t9-7AuWrFD zF*&>YUM*1<&rXDu&_@w1jAFfGWC@23Y13P9QX#9zE;Qqg!o25t*KJ>W4%SWv+pOd7 z{n5~O%;Y12jLEX}!Bb+=CH>J*;?0ER1Hp}EkcBOh68+F{XU$CHgUF3nkWEt3cC$Y@ zKwpdPpIFs1DB8oJtzmLMw^o>(HLn!AAbWl^P&p>g%XBU>~Fy<1gSn|&y{ z*K!MkW3`86SFwW1bmm~f1fd(;xvK0!-SSY48#>?oNuqcQW{2Z^5Dqqj(E6feadUK< zL?qx!A?W!jgKY>fH|Sk>mm@kz`eFD||L2d?{iI?17!G3%0%$r0G$WVrrStk!Ku@n} z+3ua>r^+ibJ0ZdbgIT5EWn$R*od(zui%x+N`;iqG{zmsqYEHbR#a7;?7_ZlaX4aQx@9uJnbzejGXDQb4xRc4G83lh6nDa^a)ag>K>PqnSL#48h4W}Dzr+urH z=#Z4A{3ynLz!CYaBL76URo)wIZUS8CL(xraLCcA`_gItMZ)N*?^{BdEf|H+14Brqp zJ=Gkn@r5r9$UC|VxImSDiI=S!Ac?*-jhDTz5-;@8g=#u9&lJM3(TpTcs4-Qr!oujb z6@o}Z`FQgBsUMQ#rM12Oig)eTU8k*{5`9CPwAEO((Vfk9g3Y6bnjSZ$>_Za9esZ6y z3Px884QG>n8)%t&a|f^I;*se|9P%NWo|TXA5k;ki4wb9gW|gjQoo-@mCXv58vZ5kzxHJLIA zxqJQQ)g?s8JU#b6eYh}+%`mMZWZb1sG#OkqMDt#;`Q1Y?lm$U_cJ+F?Yrt)t(tVlN zG~(zcHpa4VecqN%ion61)rsL}BaJ7GmlKmHNq6M!XIPI`3x#8Y9}vUUZZ@AoIH00# zG;dl7 zD62Z0IEU~9ibQyWBMt6$E zY?!4H0&?{c1XJO4^5558awa0w&9%S*m41yHc)rtYEbj9FiP8mz5-Y}wkj!KyRL}-0 zL2jFzoW$R08uiX{z0+R_=6JC6oIf7?(F+tP{e>JyOQrH&b;p^%zQwU71w`I zO11u=P_{$_?u4rW{tJp65GCL99R>A6Z{(0?zK6N4=MWikL*Y$J1KE0bs(sFadNJo6 z!U@||5&gszljm&ECwA2eLqv;Ks1;3X5XsT5O$}?Jl27QdEksro^KDl*qe}7Rx8-uk zGMB*E(gw8k@UU-xPy#R6X1eJWd6@qf6a!UjA-mZ<#>6k~S^DXF=<&koq=Y`BM-OGC z&cMeqt1g50L+~-uHeH&vZv6o_E(>@seX^y8Yyx~C(_RSCQ3eU1CPDdGrZn;b@2Sy2We@B`Gk4TzzzOa6 zGR1M2=V^RZr0-dbhGg3mCz{I)i> z(Ii1R%MG-CSHFos%m0+)!vq6ip#Tq&)pa@Dgnbufui=(2h+#Ya; ztI4f!El&#eghZ6@;)?<*3Ss6B)<-XS7u8QhmVF|+-$*%cK(VokLUyPT+LcZjf%&rs zPBOG>Qx1xd#f|w$JrtxX1_6QZo&$-Tnn1I)NKZ-(LO2@(keASl1A4_J1_9FYNPIFWqxG+N9j=z@+rnM#|mC7-KrP(kNB+#jmMGom|x>lTTEwF0iRVl(t(*$^K!85}P& z*jkhNRzI_hz0o!ClFPFYMulq*C}$7OOl+T2 z!Ewx1*THeL%x&Er%sldZmH2HPGwfcPJp5|JQ=}yR6Bfc+j#|YNvkhpee(CYx%IWw@ z;IX~#t(2ohh~{`O^nBGljp(g8xy@|JTbohH@uKAI^A{9(t7EyPZ4IG!7lJ?+(1Ji0 zrCAM9#ooo}cu^>H1#ik`*ZC8fmHb==nIrJY{$=MpNw*NnViVJW|Q zMvkvaB)r?Y}u_0$5qrR1uG&xGd!_|7}k*Kz^TTd`YumDf`_ z(MPJ;93m?Oj{nM3v}s_m`tO2c z6!lKUQXhWU;2gD&G_uX_nL5f5%tX>AI@&pkK5OwFAj)~lc}gtyzN5_f7H?^orqVfW zA+{ka0r!2LHRnmA*t;A!9i&kFcBj#K$yw#f!!^cSYEylDf2iIW3f7_gx()xJ_`21+ z?|ao(=3V(K<=FYcWk|I$ph&2ZtSjQb7&u14gx3@K4+GxUqgqyuHaPm!)57ygN&QOV zR~>EPAwxxIk9n=&CDI@9lvI1ZWRv8O1kp(wy>yUfY3LM#1SW&bRqNzI0nkFRNnY+o zD=cY!6MTPQx}}p#TXUyl?S@CCnasj|A z77u)F_%hat{vzKnL@H(~6=&To9_If@}-{sqkv=m}5WR_&`b& zwRoJ&@d{G~_tE6*%zXXidpW@2>e_`Km5SSeV+v^8WqU*jW{L%yD24xm%$j;K-P%o5LCk zXNW_dJzJQ5b}Wy5oN2G}-E4rRCa6M++0qte3+hhEfx30rXd}Wq?CKw!q5q1Ltvh=v z)@n_Yn(8Ggohskt>Q)Z34E7+WQK7WSoY27JyKYidfpi98EL@$^{3(R_4AK!uy-U3}c5ozQB*^-J3HQN!u$;AueO#^=+!mgHBL z)W%%&f^PW>5uGg-N11Y!Q~5!|c}}hHNrMR$E-NhF@#O8xZ0SsJ2!D~T;vCvqfVmIl z+LyNc7wgWVlh@Y_!V_lk0$mj7EJ`{^pWOzsldgbGD@6x6R~{wfi$WCY>tKl`L1`xG zO0;aheJ3`8ZAUH}DiK#u2uc_3`d*%JI&i&0G`+CoJ|z!ExDoo?aC(}m{e&iUiL&a7 zT}ADE+waw0%6iIrl)~V9dnSB+J2p%5{#RfyvNZM>Az!pI6vo&Ru4LONyhp6nl=V8oRAJ-`_ z)%2qMufm7);6}=L_ZUs1>1^m*ZdArru?(NH+p^naeW6B1hh0Jkm#b4D_8?Ibl-Gf` zh*YQl-26v@q6aLI4*F$1pa{3uHk#O1*V#m9v#mQ|mdW?cN z+T@INX-pRx@mgsEWWeJk;i{ilPdryS_)9En<_zZ! zW1p}zBNt{t>Lm5+f_L|EV|lX-7rM$(&DD@b*cCzih2c~#oRYAKv#rI=ms_3~B01Qw_B zk-+I&BvYYNINQt+bwxeMEld~3-&JXY*Zr+3jgx;*xO&cT;ZVg6!t5Va134_#;&aP> z@Aaf3i9IXxWFSd3og%>`SghTF2&y!aQzMmWoGxlmbx>xJ7zVe-3%6X>?r_?MCB5a- zdCFL0cojvUW|19ck#dF$p~WcB@AYDiT@a$bov1uVC4SDUCmO^1mCmUy(H4=NsLO4A zScqVTIEF{tbv#&pV!LpWV9=);qMaudCY8eZNeO%m&ubhnx~CjfWfkMS9;vIwQA-q+ zhK}|yXLwIf`4*@3;fNBR2>lPcg((HxF}xH`Rp0q&?ZwsdEn1?80$`q^xBFPE>ocKJ zI6YH2-v>-*bjr)rffzn`O~1xnCZ-*Nm-QRUoV_obzr6ljG2MkG$Y5Pd?^!35vx#kQ zno+DQ?u5pCy`4TAw%vd`rc3(bYSwh5X1VLH+fdfGCs+N2ZgdU@R$rF^nmKadBfIU$ z2h%y8zVFs-V{b6ni_HQJQ*V%t)xx=eY$-09S}Gcg9EVRlcjmp>jp_*wMnm(2@SMBH zF@6NVcW{a$L*6`O7fLCMKi;MbqnXBtM{z;{iYXP+BVTza^bM#`B;K18Oh&j;np`s6 z;@k&5y~`!y#a+SrJ~av8Ih@Yx5_k?T4Js0%Dy7LDgyt0RryHrF0xhKppmQo`RhD2Z zF~XS~V2^h9OBKeX)I$@}sHZK?4fUkS4(}<+wTJG`r@D?~%%pR29Tdd`_GhxgKfki7 zNPr3m79XLit9Ub6D@)+2#~Mfa(OJo02CyKQgIXFda=ksyM}yMR-io1{J+{i-q! z&KZ@XuCfhmG$fM!>B5H~;Y(@W^!jC35HeaZ47blUc8{Wt^dx}2pkKg`(Pw%RZ$!j% zH!!6%Lebd8xd~q}mlyv+bKDQMGeKo$K`+kjliMx|CxX<#hz&zhSNZnVSRzEQXyc#h zUJ6XTS}->dO!emCW>6gS@7zl~YKseFnkq{Gb6;9asS)vNkVNmXSl=a|9gfBvmO+Qc zKS8!ln+$C=@ylQ4xwOhfitZ_Av>P~BWjf@6>-Z>SnBrH^jjAWhp zNpdVfv}F-l__FI4EkI8o>N#V3X-i_yWk;c6r(|>p$pIOb_WT_KP1_w*=!JLkiDwI1QG;@>f;M;oN8)sGU=qedDxgdbRLwX zf|uwT@G6~koz&$gPL2w8%8!K>S(IsP64y3I@g*PJV`f0*(-ciQXJ;B=jp-}BwZI1u zBk<$*cp~sgAO{pCyV0Lwb(}+5N){q{AEkl-+lbVuR1E6UMyLyhqH9rwDrn{pB zsYvtdRXZ4LrI3voZh_ENI{=4d^fzKz>Bu<1FXOR47YB*S4=ghhICU_0>8J~RZ?AE% zfrFP4r*hNcsdhMymwW==_K-hS*TP5uSV)}-Rjr?l<4nl)Xr#!QBjlGp0>M(GuI;zB z+-kne->)nvz4Q+Phabc+M1qHw_6wSIzA(`y>W%qBr&cBQZ*-e2g12=``0#@{)ehq7 zT7JN|BF!D8;LLbANR3keF6*tS`UxZ8Zqi_Dieu7b>(AI)izlOaJGUUnSiX-oO9f&4pJWno?rdQc6vQrI9o77_!2S|Be20SP3Q zBJjXX#^i&)# zbOI&*N~;mRkV;ns%%&<$A~2h+#sA7?Y38xTR-el0oH%XSz<)AsbfZhoI>=5yBUC$# zm3&~F>mjB3n-D}Em`o4&5EAI9&^hke1;Vsnj|37o>6&_!-?ZA1Mu<{gkVXWx_>o7L zmKr}5DiY5nn*r%E+`@?sMaXxE+bzr zmk(qER156?&Y(u$2Ia>!^9eN45hErlULcrv`Z1kuCa0rI02(|ikW20_-1|s0eKH;% z%0sg48wyT_v=+`2_yuP%-$-f<^b~;=8nHy9#Lmreob1@QSj>$QbAe$SiIJiBt&eaw zi%}qmmJB)A0HX$48SV%r^S$9tUJDfjSqv3Ctm$~zLB^7J*;4Jpy@c)~M)47T$uYhR zp+P#EB4eV9egJ>K-9~1NFEcnq%CLI%%ApSx3jJIcDr7Kz+-8OftlbTfPWI;e5bz5} z;srE8)Z6aVoh#F$uNMCjdK2#wT3`X&=cT(aRET5;Z5tW*=WS~FA>e(|yhR0DE`;O_ z#5?#Q0BhIk__;$T15;BNOzT}m7l6}x{4ZQs9w6=|^crjU12|343(6;);5uKeFXV3^ zpUK~{2cNd1g?KP7L%unH{7b*wWyoJcSf@O}L!v(7u1OT1sqdc^f-}|BUQT)_QG8HZ zfYQ4F^B(^jg$vAcjSyVO|8P(cD%AY0-uD4~8(B9}NNbvbNW*TfzlaRQ2SiBhMFiey z@1c&Q*t_{g^yB}7;pGY0?obk|=2Y?pv{~{+B>J|W*yn;~8=2@&hI}8yq;`;jQ}3L9 z+hn~`FQ6|7m(Ywu8vMZDN5fmk6lNc>c%^n9akZJN9`I0*ycvKgz8?w^w+ON~5Lr^< z9&eU1kH8|?|C6QMNTI;%vqj*7Bz;1cp>;xn_5CRJh>(gw+;Eey$S;`E$!0b1DT6^8 zT>LK|Kbb!7u6pNBQQC#&e{A2jPJf<*y4mOS4hk#UYy_sj2&| zmDYYPJ9?6PSGNE%kS(y5RCGCvn!K~sh%=uJxH+9q7>?E$S3Pyeh3<8PnXWnm*9oGb z4DDqO?{QUwbpI>}3pC*}C*zfBw5Fnt8uVIec8i3}u$f3hd+f0Z%g`1@*bLd;Oh;fK%xNI?jL0 zj<~w@vx~oP&Ps|#)K`+4u0cDw3BeY>IxL93s*NHIb%RX*)Sdnbg+&7R!4ZoxzAsNv zWbp+?!dY)x6bOV*3Xp+2e*YxKalc2=X#90obK_crqk$CggBh^4lp1BeYv60~tJ1;_ z93Ev{7a$#K=1di*#_=OCS6evN`Q1|gBnP^>Uw}n0aQihXV}rOB#$CCLN&3&pu3HQjaGh(_tnAN^4B#Ev3)B=}&Ls=qcS*IAFJd&nS=f`f02DHazGeLm6zbGsP4iq>NA+uhvxjz= zy)|GhRK{y*@_LAl`RJi%-3Z+ukh@I01sUdWyeK2-5w^%2+ea&FaFumc`cmWoAZw!k zo;Z#g3pubEce6@KJO4~q&(uSmW0}=Wn`23&Co|a=$I`fc>|*2~H>An=6AgH;T??h= zCdAHhM8}1k3wpH)wTY#y+(8>ulnj1KfVt7sgn+j9QS zxgpL*v%J(~vWPvr(`&r6iMTW|!J{`UAE*mPe1aC3AGRG7Emq8dJ3xr@4EsS}B(;jT zGB)%CY=g6C8*QqX?N+)=Kd)n6jWzNxywN}8N9l|Ill+EkmAZuABFY&p(2$3`lLGHR zJVI)7$^(Loj=qO{YK=TQw)=(%G!ndf8x`0q*+iY!Qr3i^CXpPAvU@{^i^+TTRQacQduMOh{iL4oLXkR7X* zzkqpJA`TMUmg`x=^!%y2H3RfKV*R)01)3PB=v7`?jUNp4-rd+rlE}bLADgE#2l7F_ z{)7z1fF3_(F(l+c;0iD4dcs&vql04WcDk0aJr~l@_pblu`6hx$4@VN+DA?5Bnax>@ zyY`>FjJk+mFirX|_a;+*=+t4BZN1`tU z7C)TvHem~<$KRPUEW>I8{&MX+19YAKx9bU+(~zih6m|ym^!O!YvzD}_G$z;y8J&sY z5SJ$VYB?n0ttRQKFg3&2&<1G|ZA4nvQpB`o%8yb>RB$GE%j{o8Gdt*EZq$+steZ2$ ztGs7aQ>jXPdvUKrjgANR@0N-ahWwSh-iQH~gb z_srN9B1R36dc#i*P~MH0iA_(c-1@T56$7kPsVJgo8l%P>E}Hxx%<7jeS?7)syqlb# zl+K5-t1gb_9y1HnsQ8?cfWMni`q_p9F-oq@5Pm-8FN`D7e_;gP?l`E%%M7w6rndt1 zi+5ROrMJFAWa|C{agVW^plB9xwiWR*Y)&|Ad4y+oKn%hQ$-8$9tO&*1z?K*Lmx$mx znsD1IIaN7D0XQc*vP9A$%cB62A}@JHXeU|r_(pbEb0!Z_<3A8Zn9%<~NL~%II9bVN zcm&7I@Nn0`TiGi}4<$Tyh zW+r?iZ?i#7MUkkJelqbMK+ds~`5Gow%JI>a+%@;b`)AANI=twF)BLh{;6ZJc9e_fS zu*B{YLx+v$GbP4Mor7(wxe>V-=(v#Fhrlajm-0b_P;tewb&!gLp4Pgv0ItWR)VAD= zu82sqf#St~c9hQa8iqiky^MpXmj3Q95kAxbfu;RKqGh75Md4fsKdWuB`-JWz|82`4 z7J!gj!{BthlzA3oesawI-Nv}wq;7~MGt!*s@)N6{1aLeK$}m%C5KA^g;U|$K2!pkZ zjWwJe*W-eP>lu3n-G_~YRWeM1H%d0RK36L<0LM_9SQ9r+_CBKDweE+&`lBM`U4{AI zD(tl=tbf4(RRCKFfKF!_69I#2ya-2Az4`^1|3}hn+!h{z92K|L*eM z2rP!fGN^7-!WzU9L4L=GC52(Ay%DJ%>iGZqx(cAWwY7V2*W&KQrNt>ufdWN~yXygp zI|oW}w^H2Q-QAty#frPTmH*tncW-Z*H-BbMGLy+#IbV{U{Ymy-<&s-g$hlShb7;gX zllJ8+B)Nt_Ub%bJ$s>c{B!~U|yP%vSHajnJxG9+|UTQU5t&d=i`@MsP1KZlrP z35x4>LjeHW2mk=aKSNB69URQ9O@ECnsZjyiXK|wcT-NP4>CxT8V*`TS|=lq)e zg|30Lyxn(t2S}xVaDT}KiItq1G;ood*8FBnpozh}=4^VKKWiB^j>gxt>Vz@DwO?8R zW7(*xY%J+E?`s~)31a?_gb~zy)3h@0hCRiwUi4)%n=#s1I)>NYmZ37ZNZQs>J_0W= zZ&N2UXko?E5T+UN^1rBzN_SAtnzOuSMvKZPf+m83yuD=3r_}^-3u!%w~6= z_R&pAoS{b7&}#ZGUf|;*(&16mFJl&HvtQ|Ev9W668eK0_IEC`IF(V6uBATs*h_T+NL(HRK$0_edu%u$xr@c1gD(Vn011>xIkRbD#2z9({nc zQ4wNv8AmRk7ay!DdrnX3-_sw4w)wG+a==@!hlFg-sl}%x=;6QAw~Kz~Yt+Eo~7`u4j z>j;!!>~ax|6Ouy*Ewu=dmPO2?8%WEHlZ1hV&ZzTSAv4%mYpmGtoe&fAx7AvPOA8km zA71wm^H062?!5cSp%S#1ey1Izf@+l~gnGEH)yYi~=a&T6NmRN(;Y^RZXDe3Id{?RT zDDZw^GRGvOxXmAJ{M!0B#N#JNU06W+NEl7zt#{YMPjf5hLc4{U8+b)jVqJFqEv0W( z@6C*9h93~8q@NVnC;H#=iKg6SI%BmfdyL65cI|=9qbzz18 z(J-;8AE?iD--pclDo*HM_H{o2!nzihkCJBKxuW|JiUS6kVS@Jcm-qJ>*Z}KL=`Gs9 z?lHG&80+pD(ZL-8=Urrt-^UKQTH~um&wzf2$o01;;)^Rm-ctrE8PTliw?5dD z=XRWoA(%kB*z|Vz&3avT=Fmy0Sm!v?5|W5DvLIih1mAd)=tVX&Tc&}HdgMFnnx0Va ziPAQxKb!oH#-B?JBSv1q=eq zTp@iTpUqrYW$_x z4^Cspe{xO6RWaklxZYvaxu`58IU`&@Rxl!(=&YKfgy^%>b+@I-wB`Om{$9f~8RYzG@~m z)(#)^4Xlj+QbJjZ9}54@isgTOj-|p`?4Rt{E+mGhppk9eXtoQ5w+>R)rC30pdv#yh zWJa2?ml@aiwN5zkuvz47J(8q8yICO(;dXM&%4puxT)|2>j09yNiqb23m$$TJ^g(-U z^AU)oe24oM5cna+X!g|R9}?u1>VaC*ldLM=2-tD766GWN@zs+2N-g_Ah*rV$8Fq{= z#ntUISaC;9ko$678;P`0dP9w&ulYWDf`Y4JC-U<1xlR09Rg7r!tLN`abBQ!_v+>Kj zi>JORO)2KM2dVfGbQ$TMd`@kbDK&1^lapl4j8mv!!laI87|hlpebX=Q%O@Fm0@S4s zYf2WE-Gh-`N6et+i}lAuBH$sV$t-e-AB;wbACyPSS%p9E_ksGdURpE&uoOLP{Jt{S zqsA(ro^vu@SYp)4h+k|+Sv>C%r|H^tM~f1WiwOrhLmFOT2sDb;eXCOa?8j);(E){h428B?RbM(O!@N8qNCu7N;cwph4k1v zGk_(HwAIT!m${fEFX#2Sb*18bILWtBstgIdUMm^*+pvbp>4&Z&Tl;*rmNtlswC@Q_ z!|JM4Z?bBt#FJORGmwn%8C9F?gC;C+Ilo)oS#mz|H#H@__tsjr7P-v-Irfn2pQw_CuIAC6{+pNxEl5JX;;~`v#zHUfPOk7P~K_t}?hVeDlHEFpWBZ^_L9`lkZ70>;6 z3HRqi87oXtUjqvOG|>Y9*ndBi4razcW0qfQwqFW88gg=r;#h6xbmFbHQdfcyenE4e z-+veK)r+C;tGg=Z*MDO;G7RZn6m6IzsXR#^Aih@;PzcNx+oOJDul@j}-(>h;Sk-Ij z-hx#@{caU-cK+aXJl5=LV{$S$(gn)&WTaXhPA$_e8>Jj3I>OwVKfiWgvJ5$wq}OAX zA1&t>?6mv2wYP}K$Uq8*M+_S~U|i0mTVUIu7!@7?6*=n~IIk}Rrv;t9;mF(^%ykA`$q2lS!r=fVfa7E~rAinvX}tP%bmgK8U5wb;q`=2p#p= zF-F$q(XVU<$h@RBrlG`DOy^@`W#zzWp?+x2&5dfJ9`Fro4V<8mrSbDW}0uB;m}Mv*f27& zBbj54BQoB{mbVO0@mf{|nFy)qxwh{f+Qs)+)HEgxc)iG>9;lSYSp&W1Fs6n*2@GL2 z9UPYx#_*q_?S`@~2ReeHTgmcp#Ijhk{f(A+?9RYyVA0`Z8I>h+qIxP82Tfdpt?uWD z#Qa{K7dKzBUdn}&JZZtUp$toRt$00cuCNG5c(>dg9afd)0gMT5?&WkJuJ%=x(CNdt ze{OA(mg$lRdf#oMjSbpbLf)BcML?@%Hr)UYPXKBKQj56Tepeo3<~s!ERIKAaUd{QR zq0B8|$kReqSMWffLF-%7t<9W7txlD)_y@<5AKm!)%&jHYHqfHA69dJ0okX|ZS4bmI zf`~znidDQ{vA*N^3!q9CM2AP9sMgZ4!jPD#kw1nX>udRYO9rwDYIb92o%x8MAy`1s zp&3P7D6`#hB7i#cr>;`b#Kr1J(mC^T5NA(Y5g@i8n3R1B*pTY<^2ung5g`-(t$fd< ziRXzC`VzPcpjpwB!a21Vjp=Su@$i|KAf$4QW^jmMXvAnMRT1Z*M<*j4TW3$n;fEe6 zhUGp9M7bv`2`y;FE%c3W6sPe}x96HpLHSPeFid%_-S!ORuOPqO)#zC)Ca2^DBrE0- z8kE-UdP&TEz0)4YZTS3V$jkZRVfVC-MW9l)jz;pJvnKGCwXC6SE*L{d7C6`kH z&by#yW~^l7a>a=pATqi`QjlY6d5Xuj?TZEii{cYrle|RH#H2%Ps4gjFYaY%&sK4TY)VogbHH4;XqGCC;0ULNMhD)Gv`Ls5E-b^Y`9d^s z4~~tEH61l--@>$|J6UR|sEyN!!qwJH71l9s2$^X@Ar6YjqBqu3J6b~Tu`Sre;%~ld z`Eg)x)azB?l41IyM1$SRVD6dMqKqkU89BaL`fu=cuO=#t-{9d^@D z#L~ONc4V$aEixkbazJ*-C*a2nk0S(l&ou`a)589@9oU)a4gmB`#yf@+uiTH}he5aY z7Idc`HKK2*0!=jsaq(=5VqBj1N@`+$YNxvwQ>l`lSBzsIB6Uvo-6vc*u<4FZ8j*W1 zCZ9RZw#!M*h0Yb1c$VF_@ndp64QXeriWJ{wF4FQzv>NO`GZ z>6CPf9vgANvS1fzyR*wA{Yvb3*4+b+50P2|JKJhM@#wZOma4NAIBlSB{n-+mbXnM; z;*NYpG25=>f(G|v4F|b=`0*`;iDne7N|YJ&Tx%BD7A@92)vZG$b4m8bVotltEHiCs`(G z$dq!<(LT3Mh4KuUg#B$pSv8p?9zNr6>*&1lNysF_a0mKqNRs2X(dwZFg6BLXV@9$? zuahRf=ZqHczUfP*slq#avM*A-cgcr#=p{`BN-3+SNXI|zEeihG&!vKRXA8jn8!Xsm zeF5GYI@&wf0M&q2Eanczz+b&w$#4gvFDoA4N0S_z(>$FiF-11rryVSEwQWf$+Mt(tOaCppN%@q{J@)f z1HAb1m}k-9D?46iUPVUxC2UytFf5v7F%C-Dn3Arn675gR1O_*!Tfi2*1Y1V(hh_iK zGyKQCUt)$~V{DdSx7>fVrA@?a0&8d#U8*8E2dnb)nuscS8W3YCWt^_(_S#1V#bM_T zWvp+3luKq0E?A7lv@w?hk@f`3{NtA8!(0mi_4MS%ix=GXP;7mxIZu8YmPXMWCJKZ! zn0d1r=Sek~DncwMrs*h?!!Vw4{zC>;Xt-^lrMkn8_!WPWR2j#_WahkrNsDIVn~0Hf zBB*BjdJ6f+^-lky54|=r+nwsw1fJ^Yyu=JY4EGXIVroTg((e5E{xrO%N#^|;*kA*& z;dp--{+G$}b~d*5zwT>FWQ+ADRxEMn)J}s_XxLbxU`PxUGUE~rx}r}kdIN%imh_p0 z$2w86BXgI?D?E4MdCClD2(P6q5eWh?sP%Xmvhfk`PM2Zfq{cr|)>pzJy1`}Ved9wM zhH)lJNm*SJicx0x)X?5Znv!JKTTUo!k9-JiGF@0#wS_9mhc+zT#8sc-C0YBxCNUYDgM3jL%mZS+pwah~I33+5J){~Qx z_KZ~q*=6}BH3HSz?>7Ojj`;&rw%Hm+vFj6xIiKz!@2fisZ43zEV0OYHN7ph*pQNxfX_oQ?g z;)YNR#a~f}J~>u*`@$Lx88ib==_;=!EE>~i(rfN*V zbX#^mW~JUcak99^HzA_PR0bao6x^mtSO_8PbYmP^PX>l?3f6T^?|eT&PAo;8M4xd{ z2mYu$vlj1bZO3>!LB>qC`k3fQotIR$^Kzu1zG=VuF|3M_zcOQgE_L_XKcv&UN7>ck z&SI_6K&__SEBt(LS zc<1S=%*0i)zb>{zW5_G46SZ~L(Oeb1w%+oV+fzrbt4x-C+Gr#z$C*~!9uG|r$HEp?4|c4(w@VJ&Fg(!dCi0Am~~hs8z;(&NnF?VkwM)k`J6KHYyXQ*RK6DZ)dly}8rO=w z!_T;PtuGdvpxjqccY3-HD@ESg9Q&=TiS1lCb$;cpYT#+d<&7k?fOYmTafY(8-I zdcF~s1nd5C7HP_(L&I?!@ zpZ5#;m#N>%?HHd#Js+NhU77tertL5QfFBtEfD3+ifkkJ3y|BPR7r3EX>zi9KTi6<# z9;#Sc#VF%>jXICcqWYtJ?$Aq6=rxW5>~ccWKw4YYIfk+g_8PO1Yu^Kz?q}l54q>5R zbRf|SZmc~DjvE}awg{8xrv4gnvgaW$?x-{!)mmxW}_p#^v`dMO*A6%DPw_RDr z++{~+`-A#t@zfM7SYXJZH)-K0OfL_wqNAy&!y9t0qzK_5E*4UJK#U0>6J5!VvDisC zOr@`P`N;LSzeRm^$Ya2xEi5gC28{Ng?4PlrhfBP?YJDnWSYm?F2E2p%;S8re0#HW{ ztYk>{188%B%u6za;Kx@G$VAnv>ruxw>zD9qN)}5azK48}4a79z1w#`zLPD1D16m6C^7MIxGimPK`pV?Xi^HhzLLwqT>){-qPS_Tb{q%ry3?8{Ot5Xh0B099Geqr zoYdJ<-3IE~pX#Pa*2@t4_q3A%8tC-)2v`^LEM!cN$nw$Fkl3JZjxJU9RkzZb`DyiB5Ji9Fba*0pN6{6A%r2R7mrg%?2LitKNk>{|Q?9`$ z8&4Ix067s2O~(dAOB$+{`1SZSCgS*!cwjtfS(06uQfAVQ>>b3?Tj$9!{Y>{AojdX+ zxs~+eoMh#dYs!yTh0FCCt&&rTF%6rpk?2c^0 zGKQ(mUHW?BW4SLyReOkz4ZI5+b)(Y(kaVBp3hgu9_HVQ|Cz$qh&=n;$z+b=sg9OV( zPC4B~ay>$f3kzt`BdKGSwiDSqHe!uq8nM~w6y+5)sII9_j`c2rS~uj|Y>aj?vRT;S z5>AnX&|Z2v>i@VTi{#-z*m>9*EUWmJ} z_BfQN{uND%Ccv$XT(pUngFTWhs+^=+uk%KNg0YsJld zoA0>x`1|8T-@U-R_m3ZWX`ZeZMd5~hat^5ny!Y>~>sCBh?2iJFa9JMj7B*L&Zq8Xs zLqIgy*M&pyKJcLmx!a9_Sm|SuR~$#P0WD%(ls-ie*H* zf)2wQr4_jK?8@^c2%6_$p0fdUZR*26$5rx2f-P)zoL-87Z7+K0H7%fP6mUO$8|rr+8Wzy|K+JY zCvc^a7Puc~isyeB{wPa4$3M>* z|HeCjALL)R^LYmOImPpE{5OSG0L1?a&Yu%Jk9vO-a0dRH;D5!y&$*rllE1lFg8t3* zXIS~1<$2ikn?)k{zgOjtAnZB*xl8yPzaIwiyNUhjAj(U_fLqXCO&SBB4{nJ+{(8Fr F{ttj&N)G@4 literal 0 HcmV?d00001 diff --git a/doc/MCS_SQLServer_表结构说明.md b/doc/MCS_SQLServer_表结构说明.md new file mode 100644 index 00000000..08191e71 --- /dev/null +++ b/doc/MCS_SQLServer_表结构说明.md @@ -0,0 +1,3550 @@ +# MCS SQL Server 表结构说明 + +- 生成时间:2026-07-14 16:14:35 +- 服务器:`43.108.8.218:1433` +- 登录账号:`sa` +- 说明:指定连接库 `master` 中无用户业务表,以下按实际业务库分别说明。 +- 文档结构:**先数据库 → 再逐表 → 每表写完其全部字段后,再写下一张表**。 +- 字段含义:优先使用库注释;无注释时按英文字段名转译,并标注`(英文转译推测)`。 + +## 命名约定 + +| 前缀 | 含义 | +| --- | --- | +| `MESToMCS_` | MES 下发给 MCS(上辅机)的数据表 | +| `MCSToMES_` | MCS 回传给 MES 的数据表 | +| `IF_` / `If_` | 外围/子系统接口交换表 | +| `Ppt*` | 开炼/挤出相关扫描或过程数据 | + +# 1. 数据库:MESNAC_MCS_ShareDB + +- **数据库名**:`MESNAC_MCS_ShareDB` +- **用途**:密炼/上辅机主中间库(密炼相关) +- **表数量**:58 + +## 1.1 表:Application_Error_Log + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`Application_Error_Log` +- **表含义**:应用错误日志 +- **字段数量**:6 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `tablename` | varchar(50) | YES | | | 表名(英文转译推测) | +| 2 | `userName` | varchar(50) | YES | | | 用户名(英文转译推测) | +| 3 | `errorNumber` | varchar(50) | YES | | | 错误编号(英文转译推测) | +| 4 | `errorSeverity` | varchar(50) | YES | | | 错误严重级别(英文转译推测) | +| 5 | `errorState` | varchar(50) | YES | | | 错误状态(英文转译推测) | +| 6 | `errorMessage` | varchar(5000) | YES | | | 错误信息(英文转译推测) | + +--- + +## 1.2 表:IF_BarcodeLog2Mes + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`IF_BarcodeLog2Mes` +- **表含义**:接口:条码日志 -> MES +- **字段数量**:9 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Equip_Code` | varchar(50) | YES | | | 设备编码(英文转译推测) | +| 2 | `Scan_Time` | varchar(50) | YES | | | 扫描时间(英文转译推测) | +| 3 | `Scan_Bar` | varchar(50) | YES | | | 扫描Bar(英文转译推测) | +| 4 | `Material` | varchar(50) | YES | | | 物料(英文转译推测) | +| 5 | `Bin` | int | YES | | | 仓位(英文转译推测) | +| 6 | `Scan_State` | int | YES | | | 扫描状态(英文转译推测) | +| 7 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 8 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 9 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 1.3 表:IF_BinState2MES + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`IF_BinState2MES` +- **表含义**:接口:料仓状态 -> MES +- **字段数量**:7 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Equip_Code` | varchar(20) | YES | | | 设备编码(英文转译推测) | +| 2 | `Bin_Serial` | int | YES | | | 仓位Serial(英文转译推测) | +| 3 | `level_flag` | int | YES | | | 级别标志(英文转译推测) | +| 4 | `open_flag` | int | YES | | | open标志(英文转译推测) | +| 5 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 6 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 7 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 1.4 表:IF_EquipState2Mes + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`IF_EquipState2Mes` +- **表含义**:接口:设备状态 -> MES +- **字段数量**:9 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `ID` | varchar(50) | NO | | | ID(英文转译推测) | +| 2 | `Equip_Code` | varchar(50) | YES | | | 设备编码(英文转译推测) | +| 3 | `Equip_State` | varchar(50) | YES | | | 设备状态(英文转译推测) | +| 4 | `Alarm_OccurTime` | varchar(50) | YES | | | 报警Occur时间(英文转译推测) | +| 5 | `Alarm_ClearTime` | varchar(50) | YES | | | 报警Clear时间(英文转译推测) | +| 6 | `Equip_AlarmTxt` | varchar(50) | YES | | | 设备报警txt(英文转译推测) | +| 7 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 8 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 9 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 1.5 表:IF_FeedMaterialInfo2CWSS + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`IF_FeedMaterialInfo2CWSS` +- **表含义**:接口:供料信息 -> CWSS +- **字段数量**:9 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Equip_Code` | varchar(16) | YES | | | 设备编码(英文转译推测) | +| 2 | `Scan_Time` | varchar(50) | YES | | | 扫描时间(英文转译推测) | +| 3 | `Scan_Bar` | int | YES | | | 扫描Bar(英文转译推测) | +| 4 | `Material` | varchar(50) | YES | | | 物料(英文转译推测) | +| 5 | `Bin` | int | YES | | | 仓位(英文转译推测) | +| 6 | `Scan_State` | int | YES | | | 扫描状态(英文转译推测) | +| 7 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 8 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 9 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 1.6 表:IF_LrAlarmLog2Mes + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`IF_LrAlarmLog2Mes` +- **表含义**:接口:小料报警日志 -> MES +- **字段数量**:9 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Equip_code` | varchar(20) | YES | | | 设备编码(英文转译推测) | +| 2 | `Alarm_ID` | int | YES | | | 报警ID(英文转译推测) | +| 3 | `Alarm_OccurTime` | varchar(50) | YES | | | 报警Occur时间(英文转译推测) | +| 4 | `Alarm_ClearTime` | varchar(50) | YES | | | 报警Clear时间(英文转译推测) | +| 5 | `Alarm_Status` | int | YES | | | 报警状态(英文转译推测) | +| 6 | `Alarm_GUID` | char(32) | YES | | | 报警GUID(英文转译推测) | +| 7 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 8 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 9 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 1.7 表:If_Lrlot2Mes + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`If_Lrlot2Mes` +- **表含义**:接口:小料批次 -> MES +- **字段数量**:26 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Plan_id` | varchar(50) | YES | | | 计划ID(英文转译推测) | +| 2 | `Equip_code` | varchar(50) | YES | | | 设备编码(英文转译推测) | +| 3 | `Serial_Num` | int | YES | | | Serial Num(英文转译推测) | +| 4 | `Prd_date` | varchar(50) | YES | | | Prd日期(英文转译推测) | +| 5 | `Real_weight` | numeric(9,0) | YES | | | 实际重量(英文转译推测) | +| 6 | `Real_Error` | numeric(9,0) | YES | | | Real错误(英文转译推测) | +| 7 | `Waste_Time` | int | YES | | | Waste时间(英文转译推测) | +| 8 | `WarnIng_Sgn` | int | YES | | | Warn Ing Sgn(英文转译推测) | +| 9 | `Net_Weight` | numeric(9,0) | YES | | | 净重(英文转译推测) | +| 10 | `Group_BarCode` | varchar(50) | YES | | | 组条码(英文转译推测) | +| 11 | `Lot_BarCode` | varchar(50) | YES | | | 批次条码(英文转译推测) | +| 12 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 13 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 14 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | +| 15 | `BG_SetTemp` | int | YES | | | BGSet温度(英文转译推测) | +| 16 | `BG_RealTemp` | int | YES | | | BGReal温度(英文转译推测) | +| 17 | `BG_SetPress` | int | YES | | | BG Set Press(英文转译推测) | +| 18 | `BG_RealPress` | int | YES | | | BG Real Press(英文转译推测) | +| 19 | `BG_SetTime` | int | YES | | | BGSet时间(英文转译推测) | +| 20 | `BG_RealTime` | int | YES | | | BGReal时间(英文转译推测) | +| 21 | `BW_SetTemp` | int | YES | | | BWSet温度(英文转译推测) | +| 22 | `BW_RealTemp` | int | YES | | | BWReal温度(英文转译推测) | +| 23 | `BW_SetPress` | int | YES | | | BW Set Press(英文转译推测) | +| 24 | `BW_RealPress` | int | YES | | | BW Real Press(英文转译推测) | +| 25 | `BW_SetTime` | int | YES | | | BWSet时间(英文转译推测) | +| 26 | `BW_RealTime` | int | YES | | | BWReal时间(英文转译推测) | + +--- + +## 1.8 表:IF_LrPlan2Mes + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`IF_LrPlan2Mes` +- **表含义**:接口:小料计划 -> MES +- **字段数量**:20 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Plan_Id` | varchar(50) | NO | 是 | | 计划ID(英文转译推测) | +| 2 | `Equip_Code` | varchar(50) | YES | | | 设备编码(英文转译推测) | +| 3 | `Plan_Serial` | int | YES | | | 计划Serial(英文转译推测) | +| 4 | `Recipe_ID` | varchar(50) | YES | | | 配方ID(英文转译推测) | +| 5 | `Recipe_Code` | varchar(50) | YES | | | 配方编码(英文转译推测) | +| 6 | `Recipe_Name` | varchar(50) | YES | | | 配方名称(英文转译推测) | +| 7 | `Version` | varchar(50) | YES | | | 版本(英文转译推测) | +| 8 | `Mixer_Line` | varchar(50) | YES | | | Mixer行(英文转译推测) | +| 9 | `Shift_Id` | varchar(10) | YES | | | 班次ID(英文转译推测) | +| 10 | `Plan_Num` | int | YES | | | 计划Num(英文转译推测) | +| 11 | `real_num` | int | YES | | | real num(英文转译推测) | +| 12 | `Start_Date` | varchar(50) | YES | | | 开始日期(英文转译推测) | +| 13 | `End_Date` | varchar(50) | YES | | | 结束日期(英文转译推测) | +| 14 | `Weight_Man` | varchar(50) | YES | | | 重量Man(英文转译推测) | +| 15 | `Plan_Batch` | varchar(50) | YES | | | 计划批次(英文转译推测) | +| 16 | `Plan_State` | int | YES | | | 计划状态(英文转译推测) | +| 17 | `Plan_Date` | varchar(50) | YES | | | 计划日期(英文转译推测) | +| 18 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 19 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 20 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 1.9 表:IF_LrPlanGroup2MES + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`IF_LrPlanGroup2MES` +- **表含义**:接口:小料计划组 -> MES +- **字段数量**:16 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `ID` | varchar(50) | NO | 是 | | ID(英文转译推测) | +| 2 | `Plan_Id` | varchar(50) | YES | | | 计划ID(英文转译推测) | +| 3 | `Equip_Code` | varchar(50) | YES | | | 设备编码(英文转译推测) | +| 4 | `Recipe_Code` | varchar(50) | YES | | | 配方编码(英文转译推测) | +| 5 | `Recipe_Name` | varchar(50) | YES | | | 配方名称(英文转译推测) | +| 6 | `Total_weight` | numeric(9,3) | YES | | | 总重量(英文转译推测) | +| 7 | `Total_Error` | numeric(9,3) | YES | | | 合计错误(英文转译推测) | +| 8 | `TrolleyBags` | int | YES | | | Trolley Bags(英文转译推测) | +| 9 | `TrolleyNumber` | int | YES | | | Trolley编号(英文转译推测) | +| 10 | `StartNum` | int | YES | | | 开始Num(英文转译推测) | +| 11 | `EndNum` | int | YES | | | 结束Num(英文转译推测) | +| 12 | `Prd_date` | varchar(50) | YES | | | Prd日期(英文转译推测) | +| 13 | `Group_BarCode` | varchar(50) | YES | | | 组条码(英文转译推测) | +| 14 | `Write_Time` | varchar(50) | YES | | | 写入时间(英文转译推测) | +| 15 | `Read_Time` | varchar(50) | YES | | | 读取时间(英文转译推测) | +| 16 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 1.10 表:IF_LrRecipe2Mes + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`IF_LrRecipe2Mes` +- **表含义**:接口:小料配方 -> MES +- **字段数量**:13 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Plan_id` | varchar(50) | NO | 是 | | 计划ID(英文转译推测) | +| 2 | `Equip_code` | varchar(50) | NO | 是 | | 设备编码(英文转译推测) | +| 3 | `Weight_ID` | int | NO | 是 | | 重量ID(英文转译推测) | +| 4 | `Material_Code` | varchar(50) | YES | | | 物料编码(英文转译推测) | +| 5 | `Material_Name` | varchar(50) | YES | | | 物料名称(英文转译推测) | +| 6 | `Set_Weight` | numeric(9,4) | YES | | | 设定重量(英文转译推测) | +| 7 | `Set_Error` | numeric(9,4) | YES | | | Set错误(英文转译推测) | +| 8 | `CPK_Error` | numeric(9,4) | YES | | | CPK错误(英文转译推测) | +| 9 | `Batch_number` | varchar(50) | YES | | | 批次号(英文转译推测) | +| 10 | `Actual_Mater` | varchar(50) | YES | | | Actual物料(英文转译推测) | +| 11 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 12 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 13 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 1.11 表:IF_LrWeigh2Mes + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`IF_LrWeigh2Mes` +- **表含义**:接口:小料称重 -> MES +- **字段数量**:15 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Plan_id` | varchar(50) | YES | | | 计划ID(英文转译推测) | +| 2 | `Equip_code` | varchar(50) | YES | | | 设备编码(英文转译推测) | +| 3 | `Serial_Num` | int | YES | | | Serial Num(英文转译推测) | +| 4 | `Weight_ID` | int | YES | | | 重量ID(英文转译推测) | +| 5 | `Material_Code` | varchar(50) | YES | | | 物料编码(英文转译推测) | +| 6 | `Set_Weight` | numeric(9,4) | YES | | | 设定重量(英文转译推测) | +| 7 | `Set_Error` | numeric(9,4) | YES | | | Set错误(英文转译推测) | +| 8 | `Real_Weight` | numeric(9,4) | YES | | | 实际重量(英文转译推测) | +| 9 | `Real_Error` | numeric(9,4) | YES | | | Real错误(英文转译推测) | +| 10 | `Warning_Sign` | int | YES | | | Warning Sign(英文转译推测) | +| 11 | `Batch_Number` | varchar(50) | YES | | | 批次号(英文转译推测) | +| 12 | `Recipe_code` | varchar(50) | YES | | | 配方编码(英文转译推测) | +| 13 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 14 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 15 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 1.12 表:IF_MaterialBatchNumber2CWSS + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`IF_MaterialBatchNumber2CWSS` +- **表含义**:接口:物料批次号 -> CWSS +- **字段数量**:13 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `ID` | varchar(50) | NO | 是 | | ID(英文转译推测) | +| 2 | `Batch_Number` | varchar(50) | YES | | | 批次号(英文转译推测) | +| 3 | `Material_ID` | varchar(50) | YES | | | 物料ID(英文转译推测) | +| 4 | `Material_Name` | varchar(50) | YES | | | 物料名称(英文转译推测) | +| 5 | `Material_Specifica` | varchar(50) | YES | | | 物料Specifica(英文转译推测) | +| 6 | `Qty` | numeric(18,4) | YES | | | 数量(英文转译推测) | +| 7 | `ProductDate` | datetime | YES | | | Product日期(英文转译推测) | +| 8 | `ExpiryDate` | datetime | YES | | | Expiry日期(英文转译推测) | +| 9 | `Unit` | varchar(10) | YES | | | 单位(英文转译推测) | +| 10 | `ChukuId` | varchar(60) | YES | | | Chuku Id(英文转译推测) | +| 11 | `write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 12 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 13 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 1.13 表:IF_MESShift2CWSS + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`IF_MESShift2CWSS` +- **表含义**:接口:MES班次 -> CWSS +- **字段数量**:6 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Equip_code` | varchar(20) | YES | | | 设备编码(英文转译推测) | +| 2 | `ShiftDate` | varchar(20) | YES | | | 班次日期(英文转译推测) | +| 3 | `ShiftID` | int | YES | | | 班次ID(英文转译推测) | +| 4 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 5 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 6 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 1.14 表:IF_PmtBin2CWSS + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`IF_PmtBin2CWSS` +- **表含义**:接口:炭黑/粉料仓位 -> CWSS +- **字段数量**:8 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Equip_Code` | varchar(50) | YES | | | 设备编码(英文转译推测) | +| 2 | `Bin_Serial` | int | YES | | | 仓位Serial(英文转译推测) | +| 3 | `Material_ID` | varchar(50) | YES | | | 物料ID(英文转译推测) | +| 4 | `ID` | varchar(50) | YES | | | ID(英文转译推测) | +| 5 | `Bin_UseFlag` | bit | YES | | | 仓位使用标志(英文转译推测) | +| 6 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 7 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 8 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 1.15 表:IF_PmtBin2MES + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`IF_PmtBin2MES` +- **表含义**:接口:炭黑/粉料仓位 -> MES +- **字段数量**:7 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Equip_Code` | varchar(50) | NO | 是 | | 设备编码(英文转译推测) | +| 2 | `Bin_Serial` | int | NO | 是 | | 仓位Serial(英文转译推测) | +| 3 | `Material_ID` | varchar(50) | YES | | | 物料ID(英文转译推测) | +| 4 | `Bin_UseFlag` | varchar(20) | YES | | | 仓位使用标志(英文转译推测) | +| 5 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 6 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 7 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 1.16 表:IF_PmtMaterial2CWSS + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`IF_PmtMaterial2CWSS` +- **表含义**:接口:炭黑/粉料物料 -> CWSS +- **字段数量**:7 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `ID` | varchar(50) | YES | | | ID(英文转译推测) | +| 2 | `Equip_Code` | varchar(50) | YES | | | 设备编码(英文转译推测) | +| 3 | `Material_code` | varchar(50) | YES | | | 物料编码(英文转译推测) | +| 4 | `Material_name` | varchar(50) | YES | | | 物料名称(英文转译推测) | +| 5 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 6 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 7 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 1.17 表:IF_PmtRecipe2CWSS + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`IF_PmtRecipe2CWSS` +- **表含义**:接口:炭黑/粉料配方 -> CWSS +- **字段数量**:17 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Id` | varchar(50) | YES | | | ID(英文转译推测) | +| 2 | `Equip_Code` | varchar(50) | YES | | | 设备编码(英文转译推测) | +| 3 | `Recipe_Code` | varchar(50) | YES | | | 配方编码(英文转译推测) | +| 4 | `Version` | varchar(50) | YES | | | 版本(英文转译推测) | +| 5 | `Recipe_Name` | varchar(50) | YES | | | 配方名称(英文转译推测) | +| 6 | `Mixer_line` | varchar(50) | YES | | | Mixer行(英文转译推测) | +| 7 | `Recipe_Verify` | int | YES | | | 配方版本ify(英文转译推测) | +| 8 | `Total_Weight` | numeric(12,4) | YES | | | 总重量(英文转译推测) | +| 9 | `Total_Error` | numeric(12,4) | YES | | | 合计错误(英文转译推测) | +| 10 | `End_datetime` | varchar(19) | YES | | | 结束日期时间(英文转译推测) | +| 11 | `Remark` | varchar(200) | YES | | | 备注(英文转译推测) | +| 12 | `GroupBags` | int | YES | | | 组Bags(英文转译推测) | +| 13 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 14 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 15 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | +| 16 | `Validity` | int | YES | | | Validity(英文转译推测) | +| 17 | `BagWeight` | int | YES | | | Bag重量(英文转译推测) | + +--- + +## 1.18 表:IF_Pmtweigh2CWSS + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`IF_Pmtweigh2CWSS` +- **表含义**:接口:炭黑/粉料称重 -> CWSS +- **字段数量**:10 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Id` | varchar(50) | YES | | | ID(英文转译推测) | +| 2 | `Equip_Code` | varchar(50) | YES | | | 设备编码(英文转译推测) | +| 3 | `Recipe_ID` | varchar(50) | YES | | | 配方ID(英文转译推测) | +| 4 | `Weight_Id` | int | YES | | | 重量ID(英文转译推测) | +| 5 | `Material_ID` | varchar(50) | YES | | | 物料ID(英文转译推测) | +| 6 | `Set_Weight` | numeric(9,3) | YES | | | 设定重量(英文转译推测) | +| 7 | `Set_Error` | numeric(9,3) | YES | | | Set错误(英文转译推测) | +| 8 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 9 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 10 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 1.19 表:IF_RtPlan2CWSS + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`IF_RtPlan2CWSS` +- **表含义**:接口:生胶计划 -> CWSS +- **字段数量**:18 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Plan_Id` | varchar(50) | YES | | | 计划ID(英文转译推测) | +| 2 | `Equip_Code` | varchar(50) | YES | | | 设备编码(英文转译推测) | +| 3 | `Plan_Serial` | int | YES | | | 计划Serial(英文转译推测) | +| 4 | `Recipe_ID` | varchar(50) | YES | | | 配方ID(英文转译推测) | +| 5 | `Recipe_Code` | varchar(50) | YES | | | 配方编码(英文转译推测) | +| 6 | `Recipe_Name` | varchar(50) | YES | | | 配方名称(英文转译推测) | +| 7 | `Version` | varchar(50) | YES | | | 版本(英文转译推测) | +| 8 | `Mixer_Line` | varchar(50) | YES | | | Mixer行(英文转译推测) | +| 9 | `Shift_Id` | varchar(10) | YES | | | 班次ID(英文转译推测) | +| 10 | `Plan_Num` | int | YES | | | 计划Num(英文转译推测) | +| 11 | `Start_Date` | varchar(50) | YES | | | 开始日期(英文转译推测) | +| 12 | `End_Date` | varchar(50) | YES | | | 结束日期(英文转译推测) | +| 13 | `Plan_Batch` | varchar(50) | YES | | | 计划批次(英文转译推测) | +| 14 | `Plan_State` | int | YES | | | 计划状态(英文转译推测) | +| 15 | `Plan_Date` | varchar(50) | YES | | | 计划日期(英文转译推测) | +| 16 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 17 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 18 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 1.20 表:IF_RtPlan2Mes + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`IF_RtPlan2Mes` +- **表含义**:接口:生胶计划 -> MES +- **字段数量**:21 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Plan_Id` | varchar(50) | NO | 是 | | 计划ID(英文转译推测) | +| 2 | `Equip_Code` | varchar(50) | YES | | | 设备编码(英文转译推测) | +| 3 | `Plan_Serial` | int | YES | | | 计划Serial(英文转译推测) | +| 4 | `Recipe_ID` | varchar(50) | YES | | | 配方ID(英文转译推测) | +| 5 | `Recipe_Code` | varchar(50) | YES | | | 配方编码(英文转译推测) | +| 6 | `Recipe_Name` | varchar(50) | YES | | | 配方名称(英文转译推测) | +| 7 | `Version` | varchar(50) | YES | | | 版本(英文转译推测) | +| 8 | `Mixer_Line` | varchar(50) | YES | | | Mixer行(英文转译推测) | +| 9 | `Shift_Id` | varchar(10) | YES | | | 班次ID(英文转译推测) | +| 10 | `Plan_Num` | int | YES | | | 计划Num(英文转译推测) | +| 11 | `real_num` | int | YES | | | real num(英文转译推测) | +| 12 | `Start_Date` | varchar(50) | YES | | | 开始日期(英文转译推测) | +| 13 | `End_Date` | varchar(50) | YES | | | 结束日期(英文转译推测) | +| 14 | `Plan_Batch` | varchar(50) | YES | | | 计划批次(英文转译推测) | +| 15 | `Plan_State` | int | YES | | | 计划状态(英文转译推测) | +| 16 | `Plan_Date` | varchar(50) | YES | | | 计划日期(英文转译推测) | +| 17 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 18 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 19 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | +| 20 | `Weight_Man` | varchar(50) | YES | | | 重量Man(英文转译推测) | +| 21 | `Stock_Man` | nvarchar(50) | YES | | | Stock Man(英文转译推测) | + +--- + +## 1.21 表:MCCToMES_PptBarScan + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MCCToMES_PptBarScan` +- **表含义**:MCC->MES:挤出/开炼条码扫描 +- **字段数量**:19 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(50) | NO | 是 | | GUID(英文转译推测) | +| 2 | `SaveTime` | varchar(20) | YES | | | Save时间(英文转译推测) | +| 3 | `Barcode` | varchar(50) | YES | | | 条码(英文转译推测) | +| 4 | `EquipID` | int | YES | | | 设备ID(英文转译推测) | +| 5 | `Plan_ID` | varchar(50) | YES | | | 计划ID(英文转译推测) | +| 6 | `RpCode` | varchar(50) | YES | | | Rp编码(英文转译推测) | +| 7 | `RpName` | varchar(50) | YES | | | Rp名称(英文转译推测) | +| 8 | `PlanSet` | int | YES | | | 计划Set(英文转译推测) | +| 9 | `PlanComplete` | int | YES | | | 计划完成(英文转译推测) | +| 10 | `ScaleComplete` | int | YES | | | 秤完成(英文转译推测) | +| 11 | `PlanShift` | int | YES | | | 计划班次(英文转译推测) | +| 12 | `MaterCode` | varchar(50) | YES | | | 物料编码(英文转译推测) | +| 13 | `MaterName` | varchar(50) | YES | | | 物料名称(英文转译推测) | +| 14 | `MaterTypeID` | int | YES | | | 物料类型ID(英文转译推测) | +| 15 | `MaterType` | varchar(20) | YES | | | 物料类型(英文转译推测) | +| 16 | `Mater_Barcode` | varchar(50) | YES | | | 物料条码(英文转译推测) | +| 17 | `BarScanResult` | int | YES | | | Bar扫描结果(英文转译推测) | +| 18 | `Oper` | varchar(50) | YES | | | Oper(英文转译推测) | +| 19 | `Flg` | char(1) | YES | | | Flg(英文转译推测) | + +--- + +## 1.22 表:MCSToMES_Act_Mill + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MCSToMES_Act_Mill` +- **表含义**:MCS->MES:开炼机动作 +- **字段数量**:9 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(50) | NO | | | GUID(英文转译推测) | +| 2 | `act_addr` | int | YES | | | 动作addr(英文转译推测) | +| 3 | `act_name` | varchar(50) | YES | | | 动作名称(英文转译推测) | +| 4 | `act_name_en` | varchar(50) | YES | | | 动作名称en(英文转译推测) | +| 5 | `act_memo` | varchar(20) | YES | | | 动作备注(英文转译推测) | +| 6 | `act_Repaddr` | int | YES | | | 动作Repaddr(英文转译推测) | +| 7 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 8 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 9 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 1.23 表:MCSToMES_BinToMater + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MCSToMES_BinToMater` +- **表含义**:MCS->MES:仓位与物料对应关系 +- **字段数量**:14 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | | GUID(英文转译推测) | +| 2 | `EquipID` | int | NO | 是 | | 设备ID(英文转译推测) | +| 3 | `EquipName` | varchar(20) | YES | | | 设备名称(英文转译推测) | +| 4 | `EquipType` | varchar(10) | NO | 是 | | 设备类型(英文转译推测) | +| 5 | `ScaleNum` | int | NO | 是 | | 秤Num(英文转译推测) | +| 6 | `ScaleClass` | varchar(20) | YES | | | 秤Class(英文转译推测) | +| 7 | `ScaleName` | varchar(20) | YES | | | 秤名称(英文转译推测) | +| 8 | `BinNo` | int | NO | 是 | | 仓号(英文转译推测) | +| 9 | `MatCode` | varchar(30) | YES | | | 物料编码(英文转译推测) | +| 10 | `MatName` | varchar(50) | YES | | | 物料名称(英文转译推测) | +| 11 | `UseYN` | varchar(1) | YES | | | Use YN(英文转译推测) | +| 12 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 13 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 14 | `MES_Flag` | int | YES | | | MES标志(英文转译推测) | + +--- + +## 1.24 表:MCSToMES_CurrEquipState + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MCSToMES_CurrEquipState` +- **表含义**:MCS->MES:当前设备状态 +- **字段数量**:13 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | NO | 是 | | GUID(英文转译推测) | +| 2 | `EquipID` | int | YES | | | 设备ID(英文转译推测) | +| 3 | `EquipName` | varchar(10) | YES | | | 设备名称(英文转译推测) | +| 4 | `EquipType` | varchar(10) | YES | | | 设备类型(英文转译推测) | +| 5 | `SaveTime` | varchar(20) | YES | | | Save时间(英文转译推测) | +| 6 | `RpName` | varchar(40) | YES | | | Rp名称(英文转译推测) | +| 7 | `SetNum` | int | YES | | | Set Num(英文转译推测) | +| 8 | `Serial` | int | YES | | | Serial(英文转译推测) | +| 9 | `MixSpeed` | decimal(18,3) | YES | | | 密炼速度(英文转译推测) | +| 10 | `MixPower` | decimal(10,3) | YES | | | 密炼功率(英文转译推测) | +| 11 | `MixEngery` | decimal(18,3) | YES | | | 密炼Engery(英文转译推测) | +| 12 | `MixTime` | int | YES | | | 密炼时间(英文转译推测) | +| 13 | `MES_Flag` | int | YES | | (0) | MES标志(英文转译推测) | + +--- + +## 1.25 表:MCSToMES_CurrVarData_Convey + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MCSToMES_CurrVarData_Convey` +- **表含义**:MCS->MES:输送线过程变量 +- **字段数量**:7 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(50) | NO | 是 | | GUID(英文转译推测) | +| 2 | `VarName` | varchar(50) | YES | | | Var名称(英文转译推测) | +| 3 | `DataName` | varchar(50) | YES | | | 数据名称(英文转译推测) | +| 4 | `DataValue` | decimal(18,3) | YES | | | 数据数值(英文转译推测) | +| 5 | `CreateTime` | varchar(19) | YES | | | 创建时间(英文转译推测) | +| 6 | `UpdateTime` | varchar(19) | YES | | | 更新时间(英文转译推测) | +| 7 | `MESReadTime` | varchar(19) | YES | | | MES读取时间(英文转译推测) | + +--- + +## 1.26 表:MCSToMES_MixAct + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MCSToMES_MixAct` +- **表含义**:MCS->MES:密炼动作记录 +- **字段数量**:8 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `EquipID` | int | NO | 是 | | 设备ID(英文转译推测) | +| 2 | `EquipName` | varchar(10) | YES | | | 设备名称(英文转译推测) | +| 3 | `EquipType` | varchar(10) | NO | 是 | | 设备类型(英文转译推测) | +| 4 | `MixActAddress` | int | NO | 是 | | 动作地址 | +| 5 | `MixActName` | varchar(30) | YES | | | 动作名称 | +| 6 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 7 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 8 | `RW_Flag` | int | YES | | (0) | RW标志(英文转译推测) | + +--- + +## 1.27 表:MCSToMES_MixAlarm + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MCSToMES_MixAlarm` +- **表含义**:MCS->MES:密炼报警 +- **字段数量**:12 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | NO | 是 | | GUID(英文转译推测) | +| 2 | `EquipID` | int | NO | | | 机台编号 | +| 3 | `EquipName` | varchar(10) | NO | | | 机台名称 | +| 4 | `EquipType` | varchar(10) | NO | | | 机台类型 | +| 5 | `SaveTime` | varchar(19) | YES | | | Save时间(英文转译推测) | +| 6 | `EndTime` | varchar(19) | YES | | | 结束时间(英文转译推测) | +| 7 | `Barcode` | varchar(30) | NO | | | Barcode | +| 8 | `AlmCode` | varchar(6) | YES | | | 报警代码 | +| 9 | `AlarmInf` | varchar(50) | YES | | | 报警内容 | +| 10 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 11 | `ReadTime` | datetime | YES | | (0) | 读取时间(英文转译推测) | +| 12 | `MES_Flag` | int | YES | | (0) | MES标志(英文转译推测) | + +--- + +## 1.28 表:MCSToMES_MixBatch + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MCSToMES_MixBatch` +- **表含义**:MCS->MES:密炼批次 +- **字段数量**:41 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | | GUID(英文转译推测) | +| 2 | `EquipID` | int | NO | 是 | | 机台编号 | +| 3 | `EquipName` | varchar(10) | YES | | | 机台名称 | +| 4 | `EquipType` | varchar(10) | YES | | | 机台类型 | +| 5 | `Barcode` | varchar(30) | NO | 是 | | 计划编号+车次 | +| 6 | `RecipCode` | varchar(30) | YES | | | 配方代码 | +| 7 | `RecipName` | varchar(30) | YES | | | 配方名称 | +| 8 | `MaterCode` | varchar(40) | YES | | | 物料编码(英文转译推测) | +| 9 | `MaterName` | varchar(80) | YES | | | 物料名称(英文转译推测) | +| 10 | `SaveDate` | varchar(10) | YES | | | 保存日期 | +| 11 | `SaveDateTime` | varchar(20) | YES | | | 保存时间 | +| 12 | `ShiftId` | varchar(1) | YES | | | 班次;1:Night 2: Morning 3:Noon | +| 13 | `ShiftClass` | varchar(1) | YES | | | 班组;1:A 2:B 3:C 4:D | +| 14 | `PlanID` | varchar(20) | YES | | | 计划编号 | +| 15 | `SetNum` | int | YES | | | 设定车次 | +| 16 | `StartTim` | varchar(20) | YES | | | 开始时间 | +| 17 | `EndTim` | varchar(20) | YES | | | 结束时间 | +| 18 | `BatchNum` | int | YES | | | 当前车次 | +| 19 | `MixMode` | int | YES | | | 密炼模式1:遥控自动;2:遥控手动;3:本控 | +| 20 | `BatchCycleTim` | int | YES | | | 密炼循环时间(预留) | +| 21 | `CurTim` | int | YES | | | 密炼时间 | +| 22 | `MixTim` | int | YES | | | 预留 | +| 23 | `UsefulMixTim` | int | YES | | | 有效时间 | +| 24 | `IntervalTime` | int | YES | | | 间隔时间 | +| 25 | `DiscTemp` | decimal(18,3) | YES | | | 排胶温度 | +| 26 | `DiscEnergy` | decimal(18,3) | YES | | | 排胶能量 | +| 27 | `DiscTime` | int | YES | | | 排胶时间 | +| 28 | `DiscPower` | int | YES | | | 排胶功率 | +| 29 | `EmgyDiscFlag` | int | YES | | | 排胶方式;0:正常;1:超时;2:超温;3:超能量 | +| 30 | `TCU1Temp` | decimal(18,3) | YES | | | 三区温度1(预留) | +| 31 | `TCU2Temp` | decimal(18,3) | YES | | | 三区温度2(预留) | +| 32 | `TCU3Temp` | decimal(18,3) | YES | | | 三区温度3(预留) | +| 33 | `TCU4Temp` | decimal(18,3) | YES | | | 三区温度4(预留) | +| 34 | `TCUTemp` | decimal(18,3) | YES | | | 三区温度(预留) | +| 35 | `BatchWei` | decimal(18,3) | YES | | | 批次Wei(英文转译推测) | +| 36 | `oper` | varchar(20) | YES | | | oper(英文转译推测) | +| 37 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 38 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 39 | `MES_Flag` | int | YES | | (0) | MES标志(英文转译推测) | +| 40 | `WEIGHT` | decimal(18,3) | YES | | | 重量(英文转译推测) | +| 41 | `MoCode` | varchar(40) | YES | | | Mo编码(英文转译推测) | + +--- + +## 1.29 表:MCSToMES_MixCon + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MCSToMES_MixCon` +- **表含义**:MCS->MES:密炼消耗/配料明细 +- **字段数量**:8 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `EquipID` | int | NO | 是 | | 设备ID(英文转译推测) | +| 2 | `EquipName` | varchar(10) | YES | | | 设备名称(英文转译推测) | +| 3 | `EquipType` | varchar(10) | NO | 是 | | 设备类型(英文转译推测) | +| 4 | `MixConAddress` | int | NO | 是 | | 密炼ConAddress(英文转译推测) | +| 5 | `MixConName` | varchar(30) | YES | | | 密炼Con名称(英文转译推测) | +| 6 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 7 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 8 | `RW_Flag` | int | YES | | (0) | RW标志(英文转译推测) | + +--- + +## 1.30 表:MCSToMES_MixCurve + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MCSToMES_MixCurve` +- **表含义**:MCS->MES:密炼曲线数据 +- **字段数量**:9 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | | GUID(英文转译推测) | +| 2 | `EquipID` | int | NO | 是 | | 设备ID(英文转译推测) | +| 3 | `EquipName` | varchar(10) | YES | | | 设备名称(英文转译推测) | +| 4 | `EquipType` | varchar(10) | NO | 是 | | 设备类型(英文转译推测) | +| 5 | `Barcode` | varchar(30) | NO | 是 | | 条码(英文转译推测) | +| 6 | `CurveData` | ntext(1073741823) | YES | | | Curve数据(英文转译推测) | +| 7 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 8 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 9 | `MES_Flag` | int | YES | | (0) | MES标志(英文转译推测) | + +--- + +## 1.31 表:MCSToMES_mixdata_Mill + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MCSToMES_mixdata_Mill` +- **表含义**:MCS->MES:开炼机混炼数据 +- **字段数量**:14 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Barcode` | varchar(40) | YES | | | 条码(英文转译推测) | +| 2 | `Serial_id` | int | YES | | | Serial id(英文转译推测) | +| 3 | `Equip_code` | varchar(20) | YES | | | 设备编码(英文转译推测) | +| 4 | `ID` | int | YES | | | ID(英文转译推测) | +| 5 | `StepActAddress` | int | YES | | | 步骤动作Address(英文转译推测) | +| 6 | `StepMixTime` | int | YES | | | 步骤密炼时间(英文转译推测) | +| 7 | `StepMixCoolSpeed` | numeric(18,3) | YES | | | 步骤密炼Cool速度(英文转译推测) | +| 8 | `StepOpenMixSpeed` | numeric(18,3) | YES | | | 步骤Open密炼速度(英文转译推测) | +| 9 | `StepRollor` | numeric(18,3) | YES | | | 步骤Rollor(英文转译推测) | +| 10 | `StepWaterTemp` | numeric(18,3) | YES | | | 步骤Water温度(英文转译推测) | +| 11 | `StepRubTemp` | numeric(18,3) | YES | | | 步骤Rub温度(英文转译推测) | +| 12 | `StepPower` | numeric(18,3) | YES | | | 步骤功率(英文转译推测) | +| 13 | `WaterIn` | numeric(18,3) | YES | | | Water In(英文转译推测) | +| 14 | `Remarks` | varchar(50) | YES | | | 备注s(英文转译推测) | + +--- + +## 1.32 表:MCSToMES_MixExePlan + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MCSToMES_MixExePlan` +- **表含义**:MCS->MES:密炼执行计划回传 +- **字段数量**:25 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | | GUID(英文转译推测) | +| 2 | `PlanID` | varchar(20) | NO | 是 | | 计划编号 | +| 3 | `PlanSource` | varchar(1) | NO | 是 | | 计划来源(英文转译推测) | +| 4 | `EquipID` | int | NO | 是 | | 机台编号 | +| 5 | `EquipName` | varchar(10) | YES | | | 机台名称 | +| 6 | `EquipType` | varchar(10) | YES | | | 机台类型CWS:小料;MCS:上辅机 | +| 7 | `PlanDate` | varchar(10) | YES | | | 计划日期 | +| 8 | `ShiftID` | varchar(1) | YES | | | 班次1:Night 2: Morning 3 | +| 9 | `ShiftClass` | varchar(1) | YES | | | 班组1:A 2:B 3:C 4:D | +| 10 | `WorkOrder` | int | YES | | | 计划顺序 | +| 11 | `RecipCode` | varchar(40) | YES | | | 配方代码 | +| 12 | `RecipName` | varchar(40) | YES | | | 配方名称 | +| 13 | `SetNum` | int | YES | | | 设定车次 | +| 14 | `DoneNum` | int | YES | | | 完成车次 | +| 15 | `PlanState` | int | YES | | | 计划状态2:运行;4终止;5:完成 | +| 16 | `PlanStateChange` | int | YES | | | 计划状态有变化 | +| 17 | `StartTime` | datetime | YES | | | 开始时间(英文转译推测) | +| 18 | `EndTime` | datetime | YES | | | 结束时间(英文转译推测) | +| 19 | `WriteTime` | datetime | YES | | | 写入时间 | +| 20 | `ReadTime` | datetime | YES | | | 读取时间 | +| 21 | `MES_Flag` | int | YES | | (0) | 交互标识 | +| 22 | `Remark` | varchar(50) | YES | | | 备注 | +| 23 | `MaterCode` | varchar(50) | YES | | | 物料编码(英文转译推测) | +| 24 | `MaterName` | varchar(50) | YES | | | 物料名称(英文转译推测) | +| 25 | `UploadRunFlag` | int | YES | | ((0)) | UploadRun标志(英文转译推测) | + +--- + +## 1.33 表:MCSToMES_MixStep + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MCSToMES_MixStep` +- **表含义**:MCS->MES:密炼步骤执行记录 +- **字段数量**:35 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | | GUID(英文转译推测) | +| 2 | `EquipID` | int | NO | 是 | | 机台编号 | +| 3 | `EquipName` | varchar(10) | YES | | | 设备名称(英文转译推测) | +| 4 | `EquipType` | varchar(10) | NO | 是 | | 机台类型 | +| 5 | `Barcode` | varchar(30) | NO | 是 | | 条码(英文转译推测) | +| 6 | `SaveTime` | varchar(20) | YES | | | 保存时间 | +| 7 | `MixID` | int | NO | 是 | | 密炼步序 | +| 8 | `Cond` | int | YES | | | 条件地址 | +| 9 | `CondName` | varchar(40) | YES | | | 条件名称 | +| 10 | `Tim` | int | YES | | | 设定时间(预留) | +| 11 | `TimProtect` | int | YES | | | 时间保护(预留) | +| 12 | `TimAct` | int | YES | | | 实际时间 | +| 13 | `Tempe` | decimal(18,3) | YES | | | 设定温度(预留) | +| 14 | `TempProtect` | decimal(18,3) | YES | | | 温度保护(预留) | +| 15 | `TempAct` | decimal(18,3) | YES | | | 温度实际 | +| 16 | `Power` | decimal(18,3) | YES | | | 设定功率(预留) | +| 17 | `PowerProtect` | decimal(18,3) | YES | | | 功率保护(预留) | +| 18 | `PowerAct` | decimal(18,3) | YES | | | 实际功率 | +| 19 | `Energy` | decimal(18,3) | YES | | | 设定能量(预留) | +| 20 | `EnergyProtect` | decimal(18,3) | YES | | | 能量保护(预留) | +| 21 | `EnergyAct` | decimal(18,3) | YES | | | 实际能量 | +| 22 | `Act` | int | YES | | | 动作地址 | +| 23 | `ActName` | varchar(40) | YES | | | 动作名称 | +| 24 | `RamPress` | decimal(18,3) | YES | | | 设定压力(预留) | +| 25 | `RamPressAct` | decimal(18,3) | YES | | | 实际压力 | +| 26 | `RotorSpd` | decimal(18,3) | YES | | | 转速设定(预留) | +| 27 | `RotorSpdAct` | decimal(18,3) | YES | | | 实际转速 | +| 28 | `RamPos` | decimal(18,3) | YES | | | 栓位置设定(预留) | +| 29 | `RamPosAct` | decimal(18,3) | YES | | | 实际栓位置 | +| 30 | `RollGap` | decimal(18,3) | YES | | | 辊距设定(预留) | +| 31 | `RollGapAct` | decimal(18,3) | YES | | | 实际辊距(预留) | +| 32 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 33 | `CtrlMode` | int | YES | | | Ctrl模式(英文转译推测) | +| 34 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 35 | `MES_Flag` | int | YES | | (0) | MES标志(英文转译推测) | + +--- + +## 1.34 表:MCSToMES_MixWeight + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MCSToMES_MixWeight` +- **表含义**:MCS->MES:密炼称重记录 +- **字段数量**:26 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | NO | 是 | | GUID(英文转译推测) | +| 2 | `EquipID` | int | NO | | | 机台编号 | +| 3 | `EquipName` | varchar(10) | YES | | | 机台名称 | +| 4 | `EquipType` | varchar(50) | NO | | | 机台类型 | +| 5 | `Barcode` | varchar(30) | NO | | | 条码(英文转译推测) | +| 6 | `SaveTime` | varchar(20) | YES | | | 存储时间 | +| 7 | `MatCode` | varchar(30) | YES | | | 物料代码 | +| 8 | `MatName` | varchar(30) | YES | | | 物料名称 | +| 9 | `ScaleNum` | int | NO | | | 秤编号101:炭黑秤1;102:炭黑秤2;103:炭黑秤3;201:油料秤1;202:油料秤2;203:油料秤3 +301:胶料秤1;302:胶料秤2;401:小料秤1;402:小料秤2 | +| 10 | `BatchNum` | int | YES | | | 车次 | +| 11 | `Act` | int | YES | | | 动作(英文转译推测) | +| 12 | `Idx` | int | NO | | | 序号 | +| 13 | `ShiftNum` | int | YES | | | 第几批称量 | +| 14 | `SetWei` | decimal(18,3) | YES | | | 设定重量 | +| 15 | `SetTol` | decimal(18,3) | YES | | | 设定误差 | +| 16 | `ActWei` | decimal(18,3) | YES | | | 实际重量 | +| 17 | `ActTol` | decimal(18,3) | YES | | | 实际误差 | +| 18 | `StepTim` | int | YES | | | 步时间 | +| 19 | `StepMode` | int | YES | | | 步骤模式(英文转译推测) | +| 20 | `RecipeName` | varchar(30) | YES | | | 配方名称 | +| 21 | `PlanID` | varchar(20) | YES | | | 计划编号 | +| 22 | `SetBatchNum` | int | YES | | | 计划设定 | +| 23 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 24 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 25 | `MES_Flag` | int | YES | | ((0)) | MES标志(英文转译推测) | +| 26 | `MOCode` | varchar(40) | YES | | | MOCode(英文转译推测) | + +--- + +## 1.35 表:MCSToMES_MixWeiRelate + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MCSToMES_MixWeiRelate` +- **表含义**:MCS->MES:称重关联关系 +- **字段数量**:11 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(50) | NO | 是 | | GUID(英文转译推测) | +| 2 | `Barcode` | varchar(40) | YES | | | 条码(英文转译推测) | +| 3 | `EquipID` | int | YES | | | 设备ID(英文转译推测) | +| 4 | `SaveTime` | varchar(20) | YES | | | Save时间(英文转译推测) | +| 5 | `ScaleType` | int | YES | | | 秤类型(英文转译推测) | +| 6 | `ScaleNum` | int | YES | | | 秤Num(英文转译推测) | +| 7 | `BatchNum` | int | YES | | | 批次Num(英文转译推测) | +| 8 | `ShiftNum` | int | YES | | | 班次Num(英文转译推测) | +| 9 | `FeedTim` | int | YES | | | 供料Tim(英文转译推测) | +| 10 | `PlanID` | varchar(30) | YES | | | 计划ID(英文转译推测) | +| 11 | `MES_Flag` | int | YES | | | MES标志(英文转译推测) | + +--- + +## 1.36 表:MCSToMES_Ppt_Log + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MCSToMES_Ppt_Log` +- **表含义**:MCS->MES:开炼/挤出日志 +- **字段数量**:9 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | | GUID(英文转译推测) | +| 2 | `EquipID` | int | YES | | | 设备ID(英文转译推测) | +| 3 | `EquipType` | varchar(10) | YES | | | 设备类型(英文转译推测) | +| 4 | `WorkType` | varchar(20) | YES | | | Work类型(英文转译推测) | +| 5 | `OperDest` | varchar(50) | YES | | | Oper Dest(英文转译推测) | +| 6 | `OccurrenTime` | varchar(20) | YES | | | Occurren时间(英文转译推测) | +| 7 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 8 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 9 | `MES_Flag` | int | YES | | | MES标志(英文转译推测) | + +--- + +## 1.37 表:MCSToMES_Ppt_Shutdown + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MCSToMES_Ppt_Shutdown` +- **表含义**:MCS->MES:停机记录 +- **字段数量**:12 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | | GUID(英文转译推测) | +| 2 | `EquipID` | int | YES | | | 设备ID(英文转译推测) | +| 3 | `EquipType` | varchar(10) | YES | | | 设备类型(英文转译推测) | +| 4 | `ShutdownType` | varchar(50) | YES | | | 停机类型(英文转译推测) | +| 5 | `StartTime` | varchar(10) | YES | | | 开始时间(英文转译推测) | +| 6 | `EndTime` | varchar(10) | YES | | | 结束时间(英文转译推测) | +| 7 | `StopTimeSecond` | int | YES | | | Stop时间Second(英文转译推测) | +| 8 | `Memo` | varchar(50) | YES | | | 备注(英文转译推测) | +| 9 | `Auto` | int | YES | | | 自动(英文转译推测) | +| 10 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 11 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 12 | `MES_Flag` | int | YES | | | MES标志(英文转译推测) | + +--- + +## 1.38 表:MCSToMES_PptShiftConfig + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MCSToMES_PptShiftConfig` +- **表含义**:MCS->MES:开炼班次配置 +- **字段数量**:20 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(50) | YES | | | GUID(英文转译推测) | +| 2 | `EquipID` | int | NO | 是 | | 设备ID(英文转译推测) | +| 3 | `EquipName` | varchar(10) | YES | | | 设备名称(英文转译推测) | +| 4 | `EquipType` | varchar(10) | NO | 是 | | 设备类型(英文转译推测) | +| 5 | `Barcode` | varchar(30) | NO | 是 | | 条码(英文转译推测) | +| 6 | `PlanDate` | datetime | YES | | | 计划日期(英文转译推测) | +| 7 | `PlanID` | varchar(20) | NO | | | 计划ID(英文转译推测) | +| 8 | `ShiftID` | int | YES | | | 班次ID(英文转译推测) | +| 9 | `ClassID` | int | YES | | | Class ID(英文转译推测) | +| 10 | `RecipCode` | varchar(30) | YES | | | Recip编码(英文转译推测) | +| 11 | `RecipName` | varchar(30) | YES | | | Recip名称(英文转译推测) | +| 12 | `BarcodeStart` | int | NO | | | 条码开始(英文转译推测) | +| 13 | `BarcodeEnd` | int | YES | | | 条码结束(英文转译推测) | +| 14 | `TotalWeight` | decimal(18,3) | YES | | | 总重量(英文转译推测) | +| 15 | `RealWeight` | decimal(18,3) | YES | | | 实际重量(英文转译推测) | +| 16 | `Set_ShelfNum` | int | YES | | | Set Shelf Num(英文转译推测) | +| 17 | `ShelfNum` | int | YES | | | Shelf Num(英文转译推测) | +| 18 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 19 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 20 | `MES_Flag` | int | YES | | | MES标志(英文转译推测) | + +--- + +## 1.39 表:MCSToMES_RBWeight + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MCSToMES_RBWeight` +- **表含义**:MCS->MES:生胶称重 +- **字段数量**:15 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | NO | | | GUID(英文转译推测) | +| 2 | `EquipID` | int | NO | | | 机台编号 | +| 3 | `MatCode` | varchar(50) | NO | 是 | | 物料代码 | +| 4 | `MatName` | varchar(50) | YES | | | 物料名称 | +| 5 | `ScaleNum` | int | NO | | | 秤编号101:炭黑秤1;102:炭黑秤2;103:炭黑秤3;201:油料秤1;202:油料秤2;203:油料秤3 +301:胶料秤1;302:胶料秤2;401:小料秤1;402:小料秤2 | +| 6 | `SetBatchNum` | int | YES | | | 计划设定 | +| 7 | `BatchNum` | int | NO | 是 | | 车次 | +| 8 | `ActWei` | decimal(18,3) | YES | | | 实际重量 | +| 9 | `ActTol` | decimal(18,3) | YES | | | 实际误差 | +| 10 | `RecipeName` | varchar(30) | YES | | | 配方名称 | +| 11 | `PlanID` | varchar(20) | NO | 是 | | 计划编号 | +| 12 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 13 | `MES_Flag` | int | YES | | ((0)) | MES标志(英文转译推测) | +| 14 | `StartTim` | varchar(20) | YES | | | 开始时间 | +| 15 | `EndTim` | varchar(20) | YES | | | 结束时间 | + +--- + +## 1.40 表:MCSToMES_RepBatchoff + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MCSToMES_RepBatchoff` +- **表含义**:MCS->MES:排出/落盘回报 +- **字段数量**:30 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(50) | NO | 是 | | GUID(英文转译推测) | +| 2 | `EquipID` | int | YES | | | 设备ID(英文转译推测) | +| 3 | `RpCode` | varchar(50) | YES | | | Rp编码(英文转译推测) | +| 4 | `PlanID` | varchar(40) | YES | | | 计划ID(英文转译推测) | +| 5 | `RFIDCode` | varchar(50) | YES | | | RFIDCode(英文转译推测) | +| 6 | `BOEquipName` | varchar(40) | YES | | | BOEquip名称(英文转译推测) | +| 7 | `RubberType` | int | YES | | | Rubber类型(英文转译推测) | +| 8 | `RubWeight` | decimal(18,3) | YES | | | Rub重量(英文转译推测) | +| 9 | `MinSaveTime` | int | YES | | | 最小Save时间(英文转译推测) | +| 10 | `MaxSaveTime` | int | YES | | | 最大Save时间(英文转译推测) | +| 11 | `ReRubberType` | int | YES | | | ReRubber类型(英文转译推测) | +| 12 | `ReRubberPersion` | int | YES | | | Re Rubber Persion(英文转译推测) | +| 13 | `RubberTrayState` | int | YES | | | RubberTray状态(英文转译推测) | +| 14 | `ReStartTime` | varchar(20) | YES | | | Re开始时间(英文转译推测) | +| 15 | `ReEndTime` | varchar(20) | YES | | | Re结束时间(英文转译推测) | +| 16 | `TrayCount` | int | YES | | | Tray数量(英文转译推测) | +| 17 | `StartSerial` | int | YES | | | 开始Serial(英文转译推测) | +| 18 | `EndSerial` | int | YES | | | 结束Serial(英文转译推测) | +| 19 | `ReRubberWeight` | decimal(18,3) | YES | | | ReRubber重量(英文转译推测) | +| 20 | `ReRubberLength` | decimal(18,3) | YES | | | Re Rubber Length(英文转译推测) | +| 21 | `ReRubberError` | decimal(18,3) | YES | | | ReRubber错误(英文转译推测) | +| 22 | `MDErrorCount` | int | YES | | | MDError数量(英文转译推测) | +| 23 | `SampleCount` | int | YES | | | Sample数量(英文转译推测) | +| 24 | `Re1Length` | decimal(18,3) | YES | | | Re 1 Length(英文转译推测) | +| 25 | `Re2Length` | decimal(18,3) | YES | | | Re 2 Length(英文转译推测) | +| 26 | `Re3Length` | decimal(18,3) | YES | | | Re 3 Length(英文转译推测) | +| 27 | `Re4Length` | decimal(18,3) | YES | | | Re 4 Length(英文转译推测) | +| 28 | `Re5Length` | decimal(18,3) | YES | | | Re 5 Length(英文转译推测) | +| 29 | `SaveTime` | varchar(20) | YES | | | Save时间(英文转译推测) | +| 30 | `MES_Flag` | int | YES | | ((0)) | MES标志(英文转译推测) | + +--- + +## 1.41 表:MCSToMES_Scale_Calibration + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MCSToMES_Scale_Calibration` +- **表含义**:MCS->MES:秤标定记录 +- **字段数量**:11 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | | GUID(英文转译推测) | +| 2 | `ScaleNum` | int | YES | | | 秤Num(英文转译推测) | +| 3 | `ScaleClass` | varchar(20) | YES | | | 秤Class(英文转译推测) | +| 4 | `ScaleName` | varchar(20) | YES | | | 秤名称(英文转译推测) | +| 5 | `SetValue` | numeric(8,3) | YES | | | Set数值(英文转译推测) | +| 6 | `RealValue` | numeric(8,3) | YES | | | Real数值(英文转译推测) | +| 7 | `ErrValue` | numeric(8,3) | YES | | | Err数值(英文转译推测) | +| 8 | `RealTime` | datetime | YES | | | Real时间(英文转译推测) | +| 9 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 10 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 11 | `MES_Flag` | int | YES | | | MES标志(英文转译推测) | + +--- + +## 1.42 表:MesDownVerificationStatusTab + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MesDownVerificationStatusTab` +- **表含义**:MES下传校验状态表 +- **字段数量**:11 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `uuid` | nvarchar(60) | NO | 是 | | 唯一标识(英文转译推测) | +| 2 | `Version` | bigint | YES | | | 版本(英文转译推测) | +| 3 | `DeviceId` | nvarchar(30) | YES | | | Device Id(英文转译推测) | +| 4 | `PositionId` | nvarchar(20) | NO | | | 1小料 2胶料 | +| 5 | `Status` | int | NO | | ((0)) | 0通过 1不通过 | +| 6 | `UpdateFlag` | int | NO | | ((0)) | MES写1,MCC获取后写9 | +| 7 | `MESDateTime` | datetime | YES | | | MES日期时间(英文转译推测) | +| 8 | `CSWDateTime` | datetime | YES | | | CSWDate时间(英文转译推测) | +| 9 | `Remark` | nvarchar(60) | YES | | | 备注(英文转译推测) | +| 10 | `Remark1` | nvarchar(60) | YES | | | 备注1(英文转译推测) | +| 11 | `Remark2` | nvarchar(60) | YES | | | 备注2(英文转译推测) | + +--- + +## 1.43 表:MESToMCS_BarCodeInfo + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MESToMCS_BarCodeInfo` +- **表含义**:MES->MCS:条码信息下发 +- **字段数量**:12 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(64) | YES | | | GUID(英文转译推测) | +| 2 | `BarCode` | varchar(32) | YES | | | 条码(英文转译推测) | +| 3 | `MatCode` | varchar(32) | YES | | | 物料编码(英文转译推测) | +| 4 | `MatName` | varchar(32) | YES | | | 物料名称(英文转译推测) | +| 5 | `MatCategory` | varchar(32) | YES | | | Mat Category(英文转译推测) | +| 6 | `MatSubcategory` | varchar(32) | YES | | | Mat Subcategory(英文转译推测) | +| 7 | `MatWeight` | numeric(18,2) | YES | | | Mat重量(英文转译推测) | +| 8 | `MatAmount` | numeric(18,2) | YES | | | Mat Amount(英文转译推测) | +| 9 | `IsOutStore` | int | YES | | | Is Out Store(英文转译推测) | +| 10 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 11 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 12 | `MES_Flag` | int | YES | | | MES标志(英文转译推测) | + +--- + +## 1.44 表:MESToMCS_Material + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MESToMCS_Material` +- **表含义**:MES->MCS:物料主数据下发 +- **字段数量**:17 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | | GUID(英文转译推测) | +| 2 | `EquipID` | int | NO | 是 | | 机台号 | +| 3 | `EquipType` | varchar(10) | YES | | | 机台类型,CWS:小料;MCS:上辅机; | +| 4 | `MatCode` | varchar(30) | NO | 是 | | 物料代码 | +| 5 | `MatName` | varchar(30) | YES | | | 物料名称 | +| 6 | `MatBarcode` | varchar(50) | YES | | | 物料条码扫描判断信息 | +| 7 | `MatTypeID` | int | YES | | | Mat类型ID(英文转译推测) | +| 8 | `MatType` | varchar(50) | YES | | | 物料类别 | +| 9 | `MatRubTypeID` | int | YES | | | MatRub类型ID(英文转译推测) | +| 10 | `MatRubType` | varchar(10) | YES | | | 胶料类别,0:胶块;1:胶片;2:胶粒 | +| 11 | `ScaleID` | int | YES | | | 秤ID(英文转译推测) | +| 12 | `ScaleName` | varchar(20) | YES | | | 秤名称(英文转译推测) | +| 13 | `CWSID` | int | YES | | | 小料设备号 | +| 14 | `WriteTime` | datetime | YES | | | 写入时间 | +| 15 | `ReadTime` | datetime | YES | | | 读取时间 | +| 16 | `MES_Flag` | int | YES | | (0) | 交互标识 | +| 17 | `UseBar` | int | YES | | ((0)) | Use Bar(英文转译推测) | + +--- + +## 1.45 表:MESToMCS_MatSendInf + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MESToMCS_MatSendInf` +- **表含义**:MES->MCS:物料发送信息 +- **字段数量**:20 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | | GUID(英文转译推测) | +| 2 | `EquipID` | int | YES | | | 设备ID(英文转译推测) | +| 3 | `EquipType` | varchar(10) | YES | | | 设备类型(英文转译推测) | +| 4 | `MatCode` | varchar(30) | YES | | | 物料编码(英文转译推测) | +| 5 | `MatName` | varchar(30) | YES | | | 物料名称(英文转译推测) | +| 6 | `OrderID` | varchar(30) | YES | | | 订单ID(英文转译推测) | +| 7 | `MatID` | varchar(30) | YES | | | Mat ID(英文转译推测) | +| 8 | `Weight` | numeric(18,3) | YES | | | 重量(英文转译推测) | +| 9 | `MatBarcode` | varchar(30) | YES | | | Mat条码(英文转译推测) | +| 10 | `MatKey` | int | YES | | | Mat键(英文转译推测) | +| 11 | `Result` | int | YES | | | 结果(英文转译推测) | +| 12 | `MatTypeID` | int | YES | | | Mat类型ID(英文转译推测) | +| 13 | `MatType` | int | YES | | | Mat类型(英文转译推测) | +| 14 | `ScaleID` | int | YES | | | 秤ID(英文转译推测) | +| 15 | `ScaleName` | varchar(20) | YES | | | 秤名称(英文转译推测) | +| 16 | `MatRubType` | varchar(1) | YES | | | MatRub类型(英文转译推测) | +| 17 | `CWSID` | int | YES | | | CWSID(英文转译推测) | +| 18 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 19 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 20 | `MES_Flag` | int | YES | | | MES标志(英文转译推测) | + +--- + +## 1.46 表:MESToMCS_MixPlan + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MESToMCS_MixPlan` +- **表含义**:MES->MCS:密炼计划下发 +- **字段数量**:29 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | | GUID(英文转译推测) | +| 2 | `PlanId` | varchar(20) | NO | 是 | | 计划编号 | +| 3 | `PlanSource` | varchar(1) | NO | 是 | | 计划来源(英文转译推测) | +| 4 | `EquipId` | int | NO | 是 | | 机台编号 | +| 5 | `EquipName` | varchar(20) | YES | | | 机台名称 | +| 6 | `EquipType` | varchar(10) | YES | | | 机台类型 | +| 7 | `PlanDate` | varchar(10) | YES | | | 计划日期 | +| 8 | `ShiftId` | varchar(1) | YES | | | 班次1:Night 2: Morning 3:Noon | +| 9 | `ShiftClass` | varchar(1) | YES | | | 班组1:A 2:B 3:C 4:D | +| 10 | `WorkOrder` | int | YES | | | 计划顺序 | +| 11 | `RecipCode` | varchar(30) | YES | | | 配方代码 | +| 12 | `RecipName` | varchar(30) | YES | | | 配方名称 | +| 13 | `MaterCode` | varchar(40) | YES | | | 物料编码(英文转译推测) | +| 14 | `MaterName` | varchar(80) | YES | | | 物料名称(英文转译推测) | +| 15 | `MOCode` | varchar(40) | YES | | | MOCode(英文转译推测) | +| 16 | `RcipVersion` | int | YES | | | 配方版本 | +| 17 | `ProType` | int | YES | | | 配方类型1:正式 2:试验 | +| 18 | `SetNum` | int | YES | | | 计划车次 | +| 19 | `DoneNum` | int | YES | | | 完成车次 | +| 20 | `PlanState` | int | YES | | | 计划状态 | +| 21 | `MESPlanChange` | int | YES | | | MES计划变化 | +| 22 | `MCSUpdateTime` | datetime | YES | | | MCS更新时间(英文转译推测) | +| 23 | `MCSPlanChange` | int | YES | | | MCSPlan Change(英文转译推测) | +| 24 | `MESUPdateTime` | datetime | YES | | | MES更新时间(英文转译推测) | +| 25 | `WriteTime` | datetime | YES | | | 写入时间 | +| 26 | `ReadTime` | datetime | YES | | | 读取时间 | +| 27 | `MES_Flag` | int | YES | | ((0)) | MES标志(英文转译推测) | +| 28 | `Remark` | varchar(200) | YES | | | 备注 | +| 29 | `TR_STATE` | int | YES | | | TR状态(英文转译推测) | + +--- + +## 1.47 表:MESToMCS_Recipe + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MESToMCS_Recipe` +- **表含义**:MES->MCS:配方主数据下发 +- **字段数量**:49 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | ('') | GUID(英文转译推测) | +| 2 | `EquipId` | int | NO | 是 | | 机台编号 | +| 3 | `EquipName` | varchar(20) | YES | | ('') | 机台名称 | +| 4 | `EquipType` | varchar(10) | NO | 是 | | 机台类型;CWS:小料;MCS:上辅机; | +| 5 | `RecipCode` | varchar(40) | NO | 是 | | 配方代码 | +| 6 | `RecipName` | varchar(40) | YES | | ('') | 配方名称 | +| 7 | `RecipVersion` | int | NO | 是 | | 配方版本 | +| 8 | `ProType` | varchar(10) | NO | 是 | | 配方类型;1:正式 2:试验 3:批量试验 4:1段母炼 5: 2段母炼6:终炼胶 | +| 9 | `IsUse` | int | YES | | | 使用状态1:使用 0:停用 | +| 10 | `Weight` | decimal(18,3) | YES | | ((0)) | 配方总重 | +| 11 | `Dust1RecycleMode` | int | YES | | ((0)) | 回收方式1;0:不回收;1:时间回收;2:重量回收 | +| 12 | `Dust1RecycleVal` | decimal(18,3) | YES | | ((0)) | 回收参数1 | +| 13 | `Dust2RecycleMode` | int | YES | | ((0)) | 回收方式2;0:不回收;1:时间回收;2:重量回收 | +| 14 | `Dust2RecycleVal` | decimal(18,0) | YES | | ((0)) | 回收参数1 | +| 15 | `SetOverTempMinTimVal` | int | YES | | ((0)) | 超温排胶最短时间 | +| 16 | `SetOverTimVal` | int | YES | | ((0)) | 超时排胶时间 | +| 17 | `SetOverTempVal` | decimal(18,2) | YES | | ((0)) | 超温排胶温度 | +| 18 | `SetOverEnergyVal` | decimal(18,3) | YES | | ((0)) | 超能排胶能量 | +| 19 | `FeedLowTempLimit` | decimal(18,3) | YES | | ((0)) | 进胶最低温度 | +| 20 | `FeedHighTempLimit` | decimal(18,3) | YES | | ((0)) | 进胶最高温度 | +| 21 | `FeedRotorSetSpd` | decimal(18,3) | YES | | ((0)) | 备用 | +| 22 | `FeedRotorSetTol` | decimal(18,3) | YES | | ((0)) | 备用 | +| 23 | `IntervalTim` | int | YES | | ((0)) | 最小间隔时间 | +| 24 | `SetTCU1` | decimal(18,3) | YES | | ((0)) | 混炼室温度 | +| 25 | `SetTCU2` | decimal(18,3) | YES | | ((0)) | 转子温度 | +| 26 | `SetTCU3` | decimal(18,3) | YES | | ((0)) | 卸料门温度 | +| 27 | `SetTCU4` | decimal(18,3) | YES | | ((0)) | 备用 | +| 28 | `SetTCU5` | decimal(18,3) | YES | | ((0)) | 备用 | +| 29 | `CtcMaxSpd` | decimal(18,3) | YES | | ((0)) | 最大转速 | +| 30 | `CtcMinSpd` | decimal(18,3) | YES | | ((0)) | 最小转速 | +| 31 | `CtcMaxPress` | decimal(18,0) | YES | | ((0)) | 最大压力 | +| 32 | `CtcMinPress` | decimal(18,3) | YES | | ((0)) | 最小压力 | +| 33 | `CtcLimitTemp` | decimal(18,3) | YES | | ((0)) | 温度保护 | +| 34 | `CtcLimitTim` | int | YES | | ((0)) | 时间保护 | +| 35 | `ShelfLotCount` | int | YES | | ((0)) | Shelf批次数量(英文转译推测) | +| 36 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 37 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 38 | `MES_Flag` | int | YES | | ((0)) | MES标志(英文转译推测) | +| 39 | `Remark` | varchar(50) | YES | | ('') | 备注(英文转译推测) | +| 40 | `AbnormalFeedBack` | varchar(50) | YES | | ('') | Abnormal供料Back(英文转译推测) | +| 41 | `AbortId` | int | YES | | ((0)) | Abort Id(英文转译推测) | +| 42 | `ThreeTemp1` | decimal(18,3) | YES | | ((0)) | Three温度1(英文转译推测) | +| 43 | `ThreeTemp2` | decimal(18,3) | YES | | ((0)) | Three温度2(英文转译推测) | +| 44 | `ThreeTemp3` | decimal(18,3) | YES | | ((0)) | Three温度3(英文转译推测) | +| 45 | `ThreeTemp4` | decimal(18,3) | YES | | ((0)) | Three温度4(英文转译推测) | +| 46 | `DisTemp` | numeric(18,3) | YES | | ((0)) | Dis温度(英文转译推测) | +| 47 | `RepVersionTxt` | varchar(40) | YES | | | Rep版本Txt(英文转译推测) | +| 48 | `RpType` | int | YES | | | Rp类型(英文转译推测) | +| 49 | `ReRubberWeight` | numeric(18,3) | YES | | | ReRubber重量(英文转译推测) | + +--- + +## 1.48 表:MESToMCS_Recipe_BatchOff + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MESToMCS_Recipe_BatchOff` +- **表含义**:MES->MCS:配方排出参数下发 +- **字段数量**:27 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(50) | NO | | | GUID(英文转译推测) | +| 2 | `EquipID` | int | NO | 是 | | 设备ID(英文转译推测) | +| 3 | `RpCode` | varchar(50) | NO | 是 | | Rp编码(英文转译推测) | +| 4 | `RubberType` | int | YES | | | Rubber类型(英文转译推测) | +| 5 | `ReRubberType` | int | YES | | | ReRubber类型(英文转译推测) | +| 6 | `ReRubberWeight` | decimal(9,3) | YES | | | ReRubber重量(英文转译推测) | +| 7 | `ReRubberLength` | decimal(9,3) | YES | | | Re Rubber Length(英文转译推测) | +| 8 | `ReRubberCount` | int | YES | | | ReRubber数量(英文转译推测) | +| 9 | `ReRubberHeight` | decimal(9,3) | YES | | | Re Rubber Height(英文转译推测) | +| 10 | `ReRubberSpeed` | decimal(9,3) | YES | | | ReRubber速度(英文转译推测) | +| 11 | `RubberMWeight` | decimal(9,3) | YES | | | Rubber MWeight(英文转译推测) | +| 12 | `ReRubberLimitTemp` | decimal(9,3) | YES | | | ReRubberLimit温度(英文转译推测) | +| 13 | `ReRubberLead` | decimal(9,3) | YES | | | Re Rubber Lead(英文转译推测) | +| 14 | `MinSaveTime` | int | YES | | | 最小Save时间(英文转译推测) | +| 15 | `MaxSaveTime` | int | YES | | | 最大Save时间(英文转译推测) | +| 16 | `PerWeight` | decimal(9,3) | YES | | | Per重量(英文转译推测) | +| 17 | `SampleType` | int | YES | | | Sample类型(英文转译推测) | +| 18 | `SampleWeight` | decimal(9,3) | YES | | | Sample重量(英文转译推测) | +| 19 | `SampleLength` | decimal(9,3) | YES | | | Sample Length(英文转译推测) | +| 20 | `SampleCount` | int | YES | | | Sample数量(英文转译推测) | +| 21 | `RpVersion` | varchar(50) | YES | | | Rp版本(英文转译推测) | +| 22 | `RpVersionID` | int | NO | 是 | | Rp版本ID(英文转译推测) | +| 23 | `HangRubberHeight` | decimal(9,3) | YES | | | Hang Rubber Height(英文转译推测) | +| 24 | `MES_Flag` | int | YES | | | MES标志(英文转译推测) | +| 25 | `RecipVersionID` | int | YES | | | Recip版本ID(英文转译推测) | +| 26 | `ReRubberPos` | int | YES | | | Re Rubber Pos(英文转译推测) | +| 27 | `RecipVersion` | varchar(50) | YES | | | Recip版本(英文转译推测) | + +--- + +## 1.49 表:MESToMCS_Recipe_Delete + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MESToMCS_Recipe_Delete` +- **表含义**:MES->MCS:配方删除指令 +- **字段数量**:11 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | | GUID(英文转译推测) | +| 2 | `EquipId` | int | NO | 是 | | 机台编号 | +| 3 | `EquipName` | varchar(10) | YES | | | 机台名称 | +| 4 | `EquipType` | varchar(10) | NO | 是 | | 机台类型;CWS:小料;MCS:上辅机 | +| 5 | `RecipCode` | varchar(30) | NO | 是 | | 配方(物料)代码 | +| 6 | `RecipVersion` | int | NO | 是 | | 配方(物料)版本 | +| 7 | `User` | varchar(10) | YES | | | 操作人 | +| 8 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 9 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 10 | `MES_Flag` | int | YES | | (0) | MES标志(英文转译推测) | +| 11 | `Remark` | varchar(50) | YES | | | 备注(英文转译推测) | + +--- + +## 1.50 表:MESToMCS_Recipe_MillStep + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MESToMCS_Recipe_MillStep` +- **表含义**:MES->MCS:配方开炼步骤下发 +- **字段数量**:21 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(50) | YES | | | GUID(英文转译推测) | +| 2 | `EquipID` | int | NO | 是 | | 设备ID(英文转译推测) | +| 3 | `MillID` | int | NO | 是 | | 开炼ID(英文转译推测) | +| 4 | `RpCode` | varchar(50) | NO | 是 | | Rp编码(英文转译推测) | +| 5 | `StepID` | int | NO | 是 | | 步骤ID(英文转译推测) | +| 6 | `MillAction` | int | YES | | | 开炼动作(英文转译推测) | +| 7 | `ActName` | varchar(40) | YES | | | 动作名称(英文转译推测) | +| 8 | `ActName_EN` | varchar(40) | YES | | | 动作名称en(英文转译推测) | +| 9 | `MillTime` | int | YES | | | 开炼时间(英文转译推测) | +| 10 | `MillSpeed` | decimal(18,3) | YES | | | 开炼速度(英文转译推测) | +| 11 | `CoolDeviceSpeed` | decimal(18,3) | YES | | | CoolDevice速度(英文转译推测) | +| 12 | `RollGap` | decimal(18,3) | YES | | | Roll Gap(英文转译推测) | +| 13 | `WaterTemper` | decimal(18,3) | YES | | | Water Temper(英文转译推测) | +| 14 | `RubTemper` | decimal(18,3) | YES | | | Rub Temper(英文转译推测) | +| 15 | `SpeedRatio` | decimal(18,3) | YES | | | 速度Ratio(英文转译推测) | +| 16 | `Spare1` | int | YES | | ((0)) | Spare 1(英文转译推测) | +| 17 | `Spare2` | int | YES | | ((0)) | Spare 2(英文转译推测) | +| 18 | `RpVersionID` | int | NO | 是 | | Rp版本ID(英文转译推测) | +| 19 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 20 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 21 | `MES_Flag` | int | YES | | ((0)) | MES标志(英文转译推测) | + +--- + +## 1.51 表:MESToMCS_Recipe_MixDown + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MESToMCS_Recipe_MixDown` +- **表含义**:MES->MCS:配方混炼下传数据 +- **字段数量**:29 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(50) | NO | | | GUID(英文转译推测) | +| 2 | `EquipID` | int | NO | 是 | | 设备ID(英文转译推测) | +| 3 | `EquipType` | varchar(20) | NO | 是 | | 设备类型(英文转译推测) | +| 4 | `EquipName` | varchar(20) | YES | | | 设备名称(英文转译推测) | +| 5 | `RpCode` | varchar(50) | NO | 是 | | Rp编码(英文转译推测) | +| 6 | `RpName` | varchar(50) | YES | | | Rp名称(英文转译推测) | +| 7 | `RpVersion` | varchar(50) | YES | | | Rp版本(英文转译推测) | +| 8 | `SetOverTempMinTimVal` | int | YES | | | SetOver温度最小TimVal(英文转译推测) | +| 9 | `SetOverTimVal` | int | YES | | | Set Over Tim Val(英文转译推测) | +| 10 | `SetOverTempVal` | decimal(9,3) | YES | | | SetOver温度Val(英文转译推测) | +| 11 | `SetOverEnergyVal` | decimal(9,3) | YES | | | SetOver能耗Val(英文转译推测) | +| 12 | `FeedLowTempLimit` | decimal(9,3) | YES | | | 供料Low温度Limit(英文转译推测) | +| 13 | `FeedHighTempLimit` | decimal(9,3) | YES | | | 供料High温度Limit(英文转译推测) | +| 14 | `IntervalTim` | int | YES | | | Interval Tim(英文转译推测) | +| 15 | `SetTCU1` | decimal(9,3) | YES | | | Set TCU 1(英文转译推测) | +| 16 | `SetTCU2` | decimal(9,3) | YES | | | Set TCU 2(英文转译推测) | +| 17 | `SetTCU3` | decimal(9,3) | YES | | | Set TCU 3(英文转译推测) | +| 18 | `SetTCU4` | decimal(9,3) | YES | | | Set TCU 4(英文转译推测) | +| 19 | `SetTCU5` | decimal(9,3) | YES | | | Set TCU 5(英文转译推测) | +| 20 | `CtcMaxSpd` | decimal(9,3) | YES | | | Ctc最大Spd(英文转译推测) | +| 21 | `CtcMinSpd` | decimal(9,3) | YES | | | Ctc最小Spd(英文转译推测) | +| 22 | `CtcMaxPress` | decimal(9,3) | YES | | | Ctc最大Press(英文转译推测) | +| 23 | `CtcMinPress` | decimal(9,3) | YES | | | Ctc最小Press(英文转译推测) | +| 24 | `CtcLimitTemp` | decimal(9,3) | YES | | | CtcLimit温度(英文转译推测) | +| 25 | `CtcLimitTim` | int | YES | | | Ctc Limit Tim(英文转译推测) | +| 26 | `SaveTime` | varchar(20) | YES | | | Save时间(英文转译推测) | +| 27 | `LastOper` | varchar(20) | YES | | | Last Oper(英文转译推测) | +| 28 | `RpVersionID` | int | YES | | | Rp版本ID(英文转译推测) | +| 29 | `RecipVersion` | varchar(20) | YES | | | Recip版本(英文转译推测) | + +--- + +## 1.52 表:MESToMCS_Recipe_MixStep + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MESToMCS_Recipe_MixStep` +- **表含义**:MES->MCS:配方密炼步骤下发 +- **字段数量**:26 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | ('') | GUID(英文转译推测) | +| 2 | `EquipId` | int | NO | 是 | | 机台编号 | +| 3 | `EquipName` | varchar(20) | YES | | ('') | 机台名称 | +| 4 | `EquipType` | varchar(10) | NO | 是 | | 机台类型 | +| 5 | `RecipCode` | varchar(30) | NO | 是 | | 配方代码 | +| 6 | `Serial` | int | NO | 是 | | 密炼步序 | +| 7 | `Act` | int | YES | | ((0)) | 动作地址 | +| 8 | `Cond` | int | YES | | ((0)) | 条件地址 | +| 9 | `Time` | int | NO | | ((0)) | 设定时间 | +| 10 | `TimeLimit` | int | YES | | ((0)) | 时间保护 | +| 11 | `Temp` | decimal(18,3) | YES | | ((0)) | 温度 | +| 12 | `TempLimit` | decimal(18,3) | YES | | ((0)) | 温度保护 | +| 13 | `Energy` | decimal(18,3) | YES | | ((0)) | 能量 | +| 14 | `EnergyLimit` | decimal(18,3) | YES | | ((0)) | 能量保护 | +| 15 | `Power` | decimal(18,3) | YES | | ((0)) | 功率 | +| 16 | `PowerLimit` | decimal(18,3) | YES | | ((0)) | 功率保护 | +| 17 | `Press` | decimal(18,3) | YES | | ((0)) | 压力 | +| 18 | `Speed` | decimal(18,3) | YES | | ((0)) | 转速 | +| 19 | `RotorGap` | decimal(18,3) | YES | | ((0)) | 辊距 | +| 20 | `RamPos` | decimal(18,3) | YES | | ((0)) | 栓位置 | +| 21 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 22 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 23 | `MES_Flag` | int | YES | | ((0)) | MES标志(英文转译推测) | +| 24 | `Remark` | varchar(200) | YES | | ('') | 备注(英文转译推测) | +| 25 | `AbnormalFeedBack` | varchar(50) | YES | | ('') | Abnormal供料Back(英文转译推测) | +| 26 | `RecipVersion` | int | NO | 是 | | Recip版本(英文转译推测) | + +--- + +## 1.53 表:MESToMCS_Recipe_MixStep_Down + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MESToMCS_Recipe_MixStep_Down` +- **表含义**:MES->MCS:配方密炼步骤下传明细 +- **字段数量**:21 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(50) | NO | | | GUID(英文转译推测) | +| 2 | `EquipID` | int | NO | 是 | | 设备ID(英文转译推测) | +| 3 | `RpCode` | varchar(50) | NO | 是 | | Rp编码(英文转译推测) | +| 4 | `MixID` | int | NO | 是 | | 密炼ID(英文转译推测) | +| 5 | `Cond` | int | YES | | | Cond(英文转译推测) | +| 6 | `Tim` | int | YES | | | Tim(英文转译推测) | +| 7 | `TimLimit` | int | YES | | | Tim Limit(英文转译推测) | +| 8 | `Tempe` | decimal(18,3) | YES | | | 温度e(英文转译推测) | +| 9 | `TempLimit` | decimal(18,3) | YES | | | 温度Limit(英文转译推测) | +| 10 | `Energy` | decimal(18,3) | YES | | | 能耗(英文转译推测) | +| 11 | `EnergyLimit` | decimal(18,3) | YES | | | 能耗Limit(英文转译推测) | +| 12 | `Power` | int | YES | | | 功率(英文转译推测) | +| 13 | `PowerLimit` | int | YES | | | 功率Limit(英文转译推测) | +| 14 | `Act` | int | YES | | | 动作(英文转译推测) | +| 15 | `Press` | decimal(18,3) | YES | | | Press(英文转译推测) | +| 16 | `RotorSpd` | decimal(18,3) | YES | | | Rotor Spd(英文转译推测) | +| 17 | `RollGap` | decimal(18,3) | YES | | | Roll Gap(英文转译推测) | +| 18 | `RamPos` | decimal(18,3) | YES | | | Ram Pos(英文转译推测) | +| 19 | `RpVersion` | varchar(40) | YES | | | Rp版本(英文转译推测) | +| 20 | `RpVersionID` | int | YES | | | Rp版本ID(英文转译推测) | +| 21 | `Torque` | decimal(18,3) | YES | | | Torque(英文转译推测) | + +--- + +## 1.54 表:MESToMCS_Recipe_Weight + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MESToMCS_Recipe_Weight` +- **表含义**:MES->MCS:配方称重物料明细下发 +- **字段数量**:26 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | | GUID(英文转译推测) | +| 2 | `EquipId` | int | NO | 是 | | 机台编号 | +| 3 | `EquipName` | varchar(20) | YES | | ('') | 机台名称 | +| 4 | `EquipType` | varchar(10) | NO | 是 | ('') | 机台类型 | +| 5 | `RecipCode` | varchar(30) | NO | 是 | ('') | 配方代码 | +| 6 | `ScaleType` | int | NO | 是 | ((0)) | 秤类型1:炭黑 2:油料 3:胶料 4:小料 | +| 7 | `ScaleId` | int | NO | 是 | ((0)) | 秤编号,101:炭黑秤1;102:炭黑秤2;103:炭黑秤3 + 201:油料秤1;202:油料秤2;203:油料秤3;301:胶料秤1;302:胶料秤2;401:小料秤1;402:小料秤2 | +| 8 | `Serial` | int | NO | 是 | ((0)) | 称量步序 | +| 9 | `Action` | int | YES | | ((0)) | 动作;1:称量 2:称到 3:卸料,默认值0 | +| 10 | `BinNo` | int | YES | | ((0)) | 仓号(英文转译推测) | +| 11 | `MatCode` | varchar(30) | YES | | ('') | 物料代码 | +| 12 | `MatName` | varchar(30) | YES | | ('') | 物料名称 | +| 13 | `SetWeight` | decimal(18,3) | YES | | ((0)) | 设定重量 | +| 14 | `Tolerance` | decimal(18,3) | YES | | ((0)) | 设定误差 | +| 15 | `SFNum` | int | YES | | ((0)) | 导切机编号 | +| 16 | `MatType` | varchar(10) | YES | | ('') | 炭黑、油料、胶料、小料 | +| 17 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 18 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 19 | `MES_Flag` | int | YES | | ((0)) | MES标志(英文转译推测) | +| 20 | `Remark` | varchar(50) | YES | | ('') | 备注(英文转译推测) | +| 21 | `RubTypeID` | int | YES | | ((0)) | Rub类型ID(英文转译推测) | +| 22 | `MixID` | int | NO | 是 | | 密炼ID(英文转译推测) | +| 23 | `AbnormalFeedBack` | varchar(50) | YES | | ('') | Abnormal供料Back(英文转译推测) | +| 24 | `AbortId` | int | YES | | ((0)) | Abort Id(英文转译推测) | +| 25 | `RecipVersion` | int | NO | 是 | | Recip版本(英文转译推测) | +| 26 | `SetDnTol` | decimal(18,3) | YES | | ((0)) | SetDn容差(英文转译推测) | + +--- + +## 1.55 表:MESToMCS_Verify + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`MESToMCS_Verify` +- **表含义**:MES->MCS:校验/核对数据 +- **字段数量**:12 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | NO | 是 | | GUID(英文转译推测) | +| 2 | `EquipID` | int | NO | | | 机台编号 | +| 3 | `EquipName` | varchar(10) | YES | | | 机台名称 | +| 4 | `EquipType` | varchar(10) | NO | | | 机台类型 | +| 5 | `ScaleNum` | int | NO | | | 秤编号 | +| 6 | `PlanId` | varchar(20) | YES | | | 计划编号 | +| 7 | `MatCode` | varchar(30) | YES | | | 物料代码 | +| 8 | `MatName` | varchar(30) | YES | | | 物料名称 | +| 9 | `Judge` | int | YES | | | 判断结果 | +| 10 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 11 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 12 | `MES_Flag` | int | YES | | (0) | MES标志(英文转译推测) | + +--- + +## 1.56 表:PptScanBarcode + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`PptScanBarcode` +- **表含义**:条码扫描当前表 +- **字段数量**:13 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `SerialID` | int | NO | 是 | | Serial ID(英文转译推测) | +| 2 | `Barcode` | varchar(25) | NO | 是 | | 条码(英文转译推测) | +| 3 | `EquipCode` | char(5) | NO | 是 | | 设备编码(英文转译推测) | +| 4 | `MaterCode` | char(13) | NO | 是 | | 物料编码(英文转译推测) | +| 5 | `MaterName` | nvarchar(80) | YES | | | 物料名称(英文转译推测) | +| 6 | `MaterType` | char(1) | YES | | | 物料类型(英文转译推测) | +| 7 | `MaterTypeName` | char(10) | YES | | | 物料类型名称(英文转译推测) | +| 8 | `BarchNo` | varchar(50) | YES | | | Barch编号(英文转译推测) | +| 9 | `ScanWeight` | decimal(18,2) | YES | | ((0)) | 扫描重量(英文转译推测) | +| 10 | `UsedWeight` | decimal(18,2) | YES | | | Used重量(英文转译推测) | +| 11 | `QueueIndex` | int | YES | | | Queue索引(英文转译推测) | +| 12 | `IsUsed` | char(1) | YES | | | Is Used(英文转译推测) | +| 13 | `ScanDateTime` | datetime | YES | | | 扫描日期时间(英文转译推测) | + +--- + +## 1.57 表:PptScanBarcodeHis + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`PptScanBarcodeHis` +- **表含义**:条码扫描历史表 +- **字段数量**:15 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `ObjId` | int | NO | 是 | | Obj Id(英文转译推测) | +| 2 | `SerialID` | int | NO | | | Serial ID(英文转译推测) | +| 3 | `Barcode` | varchar(25) | NO | | | 条码(英文转译推测) | +| 4 | `EquipCode` | char(5) | NO | | | 设备编码(英文转译推测) | +| 5 | `MaterCode` | char(13) | NO | | | 物料编码(英文转译推测) | +| 6 | `MaterName` | nvarchar(80) | YES | | | 物料名称(英文转译推测) | +| 7 | `MaterType` | char(1) | YES | | | 物料类型(英文转译推测) | +| 8 | `MaterTypeName` | char(10) | YES | | | 物料类型名称(英文转译推测) | +| 9 | `BarchNo` | varchar(50) | YES | | | Barch编号(英文转译推测) | +| 10 | `ScanWeight` | decimal(18,2) | YES | | | 扫描重量(英文转译推测) | +| 11 | `UsedWeight` | decimal(18,2) | YES | | | Used重量(英文转译推测) | +| 12 | `QueueIndex` | int | YES | | | Queue索引(英文转译推测) | +| 13 | `IsUsed` | char(1) | YES | | | Is Used(英文转译推测) | +| 14 | `ScanDateTime` | datetime | YES | | | 扫描日期时间(英文转译推测) | +| 15 | `UpdateDateTime` | datetime | YES | | | 更新日期时间(英文转译推测) | + +--- + +## 1.58 表:PptScanBarcodeLog + +- **所属数据库**:`MESNAC_MCS_ShareDB` +- **Schema**:`dbo` +- **表名**:`PptScanBarcodeLog` +- **表含义**:条码扫描日志表 +- **字段数量**:12 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `dt` | datetime | NO | 是 | (getdate()) | dt(英文转译推测) | +| 2 | `EquipCode` | char(5) | NO | 是 | | 设备编码(英文转译推测) | +| 3 | `RecipeCode` | varchar(20) | YES | | | 配方编码(英文转译推测) | +| 4 | `scanBarcode` | varchar(30) | YES | | | 扫描条码(英文转译推测) | +| 5 | `ScanMaterCode` | char(13) | YES | | | 扫描物料编码(英文转译推测) | +| 6 | `msg` | varchar(200) | YES | | | msg(英文转译推测) | +| 7 | `Usercode` | varchar(10) | YES | | | 用户编码(英文转译推测) | +| 8 | `shiftid` | varchar(4) | YES | | | 班次ID(英文转译推测) | +| 9 | `shiftclass` | varchar(4) | YES | | | shiftclass(英文转译推测) | +| 10 | `scanflag` | varchar(1) | YES | | | 扫描标志(英文转译推测) | +| 11 | `ScanLogSignal` | int | YES | | | 扫描日志Signal(英文转译推测) | +| 12 | `ScanUsedBarMsg` | varchar(500) | YES | | | 扫描UsedBarMsg(英文转译推测) | + +--- + +# 2. 数据库:MESNAC_MCS_ShareDB_XL + +- **数据库名**:`MESNAC_MCS_ShareDB_XL` +- **用途**:小料(XL)相关中间库 +- **表数量**:54 + +## 2.1 表:Application_Error_Log + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`Application_Error_Log` +- **表含义**:应用错误日志 +- **字段数量**:6 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `tablename` | varchar(50) | YES | | | 表名(英文转译推测) | +| 2 | `userName` | varchar(50) | YES | | | 用户名(英文转译推测) | +| 3 | `errorNumber` | varchar(50) | YES | | | 错误编号(英文转译推测) | +| 4 | `errorSeverity` | varchar(50) | YES | | | 错误严重级别(英文转译推测) | +| 5 | `errorState` | varchar(50) | YES | | | 错误状态(英文转译推测) | +| 6 | `errorMessage` | varchar(5000) | YES | | | 错误信息(英文转译推测) | + +--- + +## 2.2 表:IF_BarcodeLog2Mes + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`IF_BarcodeLog2Mes` +- **表含义**:接口:条码日志 -> MES +- **字段数量**:10 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `ID` | bigint | NO | 是 | | ID(英文转译推测) | +| 2 | `Equip_Code` | varchar(50) | YES | | | 设备编码(英文转译推测) | +| 3 | `Scan_Time` | varchar(50) | YES | | | 扫描时间(英文转译推测) | +| 4 | `Scan_Bar` | varchar(50) | YES | | | 扫描Bar(英文转译推测) | +| 5 | `Material` | varchar(50) | YES | | | 物料(英文转译推测) | +| 6 | `Bin` | int | YES | | | 仓位(英文转译推测) | +| 7 | `Scan_State` | int | YES | | | 扫描状态(英文转译推测) | +| 8 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 9 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 10 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 2.3 表:IF_BinState2MES + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`IF_BinState2MES` +- **表含义**:接口:料仓状态 -> MES +- **字段数量**:7 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Equip_Code` | varchar(20) | YES | | | 设备编码(英文转译推测) | +| 2 | `Bin_Serial` | int | YES | | | 仓位Serial(英文转译推测) | +| 3 | `level_flag` | int | YES | | | 级别标志(英文转译推测) | +| 4 | `open_flag` | int | YES | | | open标志(英文转译推测) | +| 5 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 6 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 7 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 2.4 表:IF_EquipState2Mes + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`IF_EquipState2Mes` +- **表含义**:接口:设备状态 -> MES +- **字段数量**:9 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `ID` | varchar(50) | NO | | | ID(英文转译推测) | +| 2 | `Equip_Code` | varchar(50) | YES | | | 设备编码(英文转译推测) | +| 3 | `Equip_State` | varchar(50) | YES | | | 设备状态(英文转译推测) | +| 4 | `Alarm_OccurTime` | varchar(50) | YES | | | 报警Occur时间(英文转译推测) | +| 5 | `Alarm_ClearTime` | varchar(50) | YES | | | 报警Clear时间(英文转译推测) | +| 6 | `Equip_AlarmTxt` | varchar(50) | YES | | | 设备报警txt(英文转译推测) | +| 7 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 8 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 9 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 2.5 表:IF_FeedMaterialInfo2CWSS + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`IF_FeedMaterialInfo2CWSS` +- **表含义**:接口:供料信息 -> CWSS +- **字段数量**:8 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Equip_Code` | varchar(16) | YES | | | 设备编码(英文转译推测) | +| 2 | `Scan_Bar` | varchar(50) | YES | | | 扫描Bar(英文转译推测) | +| 3 | `Bin_Serial` | int | YES | | | 仓位Serial(英文转译推测) | +| 4 | `Material_code` | varchar(50) | YES | | | 物料编码(英文转译推测) | +| 5 | `Material_name` | varchar(50) | YES | | | 物料名称(英文转译推测) | +| 6 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 7 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 8 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 2.6 表:IF_LrAlarmLog2Mes + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`IF_LrAlarmLog2Mes` +- **表含义**:接口:小料报警日志 -> MES +- **字段数量**:12 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Equip_code` | varchar(20) | YES | | | 设备编码(英文转译推测) | +| 2 | `Alarm_ID` | int | YES | | | 报警ID(英文转译推测) | +| 3 | `Alarm_OccurTime` | varchar(50) | YES | | | 报警Occur时间(英文转译推测) | +| 4 | `Alarm_ClearTime` | varchar(50) | YES | | | 报警Clear时间(英文转译推测) | +| 5 | `Alarm_Status` | int | YES | | | 报警状态(英文转译推测) | +| 6 | `Alarm_GUID` | char(32) | YES | | | 报警GUID(英文转译推测) | +| 7 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 8 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 9 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | +| 10 | `Alarm_Level` | int | YES | | | 报警级别(英文转译推测) | +| 11 | `Alarm_Type` | nvarchar(50) | YES | | | 报警类型(英文转译推测) | +| 12 | `Alarm_Cn_Info` | varchar(50) | YES | | | 报警Cn信息(英文转译推测) | + +--- + +## 2.7 表:If_Lrlot2Mes + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`If_Lrlot2Mes` +- **表含义**:接口:小料批次 -> MES +- **字段数量**:26 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Plan_id` | varchar(50) | YES | | | 计划ID(英文转译推测) | +| 2 | `Equip_code` | varchar(50) | YES | | | 设备编码(英文转译推测) | +| 3 | `Serial_Num` | int | YES | | | Serial Num(英文转译推测) | +| 4 | `Prd_date` | varchar(50) | YES | | | Prd日期(英文转译推测) | +| 5 | `Real_weight` | numeric(9,0) | YES | | | 实际重量(英文转译推测) | +| 6 | `Real_Error` | numeric(9,0) | YES | | | Real错误(英文转译推测) | +| 7 | `Waste_Time` | int | YES | | | Waste时间(英文转译推测) | +| 8 | `WarnIng_Sgn` | int | YES | | | Warn Ing Sgn(英文转译推测) | +| 9 | `Net_Weight` | numeric(9,0) | YES | | | 净重(英文转译推测) | +| 10 | `Group_BarCode` | varchar(50) | YES | | | 组条码(英文转译推测) | +| 11 | `Lot_BarCode` | varchar(50) | YES | | | 批次条码(英文转译推测) | +| 12 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 13 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 14 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | +| 15 | `BG_SetTemp` | int | YES | | | BGSet温度(英文转译推测) | +| 16 | `BG_RealTemp` | int | YES | | | BGReal温度(英文转译推测) | +| 17 | `BG_SetPress` | int | YES | | | BG Set Press(英文转译推测) | +| 18 | `BG_RealPress` | int | YES | | | BG Real Press(英文转译推测) | +| 19 | `BG_SetTime` | int | YES | | | BGSet时间(英文转译推测) | +| 20 | `BG_RealTime` | int | YES | | | BGReal时间(英文转译推测) | +| 21 | `BW_SetTemp` | int | YES | | | BWSet温度(英文转译推测) | +| 22 | `BW_RealTemp` | int | YES | | | BWReal温度(英文转译推测) | +| 23 | `BW_SetPress` | int | YES | | | BW Set Press(英文转译推测) | +| 24 | `BW_RealPress` | int | YES | | | BW Real Press(英文转译推测) | +| 25 | `BW_SetTime` | int | YES | | | BWSet时间(英文转译推测) | +| 26 | `BW_RealTime` | int | YES | | | BWReal时间(英文转译推测) | + +--- + +## 2.8 表:IF_LrPlan2Mes + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`IF_LrPlan2Mes` +- **表含义**:接口:小料计划 -> MES +- **字段数量**:20 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Plan_Id` | varchar(50) | NO | 是 | | 计划ID(英文转译推测) | +| 2 | `Equip_Code` | varchar(50) | YES | | | 设备编码(英文转译推测) | +| 3 | `Plan_Serial` | int | YES | | | 计划Serial(英文转译推测) | +| 4 | `Recipe_ID` | varchar(50) | YES | | | 配方ID(英文转译推测) | +| 5 | `Recipe_Code` | varchar(50) | YES | | | 配方编码(英文转译推测) | +| 6 | `Recipe_Name` | varchar(50) | YES | | | 配方名称(英文转译推测) | +| 7 | `Version` | varchar(50) | YES | | | 版本(英文转译推测) | +| 8 | `Mixer_Line` | varchar(50) | YES | | | Mixer行(英文转译推测) | +| 9 | `Shift_Id` | varchar(10) | YES | | | 班次ID(英文转译推测) | +| 10 | `Plan_Num` | int | YES | | | 计划Num(英文转译推测) | +| 11 | `real_num` | int | YES | | | real num(英文转译推测) | +| 12 | `Start_Date` | varchar(50) | YES | | | 开始日期(英文转译推测) | +| 13 | `End_Date` | varchar(50) | YES | | | 结束日期(英文转译推测) | +| 14 | `Weight_Man` | varchar(50) | YES | | | 重量Man(英文转译推测) | +| 15 | `Plan_Batch` | varchar(50) | YES | | | 计划批次(英文转译推测) | +| 16 | `Plan_State` | int | YES | | | 计划状态(英文转译推测) | +| 17 | `Plan_Date` | varchar(50) | YES | | | 计划日期(英文转译推测) | +| 18 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 19 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 20 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 2.9 表:IF_LrPlanGroup2MES + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`IF_LrPlanGroup2MES` +- **表含义**:接口:小料计划组 -> MES +- **字段数量**:16 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `ID` | varchar(50) | NO | 是 | | ID(英文转译推测) | +| 2 | `Plan_Id` | varchar(50) | YES | | | 计划ID(英文转译推测) | +| 3 | `Equip_Code` | varchar(50) | YES | | | 设备编码(英文转译推测) | +| 4 | `Recipe_Code` | varchar(50) | YES | | | 配方编码(英文转译推测) | +| 5 | `Recipe_Name` | varchar(50) | YES | | | 配方名称(英文转译推测) | +| 6 | `Total_weight` | numeric(9,3) | YES | | | 总重量(英文转译推测) | +| 7 | `Total_Error` | numeric(9,3) | YES | | | 合计错误(英文转译推测) | +| 8 | `TrolleyBags` | int | YES | | | Trolley Bags(英文转译推测) | +| 9 | `TrolleyNumber` | int | YES | | | Trolley编号(英文转译推测) | +| 10 | `StartNum` | int | YES | | | 开始Num(英文转译推测) | +| 11 | `EndNum` | int | YES | | | 结束Num(英文转译推测) | +| 12 | `Prd_date` | varchar(50) | YES | | | Prd日期(英文转译推测) | +| 13 | `Group_BarCode` | varchar(50) | YES | | | 组条码(英文转译推测) | +| 14 | `Write_Time` | varchar(50) | YES | | | 写入时间(英文转译推测) | +| 15 | `Read_Time` | varchar(50) | YES | | | 读取时间(英文转译推测) | +| 16 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 2.10 表:IF_LrRecipe2Mes + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`IF_LrRecipe2Mes` +- **表含义**:接口:小料配方 -> MES +- **字段数量**:13 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Plan_id` | varchar(50) | NO | 是 | | 计划ID(英文转译推测) | +| 2 | `Equip_code` | varchar(50) | NO | 是 | | 设备编码(英文转译推测) | +| 3 | `Weight_ID` | int | NO | 是 | | 重量ID(英文转译推测) | +| 4 | `Material_Code` | varchar(50) | YES | | | 物料编码(英文转译推测) | +| 5 | `Material_Name` | varchar(50) | YES | | | 物料名称(英文转译推测) | +| 6 | `Set_Weight` | numeric(9,4) | YES | | | 设定重量(英文转译推测) | +| 7 | `Set_Error` | numeric(9,4) | YES | | | Set错误(英文转译推测) | +| 8 | `CPK_Error` | numeric(9,4) | YES | | | CPK错误(英文转译推测) | +| 9 | `Batch_number` | varchar(50) | YES | | | 批次号(英文转译推测) | +| 10 | `Actual_Mater` | varchar(50) | YES | | | Actual物料(英文转译推测) | +| 11 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 12 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 13 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 2.11 表:IF_LrWeigh2Mes + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`IF_LrWeigh2Mes` +- **表含义**:接口:小料称重 -> MES +- **字段数量**:17 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Plan_id` | varchar(50) | YES | | | 计划ID(英文转译推测) | +| 2 | `Equip_code` | varchar(50) | YES | | | 设备编码(英文转译推测) | +| 3 | `Serial_Num` | int | YES | | | Serial Num(英文转译推测) | +| 4 | `Weight_ID` | int | YES | | | 重量ID(英文转译推测) | +| 5 | `Material_Code` | varchar(50) | YES | | | 物料编码(英文转译推测) | +| 6 | `Set_Weight` | numeric(9,4) | YES | | | 设定重量(英文转译推测) | +| 7 | `Set_Error` | numeric(9,4) | YES | | | Set错误(英文转译推测) | +| 8 | `Real_Weight` | numeric(9,4) | YES | | | 实际重量(英文转译推测) | +| 9 | `Real_Error` | numeric(9,4) | YES | | | Real错误(英文转译推测) | +| 10 | `Warning_Sign` | int | YES | | | Warning Sign(英文转译推测) | +| 11 | `Batch_Number` | varchar(50) | YES | | | 批次号(英文转译推测) | +| 12 | `Recipe_code` | varchar(50) | YES | | | 配方编码(英文转译推测) | +| 13 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 14 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 15 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | +| 16 | `Start_Date` | varchar(20) | YES | | | 开始日期(英文转译推测) | +| 17 | `End_Date` | varchar(20) | YES | | | 结束日期(英文转译推测) | + +--- + +## 2.12 表:IF_MESShift2CWSS + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`IF_MESShift2CWSS` +- **表含义**:接口:MES班次 -> CWSS +- **字段数量**:6 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Equip_code` | varchar(20) | YES | | | 设备编码(英文转译推测) | +| 2 | `ShiftDate` | varchar(20) | YES | | | 班次日期(英文转译推测) | +| 3 | `ShiftID` | int | YES | | | 班次ID(英文转译推测) | +| 4 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 5 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 6 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 2.13 表:IF_PmtBin2CWSS + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`IF_PmtBin2CWSS` +- **表含义**:接口:炭黑/粉料仓位 -> CWSS +- **字段数量**:8 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Equip_Code` | varchar(50) | YES | | | 设备编码(英文转译推测) | +| 2 | `Bin_Serial` | int | YES | | | 仓位Serial(英文转译推测) | +| 3 | `Material_ID` | varchar(50) | YES | | | 物料ID(英文转译推测) | +| 4 | `ID` | varchar(50) | YES | | | ID(英文转译推测) | +| 5 | `Bin_UseFlag` | bit | YES | | | 仓位使用标志(英文转译推测) | +| 6 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 7 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 8 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 2.14 表:IF_PmtBin2MES + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`IF_PmtBin2MES` +- **表含义**:接口:炭黑/粉料仓位 -> MES +- **字段数量**:7 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Equip_Code` | varchar(50) | NO | 是 | | 设备编码(英文转译推测) | +| 2 | `Bin_Serial` | int | NO | 是 | | 仓位Serial(英文转译推测) | +| 3 | `Material_ID` | varchar(50) | YES | | | 物料ID(英文转译推测) | +| 4 | `Bin_UseFlag` | varchar(20) | YES | | | 仓位使用标志(英文转译推测) | +| 5 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 6 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 7 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 2.15 表:IF_PmtMaterial2CWSS + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`IF_PmtMaterial2CWSS` +- **表含义**:接口:炭黑/粉料物料 -> CWSS +- **字段数量**:7 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `ID` | varchar(50) | YES | | | ID(英文转译推测) | +| 2 | `Equip_Code` | varchar(50) | YES | | | 设备编码(英文转译推测) | +| 3 | `Material_code` | varchar(50) | YES | | | 物料编码(英文转译推测) | +| 4 | `Material_name` | varchar(50) | YES | | | 物料名称(英文转译推测) | +| 5 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 6 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 7 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 2.16 表:IF_PmtRecipe2CWSS + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`IF_PmtRecipe2CWSS` +- **表含义**:接口:炭黑/粉料配方 -> CWSS +- **字段数量**:17 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Id` | varchar(50) | YES | | | ID(英文转译推测) | +| 2 | `Equip_Code` | varchar(50) | YES | | | 设备编码(英文转译推测) | +| 3 | `Recipe_Code` | varchar(50) | YES | | | 配方编码(英文转译推测) | +| 4 | `Version` | varchar(50) | YES | | | 版本(英文转译推测) | +| 5 | `Recipe_Name` | varchar(50) | YES | | | 配方名称(英文转译推测) | +| 6 | `Mixer_line` | varchar(50) | YES | | | Mixer行(英文转译推测) | +| 7 | `Recipe_Verify` | int | YES | | | 配方版本ify(英文转译推测) | +| 8 | `Total_Weight` | numeric(12,4) | YES | | | 总重量(英文转译推测) | +| 9 | `Total_Error` | numeric(12,4) | YES | | | 合计错误(英文转译推测) | +| 10 | `End_datetime` | varchar(19) | YES | | | 结束日期时间(英文转译推测) | +| 11 | `Remark` | varchar(200) | YES | | | 备注(英文转译推测) | +| 12 | `GroupBags` | int | YES | | | 组Bags(英文转译推测) | +| 13 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 14 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 15 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | +| 16 | `Validity` | int | YES | | | Validity(英文转译推测) | +| 17 | `BagWeight` | int | YES | | | Bag重量(英文转译推测) | + +--- + +## 2.17 表:IF_Pmtweigh2CWSS + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`IF_Pmtweigh2CWSS` +- **表含义**:接口:炭黑/粉料称重 -> CWSS +- **字段数量**:10 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Id` | varchar(50) | YES | | | ID(英文转译推测) | +| 2 | `Equip_Code` | varchar(50) | YES | | | 设备编码(英文转译推测) | +| 3 | `Recipe_ID` | varchar(50) | YES | | | 配方ID(英文转译推测) | +| 4 | `Weight_Id` | int | YES | | | 重量ID(英文转译推测) | +| 5 | `Material_ID` | varchar(50) | YES | | | 物料ID(英文转译推测) | +| 6 | `Set_Weight` | numeric(9,3) | YES | | | 设定重量(英文转译推测) | +| 7 | `Set_Error` | numeric(9,3) | YES | | | Set错误(英文转译推测) | +| 8 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 9 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 10 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 2.18 表:IF_RtPlan2CWSS + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`IF_RtPlan2CWSS` +- **表含义**:接口:生胶计划 -> CWSS +- **字段数量**:18 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Plan_Id` | varchar(50) | YES | | | 计划ID(英文转译推测) | +| 2 | `Equip_Code` | varchar(50) | YES | | | 设备编码(英文转译推测) | +| 3 | `Plan_Serial` | int | YES | | | 计划Serial(英文转译推测) | +| 4 | `Recipe_ID` | varchar(50) | YES | | | 配方ID(英文转译推测) | +| 5 | `Recipe_Code` | varchar(50) | YES | | | 配方编码(英文转译推测) | +| 6 | `Recipe_Name` | varchar(50) | YES | | | 配方名称(英文转译推测) | +| 7 | `Version` | varchar(50) | YES | | | 版本(英文转译推测) | +| 8 | `Mixer_Line` | varchar(50) | YES | | | Mixer行(英文转译推测) | +| 9 | `Shift_Id` | varchar(10) | YES | | | 班次ID(英文转译推测) | +| 10 | `Plan_Num` | int | YES | | | 计划Num(英文转译推测) | +| 11 | `Start_Date` | varchar(50) | YES | | | 开始日期(英文转译推测) | +| 12 | `End_Date` | varchar(50) | YES | | | 结束日期(英文转译推测) | +| 13 | `Plan_Batch` | varchar(50) | YES | | | 计划批次(英文转译推测) | +| 14 | `Plan_State` | int | YES | | | 计划状态(英文转译推测) | +| 15 | `Plan_Date` | varchar(50) | YES | | | 计划日期(英文转译推测) | +| 16 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 17 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 18 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 2.19 表:IF_RtPlan2Mes + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`IF_RtPlan2Mes` +- **表含义**:接口:生胶计划 -> MES +- **字段数量**:21 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Plan_Id` | varchar(50) | NO | 是 | | 计划ID(英文转译推测) | +| 2 | `Equip_Code` | varchar(50) | YES | | | 设备编码(英文转译推测) | +| 3 | `Plan_Serial` | int | YES | | | 计划Serial(英文转译推测) | +| 4 | `Recipe_ID` | varchar(50) | YES | | | 配方ID(英文转译推测) | +| 5 | `Recipe_Code` | varchar(50) | YES | | | 配方编码(英文转译推测) | +| 6 | `Recipe_Name` | varchar(50) | YES | | | 配方名称(英文转译推测) | +| 7 | `Version` | varchar(50) | YES | | | 版本(英文转译推测) | +| 8 | `Mixer_Line` | varchar(50) | YES | | | Mixer行(英文转译推测) | +| 9 | `Shift_Id` | varchar(10) | YES | | | 班次ID(英文转译推测) | +| 10 | `Plan_Num` | int | YES | | | 计划Num(英文转译推测) | +| 11 | `real_num` | int | YES | | | real num(英文转译推测) | +| 12 | `Start_Date` | varchar(50) | YES | | | 开始日期(英文转译推测) | +| 13 | `End_Date` | varchar(50) | YES | | | 结束日期(英文转译推测) | +| 14 | `Plan_Batch` | varchar(50) | YES | | | 计划批次(英文转译推测) | +| 15 | `Plan_State` | int | YES | | | 计划状态(英文转译推测) | +| 16 | `Plan_Date` | varchar(50) | YES | | | 计划日期(英文转译推测) | +| 17 | `Write_Time` | varchar(20) | YES | | | 写入时间(英文转译推测) | +| 18 | `Read_Time` | varchar(20) | YES | | | 读取时间(英文转译推测) | +| 19 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | +| 20 | `Weight_Man` | varchar(50) | YES | | | 重量Man(英文转译推测) | +| 21 | `Stock_Man` | nvarchar(50) | YES | | | Stock Man(英文转译推测) | + +--- + +## 2.20 表:MCSToMES_Act_Mill + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MCSToMES_Act_Mill` +- **表含义**:MCS->MES:开炼机动作 +- **字段数量**:9 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(50) | NO | | | GUID(英文转译推测) | +| 2 | `act_addr` | int | YES | | | 动作addr(英文转译推测) | +| 3 | `act_name` | varchar(50) | YES | | | 动作名称(英文转译推测) | +| 4 | `act_name_en` | varchar(50) | YES | | | 动作名称en(英文转译推测) | +| 5 | `act_memo` | varchar(20) | YES | | | 动作备注(英文转译推测) | +| 6 | `act_Repaddr` | int | YES | | | 动作Repaddr(英文转译推测) | +| 7 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 8 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 9 | `RW_Flag` | int | YES | | | RW标志(英文转译推测) | + +--- + +## 2.21 表:MCSToMES_BinToMater + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MCSToMES_BinToMater` +- **表含义**:MCS->MES:仓位与物料对应关系 +- **字段数量**:14 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | | GUID(英文转译推测) | +| 2 | `EquipID` | int | NO | 是 | | 设备ID(英文转译推测) | +| 3 | `EquipName` | varchar(20) | YES | | | 设备名称(英文转译推测) | +| 4 | `EquipType` | varchar(10) | NO | 是 | | 设备类型(英文转译推测) | +| 5 | `ScaleNum` | int | NO | 是 | | 秤Num(英文转译推测) | +| 6 | `ScaleClass` | varchar(20) | YES | | | 秤Class(英文转译推测) | +| 7 | `ScaleName` | varchar(20) | YES | | | 秤名称(英文转译推测) | +| 8 | `BinNo` | int | NO | 是 | | 仓号(英文转译推测) | +| 9 | `MatCode` | varchar(30) | YES | | | 物料编码(英文转译推测) | +| 10 | `MatName` | varchar(50) | YES | | | 物料名称(英文转译推测) | +| 11 | `UseYN` | varchar(1) | YES | | | Use YN(英文转译推测) | +| 12 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 13 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 14 | `MES_Flag` | int | YES | | | MES标志(英文转译推测) | + +--- + +## 2.22 表:MCSToMES_CurrEquipState + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MCSToMES_CurrEquipState` +- **表含义**:MCS->MES:当前设备状态 +- **字段数量**:13 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | NO | 是 | | GUID(英文转译推测) | +| 2 | `EquipID` | int | YES | | | 设备ID(英文转译推测) | +| 3 | `EquipName` | varchar(10) | YES | | | 设备名称(英文转译推测) | +| 4 | `EquipType` | varchar(10) | YES | | | 设备类型(英文转译推测) | +| 5 | `SaveTime` | varchar(20) | YES | | | Save时间(英文转译推测) | +| 6 | `RpName` | varchar(40) | YES | | | Rp名称(英文转译推测) | +| 7 | `SetNum` | int | YES | | | Set Num(英文转译推测) | +| 8 | `Serial` | int | YES | | | Serial(英文转译推测) | +| 9 | `MixSpeed` | decimal(18,3) | YES | | | 密炼速度(英文转译推测) | +| 10 | `MixPower` | decimal(10,3) | YES | | | 密炼功率(英文转译推测) | +| 11 | `MixEngery` | decimal(18,3) | YES | | | 密炼Engery(英文转译推测) | +| 12 | `MixTime` | int | YES | | | 密炼时间(英文转译推测) | +| 13 | `MES_Flag` | int | YES | | (0) | MES标志(英文转译推测) | + +--- + +## 2.23 表:MCSToMES_CurrVarData_Convey + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MCSToMES_CurrVarData_Convey` +- **表含义**:MCS->MES:输送线过程变量 +- **字段数量**:7 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(50) | NO | 是 | | GUID(英文转译推测) | +| 2 | `VarName` | varchar(50) | YES | | | Var名称(英文转译推测) | +| 3 | `DataName` | varchar(50) | YES | | | 数据名称(英文转译推测) | +| 4 | `DataValue` | decimal(18,3) | YES | | | 数据数值(英文转译推测) | +| 5 | `CreateTime` | varchar(19) | YES | | | 创建时间(英文转译推测) | +| 6 | `UpdateTime` | varchar(19) | YES | | | 更新时间(英文转译推测) | +| 7 | `MESReadTime` | varchar(19) | YES | | | MES读取时间(英文转译推测) | + +--- + +## 2.24 表:MCSToMES_MixAct + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MCSToMES_MixAct` +- **表含义**:MCS->MES:密炼动作记录 +- **字段数量**:8 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `EquipID` | int | NO | 是 | | 设备ID(英文转译推测) | +| 2 | `EquipName` | varchar(10) | YES | | | 设备名称(英文转译推测) | +| 3 | `EquipType` | varchar(10) | NO | 是 | | 设备类型(英文转译推测) | +| 4 | `MixActAddress` | int | NO | 是 | | 动作地址 | +| 5 | `MixActName` | varchar(30) | YES | | | 动作名称 | +| 6 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 7 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 8 | `RW_Flag` | int | YES | | (0) | RW标志(英文转译推测) | + +--- + +## 2.25 表:MCSToMES_MixAlarm + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MCSToMES_MixAlarm` +- **表含义**:MCS->MES:密炼报警 +- **字段数量**:12 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | NO | 是 | | GUID(英文转译推测) | +| 2 | `EquipID` | int | NO | | | 机台编号 | +| 3 | `EquipName` | varchar(10) | NO | | | 机台名称 | +| 4 | `EquipType` | varchar(10) | NO | | | 机台类型 | +| 5 | `SaveTime` | varchar(19) | YES | | | Save时间(英文转译推测) | +| 6 | `EndTime` | varchar(19) | YES | | | 结束时间(英文转译推测) | +| 7 | `Barcode` | varchar(30) | NO | | | Barcode | +| 8 | `AlmCode` | varchar(6) | YES | | | 报警代码 | +| 9 | `AlarmInf` | varchar(50) | YES | | | 报警内容 | +| 10 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 11 | `ReadTime` | datetime | YES | | (0) | 读取时间(英文转译推测) | +| 12 | `MES_Flag` | int | YES | | (0) | MES标志(英文转译推测) | + +--- + +## 2.26 表:MCSToMES_MixBatch + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MCSToMES_MixBatch` +- **表含义**:MCS->MES:密炼批次 +- **字段数量**:41 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | | GUID(英文转译推测) | +| 2 | `EquipID` | int | NO | 是 | | 机台编号 | +| 3 | `EquipName` | varchar(10) | YES | | | 机台名称 | +| 4 | `EquipType` | varchar(10) | YES | | | 机台类型 | +| 5 | `Barcode` | varchar(30) | NO | 是 | | 计划编号+车次 | +| 6 | `RecipCode` | varchar(30) | YES | | | 配方代码 | +| 7 | `RecipName` | varchar(30) | YES | | | 配方名称 | +| 8 | `MaterCode` | varchar(40) | YES | | | 物料编码(英文转译推测) | +| 9 | `MaterName` | varchar(80) | YES | | | 物料名称(英文转译推测) | +| 10 | `SaveDate` | varchar(10) | YES | | | 保存日期 | +| 11 | `SaveDateTime` | varchar(20) | YES | | | 保存时间 | +| 12 | `ShiftId` | varchar(1) | YES | | | 班次;1:Night 2: Morning 3:Noon | +| 13 | `ShiftClass` | varchar(1) | YES | | | 班组;1:A 2:B 3:C 4:D | +| 14 | `PlanID` | varchar(20) | YES | | | 计划编号 | +| 15 | `SetNum` | int | YES | | | 设定车次 | +| 16 | `StartTim` | varchar(20) | YES | | | 开始时间 | +| 17 | `EndTim` | varchar(20) | YES | | | 结束时间 | +| 18 | `BatchNum` | int | YES | | | 当前车次 | +| 19 | `MixMode` | int | YES | | | 密炼模式1:遥控自动;2:遥控手动;3:本控 | +| 20 | `BatchCycleTim` | int | YES | | | 密炼循环时间(预留) | +| 21 | `CurTim` | int | YES | | | 密炼时间 | +| 22 | `MixTim` | int | YES | | | 预留 | +| 23 | `UsefulMixTim` | int | YES | | | 有效时间 | +| 24 | `IntervalTime` | int | YES | | | 间隔时间 | +| 25 | `DiscTemp` | decimal(18,3) | YES | | | 排胶温度 | +| 26 | `DiscEnergy` | decimal(18,3) | YES | | | 排胶能量 | +| 27 | `DiscTime` | int | YES | | | 排胶时间 | +| 28 | `DiscPower` | int | YES | | | 排胶功率 | +| 29 | `EmgyDiscFlag` | int | YES | | | 排胶方式;0:正常;1:超时;2:超温;3:超能量 | +| 30 | `TCU1Temp` | decimal(18,3) | YES | | | 三区温度1(预留) | +| 31 | `TCU2Temp` | decimal(18,3) | YES | | | 三区温度2(预留) | +| 32 | `TCU3Temp` | decimal(18,3) | YES | | | 三区温度3(预留) | +| 33 | `TCU4Temp` | decimal(18,3) | YES | | | 三区温度4(预留) | +| 34 | `TCUTemp` | decimal(18,3) | YES | | | 三区温度(预留) | +| 35 | `BatchWei` | decimal(18,3) | YES | | | 批次Wei(英文转译推测) | +| 36 | `oper` | varchar(20) | YES | | | oper(英文转译推测) | +| 37 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 38 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 39 | `MES_Flag` | int | YES | | (0) | MES标志(英文转译推测) | +| 40 | `WEIGHT` | decimal(18,3) | YES | | | 重量(英文转译推测) | +| 41 | `MoCode` | varchar(40) | YES | | | Mo编码(英文转译推测) | + +--- + +## 2.27 表:MCSToMES_MixCon + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MCSToMES_MixCon` +- **表含义**:MCS->MES:密炼消耗/配料明细 +- **字段数量**:8 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `EquipID` | int | NO | 是 | | 设备ID(英文转译推测) | +| 2 | `EquipName` | varchar(10) | YES | | | 设备名称(英文转译推测) | +| 3 | `EquipType` | varchar(10) | NO | 是 | | 设备类型(英文转译推测) | +| 4 | `MixConAddress` | int | NO | 是 | | 密炼ConAddress(英文转译推测) | +| 5 | `MixConName` | varchar(30) | YES | | | 密炼Con名称(英文转译推测) | +| 6 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 7 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 8 | `RW_Flag` | int | YES | | (0) | RW标志(英文转译推测) | + +--- + +## 2.28 表:MCSToMES_MixCurve + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MCSToMES_MixCurve` +- **表含义**:MCS->MES:密炼曲线数据 +- **字段数量**:9 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | | GUID(英文转译推测) | +| 2 | `EquipID` | int | NO | 是 | | 设备ID(英文转译推测) | +| 3 | `EquipName` | varchar(10) | YES | | | 设备名称(英文转译推测) | +| 4 | `EquipType` | varchar(10) | NO | 是 | | 设备类型(英文转译推测) | +| 5 | `Barcode` | varchar(30) | NO | 是 | | 条码(英文转译推测) | +| 6 | `CurveData` | ntext(1073741823) | YES | | | Curve数据(英文转译推测) | +| 7 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 8 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 9 | `MES_Flag` | int | YES | | (0) | MES标志(英文转译推测) | + +--- + +## 2.29 表:MCSToMES_mixdata_Mill + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MCSToMES_mixdata_Mill` +- **表含义**:MCS->MES:开炼机混炼数据 +- **字段数量**:14 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `Barcode` | varchar(40) | YES | | | 条码(英文转译推测) | +| 2 | `Serial_id` | int | YES | | | Serial id(英文转译推测) | +| 3 | `Equip_code` | varchar(20) | YES | | | 设备编码(英文转译推测) | +| 4 | `ID` | int | YES | | | ID(英文转译推测) | +| 5 | `StepActAddress` | int | YES | | | 步骤动作Address(英文转译推测) | +| 6 | `StepMixTime` | int | YES | | | 步骤密炼时间(英文转译推测) | +| 7 | `StepMixCoolSpeed` | numeric(18,3) | YES | | | 步骤密炼Cool速度(英文转译推测) | +| 8 | `StepOpenMixSpeed` | numeric(18,3) | YES | | | 步骤Open密炼速度(英文转译推测) | +| 9 | `StepRollor` | numeric(18,3) | YES | | | 步骤Rollor(英文转译推测) | +| 10 | `StepWaterTemp` | numeric(18,3) | YES | | | 步骤Water温度(英文转译推测) | +| 11 | `StepRubTemp` | numeric(18,3) | YES | | | 步骤Rub温度(英文转译推测) | +| 12 | `StepPower` | numeric(18,3) | YES | | | 步骤功率(英文转译推测) | +| 13 | `WaterIn` | numeric(18,3) | YES | | | Water In(英文转译推测) | +| 14 | `Remarks` | varchar(50) | YES | | | 备注s(英文转译推测) | + +--- + +## 2.30 表:MCSToMES_MixExePlan + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MCSToMES_MixExePlan` +- **表含义**:MCS->MES:密炼执行计划回传 +- **字段数量**:25 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | | GUID(英文转译推测) | +| 2 | `PlanID` | varchar(20) | NO | 是 | | 计划编号 | +| 3 | `PlanSource` | varchar(1) | NO | 是 | | 计划来源(英文转译推测) | +| 4 | `EquipID` | int | NO | 是 | | 机台编号 | +| 5 | `EquipName` | varchar(10) | YES | | | 机台名称 | +| 6 | `EquipType` | varchar(10) | YES | | | 机台类型CWS:小料;MCS:上辅机 | +| 7 | `PlanDate` | varchar(10) | YES | | | 计划日期 | +| 8 | `ShiftID` | varchar(1) | YES | | | 班次1:Night 2: Morning 3 | +| 9 | `ShiftClass` | varchar(1) | YES | | | 班组1:A 2:B 3:C 4:D | +| 10 | `WorkOrder` | int | YES | | | 计划顺序 | +| 11 | `RecipCode` | varchar(40) | YES | | | 配方代码 | +| 12 | `RecipName` | varchar(40) | YES | | | 配方名称 | +| 13 | `SetNum` | int | YES | | | 设定车次 | +| 14 | `DoneNum` | int | YES | | | 完成车次 | +| 15 | `PlanState` | int | YES | | | 计划状态2:运行;4终止;5:完成 | +| 16 | `PlanStateChange` | int | YES | | | 计划状态有变化 | +| 17 | `StartTime` | datetime | YES | | | 开始时间(英文转译推测) | +| 18 | `EndTime` | datetime | YES | | | 结束时间(英文转译推测) | +| 19 | `WriteTime` | datetime | YES | | | 写入时间 | +| 20 | `ReadTime` | datetime | YES | | | 读取时间 | +| 21 | `MES_Flag` | int | YES | | (0) | 交互标识 | +| 22 | `Remark` | varchar(50) | YES | | | 备注 | +| 23 | `MaterCode` | varchar(50) | YES | | | 物料编码(英文转译推测) | +| 24 | `MaterName` | varchar(50) | YES | | | 物料名称(英文转译推测) | +| 25 | `UploadRunFlag` | int | YES | | ((0)) | UploadRun标志(英文转译推测) | + +--- + +## 2.31 表:MCSToMES_MixStep + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MCSToMES_MixStep` +- **表含义**:MCS->MES:密炼步骤执行记录 +- **字段数量**:35 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | | GUID(英文转译推测) | +| 2 | `EquipID` | int | NO | 是 | | 机台编号 | +| 3 | `EquipName` | varchar(10) | YES | | | 设备名称(英文转译推测) | +| 4 | `EquipType` | varchar(10) | NO | 是 | | 机台类型 | +| 5 | `Barcode` | varchar(30) | NO | 是 | | 条码(英文转译推测) | +| 6 | `SaveTime` | varchar(20) | YES | | | 保存时间 | +| 7 | `MixID` | int | NO | 是 | | 密炼步序 | +| 8 | `Cond` | int | YES | | | 条件地址 | +| 9 | `CondName` | varchar(40) | YES | | | 条件名称 | +| 10 | `Tim` | int | YES | | | 设定时间(预留) | +| 11 | `TimProtect` | int | YES | | | 时间保护(预留) | +| 12 | `TimAct` | int | YES | | | 实际时间 | +| 13 | `Tempe` | decimal(18,3) | YES | | | 设定温度(预留) | +| 14 | `TempProtect` | decimal(18,3) | YES | | | 温度保护(预留) | +| 15 | `TempAct` | decimal(18,3) | YES | | | 温度实际 | +| 16 | `Power` | decimal(18,3) | YES | | | 设定功率(预留) | +| 17 | `PowerProtect` | decimal(18,3) | YES | | | 功率保护(预留) | +| 18 | `PowerAct` | decimal(18,3) | YES | | | 实际功率 | +| 19 | `Energy` | decimal(18,3) | YES | | | 设定能量(预留) | +| 20 | `EnergyProtect` | decimal(18,3) | YES | | | 能量保护(预留) | +| 21 | `EnergyAct` | decimal(18,3) | YES | | | 实际能量 | +| 22 | `Act` | int | YES | | | 动作地址 | +| 23 | `ActName` | varchar(40) | YES | | | 动作名称 | +| 24 | `RamPress` | decimal(18,3) | YES | | | 设定压力(预留) | +| 25 | `RamPressAct` | decimal(18,3) | YES | | | 实际压力 | +| 26 | `RotorSpd` | decimal(18,3) | YES | | | 转速设定(预留) | +| 27 | `RotorSpdAct` | decimal(18,3) | YES | | | 实际转速 | +| 28 | `RamPos` | decimal(18,3) | YES | | | 栓位置设定(预留) | +| 29 | `RamPosAct` | decimal(18,3) | YES | | | 实际栓位置 | +| 30 | `RollGap` | decimal(18,3) | YES | | | 辊距设定(预留) | +| 31 | `RollGapAct` | decimal(18,3) | YES | | | 实际辊距(预留) | +| 32 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 33 | `CtrlMode` | int | YES | | | Ctrl模式(英文转译推测) | +| 34 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 35 | `MES_Flag` | int | YES | | (0) | MES标志(英文转译推测) | + +--- + +## 2.32 表:MCSToMES_MixWeight + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MCSToMES_MixWeight` +- **表含义**:MCS->MES:密炼称重记录 +- **字段数量**:26 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | NO | 是 | | GUID(英文转译推测) | +| 2 | `EquipID` | int | NO | | | 机台编号 | +| 3 | `EquipName` | varchar(10) | YES | | | 机台名称 | +| 4 | `EquipType` | varchar(50) | NO | | | 机台类型 | +| 5 | `Barcode` | varchar(30) | NO | | | 条码(英文转译推测) | +| 6 | `SaveTime` | varchar(20) | YES | | | 存储时间 | +| 7 | `MatCode` | varchar(30) | YES | | | 物料代码 | +| 8 | `MatName` | varchar(30) | YES | | | 物料名称 | +| 9 | `ScaleNum` | int | NO | | | 秤编号101:炭黑秤1;102:炭黑秤2;103:炭黑秤3;201:油料秤1;202:油料秤2;203:油料秤3 +301:胶料秤1;302:胶料秤2;401:小料秤1;402:小料秤2 | +| 10 | `BatchNum` | int | YES | | | 车次 | +| 11 | `Act` | int | YES | | | 动作(英文转译推测) | +| 12 | `Idx` | int | NO | | | 序号 | +| 13 | `ShiftNum` | int | YES | | | 第几批称量 | +| 14 | `SetWei` | decimal(18,3) | YES | | | 设定重量 | +| 15 | `SetTol` | decimal(18,3) | YES | | | 设定误差 | +| 16 | `ActWei` | decimal(18,3) | YES | | | 实际重量 | +| 17 | `ActTol` | decimal(18,3) | YES | | | 实际误差 | +| 18 | `StepTim` | int | YES | | | 步时间 | +| 19 | `StepMode` | int | YES | | | 步骤模式(英文转译推测) | +| 20 | `RecipeName` | varchar(30) | YES | | | 配方名称 | +| 21 | `PlanID` | varchar(20) | YES | | | 计划编号 | +| 22 | `SetBatchNum` | int | YES | | | 计划设定 | +| 23 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 24 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 25 | `MES_Flag` | int | YES | | ((0)) | MES标志(英文转译推测) | +| 26 | `MOCode` | varchar(40) | YES | | | MOCode(英文转译推测) | + +--- + +## 2.33 表:MCSToMES_MixWeiRelate + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MCSToMES_MixWeiRelate` +- **表含义**:MCS->MES:称重关联关系 +- **字段数量**:11 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(50) | NO | 是 | | GUID(英文转译推测) | +| 2 | `Barcode` | varchar(40) | YES | | | 条码(英文转译推测) | +| 3 | `EquipID` | int | YES | | | 设备ID(英文转译推测) | +| 4 | `SaveTime` | varchar(20) | YES | | | Save时间(英文转译推测) | +| 5 | `ScaleType` | int | YES | | | 秤类型(英文转译推测) | +| 6 | `ScaleNum` | int | YES | | | 秤Num(英文转译推测) | +| 7 | `BatchNum` | int | YES | | | 批次Num(英文转译推测) | +| 8 | `ShiftNum` | int | YES | | | 班次Num(英文转译推测) | +| 9 | `FeedTim` | int | YES | | | 供料Tim(英文转译推测) | +| 10 | `PlanID` | varchar(30) | YES | | | 计划ID(英文转译推测) | +| 11 | `Mes_Flag` | char(1) | YES | | | MES标志(英文转译推测) | + +--- + +## 2.34 表:MCSToMES_Ppt_Log + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MCSToMES_Ppt_Log` +- **表含义**:MCS->MES:开炼/挤出日志 +- **字段数量**:9 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | | GUID(英文转译推测) | +| 2 | `EquipID` | int | YES | | | 设备ID(英文转译推测) | +| 3 | `EquipType` | varchar(10) | YES | | | 设备类型(英文转译推测) | +| 4 | `WorkType` | varchar(20) | YES | | | Work类型(英文转译推测) | +| 5 | `OperDest` | varchar(50) | YES | | | Oper Dest(英文转译推测) | +| 6 | `OccurrenTime` | varchar(20) | YES | | | Occurren时间(英文转译推测) | +| 7 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 8 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 9 | `MES_Flag` | int | YES | | | MES标志(英文转译推测) | + +--- + +## 2.35 表:MCSToMES_Ppt_Shutdown + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MCSToMES_Ppt_Shutdown` +- **表含义**:MCS->MES:停机记录 +- **字段数量**:12 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | | GUID(英文转译推测) | +| 2 | `EquipID` | int | YES | | | 设备ID(英文转译推测) | +| 3 | `EquipType` | varchar(10) | YES | | | 设备类型(英文转译推测) | +| 4 | `ShutdownType` | varchar(50) | YES | | | 停机类型(英文转译推测) | +| 5 | `StartTime` | varchar(10) | YES | | | 开始时间(英文转译推测) | +| 6 | `EndTime` | varchar(10) | YES | | | 结束时间(英文转译推测) | +| 7 | `StopTimeSecond` | int | YES | | | Stop时间Second(英文转译推测) | +| 8 | `Memo` | varchar(50) | YES | | | 备注(英文转译推测) | +| 9 | `Auto` | int | YES | | | 自动(英文转译推测) | +| 10 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 11 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 12 | `MES_Flag` | int | YES | | | MES标志(英文转译推测) | + +--- + +## 2.36 表:MCSToMES_PptShiftConfig + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MCSToMES_PptShiftConfig` +- **表含义**:MCS->MES:开炼班次配置 +- **字段数量**:20 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(50) | YES | | | GUID(英文转译推测) | +| 2 | `EquipID` | int | NO | 是 | | 设备ID(英文转译推测) | +| 3 | `EquipName` | varchar(10) | YES | | | 设备名称(英文转译推测) | +| 4 | `EquipType` | varchar(10) | NO | 是 | | 设备类型(英文转译推测) | +| 5 | `Barcode` | varchar(30) | NO | 是 | | 条码(英文转译推测) | +| 6 | `PlanDate` | datetime | YES | | | 计划日期(英文转译推测) | +| 7 | `PlanID` | varchar(20) | NO | | | 计划ID(英文转译推测) | +| 8 | `ShiftID` | int | YES | | | 班次ID(英文转译推测) | +| 9 | `ClassID` | int | YES | | | Class ID(英文转译推测) | +| 10 | `RecipCode` | varchar(30) | YES | | | Recip编码(英文转译推测) | +| 11 | `RecipName` | varchar(30) | YES | | | Recip名称(英文转译推测) | +| 12 | `BarcodeStart` | int | NO | | | 条码开始(英文转译推测) | +| 13 | `BarcodeEnd` | int | YES | | | 条码结束(英文转译推测) | +| 14 | `TotalWeight` | decimal(18,3) | YES | | | 总重量(英文转译推测) | +| 15 | `RealWeight` | decimal(18,3) | YES | | | 实际重量(英文转译推测) | +| 16 | `Set_ShelfNum` | int | YES | | | Set Shelf Num(英文转译推测) | +| 17 | `ShelfNum` | int | YES | | | Shelf Num(英文转译推测) | +| 18 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 19 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 20 | `MES_Flag` | int | YES | | | MES标志(英文转译推测) | + +--- + +## 2.37 表:MCSToMES_RepBatchoff + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MCSToMES_RepBatchoff` +- **表含义**:MCS->MES:排出/落盘回报 +- **字段数量**:30 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(50) | NO | 是 | | GUID(英文转译推测) | +| 2 | `EquipID` | int | YES | | | 设备ID(英文转译推测) | +| 3 | `RpCode` | varchar(50) | YES | | | Rp编码(英文转译推测) | +| 4 | `PlanID` | varchar(40) | YES | | | 计划ID(英文转译推测) | +| 5 | `RFIDCode` | varchar(50) | YES | | | RFIDCode(英文转译推测) | +| 6 | `BOEquipName` | varchar(40) | YES | | | BOEquip名称(英文转译推测) | +| 7 | `RubberType` | int | YES | | | Rubber类型(英文转译推测) | +| 8 | `RubWeight` | decimal(18,3) | YES | | | Rub重量(英文转译推测) | +| 9 | `MinSaveTime` | int | YES | | | 最小Save时间(英文转译推测) | +| 10 | `MaxSaveTime` | int | YES | | | 最大Save时间(英文转译推测) | +| 11 | `ReRubberType` | int | YES | | | ReRubber类型(英文转译推测) | +| 12 | `ReRubberPersion` | int | YES | | | Re Rubber Persion(英文转译推测) | +| 13 | `RubberTrayState` | int | YES | | | RubberTray状态(英文转译推测) | +| 14 | `ReStartTime` | varchar(20) | YES | | | Re开始时间(英文转译推测) | +| 15 | `ReEndTime` | varchar(20) | YES | | | Re结束时间(英文转译推测) | +| 16 | `TrayCount` | int | YES | | | Tray数量(英文转译推测) | +| 17 | `StartSerial` | int | YES | | | 开始Serial(英文转译推测) | +| 18 | `EndSerial` | int | YES | | | 结束Serial(英文转译推测) | +| 19 | `ReRubberWeight` | decimal(18,3) | YES | | | ReRubber重量(英文转译推测) | +| 20 | `ReRubberLength` | decimal(18,3) | YES | | | Re Rubber Length(英文转译推测) | +| 21 | `ReRubberError` | decimal(18,3) | YES | | | ReRubber错误(英文转译推测) | +| 22 | `MDErrorCount` | int | YES | | | MDError数量(英文转译推测) | +| 23 | `SampleCount` | int | YES | | | Sample数量(英文转译推测) | +| 24 | `Re1Length` | decimal(18,3) | YES | | | Re 1 Length(英文转译推测) | +| 25 | `Re2Length` | decimal(18,3) | YES | | | Re 2 Length(英文转译推测) | +| 26 | `Re3Length` | decimal(18,3) | YES | | | Re 3 Length(英文转译推测) | +| 27 | `Re4Length` | decimal(18,3) | YES | | | Re 4 Length(英文转译推测) | +| 28 | `Re5Length` | decimal(18,3) | YES | | | Re 5 Length(英文转译推测) | +| 29 | `SaveTime` | varchar(20) | YES | | | Save时间(英文转译推测) | +| 30 | `MES_Flag` | int | YES | | ((0)) | MES标志(英文转译推测) | + +--- + +## 2.38 表:MCSToMES_Scale_Calibration + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MCSToMES_Scale_Calibration` +- **表含义**:MCS->MES:秤标定记录 +- **字段数量**:11 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | | GUID(英文转译推测) | +| 2 | `ScaleNum` | int | YES | | | 秤Num(英文转译推测) | +| 3 | `ScaleClass` | varchar(20) | YES | | | 秤Class(英文转译推测) | +| 4 | `ScaleName` | varchar(20) | YES | | | 秤名称(英文转译推测) | +| 5 | `SetValue` | numeric(8,3) | YES | | | Set数值(英文转译推测) | +| 6 | `RealValue` | numeric(8,3) | YES | | | Real数值(英文转译推测) | +| 7 | `ErrValue` | numeric(8,3) | YES | | | Err数值(英文转译推测) | +| 8 | `RealTime` | datetime | YES | | | Real时间(英文转译推测) | +| 9 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 10 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 11 | `MES_Flag` | int | YES | | | MES标志(英文转译推测) | + +--- + +## 2.39 表:MESToMCS_BarCodeInfo + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MESToMCS_BarCodeInfo` +- **表含义**:MES->MCS:条码信息下发 +- **字段数量**:12 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(64) | YES | | | GUID(英文转译推测) | +| 2 | `BarCode` | varchar(32) | YES | | | 条码(英文转译推测) | +| 3 | `MatCode` | varchar(32) | YES | | | 物料编码(英文转译推测) | +| 4 | `MatName` | varchar(32) | YES | | | 物料名称(英文转译推测) | +| 5 | `MatCategory` | varchar(32) | YES | | | Mat Category(英文转译推测) | +| 6 | `MatSubcategory` | varchar(32) | YES | | | Mat Subcategory(英文转译推测) | +| 7 | `MatWeight` | numeric(18,2) | YES | | | Mat重量(英文转译推测) | +| 8 | `MatAmount` | numeric(18,2) | YES | | | Mat Amount(英文转译推测) | +| 9 | `IsOutStore` | int | YES | | | Is Out Store(英文转译推测) | +| 10 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 11 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 12 | `MES_Flag` | int | YES | | | MES标志(英文转译推测) | + +--- + +## 2.40 表:MESToMCS_Material + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MESToMCS_Material` +- **表含义**:MES->MCS:物料主数据下发 +- **字段数量**:17 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | | GUID(英文转译推测) | +| 2 | `EquipID` | int | NO | 是 | | 机台号 | +| 3 | `EquipType` | varchar(10) | YES | | | 机台类型,CWS:小料;MCS:上辅机; | +| 4 | `MatCode` | varchar(30) | NO | 是 | | 物料代码 | +| 5 | `MatName` | varchar(30) | YES | | | 物料名称 | +| 6 | `MatBarcode` | varchar(50) | YES | | | 物料条码扫描判断信息 | +| 7 | `MatTypeID` | int | YES | | | Mat类型ID(英文转译推测) | +| 8 | `MatType` | varchar(50) | YES | | | 物料类别 | +| 9 | `MatRubTypeID` | int | YES | | | MatRub类型ID(英文转译推测) | +| 10 | `MatRubType` | varchar(10) | YES | | | 胶料类别,0:胶块;1:胶片;2:胶粒 | +| 11 | `ScaleID` | int | YES | | | 秤ID(英文转译推测) | +| 12 | `ScaleName` | varchar(20) | YES | | | 秤名称(英文转译推测) | +| 13 | `CWSID` | int | YES | | | 小料设备号 | +| 14 | `WriteTime` | datetime | YES | | | 写入时间 | +| 15 | `ReadTime` | datetime | YES | | | 读取时间 | +| 16 | `MES_Flag` | int | YES | | (0) | 交互标识 | +| 17 | `UseBar` | int | YES | | ((0)) | Use Bar(英文转译推测) | + +--- + +## 2.41 表:MESToMCS_MatSendInf + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MESToMCS_MatSendInf` +- **表含义**:MES->MCS:物料发送信息 +- **字段数量**:20 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | | GUID(英文转译推测) | +| 2 | `EquipID` | int | YES | | | 设备ID(英文转译推测) | +| 3 | `EquipType` | varchar(10) | YES | | | 设备类型(英文转译推测) | +| 4 | `MatCode` | varchar(30) | YES | | | 物料编码(英文转译推测) | +| 5 | `MatName` | varchar(30) | YES | | | 物料名称(英文转译推测) | +| 6 | `OrderID` | varchar(30) | YES | | | 订单ID(英文转译推测) | +| 7 | `MatID` | varchar(30) | YES | | | Mat ID(英文转译推测) | +| 8 | `Weight` | numeric(18,3) | YES | | | 重量(英文转译推测) | +| 9 | `MatBarcode` | varchar(30) | YES | | | Mat条码(英文转译推测) | +| 10 | `MatKey` | int | YES | | | Mat键(英文转译推测) | +| 11 | `Result` | int | YES | | | 结果(英文转译推测) | +| 12 | `MatTypeID` | int | YES | | | Mat类型ID(英文转译推测) | +| 13 | `MatType` | int | YES | | | Mat类型(英文转译推测) | +| 14 | `ScaleID` | int | YES | | | 秤ID(英文转译推测) | +| 15 | `ScaleName` | varchar(20) | YES | | | 秤名称(英文转译推测) | +| 16 | `MatRubType` | varchar(1) | YES | | | MatRub类型(英文转译推测) | +| 17 | `CWSID` | int | YES | | | CWSID(英文转译推测) | +| 18 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 19 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 20 | `MES_Flag` | int | YES | | | MES标志(英文转译推测) | + +--- + +## 2.42 表:MESToMCS_MixPlan + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MESToMCS_MixPlan` +- **表含义**:MES->MCS:密炼计划下发 +- **字段数量**:29 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | | GUID(英文转译推测) | +| 2 | `PlanId` | varchar(20) | NO | 是 | | 计划编号 | +| 3 | `PlanSource` | varchar(1) | NO | 是 | | 计划来源(英文转译推测) | +| 4 | `EquipId` | int | NO | 是 | | 机台编号 | +| 5 | `EquipName` | varchar(20) | YES | | | 机台名称 | +| 6 | `EquipType` | varchar(10) | YES | | | 机台类型 | +| 7 | `PlanDate` | varchar(10) | YES | | | 计划日期 | +| 8 | `ShiftId` | varchar(1) | YES | | | 班次1:Night 2: Morning 3:Noon | +| 9 | `ShiftClass` | varchar(1) | YES | | | 班组1:A 2:B 3:C 4:D | +| 10 | `WorkOrder` | int | YES | | | 计划顺序 | +| 11 | `RecipCode` | varchar(30) | YES | | | 配方代码 | +| 12 | `RecipName` | varchar(30) | YES | | | 配方名称 | +| 13 | `MaterCode` | varchar(40) | YES | | | 物料编码(英文转译推测) | +| 14 | `MaterName` | varchar(80) | YES | | | 物料名称(英文转译推测) | +| 15 | `MOCode` | varchar(40) | YES | | | MOCode(英文转译推测) | +| 16 | `RcipVersion` | int | YES | | | 配方版本 | +| 17 | `ProType` | int | YES | | | 配方类型1:正式 2:试验 | +| 18 | `SetNum` | int | YES | | | 计划车次 | +| 19 | `DoneNum` | int | YES | | | 完成车次 | +| 20 | `PlanState` | int | YES | | | 计划状态 | +| 21 | `MESPlanChange` | int | YES | | | MES计划变化 | +| 22 | `MCSUpdateTime` | datetime | YES | | | MCS更新时间(英文转译推测) | +| 23 | `MCSPlanChange` | int | YES | | | MCSPlan Change(英文转译推测) | +| 24 | `MESUPdateTime` | datetime | YES | | | MES更新时间(英文转译推测) | +| 25 | `WriteTime` | datetime | YES | | | 写入时间 | +| 26 | `ReadTime` | datetime | YES | | | 读取时间 | +| 27 | `MES_Flag` | int | YES | | ((0)) | MES标志(英文转译推测) | +| 28 | `Remark` | varchar(200) | YES | | | 备注 | +| 29 | `TR_STATE` | int | YES | | | TR状态(英文转译推测) | + +--- + +## 2.43 表:MESToMCS_Recipe + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MESToMCS_Recipe` +- **表含义**:MES->MCS:配方主数据下发 +- **字段数量**:49 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | ('') | GUID(英文转译推测) | +| 2 | `EquipId` | int | NO | 是 | | 机台编号 | +| 3 | `EquipName` | varchar(20) | YES | | ('') | 机台名称 | +| 4 | `EquipType` | varchar(10) | NO | 是 | | 机台类型;CWS:小料;MCS:上辅机; | +| 5 | `RecipCode` | varchar(40) | NO | 是 | | 配方代码 | +| 6 | `RecipName` | varchar(40) | YES | | ('') | 配方名称 | +| 7 | `RecipVersion` | int | NO | 是 | | 配方版本 | +| 8 | `ProType` | varchar(10) | NO | 是 | | 配方类型;1:正式 2:试验 3:批量试验 4:1段母炼 5: 2段母炼6:终炼胶 | +| 9 | `IsUse` | int | YES | | | 使用状态1:使用 0:停用 | +| 10 | `Weight` | decimal(18,3) | YES | | ((0)) | 配方总重 | +| 11 | `Dust1RecycleMode` | int | YES | | ((0)) | 回收方式1;0:不回收;1:时间回收;2:重量回收 | +| 12 | `Dust1RecycleVal` | decimal(18,3) | YES | | ((0)) | 回收参数1 | +| 13 | `Dust2RecycleMode` | int | YES | | ((0)) | 回收方式2;0:不回收;1:时间回收;2:重量回收 | +| 14 | `Dust2RecycleVal` | decimal(18,0) | YES | | ((0)) | 回收参数1 | +| 15 | `SetOverTempMinTimVal` | int | YES | | ((0)) | 超温排胶最短时间 | +| 16 | `SetOverTimVal` | int | YES | | ((0)) | 超时排胶时间 | +| 17 | `SetOverTempVal` | decimal(18,2) | YES | | ((0)) | 超温排胶温度 | +| 18 | `SetOverEnergyVal` | decimal(18,3) | YES | | ((0)) | 超能排胶能量 | +| 19 | `FeedLowTempLimit` | decimal(18,3) | YES | | ((0)) | 进胶最低温度 | +| 20 | `FeedHighTempLimit` | decimal(18,3) | YES | | ((0)) | 进胶最高温度 | +| 21 | `FeedRotorSetSpd` | decimal(18,3) | YES | | ((0)) | 备用 | +| 22 | `FeedRotorSetTol` | decimal(18,3) | YES | | ((0)) | 备用 | +| 23 | `IntervalTim` | int | YES | | ((0)) | 最小间隔时间 | +| 24 | `SetTCU1` | decimal(18,3) | YES | | ((0)) | 混炼室温度 | +| 25 | `SetTCU2` | decimal(18,3) | YES | | ((0)) | 转子温度 | +| 26 | `SetTCU3` | decimal(18,3) | YES | | ((0)) | 卸料门温度 | +| 27 | `SetTCU4` | decimal(18,3) | YES | | ((0)) | 备用 | +| 28 | `SetTCU5` | decimal(18,3) | YES | | ((0)) | 备用 | +| 29 | `CtcMaxSpd` | decimal(18,3) | YES | | ((0)) | 最大转速 | +| 30 | `CtcMinSpd` | decimal(18,3) | YES | | ((0)) | 最小转速 | +| 31 | `CtcMaxPress` | decimal(18,0) | YES | | ((0)) | 最大压力 | +| 32 | `CtcMinPress` | decimal(18,3) | YES | | ((0)) | 最小压力 | +| 33 | `CtcLimitTemp` | decimal(18,3) | YES | | ((0)) | 温度保护 | +| 34 | `CtcLimitTim` | int | YES | | ((0)) | 时间保护 | +| 35 | `ShelfLotCount` | int | YES | | ((0)) | Shelf批次数量(英文转译推测) | +| 36 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 37 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 38 | `MES_Flag` | int | YES | | ((0)) | MES标志(英文转译推测) | +| 39 | `Remark` | varchar(50) | YES | | ('') | 备注(英文转译推测) | +| 40 | `AbnormalFeedBack` | varchar(50) | YES | | ('') | Abnormal供料Back(英文转译推测) | +| 41 | `AbortId` | int | YES | | ((0)) | Abort Id(英文转译推测) | +| 42 | `ThreeTemp1` | decimal(18,3) | YES | | ((0)) | Three温度1(英文转译推测) | +| 43 | `ThreeTemp2` | decimal(18,3) | YES | | ((0)) | Three温度2(英文转译推测) | +| 44 | `ThreeTemp3` | decimal(18,3) | YES | | ((0)) | Three温度3(英文转译推测) | +| 45 | `ThreeTemp4` | decimal(18,3) | YES | | ((0)) | Three温度4(英文转译推测) | +| 46 | `RepVersionTxt` | varchar(40) | YES | | | Rep版本Txt(英文转译推测) | +| 47 | `RpType` | int | YES | | ((0)) | Rp类型(英文转译推测) | +| 48 | `ReRubberWeight` | numeric(18,3) | YES | | ((0)) | ReRubber重量(英文转译推测) | +| 49 | `DisTemp` | numeric(18,3) | YES | | ((0)) | Dis温度(英文转译推测) | + +--- + +## 2.44 表:MESToMCS_Recipe_BatchOff + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MESToMCS_Recipe_BatchOff` +- **表含义**:MES->MCS:配方排出参数下发 +- **字段数量**:27 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(50) | NO | | | GUID(英文转译推测) | +| 2 | `EquipID` | int | NO | 是 | | 设备ID(英文转译推测) | +| 3 | `RpCode` | varchar(50) | NO | 是 | | Rp编码(英文转译推测) | +| 4 | `RubberType` | int | YES | | | Rubber类型(英文转译推测) | +| 5 | `ReRubberType` | int | YES | | | ReRubber类型(英文转译推测) | +| 6 | `ReRubberWeight` | decimal(9,3) | YES | | | ReRubber重量(英文转译推测) | +| 7 | `ReRubberLength` | decimal(9,3) | YES | | | Re Rubber Length(英文转译推测) | +| 8 | `ReRubberCount` | int | YES | | | ReRubber数量(英文转译推测) | +| 9 | `ReRubberHeight` | decimal(9,3) | YES | | | Re Rubber Height(英文转译推测) | +| 10 | `ReRubberSpeed` | decimal(9,3) | YES | | | ReRubber速度(英文转译推测) | +| 11 | `RubberMWeight` | decimal(9,3) | YES | | | Rubber MWeight(英文转译推测) | +| 12 | `ReRubberLimitTemp` | decimal(9,3) | YES | | | ReRubberLimit温度(英文转译推测) | +| 13 | `ReRubberLead` | decimal(9,3) | YES | | | Re Rubber Lead(英文转译推测) | +| 14 | `MinSaveTime` | int | YES | | | 最小Save时间(英文转译推测) | +| 15 | `MaxSaveTime` | int | YES | | | 最大Save时间(英文转译推测) | +| 16 | `PerWeight` | decimal(9,3) | YES | | | Per重量(英文转译推测) | +| 17 | `SampleType` | int | YES | | | Sample类型(英文转译推测) | +| 18 | `SampleWeight` | decimal(9,3) | YES | | | Sample重量(英文转译推测) | +| 19 | `SampleLength` | decimal(9,3) | YES | | | Sample Length(英文转译推测) | +| 20 | `SampleCount` | int | YES | | | Sample数量(英文转译推测) | +| 21 | `RpVersion` | varchar(50) | YES | | | Rp版本(英文转译推测) | +| 22 | `RpVersionID` | int | NO | 是 | | Rp版本ID(英文转译推测) | +| 23 | `HangRubberHeight` | decimal(9,3) | YES | | | Hang Rubber Height(英文转译推测) | +| 24 | `MES_Flag` | int | YES | | | MES标志(英文转译推测) | +| 25 | `RecipVersionID` | int | YES | | | Recip版本ID(英文转译推测) | +| 26 | `ReRubberPos` | int | YES | | | Re Rubber Pos(英文转译推测) | +| 27 | `RecipVersion` | varchar(50) | YES | | | Recip版本(英文转译推测) | + +--- + +## 2.45 表:MESToMCS_Recipe_Delete + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MESToMCS_Recipe_Delete` +- **表含义**:MES->MCS:配方删除指令 +- **字段数量**:11 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | | GUID(英文转译推测) | +| 2 | `EquipId` | int | NO | 是 | | 机台编号 | +| 3 | `EquipName` | varchar(10) | YES | | | 机台名称 | +| 4 | `EquipType` | varchar(10) | NO | 是 | | 机台类型;CWS:小料;MCS:上辅机 | +| 5 | `RecipCode` | varchar(30) | NO | 是 | | 配方(物料)代码 | +| 6 | `RecipVersion` | int | NO | 是 | | 配方(物料)版本 | +| 7 | `User` | varchar(10) | YES | | | 操作人 | +| 8 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 9 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 10 | `MES_Flag` | int | YES | | (0) | MES标志(英文转译推测) | +| 11 | `Remark` | varchar(50) | YES | | | 备注(英文转译推测) | + +--- + +## 2.46 表:MESToMCS_Recipe_MillStep + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MESToMCS_Recipe_MillStep` +- **表含义**:MES->MCS:配方开炼步骤下发 +- **字段数量**:21 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(50) | YES | | | GUID(英文转译推测) | +| 2 | `EquipID` | int | NO | 是 | | 设备ID(英文转译推测) | +| 3 | `MillID` | int | NO | 是 | | 开炼ID(英文转译推测) | +| 4 | `RpCode` | varchar(50) | NO | 是 | | Rp编码(英文转译推测) | +| 5 | `StepID` | int | NO | 是 | | 步骤ID(英文转译推测) | +| 6 | `MillAction` | int | YES | | | 开炼动作(英文转译推测) | +| 7 | `ActName` | varchar(40) | YES | | | 动作名称(英文转译推测) | +| 8 | `ActName_EN` | varchar(40) | YES | | | 动作名称en(英文转译推测) | +| 9 | `MillTime` | int | YES | | | 开炼时间(英文转译推测) | +| 10 | `MillSpeed` | decimal(18,3) | YES | | | 开炼速度(英文转译推测) | +| 11 | `CoolDeviceSpeed` | decimal(18,3) | YES | | | CoolDevice速度(英文转译推测) | +| 12 | `RollGap` | decimal(18,3) | YES | | | Roll Gap(英文转译推测) | +| 13 | `WaterTemper` | decimal(18,3) | YES | | | Water Temper(英文转译推测) | +| 14 | `RubTemper` | decimal(18,3) | YES | | | Rub Temper(英文转译推测) | +| 15 | `SpeedRatio` | decimal(18,3) | YES | | | 速度Ratio(英文转译推测) | +| 16 | `Spare1` | int | YES | | ((0)) | Spare 1(英文转译推测) | +| 17 | `Spare2` | int | YES | | ((0)) | Spare 2(英文转译推测) | +| 18 | `RpVersionID` | int | NO | 是 | | Rp版本ID(英文转译推测) | +| 19 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 20 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 21 | `MES_Flag` | int | YES | | ((0)) | MES标志(英文转译推测) | + +--- + +## 2.47 表:MESToMCS_Recipe_MixDown + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MESToMCS_Recipe_MixDown` +- **表含义**:MES->MCS:配方混炼下传数据 +- **字段数量**:28 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(50) | NO | | | GUID(英文转译推测) | +| 2 | `EquipID` | int | NO | 是 | | 设备ID(英文转译推测) | +| 3 | `EquipType` | varchar(20) | NO | 是 | | 设备类型(英文转译推测) | +| 4 | `EquipName` | varchar(20) | YES | | | 设备名称(英文转译推测) | +| 5 | `RpCode` | varchar(50) | NO | 是 | | Rp编码(英文转译推测) | +| 6 | `RpName` | varchar(50) | YES | | | Rp名称(英文转译推测) | +| 7 | `RpVersion` | varchar(50) | YES | | | Rp版本(英文转译推测) | +| 8 | `SetOverTempMinTimVal` | int | YES | | | SetOver温度最小TimVal(英文转译推测) | +| 9 | `SetOverTimVal` | int | YES | | | Set Over Tim Val(英文转译推测) | +| 10 | `SetOverTempVal` | decimal(9,3) | YES | | | SetOver温度Val(英文转译推测) | +| 11 | `SetOverEnergyVal` | decimal(9,3) | YES | | | SetOver能耗Val(英文转译推测) | +| 12 | `FeedLowTempLimit` | decimal(9,3) | YES | | | 供料Low温度Limit(英文转译推测) | +| 13 | `FeedHighTempLimit` | decimal(9,3) | YES | | | 供料High温度Limit(英文转译推测) | +| 14 | `IntervalTim` | int | YES | | | Interval Tim(英文转译推测) | +| 15 | `SetTCU1` | decimal(9,3) | YES | | | Set TCU 1(英文转译推测) | +| 16 | `SetTCU2` | decimal(9,3) | YES | | | Set TCU 2(英文转译推测) | +| 17 | `SetTCU3` | decimal(9,3) | YES | | | Set TCU 3(英文转译推测) | +| 18 | `SetTCU4` | decimal(9,3) | YES | | | Set TCU 4(英文转译推测) | +| 19 | `SetTCU5` | decimal(9,3) | YES | | | Set TCU 5(英文转译推测) | +| 20 | `CtcMaxSpd` | decimal(9,3) | YES | | | Ctc最大Spd(英文转译推测) | +| 21 | `CtcMinSpd` | decimal(9,3) | YES | | | Ctc最小Spd(英文转译推测) | +| 22 | `CtcMaxPress` | decimal(9,3) | YES | | | Ctc最大Press(英文转译推测) | +| 23 | `CtcMinPress` | decimal(9,3) | YES | | | Ctc最小Press(英文转译推测) | +| 24 | `CtcLimitTemp` | decimal(9,3) | YES | | | CtcLimit温度(英文转译推测) | +| 25 | `CtcLimitTim` | int | YES | | | Ctc Limit Tim(英文转译推测) | +| 26 | `SaveTime` | varchar(20) | YES | | | Save时间(英文转译推测) | +| 27 | `LastOper` | varchar(20) | YES | | | Last Oper(英文转译推测) | +| 28 | `RpVersionID` | int | YES | | | Rp版本ID(英文转译推测) | + +--- + +## 2.48 表:MESToMCS_Recipe_MixStep + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MESToMCS_Recipe_MixStep` +- **表含义**:MES->MCS:配方密炼步骤下发 +- **字段数量**:26 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | ('') | GUID(英文转译推测) | +| 2 | `EquipId` | int | NO | 是 | | 机台编号 | +| 3 | `EquipName` | varchar(20) | YES | | ('') | 机台名称 | +| 4 | `EquipType` | varchar(10) | NO | 是 | | 机台类型 | +| 5 | `RecipCode` | varchar(30) | NO | 是 | | 配方代码 | +| 6 | `Serial` | int | NO | 是 | | 密炼步序 | +| 7 | `Act` | int | YES | | ((0)) | 动作地址 | +| 8 | `Cond` | int | YES | | ((0)) | 条件地址 | +| 9 | `Time` | int | NO | | ((0)) | 设定时间 | +| 10 | `TimeLimit` | int | YES | | ((0)) | 时间保护 | +| 11 | `Temp` | decimal(18,3) | YES | | ((0)) | 温度 | +| 12 | `TempLimit` | decimal(18,3) | YES | | ((0)) | 温度保护 | +| 13 | `Energy` | decimal(18,3) | YES | | ((0)) | 能量 | +| 14 | `EnergyLimit` | decimal(18,3) | YES | | ((0)) | 能量保护 | +| 15 | `Power` | decimal(18,3) | YES | | ((0)) | 功率 | +| 16 | `PowerLimit` | decimal(18,3) | YES | | ((0)) | 功率保护 | +| 17 | `Press` | decimal(18,3) | YES | | ((0)) | 压力 | +| 18 | `Speed` | decimal(18,3) | YES | | ((0)) | 转速 | +| 19 | `RotorGap` | decimal(18,3) | YES | | ((0)) | 辊距 | +| 20 | `RamPos` | decimal(18,3) | YES | | ((0)) | 栓位置 | +| 21 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 22 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 23 | `MES_Flag` | int | YES | | ((0)) | MES标志(英文转译推测) | +| 24 | `Remark` | varchar(200) | YES | | ('') | 备注(英文转译推测) | +| 25 | `AbnormalFeedBack` | varchar(50) | YES | | ('') | Abnormal供料Back(英文转译推测) | +| 26 | `RecipVersion` | int | NO | 是 | | Recip版本(英文转译推测) | + +--- + +## 2.49 表:MESToMCS_Recipe_MixStep_Down + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MESToMCS_Recipe_MixStep_Down` +- **表含义**:MES->MCS:配方密炼步骤下传明细 +- **字段数量**:21 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(50) | NO | | | GUID(英文转译推测) | +| 2 | `EquipID` | int | NO | 是 | | 设备ID(英文转译推测) | +| 3 | `RpCode` | varchar(50) | NO | 是 | | Rp编码(英文转译推测) | +| 4 | `MixID` | int | NO | 是 | | 密炼ID(英文转译推测) | +| 5 | `Cond` | int | YES | | | Cond(英文转译推测) | +| 6 | `Tim` | int | YES | | | Tim(英文转译推测) | +| 7 | `TimLimit` | int | YES | | | Tim Limit(英文转译推测) | +| 8 | `Tempe` | decimal(18,3) | YES | | | 温度e(英文转译推测) | +| 9 | `TempLimit` | decimal(18,3) | YES | | | 温度Limit(英文转译推测) | +| 10 | `Energy` | decimal(18,3) | YES | | | 能耗(英文转译推测) | +| 11 | `EnergyLimit` | decimal(18,3) | YES | | | 能耗Limit(英文转译推测) | +| 12 | `Power` | int | YES | | | 功率(英文转译推测) | +| 13 | `PowerLimit` | int | YES | | | 功率Limit(英文转译推测) | +| 14 | `Act` | int | YES | | | 动作(英文转译推测) | +| 15 | `Press` | decimal(18,3) | YES | | | Press(英文转译推测) | +| 16 | `RotorSpd` | decimal(18,3) | YES | | | Rotor Spd(英文转译推测) | +| 17 | `RollGap` | decimal(18,3) | YES | | | Roll Gap(英文转译推测) | +| 18 | `RamPos` | decimal(18,3) | YES | | | Ram Pos(英文转译推测) | +| 19 | `RpVersion` | varchar(40) | YES | | | Rp版本(英文转译推测) | +| 20 | `RpVersionID` | int | YES | | | Rp版本ID(英文转译推测) | +| 21 | `Torque` | decimal(18,3) | YES | | | Torque(英文转译推测) | + +--- + +## 2.50 表:MESToMCS_Recipe_Weight + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MESToMCS_Recipe_Weight` +- **表含义**:MES->MCS:配方称重物料明细下发 +- **字段数量**:26 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | YES | | | GUID(英文转译推测) | +| 2 | `EquipId` | int | NO | 是 | | 机台编号 | +| 3 | `EquipName` | varchar(20) | YES | | ('') | 机台名称 | +| 4 | `EquipType` | varchar(10) | NO | 是 | ('') | 机台类型 | +| 5 | `RecipCode` | varchar(30) | NO | 是 | ('') | 配方代码 | +| 6 | `ScaleType` | int | NO | 是 | ((0)) | 秤类型1:炭黑 2:油料 3:胶料 4:小料 | +| 7 | `ScaleId` | int | NO | 是 | ((0)) | 秤编号,101:炭黑秤1;102:炭黑秤2;103:炭黑秤3 + 201:油料秤1;202:油料秤2;203:油料秤3;301:胶料秤1;302:胶料秤2;401:小料秤1;402:小料秤2 | +| 8 | `Serial` | int | NO | 是 | ((0)) | 称量步序 | +| 9 | `Action` | int | YES | | ((0)) | 动作;1:称量 2:称到 3:卸料,默认值0 | +| 10 | `BinNo` | int | YES | | ((0)) | 仓号(英文转译推测) | +| 11 | `MatCode` | varchar(30) | YES | | ('') | 物料代码 | +| 12 | `MatName` | varchar(30) | YES | | ('') | 物料名称 | +| 13 | `SetWeight` | decimal(18,3) | YES | | ((0)) | 设定重量 | +| 14 | `Tolerance` | decimal(18,3) | YES | | ((0)) | 设定误差 | +| 15 | `SFNum` | int | YES | | ((0)) | 导切机编号 | +| 16 | `MatType` | varchar(10) | YES | | ('') | 炭黑、油料、胶料、小料 | +| 17 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 18 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 19 | `MES_Flag` | int | YES | | ((0)) | MES标志(英文转译推测) | +| 20 | `Remark` | varchar(50) | YES | | ('') | 备注(英文转译推测) | +| 21 | `RubTypeID` | int | YES | | ((0)) | Rub类型ID(英文转译推测) | +| 22 | `MixID` | int | NO | 是 | | 密炼ID(英文转译推测) | +| 23 | `AbnormalFeedBack` | varchar(50) | YES | | ('') | Abnormal供料Back(英文转译推测) | +| 24 | `AbortId` | int | YES | | ((0)) | Abort Id(英文转译推测) | +| 25 | `RecipVersion` | int | NO | 是 | | Recip版本(英文转译推测) | +| 26 | `SetDnTol` | decimal(18,3) | YES | | ((0)) | SetDn容差(英文转译推测) | + +--- + +## 2.51 表:MESToMCS_Verify + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`MESToMCS_Verify` +- **表含义**:MES->MCS:校验/核对数据 +- **字段数量**:12 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `GUID` | varchar(40) | NO | 是 | | GUID(英文转译推测) | +| 2 | `EquipID` | int | NO | | | 机台编号 | +| 3 | `EquipName` | varchar(10) | YES | | | 机台名称 | +| 4 | `EquipType` | varchar(10) | NO | | | 机台类型 | +| 5 | `ScaleNum` | int | NO | | | 秤编号 | +| 6 | `PlanId` | varchar(20) | YES | | | 计划编号 | +| 7 | `MatCode` | varchar(30) | YES | | | 物料代码 | +| 8 | `MatName` | varchar(30) | YES | | | 物料名称 | +| 9 | `Judge` | int | YES | | | 判断结果 | +| 10 | `WriteTime` | datetime | YES | | | 写入时间(英文转译推测) | +| 11 | `ReadTime` | datetime | YES | | | 读取时间(英文转译推测) | +| 12 | `MES_Flag` | int | YES | | (0) | MES标志(英文转译推测) | + +--- + +## 2.52 表:PptScanBarcode + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`PptScanBarcode` +- **表含义**:条码扫描当前表 +- **字段数量**:13 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `SerialID` | int | NO | 是 | | Serial ID(英文转译推测) | +| 2 | `Barcode` | varchar(25) | NO | 是 | | 条码(英文转译推测) | +| 3 | `EquipCode` | char(5) | NO | 是 | | 设备编码(英文转译推测) | +| 4 | `MaterCode` | char(13) | NO | 是 | | 物料编码(英文转译推测) | +| 5 | `MaterName` | nvarchar(80) | YES | | | 物料名称(英文转译推测) | +| 6 | `MaterType` | char(1) | YES | | | 物料类型(英文转译推测) | +| 7 | `MaterTypeName` | char(10) | YES | | | 物料类型名称(英文转译推测) | +| 8 | `BarchNo` | varchar(50) | YES | | | Barch编号(英文转译推测) | +| 9 | `ScanWeight` | decimal(18,2) | YES | | ((0)) | 扫描重量(英文转译推测) | +| 10 | `UsedWeight` | decimal(18,2) | YES | | | Used重量(英文转译推测) | +| 11 | `QueueIndex` | int | YES | | | Queue索引(英文转译推测) | +| 12 | `IsUsed` | char(1) | YES | | | Is Used(英文转译推测) | +| 13 | `ScanDateTime` | datetime | YES | | | 扫描日期时间(英文转译推测) | + +--- + +## 2.53 表:PptScanBarcodeHis + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`PptScanBarcodeHis` +- **表含义**:条码扫描历史表 +- **字段数量**:15 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `ObjId` | int | NO | 是 | | Obj Id(英文转译推测) | +| 2 | `SerialID` | int | NO | | | Serial ID(英文转译推测) | +| 3 | `Barcode` | varchar(25) | NO | | | 条码(英文转译推测) | +| 4 | `EquipCode` | char(5) | NO | | | 设备编码(英文转译推测) | +| 5 | `MaterCode` | char(13) | NO | | | 物料编码(英文转译推测) | +| 6 | `MaterName` | nvarchar(80) | YES | | | 物料名称(英文转译推测) | +| 7 | `MaterType` | char(1) | YES | | | 物料类型(英文转译推测) | +| 8 | `MaterTypeName` | char(10) | YES | | | 物料类型名称(英文转译推测) | +| 9 | `BarchNo` | varchar(50) | YES | | | Barch编号(英文转译推测) | +| 10 | `ScanWeight` | decimal(18,2) | YES | | | 扫描重量(英文转译推测) | +| 11 | `UsedWeight` | decimal(18,2) | YES | | | Used重量(英文转译推测) | +| 12 | `QueueIndex` | int | YES | | | Queue索引(英文转译推测) | +| 13 | `IsUsed` | char(1) | YES | | | Is Used(英文转译推测) | +| 14 | `ScanDateTime` | datetime | YES | | | 扫描日期时间(英文转译推测) | +| 15 | `UpdateDateTime` | datetime | YES | | | 更新日期时间(英文转译推测) | + +--- + +## 2.54 表:PptScanBarcodeLog + +- **所属数据库**:`MESNAC_MCS_ShareDB_XL` +- **Schema**:`dbo` +- **表名**:`PptScanBarcodeLog` +- **表含义**:条码扫描日志表 +- **字段数量**:12 + +### 字段说明 + +| 序号 | 字段名 | 数据类型 | 可空 | 主键 | 默认值 | 字段含义 | +| --- | --- | --- | --- | --- | --- | --- | +| 1 | `dt` | datetime | NO | 是 | (getdate()) | dt(英文转译推测) | +| 2 | `EquipCode` | char(5) | NO | 是 | | 设备编码(英文转译推测) | +| 3 | `RecipeCode` | varchar(20) | YES | | | 配方编码(英文转译推测) | +| 4 | `scanBarcode` | varchar(30) | YES | | | 扫描条码(英文转译推测) | +| 5 | `ScanMaterCode` | char(13) | YES | | | 扫描物料编码(英文转译推测) | +| 6 | `msg` | varchar(200) | YES | | | msg(英文转译推测) | +| 7 | `Usercode` | varchar(10) | YES | | | 用户编码(英文转译推测) | +| 8 | `shiftid` | varchar(4) | YES | | | 班次ID(英文转译推测) | +| 9 | `shiftclass` | varchar(4) | YES | | | shiftclass(英文转译推测) | +| 10 | `scanflag` | varchar(1) | YES | | | 扫描标志(英文转译推测) | +| 11 | `ScanLogSignal` | int | YES | | | 扫描日志Signal(英文转译推测) | +| 12 | `ScanUsedBarMsg` | varchar(500) | YES | | | 扫描UsedBarMsg(英文转译推测) | + +--- diff --git a/doc/MCS_SQLServer_表结构说明.xlsx b/doc/MCS_SQLServer_表结构说明.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..ec41c49a8f4c5563d4b6be4ada3dcb5c848af0bc GIT binary patch literal 201507 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�HltRED!13{}XV~8O}o|daQG_Qa6 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[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

f9l_3`?tNVoH~q&u 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

qz4O6 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 /sys/sync/batch) filterChainDefinitionMap.put("/sys/sync/batch", "anon"); + //update-begin---author:cursor ---date:2026-07-20 for:【设备库桥接】桌面代理免密接口----------- + filterChainDefinitionMap.put("/sys/device/agent/**", "anon"); + filterChainDefinitionMap.put("/xslmes/deviceAgent/anon/**", "anon"); + //update-end---author:cursor ---date:2026-07-20 for:【设备库桥接】桌面代理免密接口----------- // 添加自己的过滤器并且取名为jwt Map filterMap = new HashMap(1); diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/pom.xml b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/pom.xml index d6825359..085807f0 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/pom.xml +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/pom.xml @@ -36,5 +36,11 @@ jeecg-module-print ${jeecgboot.version} + + + org.jeecgframework.boot3 + jeecg-module-device-sync + ${jeecgboot.version} + diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslAutoSmallMaterialDemandPlanController.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslAutoSmallMaterialDemandPlanController.java index 68ee8818..bf72f3c5 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslAutoSmallMaterialDemandPlanController.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslAutoSmallMaterialDemandPlanController.java @@ -130,6 +130,7 @@ public class MesXslAutoSmallMaterialDemandPlanController { } sql.append("GROUP BY "); + sql.append(statDateExpr).append(", "); if (groupedByMachine) { sql.append(machineExpr).append(", "); } @@ -160,14 +161,20 @@ public class MesXslAutoSmallMaterialDemandPlanController { private String buildOrderBy(boolean groupedByMachine) { if (groupedByMachine) { - return " ORDER BY machineName, rawMaterialName"; + return " ORDER BY statDate DESC, machineName, rawMaterialName"; } - return " ORDER BY rawMaterialName"; + return " ORDER BY statDate DESC, rawMaterialName"; } private String buildRowId(MesXslSmallMaterialDemandPlanSummary row, boolean groupedByMachine, int seq) { String machine = groupedByMachine ? StringUtils.defaultString(row.getMachineName()) : "ALL"; - return machine + "_" + StringUtils.defaultString(row.getRawMaterialName()) + "_" + seq; + return StringUtils.defaultString(row.getStatDate()) + + "_" + + machine + + "_" + + StringUtils.defaultString(row.getRawMaterialName()) + + "_" + + seq; } private String trim(String value) { diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslManualSmallMaterialDemandPlanController.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslManualSmallMaterialDemandPlanController.java index 6e31ea70..868a7d1d 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslManualSmallMaterialDemandPlanController.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslManualSmallMaterialDemandPlanController.java @@ -130,6 +130,7 @@ public class MesXslManualSmallMaterialDemandPlanController { } sql.append("GROUP BY "); + sql.append(statDateExpr).append(", "); if (groupedByMachine) { sql.append(machineExpr).append(", "); } @@ -160,14 +161,20 @@ public class MesXslManualSmallMaterialDemandPlanController { private String buildOrderBy(boolean groupedByMachine) { if (groupedByMachine) { - return " ORDER BY machineName, rawMaterialName"; + return " ORDER BY statDate DESC, machineName, rawMaterialName"; } - return " ORDER BY rawMaterialName"; + return " ORDER BY statDate DESC, rawMaterialName"; } private String buildRowId(MesXslSmallMaterialDemandPlanSummary row, boolean groupedByMachine, int seq) { String machine = groupedByMachine ? StringUtils.defaultString(row.getMachineName()) : "ALL"; - return machine + "_" + StringUtils.defaultString(row.getRawMaterialName()) + "_" + seq; + return StringUtils.defaultString(row.getStatDate()) + + "_" + + machine + + "_" + + StringUtils.defaultString(row.getRawMaterialName()) + + "_" + + seq; } private String trim(String value) { diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslMillActionController.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslMillActionController.java new file mode 100644 index 00000000..351f7739 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslMillActionController.java @@ -0,0 +1,60 @@ +package org.jeecg.modules.xslmes.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.system.base.controller.JeecgController; +import org.jeecg.common.system.query.QueryGenerator; +import org.jeecg.modules.xslmes.entity.MesXslMillAction; +import org.jeecg.modules.xslmes.service.IMesXslMillActionService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.ModelAndView; + +/** + * MES 开炼机动作接收表 + */ +@Tag(name = "开炼机动作接收") +@RestController +@RequestMapping("/xslmes/mesXslMillAction") +public class MesXslMillActionController extends JeecgController { + + @Autowired private IMesXslMillActionService mesXslMillActionService; + + @Operation(summary = "开炼机动作接收-分页列表查询") + @GetMapping("/list") + public Result> queryPageList( + MesXslMillAction model, + @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo, + @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize, + HttpServletRequest req) { + QueryWrapper queryWrapper = QueryGenerator.initQueryWrapper(model, req.getParameterMap()); + queryWrapper.orderByDesc("update_time"); + IPage pageList = mesXslMillActionService.page(new Page<>(pageNo, pageSize), queryWrapper); + return Result.OK(pageList); + } + + @Operation(summary = "开炼机动作接收-通过id查询") + @GetMapping("/queryById") + public Result queryById(@RequestParam(name = "id", required = true) String id) { + MesXslMillAction entity = mesXslMillActionService.getById(id); + if (entity == null) { + return Result.error("未找到对应数据"); + } + return Result.OK(entity); + } + + @RequiresPermissions("xslmes:mes_xsl_mill_action:exportXls") + @RequestMapping(value = "/exportXls") + public ModelAndView exportXls(HttpServletRequest request, MesXslMillAction model) { + return super.exportXls(request, model, MesXslMillAction.class, "开炼机动作接收"); + } +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslRawMaterialDemandPlanController.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslRawMaterialDemandPlanController.java index c3974483..da757aca 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslRawMaterialDemandPlanController.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslRawMaterialDemandPlanController.java @@ -95,9 +95,12 @@ public class MesXslRawMaterialDemandPlanController { private String buildGroupedSql( MesXslRawMaterialDemandPlanSummary query, boolean groupedByMachine, List params) { - String planDateExpr = "DATE(t.plan_date)"; + boolean hasPlanDate = hasColumn("mes_xsl_raw_material_demand_plan", "plan_date"); + boolean hasMaterialId = hasColumn("mes_xsl_raw_material_demand_plan", "material_id"); + String planDateExpr = hasPlanDate ? "t.plan_date" : "DATE(t.create_time)"; String machineExpr = "COALESCE(NULLIF(TRIM(t.machine_name),''), '')"; - String materialIdExpr = "COALESCE(NULLIF(TRIM(t.material_id),''), '')"; + String materialIdExpr = + hasMaterialId ? "COALESCE(NULLIF(TRIM(t.material_id),''), '')" : "''"; String erpCodeExpr = "COALESCE(NULLIF(TRIM(t.erp_code),''), '')"; String rawMaterialExpr = "COALESCE(NULLIF(TRIM(t.raw_material_name),''), '')"; @@ -192,4 +195,12 @@ public class MesXslRawMaterialDemandPlanController { private String trim(String value) { return value == null ? "" : value.trim(); } + + private boolean hasColumn(String tableName, String columnName) { + String sql = + "SELECT COUNT(1) FROM information_schema.COLUMNS " + + "WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?"; + Long count = jdbcTemplate.queryForObject(sql, Long.class, tableName, columnName); + return count != null && count > 0; + } } diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/deviceagent/MesXslDeviceAgentAnonController.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/deviceagent/MesXslDeviceAgentAnonController.java new file mode 100644 index 00000000..bb91843c --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/deviceagent/MesXslDeviceAgentAnonController.java @@ -0,0 +1,113 @@ +package org.jeecg.modules.xslmes.deviceagent; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.common.api.vo.Result; +import org.jeecg.modules.device.sync.agent.DeviceAgentCommandService; +import org.jeecg.modules.device.sync.agent.DeviceAgentResult; +import org.jeecg.modules.device.sync.agent.DeviceAgentSession; +import org.jeecg.modules.device.sync.agent.DeviceAgentSessionRegistry; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 桌面设备代理免密接口(注册/心跳/分块回传)。 + */ +@Tag(name = "桌面设备代理") +@Slf4j +@RestController +@RequestMapping("/xslmes/deviceAgent/anon") +@RequiredArgsConstructor +public class MesXslDeviceAgentAnonController { + + private final DeviceAgentSessionRegistry sessionRegistry; + private final DeviceAgentCommandService commandService; + + @Operation(summary = "注册/心跳") + @PostMapping("/register") + public Result> register(@RequestBody Map body) { + String deviceId = str(body, "deviceId"); + if (StringUtils.isBlank(deviceId)) { + return Result.error("deviceId不能为空"); + } + String hostName = str(body, "hostName"); + Boolean dbConnected = bool(body.get("dbConnected")); + String dbMessage = str(body, "dbMessage"); + sessionRegistry.registerOrHeartbeat(deviceId, hostName, dbConnected, dbMessage); + Map data = new HashMap<>(); + data.put("deviceId", deviceId); + data.put("accepted", true); + return Result.OK(data); + } + + @Operation(summary = "心跳") + @PostMapping("/heartbeat") + public Result heartbeat(@RequestBody Map body) { + String deviceId = str(body, "deviceId"); + if (StringUtils.isBlank(deviceId)) { + return Result.error("deviceId不能为空"); + } + sessionRegistry.registerOrHeartbeat(deviceId, str(body, "hostName"), bool(body.get("dbConnected")), str(body, "dbMessage")); + return Result.OK("ok"); + } + + @Operation(summary = "命令结果分块回传(大数据量)") + @PostMapping("/resultChunk") + public Result resultChunk(@RequestBody DeviceAgentResult result) { + if (result == null || StringUtils.isBlank(result.getRequestId())) { + return Result.error("requestId不能为空"); + } + // 当前实现:整包完成;桌面可在单次 chunk 中带 lastChunk=true 直接完成 + if (Boolean.TRUE.equals(result.getLastChunk()) || result.getChunkTotal() == null || result.getChunkTotal() <= 1) { + commandService.completeResult(result); + } else { + log.info("收到分块回传 requestId={} chunk={}/{}", result.getRequestId(), result.getChunkIndex(), result.getChunkTotal()); + // 简化:仅最后一块完成;中间块忽略合并(桌面端大结果优先走单次 STOMP 或一次 REST 全量) + if (Boolean.TRUE.equals(result.getLastChunk())) { + commandService.completeResult(result); + } + } + return Result.OK("ok"); + } + + @Operation(summary = "在线代理列表") + @GetMapping("/online") + public Result> online() { + return Result.OK(sessionRegistry.listOnline()); + } + + @Operation(summary = "查询代理状态") + @GetMapping("/status") + public Result status(@RequestParam(name = "deviceId", required = false) String deviceId) { + return Result.OK(sessionRegistry.pickOnlineAgent(deviceId)); + } + + private static String str(Map body, String key) { + if (body == null || body.get(key) == null) { + return null; + } + String v = String.valueOf(body.get(key)).trim(); + return v.isEmpty() || "null".equalsIgnoreCase(v) ? null : v; + } + + private static Boolean bool(Object v) { + if (v == null) { + return null; + } + if (v instanceof Boolean) { + return (Boolean) v; + } + return Boolean.parseBoolean(String.valueOf(v)); + } +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/entity/MesXslMillAction.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/entity/MesXslMillAction.java new file mode 100644 index 00000000..9e50eee1 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/entity/MesXslMillAction.java @@ -0,0 +1,86 @@ +package org.jeecg.modules.xslmes.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import java.io.Serializable; +import java.util.Date; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.jeecgframework.poi.excel.annotation.Excel; +import org.springframework.format.annotation.DateTimeFormat; + +/** + * MES 开炼机动作接收表(采集自 MCSToMES_Act_Mill) + */ +@Data +@TableName("mes_xsl_mill_action") +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@Schema(description = "MES开炼机动作接收表") +public class MesXslMillAction implements Serializable { + + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.ASSIGN_ID) + private String id; + + @Excel(name = "GUID", width = 36) + @Schema(description = "源表GUID") + private String guid; + + @Excel(name = "动作地址", width = 10) + @Schema(description = "动作地址") + private Integer actAddr; + + @Excel(name = "动作名称", width = 20) + @Schema(description = "动作名称") + private String actName; + + @Excel(name = "动作名称(英)", width = 20) + @Schema(description = "动作名称(英)") + private String actNameEn; + + @Excel(name = "动作备注", width = 15) + @Schema(description = "动作备注") + private String actMemo; + + @Excel(name = "动作关联地址", width = 12) + @Schema(description = "动作关联地址") + private Integer actRepaddr; + + @Excel(name = "写入时间", width = 20, format = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Schema(description = "写入时间") + private Date writeTime; + + @Excel(name = "读取时间", width = 20, format = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Schema(description = "读取时间") + private Date readTime; + + @Excel(name = "读写标识", width = 10) + @Schema(description = "读写标识") + private Integer rwFlag; + + private Integer tenantId; + private String sysOrgCode; + private String createBy; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + private String updateBy; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + private Integer delFlag; +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mapper/MesXslMillActionMapper.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mapper/MesXslMillActionMapper.java new file mode 100644 index 00000000..1ba4300c --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mapper/MesXslMillActionMapper.java @@ -0,0 +1,6 @@ +package org.jeecg.modules.xslmes.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.jeecg.modules.xslmes.entity.MesXslMillAction; + +public interface MesXslMillActionMapper extends BaseMapper {} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/DesktopAgentMcsDataAccess.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/DesktopAgentMcsDataAccess.java new file mode 100644 index 00000000..6f4f336a --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/DesktopAgentMcsDataAccess.java @@ -0,0 +1,85 @@ +package org.jeecg.modules.xslmes.mcs.access; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.modules.device.sync.agent.DeviceAgentCommandService; +import org.jeecg.modules.device.sync.agent.DeviceAgentResult; +import org.jeecg.modules.device.sync.agent.DeviceAgentSession; +import org.jeecg.modules.device.sync.agent.DeviceAgentSessionRegistry; +import org.jeecg.modules.xslmes.mcs.datasource.McsDataSourceManager; +import org.jeecg.modules.xslmes.mcs.entity.MesXslMcsDbConfig; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 经桌面端代理访问设备库。 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class DesktopAgentMcsDataAccess implements IMcsDataAccess { + + private final DeviceAgentCommandService commandService; + private final DeviceAgentSessionRegistry sessionRegistry; + private final McsDataSourceManager mcsDataSourceManager; + + @Override + public boolean isAgentMode() { + return true; + } + + @Override + public String resolveAgentDeviceId() { + MesXslMcsDbConfig cfg = mcsDataSourceManager.getCachedConfig(); + String preferred = cfg == null ? null : cfg.getAgentDeviceId(); + DeviceAgentSession session = sessionRegistry.pickOnlineAgent(preferred); + if (session == null) { + throw new IllegalStateException("无在线桌面设备代理,无法访问设备库"); + } + return session.getDeviceId(); + } + + @Override + public List> query(String sql, List params) { + String deviceId = resolveAgentDeviceId(); + DeviceAgentResult result = commandService.query(deviceId, sql, params, 60_000); + return result.getRows() == null ? List.of() : result.getRows(); + } + + @Override + public int execute(String sql, List params) { + String deviceId = resolveAgentDeviceId(); + DeviceAgentResult result = commandService.write(deviceId, sql, params, 60_000); + return result.getAffected() == null ? 0 : result.getAffected(); + } + + @Override + public int pushRows(String table, String mode, List keys, List> rows) { + String deviceId = resolveAgentDeviceId(); + Map payload = new HashMap<>(); + payload.put("mode", StringUtils.defaultIfBlank(mode, "insert")); + if (keys != null) { + payload.put("keys", keys); + } + payload.put("rows", rows == null ? new ArrayList<>() : rows); + DeviceAgentResult result = commandService.syncPush(deviceId, table, payload, 60_000); + return result.getAffected() == null ? 0 : result.getAffected(); + } + + @Override + public boolean testConnection() { + try { + String deviceId = resolveAgentDeviceId(); + DeviceAgentResult result = commandService.test(deviceId, 10_000); + return result != null && result.isSuccess(); + } catch (Exception e) { + log.warn("桌面代理中间库测试失败: {}", e.getMessage()); + return false; + } + } +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/DirectJdbcMcsDataAccess.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/DirectJdbcMcsDataAccess.java new file mode 100644 index 00000000..f7901a07 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/DirectJdbcMcsDataAccess.java @@ -0,0 +1,159 @@ +package org.jeecg.modules.xslmes.mcs.access; + +import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.jeecg.modules.xslmes.mcs.datasource.McsDataSourceManager; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * 服务端直连 SQL Server(调试/兼容保留)。 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class DirectJdbcMcsDataAccess implements IMcsDataAccess { + + private final DataSource dataSource; + private final McsDataSourceManager mcsDataSourceManager; + + @Override + public boolean isAgentMode() { + return false; + } + + @Override + public String resolveAgentDeviceId() { + return null; + } + + @Override + public List> query(String sql, List params) { + JdbcTemplate jt = new JdbcTemplate(requireDs()); + if (params == null || params.isEmpty()) { + return jt.queryForList(sql); + } + return jt.queryForList(sql, params.toArray()); + } + + @Override + public int execute(String sql, List params) { + JdbcTemplate jt = new JdbcTemplate(requireDs()); + if (params == null || params.isEmpty()) { + return jt.update(sql); + } + return jt.update(sql, params.toArray()); + } + + @Override + public int pushRows(String table, String mode, List keys, List> rows) { + if (rows == null || rows.isEmpty()) { + return 0; + } + int affected = 0; + for (Map row : rows) { + if ("update".equalsIgnoreCase(mode) || "upsert".equalsIgnoreCase(mode)) { + int u = updateByKeys(table, keys, row); + if (u > 0) { + affected += u; + continue; + } + if ("update".equalsIgnoreCase(mode)) { + continue; + } + } + affected += insertRow(table, row); + } + return affected; + } + + @Override + public boolean testConnection() { + try { + query("SELECT 1", null); + return true; + } catch (Exception e) { + log.warn("直连中间库测试失败: {}", e.getMessage()); + return false; + } + } + + private int insertRow(String table, Map row) { + List cols = new ArrayList<>(row.keySet()); + StringBuilder sql = new StringBuilder("INSERT INTO [").append(table).append("] ("); + StringBuilder values = new StringBuilder(" VALUES ("); + List params = new ArrayList<>(); + for (int i = 0; i < cols.size(); i++) { + if (i > 0) { + sql.append(','); + values.append(','); + } + sql.append('[').append(cols.get(i)).append(']'); + values.append('?'); + params.add(row.get(cols.get(i))); + } + sql.append(')').append(values).append(')'); + return execute(sql.toString(), params); + } + + private int updateByKeys(String table, List keys, Map row) { + if (keys == null || keys.isEmpty()) { + return 0; + } + List setCols = new ArrayList<>(); + for (String c : row.keySet()) { + boolean isKey = keys.stream().anyMatch(k -> k.equalsIgnoreCase(c)); + if (!isKey) { + setCols.add(c); + } + } + if (setCols.isEmpty()) { + return 0; + } + StringBuilder sql = new StringBuilder("UPDATE [").append(table).append("] SET "); + List params = new ArrayList<>(); + for (int i = 0; i < setCols.size(); i++) { + if (i > 0) { + sql.append(','); + } + sql.append('[').append(setCols.get(i)).append("]=?"); + params.add(getIgnoreCase(row, setCols.get(i))); + } + sql.append(" WHERE "); + for (int i = 0; i < keys.size(); i++) { + if (i > 0) { + sql.append(" AND "); + } + sql.append('[').append(keys.get(i)).append("]=?"); + params.add(getIgnoreCase(row, keys.get(i))); + } + return execute(sql.toString(), params); + } + + private Object getIgnoreCase(Map row, String key) { + if (row.containsKey(key)) { + return row.get(key); + } + for (Map.Entry e : row.entrySet()) { + if (e.getKey() != null && e.getKey().equalsIgnoreCase(key)) { + return e.getValue(); + } + } + return null; + } + + private DataSource requireDs() { + DynamicRoutingDataSource routing = (DynamicRoutingDataSource) dataSource; + DataSource src = routing.getDataSources().get(McsDataSourceManager.DS_KEY); + if (src == null) { + throw new IllegalStateException("中间库数据源 " + McsDataSourceManager.DS_KEY + " 未注册"); + } + return src; + } +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/IMcsDataAccess.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/IMcsDataAccess.java new file mode 100644 index 00000000..da39a732 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/IMcsDataAccess.java @@ -0,0 +1,31 @@ +package org.jeecg.modules.xslmes.mcs.access; + +import java.util.List; +import java.util.Map; + +/** + * MCS 中间库访问抽象:直连 JDBC 或经桌面代理。 + */ +public interface IMcsDataAccess { + + boolean isAgentMode(); + + String resolveAgentDeviceId(); + + List> query(String sql, List params); + + int execute(String sql, List params); + + /** + * 批量写入中间表(EQ_SYNC_PUSH)。 + * + * @param table 白名单表名 + * @param mode insert / update / upsert + * @param keys 匹配键(update/upsert) + * @param rows 行数据 + * @return 影响行数 + */ + int pushRows(String table, String mode, List keys, List> rows); + + boolean testConnection(); +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/McsDataAccessFacade.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/McsDataAccessFacade.java new file mode 100644 index 00000000..0519b9cb --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/McsDataAccessFacade.java @@ -0,0 +1,65 @@ +package org.jeecg.modules.xslmes.mcs.access; + +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.modules.xslmes.mcs.datasource.McsDataSourceManager; +import org.jeecg.modules.xslmes.mcs.entity.MesXslMcsDbConfig; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 按配置在「桌面代理 / 直连」之间切换。 + */ +@Service +@RequiredArgsConstructor +public class McsDataAccessFacade implements IMcsDataAccess { + + public static final String MODE_DESKTOP_AGENT = "DESKTOP_AGENT"; + public static final String MODE_DIRECT = "DIRECT"; + + private final McsDataSourceManager mcsDataSourceManager; + private final DesktopAgentMcsDataAccess desktopAgentMcsDataAccess; + private final DirectJdbcMcsDataAccess directJdbcMcsDataAccess; + + public IMcsDataAccess current() { + return isAgentMode() ? desktopAgentMcsDataAccess : directJdbcMcsDataAccess; + } + + @Override + public boolean isAgentMode() { + MesXslMcsDbConfig cfg = mcsDataSourceManager.getCachedConfig(); + if (cfg == null) { + // 默认优先桌面代理 + return true; + } + String mode = StringUtils.defaultIfBlank(cfg.getAccessMode(), MODE_DESKTOP_AGENT).trim().toUpperCase(); + return MODE_DESKTOP_AGENT.equals(mode); + } + + @Override + public String resolveAgentDeviceId() { + return current().resolveAgentDeviceId(); + } + + @Override + public List> query(String sql, List params) { + return current().query(sql, params); + } + + @Override + public int execute(String sql, List params) { + return current().execute(sql, params); + } + + @Override + public int pushRows(String table, String mode, List keys, List> rows) { + return current().pushRows(table, mode, keys, rows); + } + + @Override + public boolean testConnection() { + return current().testConnection(); + } +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/MesToMcsPushService.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/MesToMcsPushService.java new file mode 100644 index 00000000..d745f9a4 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/MesToMcsPushService.java @@ -0,0 +1,30 @@ +package org.jeecg.modules.xslmes.mcs.access; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * MES → MCS 经桌面代理下发(EQ_SYNC_PUSH)。 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class MesToMcsPushService { + + private final McsDataAccessFacade mcsDataAccessFacade; + + public int push(String table, String mode, List keys, List> rows) { + if (StringUtils.isBlank(table)) { + throw new IllegalArgumentException("table 不能为空"); + } + int affected = mcsDataAccessFacade.pushRows(table, mode, keys, rows); + log.info("MES→MCS 下发完成 table={} mode={} rows={} affected={}", + table, mode, rows == null ? 0 : rows.size(), affected); + return affected; + } +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/proxy/AgentProxyConnection.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/proxy/AgentProxyConnection.java new file mode 100644 index 00000000..5209628a --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/proxy/AgentProxyConnection.java @@ -0,0 +1,290 @@ +package org.jeecg.modules.xslmes.mcs.access.proxy; + +import org.jeecg.modules.xslmes.mcs.access.IMcsDataAccess; + +import java.sql.Array; +import java.sql.Blob; +import java.sql.CallableStatement; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.NClob; +import java.sql.PreparedStatement; +import java.sql.SQLClientInfoException; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.SQLXML; +import java.sql.Savepoint; +import java.sql.Statement; +import java.sql.Struct; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Executor; + +public class AgentProxyConnection implements Connection { + + private final IMcsDataAccess dataAccess; + private boolean closed; + + public AgentProxyConnection(IMcsDataAccess dataAccess) { + this.dataAccess = dataAccess; + } + + @Override + public Statement createStatement() { + return new AgentProxyStatement(dataAccess, null); + } + + @Override + public PreparedStatement prepareStatement(String sql) { + return new AgentProxyPreparedStatement(dataAccess, sql); + } + + @Override + public CallableStatement prepareCall(String sql) throws SQLException { + throw new SQLException("CallableStatement not supported by agent proxy"); + } + + @Override + public String nativeSQL(String sql) { + return sql; + } + + @Override + public void setAutoCommit(boolean autoCommit) { + } + + @Override + public boolean getAutoCommit() { + return true; + } + + @Override + public void commit() { + } + + @Override + public void rollback() { + } + + @Override + public void close() { + closed = true; + } + + @Override + public boolean isClosed() { + return closed; + } + + @Override + public DatabaseMetaData getMetaData() throws SQLException { + throw new SQLException("DatabaseMetaData not supported by agent proxy"); + } + + @Override + public void setReadOnly(boolean readOnly) { + } + + @Override + public boolean isReadOnly() { + return false; + } + + @Override + public void setCatalog(String catalog) { + } + + @Override + public String getCatalog() { + return null; + } + + @Override + public void setTransactionIsolation(int level) { + } + + @Override + public int getTransactionIsolation() { + return Connection.TRANSACTION_READ_COMMITTED; + } + + @Override + public SQLWarning getWarnings() { + return null; + } + + @Override + public void clearWarnings() { + } + + @Override + public Statement createStatement(int resultSetType, int resultSetConcurrency) { + return createStatement(); + } + + @Override + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) { + return prepareStatement(sql); + } + + @Override + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + throw new SQLException("CallableStatement not supported"); + } + + @Override + public Map> getTypeMap() { + return Map.of(); + } + + @Override + public void setTypeMap(Map> map) { + } + + @Override + public void setHoldability(int holdability) { + } + + @Override + public int getHoldability() { + return 0; + } + + @Override + public Savepoint setSavepoint() throws SQLException { + throw new SQLException("Savepoint not supported"); + } + + @Override + public Savepoint setSavepoint(String name) throws SQLException { + throw new SQLException("Savepoint not supported"); + } + + @Override + public void rollback(Savepoint savepoint) { + } + + @Override + public void releaseSavepoint(Savepoint savepoint) { + } + + @Override + public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) { + return createStatement(); + } + + @Override + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) { + return prepareStatement(sql); + } + + @Override + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + throw new SQLException("CallableStatement not supported"); + } + + @Override + public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) { + return prepareStatement(sql); + } + + @Override + public PreparedStatement prepareStatement(String sql, int[] columnIndexes) { + return prepareStatement(sql); + } + + @Override + public PreparedStatement prepareStatement(String sql, String[] columnNames) { + return prepareStatement(sql); + } + + @Override + public Clob createClob() throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public Blob createBlob() throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public NClob createNClob() throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public SQLXML createSQLXML() throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public boolean isValid(int timeout) { + return !closed; + } + + @Override + public void setClientInfo(String name, String value) throws SQLClientInfoException { + } + + @Override + public void setClientInfo(Properties properties) throws SQLClientInfoException { + } + + @Override + public String getClientInfo(String name) { + return null; + } + + @Override + public Properties getClientInfo() { + return new Properties(); + } + + @Override + public Array createArrayOf(String typeName, Object[] elements) throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public Struct createStruct(String typeName, Object[] attributes) throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public void setSchema(String schema) { + } + + @Override + public String getSchema() { + return null; + } + + @Override + public void abort(Executor executor) { + close(); + } + + @Override + public void setNetworkTimeout(Executor executor, int milliseconds) { + } + + @Override + public int getNetworkTimeout() { + return 0; + } + + @Override + public T unwrap(Class iface) throws SQLException { + if (iface.isInstance(this)) { + return iface.cast(this); + } + throw new SQLException("Not a wrapper"); + } + + @Override + public boolean isWrapperFor(Class iface) { + return iface.isInstance(this); + } +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/proxy/AgentProxyDataSource.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/proxy/AgentProxyDataSource.java new file mode 100644 index 00000000..2d711b5c --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/proxy/AgentProxyDataSource.java @@ -0,0 +1,68 @@ +package org.jeecg.modules.xslmes.mcs.access.proxy; + +import org.jeecg.modules.xslmes.mcs.access.IMcsDataAccess; + +import javax.sql.DataSource; +import java.io.PrintWriter; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.util.logging.Logger; + +/** + * 将 JDBC 调用转发到桌面设备代理的 DataSource,供 @DS("sqlserver_mcs") 复用。 + */ +public class AgentProxyDataSource implements DataSource { + + private final IMcsDataAccess dataAccess; + + public AgentProxyDataSource(IMcsDataAccess dataAccess) { + this.dataAccess = dataAccess; + } + + @Override + public Connection getConnection() { + return new AgentProxyConnection(dataAccess); + } + + @Override + public Connection getConnection(String username, String password) { + return getConnection(); + } + + @Override + public T unwrap(Class iface) throws SQLException { + if (iface.isInstance(this)) { + return iface.cast(this); + } + throw new SQLException("Not a wrapper for " + iface); + } + + @Override + public boolean isWrapperFor(Class iface) { + return iface.isInstance(this); + } + + @Override + public PrintWriter getLogWriter() { + return null; + } + + @Override + public void setLogWriter(PrintWriter out) { + } + + @Override + public void setLoginTimeout(int seconds) { + } + + @Override + public int getLoginTimeout() { + return 0; + } + + @Override + public Logger getParentLogger() throws SQLFeatureNotSupportedException { + throw new SQLFeatureNotSupportedException(); + } +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/proxy/AgentProxyPreparedStatement.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/proxy/AgentProxyPreparedStatement.java new file mode 100644 index 00000000..00429133 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/proxy/AgentProxyPreparedStatement.java @@ -0,0 +1,565 @@ +package org.jeecg.modules.xslmes.mcs.access.proxy; + +import org.jeecg.modules.xslmes.mcs.access.IMcsDataAccess; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.Date; +import java.sql.NClob; +import java.sql.ParameterMetaData; +import java.sql.PreparedStatement; +import java.sql.Ref; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.RowId; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.SQLXML; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; +import java.util.Map; + +public class AgentProxyPreparedStatement implements PreparedStatement { + + private final IMcsDataAccess dataAccess; + private final String sql; + private final List params = new ArrayList<>(); + private ResultSet currentRs; + private int updateCount = -1; + + public AgentProxyPreparedStatement(IMcsDataAccess dataAccess, String sql) { + this.dataAccess = dataAccess; + this.sql = sql; + ensureCapacity(16); + } + + private void ensureCapacity(int index1Based) { + while (params.size() < index1Based) { + params.add(null); + } + } + + private void setParam(int parameterIndex, Object value) { + ensureCapacity(parameterIndex); + params.set(parameterIndex - 1, value); + } + + @Override + public ResultSet executeQuery() throws SQLException { + try { + List> rows = dataAccess.query(sql, new ArrayList<>(params)); + currentRs = new MapListResultSet(rows); + updateCount = -1; + return currentRs; + } catch (Exception e) { + throw new SQLException(e.getMessage(), e); + } + } + + @Override + public int executeUpdate() throws SQLException { + try { + updateCount = dataAccess.execute(sql, new ArrayList<>(params)); + currentRs = null; + return updateCount; + } catch (Exception e) { + throw new SQLException(e.getMessage(), e); + } + } + + @Override + public boolean execute() throws SQLException { + String trimmed = sql == null ? "" : sql.trim(); + if (trimmed.regionMatches(true, 0, "SELECT", 0, 6)) { + executeQuery(); + return true; + } + executeUpdate(); + return false; + } + + @Override + public void setNull(int parameterIndex, int sqlType) { + setParam(parameterIndex, null); + } + + @Override + public void setBoolean(int parameterIndex, boolean x) { + setParam(parameterIndex, x); + } + + @Override + public void setByte(int parameterIndex, byte x) { + setParam(parameterIndex, x); + } + + @Override + public void setShort(int parameterIndex, short x) { + setParam(parameterIndex, x); + } + + @Override + public void setInt(int parameterIndex, int x) { + setParam(parameterIndex, x); + } + + @Override + public void setLong(int parameterIndex, long x) { + setParam(parameterIndex, x); + } + + @Override + public void setFloat(int parameterIndex, float x) { + setParam(parameterIndex, x); + } + + @Override + public void setDouble(int parameterIndex, double x) { + setParam(parameterIndex, x); + } + + @Override + public void setBigDecimal(int parameterIndex, BigDecimal x) { + setParam(parameterIndex, x); + } + + @Override + public void setString(int parameterIndex, String x) { + setParam(parameterIndex, x); + } + + @Override + public void setBytes(int parameterIndex, byte[] x) { + setParam(parameterIndex, x); + } + + @Override + public void setDate(int parameterIndex, Date x) { + setParam(parameterIndex, x); + } + + @Override + public void setTime(int parameterIndex, Time x) { + setParam(parameterIndex, x); + } + + @Override + public void setTimestamp(int parameterIndex, Timestamp x) { + setParam(parameterIndex, x); + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { + throw new SQLException("setAsciiStream not supported"); + } + + @Override + @Deprecated + public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public void clearParameters() { + params.clear(); + } + + @Override + public void setObject(int parameterIndex, Object x, int targetSqlType) { + setParam(parameterIndex, x); + } + + @Override + public void setObject(int parameterIndex, Object x) { + setParam(parameterIndex, x); + } + + @Override + public void addBatch() { + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public void setRef(int parameterIndex, Ref x) { + setParam(parameterIndex, x); + } + + @Override + public void setBlob(int parameterIndex, Blob x) { + setParam(parameterIndex, x); + } + + @Override + public void setClob(int parameterIndex, Clob x) { + setParam(parameterIndex, x); + } + + @Override + public void setArray(int parameterIndex, Array x) { + setParam(parameterIndex, x); + } + + @Override + public ResultSetMetaData getMetaData() { + return currentRs == null ? null : safeMeta(); + } + + private ResultSetMetaData safeMeta() { + try { + return currentRs.getMetaData(); + } catch (SQLException e) { + return null; + } + } + + @Override + public void setDate(int parameterIndex, Date x, Calendar cal) { + setParam(parameterIndex, x); + } + + @Override + public void setTime(int parameterIndex, Time x, Calendar cal) { + setParam(parameterIndex, x); + } + + @Override + public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) { + setParam(parameterIndex, x); + } + + @Override + public void setNull(int parameterIndex, int sqlType, String typeName) { + setParam(parameterIndex, null); + } + + @Override + public void setURL(int parameterIndex, URL x) { + setParam(parameterIndex, x); + } + + @Override + public ParameterMetaData getParameterMetaData() throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public void setRowId(int parameterIndex, RowId x) { + setParam(parameterIndex, x); + } + + @Override + public void setNString(int parameterIndex, String value) { + setParam(parameterIndex, value); + } + + @Override + public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public void setNClob(int parameterIndex, NClob value) { + setParam(parameterIndex, value); + } + + @Override + public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public void setSQLXML(int parameterIndex, SQLXML xmlObject) { + setParam(parameterIndex, xmlObject); + } + + @Override + public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) { + setParam(parameterIndex, x); + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public void setClob(int parameterIndex, Reader reader) throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public void setNClob(int parameterIndex, Reader reader) throws SQLException { + throw new SQLException("not supported"); + } + + // Statement methods + @Override + public ResultSet executeQuery(String sql) throws SQLException { + throw new SQLException("use executeQuery()"); + } + + @Override + public int executeUpdate(String sql) throws SQLException { + throw new SQLException("use executeUpdate()"); + } + + @Override + public void close() { + currentRs = null; + } + + @Override + public int getMaxFieldSize() { + return 0; + } + + @Override + public void setMaxFieldSize(int max) { + } + + @Override + public int getMaxRows() { + return 0; + } + + @Override + public void setMaxRows(int max) { + } + + @Override + public void setEscapeProcessing(boolean enable) { + } + + @Override + public int getQueryTimeout() { + return 0; + } + + @Override + public void setQueryTimeout(int seconds) { + } + + @Override + public void cancel() { + } + + @Override + public SQLWarning getWarnings() { + return null; + } + + @Override + public void clearWarnings() { + } + + @Override + public void setCursorName(String name) { + } + + @Override + public boolean execute(String sql) throws SQLException { + throw new SQLException("use execute()"); + } + + @Override + public ResultSet getResultSet() { + return currentRs; + } + + @Override + public int getUpdateCount() { + return updateCount; + } + + @Override + public boolean getMoreResults() { + return false; + } + + @Override + public void setFetchDirection(int direction) { + } + + @Override + public int getFetchDirection() { + return ResultSet.FETCH_FORWARD; + } + + @Override + public void setFetchSize(int rows) { + } + + @Override + public int getFetchSize() { + return 0; + } + + @Override + public int getResultSetConcurrency() { + return ResultSet.CONCUR_READ_ONLY; + } + + @Override + public int getResultSetType() { + return ResultSet.TYPE_FORWARD_ONLY; + } + + @Override + public void addBatch(String sql) { + } + + @Override + public void clearBatch() { + } + + @Override + public int[] executeBatch() { + return new int[0]; + } + + @Override + public Connection getConnection() { + return null; + } + + @Override + public boolean getMoreResults(int current) { + return false; + } + + @Override + public ResultSet getGeneratedKeys() { + return null; + } + + @Override + public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public int executeUpdate(String sql, String[] columnNames) throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public boolean execute(String sql, int[] columnIndexes) throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public boolean execute(String sql, String[] columnNames) throws SQLException { + throw new SQLException("not supported"); + } + + @Override + public int getResultSetHoldability() { + return 0; + } + + @Override + public boolean isClosed() { + return false; + } + + @Override + public void setPoolable(boolean poolable) { + } + + @Override + public boolean isPoolable() { + return false; + } + + @Override + public void closeOnCompletion() { + } + + @Override + public boolean isCloseOnCompletion() { + return false; + } + + @Override + public T unwrap(Class iface) throws SQLException { + throw new SQLException("not a wrapper"); + } + + @Override + public boolean isWrapperFor(Class iface) { + return false; + } +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/proxy/AgentProxyStatement.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/proxy/AgentProxyStatement.java new file mode 100644 index 00000000..03995183 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/proxy/AgentProxyStatement.java @@ -0,0 +1,249 @@ +package org.jeecg.modules.xslmes.mcs.access.proxy; + +import org.jeecg.modules.xslmes.mcs.access.IMcsDataAccess; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class AgentProxyStatement implements Statement { + + private final IMcsDataAccess dataAccess; + private ResultSet currentRs; + private int updateCount = -1; + + public AgentProxyStatement(IMcsDataAccess dataAccess, String ignored) { + this.dataAccess = dataAccess; + } + + @Override + public ResultSet executeQuery(String sql) throws SQLException { + try { + List> rows = dataAccess.query(sql, new ArrayList<>()); + currentRs = new MapListResultSet(rows); + updateCount = -1; + return currentRs; + } catch (Exception e) { + throw new SQLException(e.getMessage(), e); + } + } + + @Override + public int executeUpdate(String sql) throws SQLException { + try { + updateCount = dataAccess.execute(sql, new ArrayList<>()); + currentRs = null; + return updateCount; + } catch (Exception e) { + throw new SQLException(e.getMessage(), e); + } + } + + @Override + public void close() { + currentRs = null; + } + + @Override + public int getMaxFieldSize() { + return 0; + } + + @Override + public void setMaxFieldSize(int max) { + } + + @Override + public int getMaxRows() { + return 0; + } + + @Override + public void setMaxRows(int max) { + } + + @Override + public void setEscapeProcessing(boolean enable) { + } + + @Override + public int getQueryTimeout() { + return 0; + } + + @Override + public void setQueryTimeout(int seconds) { + } + + @Override + public void cancel() { + } + + @Override + public SQLWarning getWarnings() { + return null; + } + + @Override + public void clearWarnings() { + } + + @Override + public void setCursorName(String name) { + } + + @Override + public boolean execute(String sql) throws SQLException { + String trimmed = sql == null ? "" : sql.trim(); + if (trimmed.regionMatches(true, 0, "SELECT", 0, 6)) { + executeQuery(sql); + return true; + } + executeUpdate(sql); + return false; + } + + @Override + public ResultSet getResultSet() { + return currentRs; + } + + @Override + public int getUpdateCount() { + return updateCount; + } + + @Override + public boolean getMoreResults() { + return false; + } + + @Override + public void setFetchDirection(int direction) { + } + + @Override + public int getFetchDirection() { + return ResultSet.FETCH_FORWARD; + } + + @Override + public void setFetchSize(int rows) { + } + + @Override + public int getFetchSize() { + return 0; + } + + @Override + public int getResultSetConcurrency() { + return ResultSet.CONCUR_READ_ONLY; + } + + @Override + public int getResultSetType() { + return ResultSet.TYPE_FORWARD_ONLY; + } + + @Override + public void addBatch(String sql) { + } + + @Override + public void clearBatch() { + } + + @Override + public int[] executeBatch() { + return new int[0]; + } + + @Override + public Connection getConnection() { + return null; + } + + @Override + public boolean getMoreResults(int current) { + return false; + } + + @Override + public ResultSet getGeneratedKeys() { + return null; + } + + @Override + public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + return executeUpdate(sql); + } + + @Override + public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { + return executeUpdate(sql); + } + + @Override + public int executeUpdate(String sql, String[] columnNames) throws SQLException { + return executeUpdate(sql); + } + + @Override + public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { + return execute(sql); + } + + @Override + public boolean execute(String sql, int[] columnIndexes) throws SQLException { + return execute(sql); + } + + @Override + public boolean execute(String sql, String[] columnNames) throws SQLException { + return execute(sql); + } + + @Override + public int getResultSetHoldability() { + return 0; + } + + @Override + public boolean isClosed() { + return false; + } + + @Override + public void setPoolable(boolean poolable) { + } + + @Override + public boolean isPoolable() { + return false; + } + + @Override + public void closeOnCompletion() { + } + + @Override + public boolean isCloseOnCompletion() { + return false; + } + + @Override + public T unwrap(Class iface) throws SQLException { + throw new SQLException("not a wrapper"); + } + + @Override + public boolean isWrapperFor(Class iface) { + return false; + } +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/proxy/MapListResultSet.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/proxy/MapListResultSet.java new file mode 100644 index 00000000..942dd49c --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/access/proxy/MapListResultSet.java @@ -0,0 +1,1240 @@ +package org.jeecg.modules.xslmes.mcs.access.proxy; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Date; +import java.sql.NClob; +import java.sql.Ref; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.RowId; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.SQLXML; +import java.sql.Statement; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Types; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; +import java.util.Map; + +/** + * 将 List<Map> 包装为只读 ResultSet,供 MyBatis 消费。 + */ +public class MapListResultSet implements ResultSet { + + private final List> rows; + private final List columns; + private int cursor = -1; + private boolean closed; + + public MapListResultSet(List> rows) { + this.rows = rows == null ? List.of() : rows; + this.columns = new ArrayList<>(); + if (!this.rows.isEmpty()) { + columns.addAll(this.rows.get(0).keySet()); + } + } + + private Map current() throws SQLException { + if (cursor < 0 || cursor >= rows.size()) { + throw new SQLException("cursor out of range"); + } + return rows.get(cursor); + } + + private Object byIndex(int columnIndex) throws SQLException { + if (columnIndex < 1 || columnIndex > columns.size()) { + throw new SQLException("invalid column index " + columnIndex); + } + String name = columns.get(columnIndex - 1); + return getIgnoreCase(current(), name); + } + + private Object getIgnoreCase(Map row, String label) { + if (row.containsKey(label)) { + return row.get(label); + } + for (Map.Entry e : row.entrySet()) { + if (e.getKey() != null && e.getKey().equalsIgnoreCase(label)) { + return e.getValue(); + } + } + return null; + } + + @Override + public boolean next() { + if (cursor + 1 >= rows.size()) { + cursor = rows.size(); + return false; + } + cursor++; + return true; + } + + @Override + public void close() { + closed = true; + } + + @Override + public boolean wasNull() { + return false; + } + + @Override + public String getString(int columnIndex) throws SQLException { + Object v = byIndex(columnIndex); + return v == null ? null : String.valueOf(v); + } + + @Override + public boolean getBoolean(int columnIndex) throws SQLException { + Object v = byIndex(columnIndex); + if (v == null) { + return false; + } + if (v instanceof Boolean) { + return (Boolean) v; + } + return Boolean.parseBoolean(String.valueOf(v)); + } + + @Override + public byte getByte(int columnIndex) throws SQLException { + return (byte) getInt(columnIndex); + } + + @Override + public short getShort(int columnIndex) throws SQLException { + return (short) getInt(columnIndex); + } + + @Override + public int getInt(int columnIndex) throws SQLException { + Object v = byIndex(columnIndex); + if (v == null) { + return 0; + } + if (v instanceof Number) { + return ((Number) v).intValue(); + } + return Integer.parseInt(String.valueOf(v)); + } + + @Override + public long getLong(int columnIndex) throws SQLException { + Object v = byIndex(columnIndex); + if (v == null) { + return 0L; + } + if (v instanceof Number) { + return ((Number) v).longValue(); + } + return Long.parseLong(String.valueOf(v)); + } + + @Override + public float getFloat(int columnIndex) throws SQLException { + Object v = byIndex(columnIndex); + if (v == null) { + return 0f; + } + if (v instanceof Number) { + return ((Number) v).floatValue(); + } + return Float.parseFloat(String.valueOf(v)); + } + + @Override + public double getDouble(int columnIndex) throws SQLException { + Object v = byIndex(columnIndex); + if (v == null) { + return 0d; + } + if (v instanceof Number) { + return ((Number) v).doubleValue(); + } + return Double.parseDouble(String.valueOf(v)); + } + + @Override + @Deprecated + public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException { + return getBigDecimal(columnIndex); + } + + @Override + public byte[] getBytes(int columnIndex) throws SQLException { + Object v = byIndex(columnIndex); + return v instanceof byte[] ? (byte[]) v : null; + } + + @Override + public Date getDate(int columnIndex) throws SQLException { + Object v = byIndex(columnIndex); + if (v == null) { + return null; + } + if (v instanceof Date) { + return (Date) v; + } + if (v instanceof java.util.Date) { + return new Date(((java.util.Date) v).getTime()); + } + return Date.valueOf(String.valueOf(v).substring(0, 10)); + } + + @Override + public Time getTime(int columnIndex) throws SQLException { + Object v = byIndex(columnIndex); + return v == null ? null : Time.valueOf(String.valueOf(v)); + } + + @Override + public Timestamp getTimestamp(int columnIndex) throws SQLException { + Object v = byIndex(columnIndex); + if (v == null) { + return null; + } + if (v instanceof Timestamp) { + return (Timestamp) v; + } + if (v instanceof java.util.Date) { + return new Timestamp(((java.util.Date) v).getTime()); + } + return Timestamp.valueOf(String.valueOf(v)); + } + + @Override + public InputStream getAsciiStream(int columnIndex) { + return null; + } + + @Override + @Deprecated + public InputStream getUnicodeStream(int columnIndex) { + return null; + } + + @Override + public InputStream getBinaryStream(int columnIndex) { + return null; + } + + @Override + public String getString(String columnLabel) throws SQLException { + Object v = getIgnoreCase(current(), columnLabel); + return v == null ? null : String.valueOf(v); + } + + @Override + public boolean getBoolean(String columnLabel) throws SQLException { + return getBoolean(findColumn(columnLabel)); + } + + @Override + public byte getByte(String columnLabel) throws SQLException { + return getByte(findColumn(columnLabel)); + } + + @Override + public short getShort(String columnLabel) throws SQLException { + return getShort(findColumn(columnLabel)); + } + + @Override + public int getInt(String columnLabel) throws SQLException { + return getInt(findColumn(columnLabel)); + } + + @Override + public long getLong(String columnLabel) throws SQLException { + return getLong(findColumn(columnLabel)); + } + + @Override + public float getFloat(String columnLabel) throws SQLException { + return getFloat(findColumn(columnLabel)); + } + + @Override + public double getDouble(String columnLabel) throws SQLException { + return getDouble(findColumn(columnLabel)); + } + + @Override + @Deprecated + public BigDecimal getBigDecimal(String columnLabel, int scale) throws SQLException { + return getBigDecimal(columnLabel); + } + + @Override + public byte[] getBytes(String columnLabel) throws SQLException { + return getBytes(findColumn(columnLabel)); + } + + @Override + public Date getDate(String columnLabel) throws SQLException { + return getDate(findColumn(columnLabel)); + } + + @Override + public Time getTime(String columnLabel) throws SQLException { + return getTime(findColumn(columnLabel)); + } + + @Override + public Timestamp getTimestamp(String columnLabel) throws SQLException { + return getTimestamp(findColumn(columnLabel)); + } + + @Override + public InputStream getAsciiStream(String columnLabel) { + return null; + } + + @Override + @Deprecated + public InputStream getUnicodeStream(String columnLabel) { + return null; + } + + @Override + public InputStream getBinaryStream(String columnLabel) { + return null; + } + + @Override + public SQLWarning getWarnings() { + return null; + } + + @Override + public void clearWarnings() { + } + + @Override + public String getCursorName() { + return null; + } + + @Override + public ResultSetMetaData getMetaData() { + return new ResultSetMetaData() { + @Override + public int getColumnCount() { + return columns.size(); + } + + @Override + public boolean isAutoIncrement(int column) { + return false; + } + + @Override + public boolean isCaseSensitive(int column) { + return false; + } + + @Override + public boolean isSearchable(int column) { + return true; + } + + @Override + public boolean isCurrency(int column) { + return false; + } + + @Override + public int isNullable(int column) { + return columnNullable; + } + + @Override + public boolean isSigned(int column) { + return true; + } + + @Override + public int getColumnDisplaySize(int column) { + return 64; + } + + @Override + public String getColumnLabel(int column) { + return columns.get(column - 1); + } + + @Override + public String getColumnName(int column) { + return columns.get(column - 1); + } + + @Override + public String getSchemaName(int column) { + return ""; + } + + @Override + public int getPrecision(int column) { + return 0; + } + + @Override + public int getScale(int column) { + return 0; + } + + @Override + public String getTableName(int column) { + return ""; + } + + @Override + public String getCatalogName(int column) { + return ""; + } + + @Override + public int getColumnType(int column) { + return Types.VARCHAR; + } + + @Override + public String getColumnTypeName(int column) { + return "VARCHAR"; + } + + @Override + public boolean isReadOnly(int column) { + return true; + } + + @Override + public boolean isWritable(int column) { + return false; + } + + @Override + public boolean isDefinitelyWritable(int column) { + return false; + } + + @Override + public String getColumnClassName(int column) { + return String.class.getName(); + } + + @Override + public T unwrap(Class iface) throws SQLException { + throw new SQLException("not a wrapper"); + } + + @Override + public boolean isWrapperFor(Class iface) { + return false; + } + }; + } + + @Override + public Object getObject(int columnIndex) throws SQLException { + return byIndex(columnIndex); + } + + @Override + public Object getObject(String columnLabel) throws SQLException { + return getIgnoreCase(current(), columnLabel); + } + + @Override + public int findColumn(String columnLabel) throws SQLException { + for (int i = 0; i < columns.size(); i++) { + if (columns.get(i).equalsIgnoreCase(columnLabel)) { + return i + 1; + } + } + // dynamic: add missing label from current row + Object v = getIgnoreCase(current(), columnLabel); + if (v != null || current().keySet().stream().anyMatch(k -> k.equalsIgnoreCase(columnLabel))) { + columns.add(columnLabel); + return columns.size(); + } + throw new SQLException("column not found: " + columnLabel); + } + + // Remaining methods: delegate minimally / unsupported + @Override + public Reader getCharacterStream(int columnIndex) { + return null; + } + + @Override + public Reader getCharacterStream(String columnLabel) { + return null; + } + + @Override + public BigDecimal getBigDecimal(int columnIndex) throws SQLException { + Object v = byIndex(columnIndex); + if (v == null) { + return null; + } + if (v instanceof BigDecimal) { + return (BigDecimal) v; + } + return new BigDecimal(String.valueOf(v)); + } + + @Override + public BigDecimal getBigDecimal(String columnLabel) throws SQLException { + return getBigDecimal(findColumn(columnLabel)); + } + + @Override + public boolean isBeforeFirst() { + return cursor < 0; + } + + @Override + public boolean isAfterLast() { + return cursor >= rows.size(); + } + + @Override + public boolean isFirst() { + return cursor == 0; + } + + @Override + public boolean isLast() { + return cursor == rows.size() - 1; + } + + @Override + public void beforeFirst() { + cursor = -1; + } + + @Override + public void afterLast() { + cursor = rows.size(); + } + + @Override + public boolean first() { + if (rows.isEmpty()) { + return false; + } + cursor = 0; + return true; + } + + @Override + public boolean last() { + if (rows.isEmpty()) { + return false; + } + cursor = rows.size() - 1; + return true; + } + + @Override + public int getRow() { + return cursor + 1; + } + + @Override + public boolean absolute(int row) { + if (row < 1 || row > rows.size()) { + return false; + } + cursor = row - 1; + return true; + } + + @Override + public boolean relative(int rows) { + return absolute(cursor + 1 + rows); + } + + @Override + public boolean previous() { + if (cursor <= 0) { + cursor = -1; + return false; + } + cursor--; + return true; + } + + @Override + public void setFetchDirection(int direction) { + } + + @Override + public int getFetchDirection() { + return FETCH_FORWARD; + } + + @Override + public void setFetchSize(int rows) { + } + + @Override + public int getFetchSize() { + return 0; + } + + @Override + public int getType() { + return TYPE_FORWARD_ONLY; + } + + @Override + public int getConcurrency() { + return CONCUR_READ_ONLY; + } + + @Override + public boolean rowUpdated() { + return false; + } + + @Override + public boolean rowInserted() { + return false; + } + + @Override + public boolean rowDeleted() { + return false; + } + + @Override + public void updateNull(int columnIndex) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateBoolean(int columnIndex, boolean x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateByte(int columnIndex, byte x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateShort(int columnIndex, short x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateInt(int columnIndex, int x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateLong(int columnIndex, long x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateFloat(int columnIndex, float x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateDouble(int columnIndex, double x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateString(int columnIndex, String x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateBytes(int columnIndex, byte[] x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateDate(int columnIndex, Date x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateTime(int columnIndex, Time x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateAsciiStream(int columnIndex, InputStream x, int length) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateBinaryStream(int columnIndex, InputStream x, int length) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateCharacterStream(int columnIndex, Reader x, int length) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateObject(int columnIndex, Object x, int scaleOrLength) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateObject(int columnIndex, Object x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateNull(String columnLabel) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateBoolean(String columnLabel, boolean x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateByte(String columnLabel, byte x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateShort(String columnLabel, short x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateInt(String columnLabel, int x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateLong(String columnLabel, long x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateFloat(String columnLabel, float x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateDouble(String columnLabel, double x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateBigDecimal(String columnLabel, BigDecimal x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateString(String columnLabel, String x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateBytes(String columnLabel, byte[] x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateDate(String columnLabel, Date x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateTime(String columnLabel, Time x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateTimestamp(String columnLabel, Timestamp x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateAsciiStream(String columnLabel, InputStream x, int length) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateBinaryStream(String columnLabel, InputStream x, int length) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateCharacterStream(String columnLabel, Reader reader, int length) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateObject(String columnLabel, Object x, int scaleOrLength) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateObject(String columnLabel, Object x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void insertRow() throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateRow() throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void deleteRow() throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void refreshRow() { + } + + @Override + public void cancelRowUpdates() { + } + + @Override + public void moveToInsertRow() throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void moveToCurrentRow() { + } + + @Override + public Statement getStatement() { + return null; + } + + @Override + public Object getObject(int columnIndex, Map> map) throws SQLException { + return getObject(columnIndex); + } + + @Override + public Ref getRef(int columnIndex) { + return null; + } + + @Override + public Blob getBlob(int columnIndex) { + return null; + } + + @Override + public Clob getClob(int columnIndex) { + return null; + } + + @Override + public Array getArray(int columnIndex) { + return null; + } + + @Override + public Object getObject(String columnLabel, Map> map) throws SQLException { + return getObject(columnLabel); + } + + @Override + public Ref getRef(String columnLabel) { + return null; + } + + @Override + public Blob getBlob(String columnLabel) { + return null; + } + + @Override + public Clob getClob(String columnLabel) { + return null; + } + + @Override + public Array getArray(String columnLabel) { + return null; + } + + @Override + public Date getDate(int columnIndex, Calendar cal) throws SQLException { + return getDate(columnIndex); + } + + @Override + public Date getDate(String columnLabel, Calendar cal) throws SQLException { + return getDate(columnLabel); + } + + @Override + public Time getTime(int columnIndex, Calendar cal) throws SQLException { + return getTime(columnIndex); + } + + @Override + public Time getTime(String columnLabel, Calendar cal) throws SQLException { + return getTime(columnLabel); + } + + @Override + public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException { + return getTimestamp(columnIndex); + } + + @Override + public Timestamp getTimestamp(String columnLabel, Calendar cal) throws SQLException { + return getTimestamp(columnLabel); + } + + @Override + public URL getURL(int columnIndex) { + return null; + } + + @Override + public URL getURL(String columnLabel) { + return null; + } + + @Override + public void updateRef(int columnIndex, Ref x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateRef(String columnLabel, Ref x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateBlob(int columnIndex, Blob x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateBlob(String columnLabel, Blob x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateClob(int columnIndex, Clob x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateClob(String columnLabel, Clob x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateArray(int columnIndex, Array x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateArray(String columnLabel, Array x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public RowId getRowId(int columnIndex) { + return null; + } + + @Override + public RowId getRowId(String columnLabel) { + return null; + } + + @Override + public void updateRowId(int columnIndex, RowId x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateRowId(String columnLabel, RowId x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public int getHoldability() { + return HOLD_CURSORS_OVER_COMMIT; + } + + @Override + public boolean isClosed() { + return closed; + } + + @Override + public void updateNString(int columnIndex, String nString) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateNString(String columnLabel, String nString) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateNClob(int columnIndex, NClob nClob) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateNClob(String columnLabel, NClob nClob) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public NClob getNClob(int columnIndex) { + return null; + } + + @Override + public NClob getNClob(String columnLabel) { + return null; + } + + @Override + public SQLXML getSQLXML(int columnIndex) { + return null; + } + + @Override + public SQLXML getSQLXML(String columnLabel) { + return null; + } + + @Override + public void updateSQLXML(int columnIndex, SQLXML xmlObject) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateSQLXML(String columnLabel, SQLXML xmlObject) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public String getNString(int columnIndex) throws SQLException { + return getString(columnIndex); + } + + @Override + public String getNString(String columnLabel) throws SQLException { + return getString(columnLabel); + } + + @Override + public Reader getNCharacterStream(int columnIndex) { + return null; + } + + @Override + public Reader getNCharacterStream(String columnLabel) { + return null; + } + + @Override + public void updateNCharacterStream(int columnIndex, Reader x, long length) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateNCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateAsciiStream(int columnIndex, InputStream x, long length) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateBinaryStream(int columnIndex, InputStream x, long length) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateCharacterStream(int columnIndex, Reader x, long length) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateAsciiStream(String columnLabel, InputStream x, long length) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateBinaryStream(String columnLabel, InputStream x, long length) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateBlob(int columnIndex, InputStream inputStream, long length) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateBlob(String columnLabel, InputStream inputStream, long length) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateClob(int columnIndex, Reader reader, long length) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateClob(String columnLabel, Reader reader, long length) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateNClob(int columnIndex, Reader reader, long length) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateNClob(String columnLabel, Reader reader, long length) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateNCharacterStream(int columnIndex, Reader x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateNCharacterStream(String columnLabel, Reader reader) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateAsciiStream(int columnIndex, InputStream x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateBinaryStream(int columnIndex, InputStream x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateCharacterStream(int columnIndex, Reader x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateAsciiStream(String columnLabel, InputStream x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateBinaryStream(String columnLabel, InputStream x) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateCharacterStream(String columnLabel, Reader reader) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateBlob(int columnIndex, InputStream inputStream) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateBlob(String columnLabel, InputStream inputStream) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateClob(int columnIndex, Reader reader) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateClob(String columnLabel, Reader reader) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateNClob(int columnIndex, Reader reader) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public void updateNClob(String columnLabel, Reader reader) throws SQLException { + throw new SQLException("read-only"); + } + + @Override + public T getObject(int columnIndex, Class type) throws SQLException { + return type.cast(getObject(columnIndex)); + } + + @Override + public T getObject(String columnLabel, Class type) throws SQLException { + return type.cast(getObject(columnLabel)); + } + + @Override + public T unwrap(Class iface) throws SQLException { + throw new SQLException("not a wrapper"); + } + + @Override + public boolean isWrapperFor(Class iface) { + return false; + } +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/controller/McsToMesActMillController.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/controller/McsToMesActMillController.java new file mode 100644 index 00000000..708434fa --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/controller/McsToMesActMillController.java @@ -0,0 +1,59 @@ +package org.jeecg.modules.xslmes.mcs.controller; + +import jakarta.servlet.http.HttpServletRequest; +import org.jeecg.common.api.vo.Result; +import org.jeecg.modules.xslmes.mcs.util.McsQueryHelper; +import org.jeecg.modules.xslmes.mcs.entity.McsToMesActMill; +import org.jeecg.modules.xslmes.mcs.service.IMcsToMesActMillService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.extern.slf4j.Slf4j; +import org.jeecg.common.system.base.controller.JeecgController; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.ModelAndView; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import org.apache.shiro.authz.annotation.RequiresPermissions; + +/** + * 开炼机动作(中间库 MCSToMES_Act_Mill) + */ +@Tag(name = "开炼机动作(中间库)") +@RestController +@RequestMapping("/xslmes/mcs/mcsToMesActMill") +@Slf4j +public class McsToMesActMillController extends JeecgController { + + @Autowired + private IMcsToMesActMillService mcsToMesActMillService; + + @Operation(summary = "开炼机动作(中间库)-分页列表查询") + @GetMapping(value = "/list") + public Result> queryPageList(McsToMesActMill mcsToMesActMill, + @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo, + @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize, + HttpServletRequest req) { + QueryWrapper queryWrapper = McsQueryHelper.buildWrapper(mcsToMesActMill, req.getParameterMap()); + Page page = new Page<>(pageNo, pageSize); + IPage pageList = mcsToMesActMillService.page(page, queryWrapper); + return Result.OK(pageList); + } + + @Operation(summary = "开炼机动作(中间库)-通过id查询") + @GetMapping(value = "/queryById") + public Result queryById(@RequestParam(name = "id", required = true) String id) { + McsToMesActMill entity = mcsToMesActMillService.getById(id); + if (entity == null) { + return Result.error("未找到对应数据"); + } + return Result.OK(entity); + } + + @RequiresPermissions("xslmes:mcsToMesActMill:exportXls") + @RequestMapping(value = "/exportXls") + public ModelAndView exportXls(HttpServletRequest request, McsToMesActMill mcsToMesActMill) { + return super.exportXls(request, mcsToMesActMill, McsToMesActMill.class, "开炼机动作(中间库)"); + } +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/controller/MesToMcsPushController.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/controller/MesToMcsPushController.java new file mode 100644 index 00000000..89c858cf --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/controller/MesToMcsPushController.java @@ -0,0 +1,41 @@ +package org.jeecg.modules.xslmes.mcs.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.jeecg.common.api.vo.Result; +import org.jeecg.modules.xslmes.mcs.access.MesToMcsPushService; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * MES → MCS 经桌面代理下发。 + */ +@Tag(name = "MES上辅机下发") +@RestController +@RequestMapping("/xslmes/mcs/push") +@RequiredArgsConstructor +public class MesToMcsPushController { + + private final MesToMcsPushService mesToMcsPushService; + + @Operation(summary = "经桌面代理写入中间表") + @PostMapping("/rows") + @SuppressWarnings("unchecked") + public Result> pushRows(@RequestBody Map body) { + String table = body == null ? null : String.valueOf(body.getOrDefault("table", "")); + String mode = body == null ? "insert" : String.valueOf(body.getOrDefault("mode", "insert")); + List keys = body == null ? null : (List) body.get("keys"); + List> rows = body == null ? null : (List>) body.get("rows"); + int affected = mesToMcsPushService.push(table, mode, keys, rows); + Map data = new HashMap<>(); + data.put("affected", affected); + return Result.OK(data); + } +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/controller/MesXslMcsDbConfigController.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/controller/MesXslMcsDbConfigController.java index 7bbb9188..ebc0944d 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/controller/MesXslMcsDbConfigController.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/controller/MesXslMcsDbConfigController.java @@ -29,6 +29,9 @@ public class MesXslMcsDbConfigController { @Autowired private McsDataSourceManager mcsDataSourceManager; + @Autowired + private org.jeecg.modules.device.sync.agent.DeviceAgentSessionRegistry agentSessionRegistry; + private Integer resolveTenantId(Integer tenantId) { if (MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL) { if (tenantId == null) { @@ -75,11 +78,21 @@ public class MesXslMcsDbConfigController { data.put("dbConfigActive", mcsDataSourceManager.isDbConfigActive()); data.put("readEnabled", mcsDataSourceManager.isReadEnabled()); data.put("writeEnabled", mcsDataSourceManager.isWriteEnabled()); + boolean agentMode = mcsDataSourceManager.isDesktopAgentMode(); + data.put("accessMode", agentMode ? "DESKTOP_AGENT" : "DIRECT"); + data.put("agentMode", agentMode); if (cfg != null) { data.put("serverHost", cfg.getServerHost()); data.put("serverPort", cfg.getServerPort()); data.put("dbName", cfg.getDbName()); + data.put("agentDeviceId", cfg.getAgentDeviceId()); } + var agent = agentSessionRegistry.pickOnlineAgent(cfg == null ? null : cfg.getAgentDeviceId()); + data.put("agentOnline", agent != null && agent.isOnline()); + data.put("agentDbConnected", agent != null && agent.isDbConnected()); + data.put("agentHostName", agent == null ? null : agent.getHostName()); + data.put("agentDbMessage", agent == null ? null : agent.getDbMessage()); + data.put("agentLastSeenAt", agent == null ? null : agent.getLastSeenAt()); return Result.OK(data); } } diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/datasource/McsDataSourceManager.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/datasource/McsDataSourceManager.java index 40e1b68a..85a15a46 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/datasource/McsDataSourceManager.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/datasource/McsDataSourceManager.java @@ -9,7 +9,11 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; import org.jeecg.common.util.dynamic.db.DataSourceCachePool; import org.jeecg.modules.system.util.SecurityUtil; +import org.jeecg.modules.xslmes.mcs.access.DesktopAgentMcsDataAccess; +import org.jeecg.modules.xslmes.mcs.access.McsDataAccessFacade; +import org.jeecg.modules.xslmes.mcs.access.proxy.AgentProxyDataSource; import org.jeecg.modules.xslmes.mcs.entity.MesXslMcsDbConfig; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @@ -37,6 +41,9 @@ public class McsDataSourceManager { @Autowired private DruidDataSourceCreator dataSourceCreator; + @Autowired + private ObjectProvider desktopAgentMcsDataAccessProvider; + @Value("${spring.datasource.dynamic.datasource.sqlserver_mcs.url:}") private String ymlUrl; @@ -98,6 +105,17 @@ public class McsDataSourceManager { } if (Integer.valueOf(1).equals(config.getStatus())) { try { + if (isDesktopAgentMode(config)) { + DesktopAgentMcsDataAccess agent = desktopAgentMcsDataAccessProvider.getIfAvailable(); + if (agent == null) { + throw new IllegalStateException("桌面代理访问组件未就绪"); + } + routing.addDataSource(DS_KEY, new AgentProxyDataSource(agent)); + cachedConfig.set(config); + log.info("[MCS中间库] 已切换为桌面代理模式 sqlserver_mcs -> AgentProxy, deviceId={}", + config.getAgentDeviceId()); + return; + } String plainPassword = decryptPassword(config.getDbPassword()); DataSourceProperty property = buildDataSourceProperty(config, plainPassword); DataSource mcsDs = dataSourceCreator.createDataSource(property); @@ -117,6 +135,21 @@ public class McsDataSourceManager { log.info("[MCS中间库] 连接未启用,读写开关仍按页面配置生效"); } + public boolean isDesktopAgentMode(MesXslMcsDbConfig config) { + MesXslMcsDbConfig cfg = config != null ? config : cachedConfig.get(); + if (cfg == null) { + return true; + } + String mode = StringUtils.isBlank(cfg.getAccessMode()) + ? McsDataAccessFacade.MODE_DESKTOP_AGENT + : cfg.getAccessMode().trim().toUpperCase(); + return McsDataAccessFacade.MODE_DESKTOP_AGENT.equals(mode); + } + + public boolean isDesktopAgentMode() { + return isDesktopAgentMode(null); + } + private void closeAndRemoveDataSource(DynamicRoutingDataSource routing) { if (!routing.getDataSources().containsKey(DS_KEY)) { return; diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/entity/McsToMesActMill.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/entity/McsToMesActMill.java new file mode 100644 index 00000000..91461a97 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/entity/McsToMesActMill.java @@ -0,0 +1,76 @@ +package org.jeecg.modules.xslmes.mcs.entity; + +import java.io.Serializable; +import java.util.Date; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import com.fasterxml.jackson.annotation.JsonFormat; +import org.springframework.format.annotation.DateTimeFormat; +import org.jeecgframework.poi.excel.annotation.Excel; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * 开炼机动作(中间库 MCSToMES_Act_Mill) + */ +@Data +@TableName("MCSToMES_Act_Mill") +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@Schema(description = "开炼机动作(中间库)") +public class McsToMesActMill implements Serializable { + private static final long serialVersionUID = 1L; + + @TableId(value = "GUID", type = IdType.INPUT) + @Schema(description = "GUID") + @Excel(name = "GUID", width = 36) + private String guid; + + @Excel(name = "动作地址", width = 10) + @Schema(description = "动作地址") + @TableField("act_addr") + private Integer actAddr; + + @Excel(name = "动作名称", width = 20) + @Schema(description = "动作名称") + @TableField("act_name") + private String actName; + + @Excel(name = "动作名称(英)", width = 20) + @Schema(description = "动作名称(英)") + @TableField("act_name_en") + private String actNameEn; + + @Excel(name = "动作备注", width = 15) + @Schema(description = "动作备注") + @TableField("act_memo") + private String actMemo; + + @Excel(name = "动作关联地址", width = 12) + @Schema(description = "动作关联地址") + @TableField("act_Repaddr") + private Integer actRepaddr; + + @Excel(name = "写入时间", width = 20, format = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Schema(description = "写入时间") + @TableField("WriteTime") + private Date writeTime; + + @Excel(name = "读取时间", width = 20, format = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Schema(description = "读取时间") + @TableField("ReadTime") + private Date readTime; + + @Excel(name = "读写标识", width = 10) + @Schema(description = "读写标识") + @TableField("RW_Flag") + private Integer rwFlag; +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/entity/MesXslMcsDbConfig.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/entity/MesXslMcsDbConfig.java index ebda2f5a..bd9f0968 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/entity/MesXslMcsDbConfig.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/entity/MesXslMcsDbConfig.java @@ -66,6 +66,14 @@ public class MesXslMcsDbConfig implements Serializable { @Schema(description = "备注") private String remark; + //update-begin---author:cursor ---date:2026-07-20 for:【设备库桥接】访问模式与绑定桌面代理----------- + @Schema(description = "访问模式:DESKTOP_AGENT=桌面代理,DIRECT=服务端直连") + private String accessMode; + + @Schema(description = "绑定的桌面代理 deviceId") + private String agentDeviceId; + //update-end---author:cursor ---date:2026-07-20 for:【设备库桥接】访问模式与绑定桌面代理----------- + private String createBy; @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/mapper/McsToMesActMillMapper.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/mapper/McsToMesActMillMapper.java new file mode 100644 index 00000000..a0c5a449 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/mapper/McsToMesActMillMapper.java @@ -0,0 +1,10 @@ +package org.jeecg.modules.xslmes.mcs.mapper; + +import org.jeecg.modules.xslmes.mcs.entity.McsToMesActMill; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 开炼机动作(中间库) + */ +public interface McsToMesActMillMapper extends BaseMapper { +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/service/IMcsToMesActMillService.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/service/IMcsToMesActMillService.java new file mode 100644 index 00000000..9522afec --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/service/IMcsToMesActMillService.java @@ -0,0 +1,10 @@ +package org.jeecg.modules.xslmes.mcs.service; + +import org.jeecg.modules.xslmes.mcs.entity.McsToMesActMill; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 开炼机动作(中间库) + */ +public interface IMcsToMesActMillService extends IService { +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/service/impl/McsToMesActMillServiceImpl.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/service/impl/McsToMesActMillServiceImpl.java new file mode 100644 index 00000000..0ef4a5d5 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/service/impl/McsToMesActMillServiceImpl.java @@ -0,0 +1,16 @@ +package org.jeecg.modules.xslmes.mcs.service.impl; + +import org.jeecg.modules.xslmes.mcs.entity.McsToMesActMill; +import org.jeecg.modules.xslmes.mcs.mapper.McsToMesActMillMapper; +import org.jeecg.modules.xslmes.mcs.service.IMcsToMesActMillService; +import org.springframework.stereotype.Service; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.baomidou.dynamic.datasource.annotation.DS; + +/** + * 开炼机动作(中间库) + */ +@Service +@DS("sqlserver_mcs") +public class McsToMesActMillServiceImpl extends ServiceImpl implements IMcsToMesActMillService { +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/service/impl/MesXslMcsDbConfigServiceImpl.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/service/impl/MesXslMcsDbConfigServiceImpl.java index 61815579..92d7b527 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/service/impl/MesXslMcsDbConfigServiceImpl.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/service/impl/MesXslMcsDbConfigServiceImpl.java @@ -30,6 +30,9 @@ public class MesXslMcsDbConfigServiceImpl extends ServiceImpl fields) { String sourceTable = trim(cfg.getSourceTable()); @@ -91,7 +94,6 @@ public class GenericMcsSyncEngine { } String mode = StringUtils.isBlank(cfg.getSyncMode()) ? MODE_FULL : cfg.getSyncMode().trim().toUpperCase(); - JdbcTemplate sourceJt = new JdbcTemplate(getSourceDataSource()); JdbcTemplate targetJt = new JdbcTemplate(dataSource); // 目标表标准字段探测 + 自动填充列 @@ -134,7 +136,7 @@ public class GenericMcsSyncEngine { String predicate = flagPredicate(flagCol, cfg.getFlagCondition(), cfg.getFlagMatchValue()); String sql = "SELECT TOP " + limit + " " + colList(srcCols) + " FROM [" + sourceTable + "]" + " WHERE (" + predicate + ")"; - rows = sourceJt.queryForList(sql); + rows = mcsDataAccessFacade.query(sql, null); if (rows.isEmpty()) { return "增量采集:无待采集数据"; } @@ -149,10 +151,10 @@ public class GenericMcsSyncEngine { sql.append(" AND [").append(incrCol).append("] < ?"); args.add(window[1]); } - rows = sourceJt.queryForList(sql.toString(), args.toArray()); + rows = mcsDataAccessFacade.query(sql.toString(), args); } else { // FULL - rows = sourceJt.queryForList("SELECT " + colList(srcCols) + " FROM [" + sourceTable + "]"); + rows = mcsDataAccessFacade.query("SELECT " + colList(srcCols) + " FROM [" + sourceTable + "]", null); } if (rows.isEmpty()) { @@ -209,7 +211,7 @@ public class GenericMcsSyncEngine { // INCR(标记回写):对本批所有源行回写标记值,下轮不再采集 if (flagMode) { String writeValue = StringUtils.isBlank(cfg.getFlagWriteValue()) ? FLAG_SYNCED : cfg.getFlagWriteValue(); - int marked = writeBackFlag(sourceJt, sourceTable, flagCol, cfg.getFlagCondition(), cfg.getFlagMatchValue(), writeValue, keyMaps, rows); + int marked = writeBackFlag(sourceTable, flagCol, cfg.getFlagCondition(), cfg.getFlagMatchValue(), writeValue, keyMaps, rows); return String.format("增量采集:新增%d,更新%d,未变%d,回写标记%d,源%d条", ins, upd, unchanged, marked, rows.size()); } @@ -252,14 +254,14 @@ public class GenericMcsSyncEngine { * 避免误标在本轮 SELECT 之后才进入中间库、尚未采集的新数据; * 并以采集条件谓词做守卫,避开本轮已被其他进程改动的行。

*/ - private int writeBackFlag(JdbcTemplate sourceJt, String sourceTable, String flagCol, String condition, + private int writeBackFlag(String sourceTable, String flagCol, String condition, String matchValue, String writeValue, List keyMaps, List> rows) { validateIdent(flagCol); StringBuilder sql = new StringBuilder("UPDATE [").append(sourceTable).append("] SET [") .append(flagCol).append("] = ? WHERE "); sql.append(keyMaps.stream().map(k -> "[" + k.getSourceField() + "] = ?").collect(Collectors.joining(" AND "))); sql.append(" AND (").append(flagPredicate(flagCol, condition, matchValue)).append(")"); - List argsList = new ArrayList<>(); + int total = 0; Set handled = new HashSet<>(); for (Map row : rows) { Map rci = ci(row); @@ -272,9 +274,9 @@ public class GenericMcsSyncEngine { for (MesXslMcsSyncField k : keyMaps) { args.add(rci.get(k.getSourceField())); } - argsList.add(args.toArray()); + total += mcsDataAccessFacade.execute(sql.toString(), args); } - return batch(sourceJt, sql.toString(), argsList); + return total; } //update-end---author:GHT ---date:20260617 for:【MES上辅机】增量匹配改为标记位回写----------- @@ -502,15 +504,6 @@ public class GenericMcsSyncEngine { return maps.stream().anyMatch(m -> m.getTargetField() != null && m.getTargetField().equalsIgnoreCase(targetCol)); } - private DataSource getSourceDataSource() { - DynamicRoutingDataSource routing = (DynamicRoutingDataSource) dataSource; - DataSource src = routing.getDataSources().get(McsDataSourceManager.DS_KEY); - if (src == null) { - throw new IllegalStateException("中间库数据源 " + McsDataSourceManager.DS_KEY + " 未注册"); - } - return src; - } - private void validateIdent(String name) { if (name == null || !IDENT.matcher(name).matches()) { throw new IllegalArgumentException("非法的表名或字段名: " + name); diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslMillActionService.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslMillActionService.java new file mode 100644 index 00000000..28641df0 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslMillActionService.java @@ -0,0 +1,6 @@ +package org.jeecg.modules.xslmes.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.jeecg.modules.xslmes.entity.MesXslMillAction; + +public interface IMesXslMillActionService extends IService {} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMillActionServiceImpl.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMillActionServiceImpl.java new file mode 100644 index 00000000..5c187948 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMillActionServiceImpl.java @@ -0,0 +1,10 @@ +package org.jeecg.modules.xslmes.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.jeecg.modules.xslmes.entity.MesXslMillAction; +import org.jeecg.modules.xslmes.mapper.MesXslMillActionMapper; +import org.jeecg.modules.xslmes.service.IMesXslMillActionService; +import org.springframework.stereotype.Service; + +@Service +public class MesXslMillActionServiceImpl extends ServiceImpl implements IMesXslMillActionService {} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMixingProductionPlanServiceImpl.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMixingProductionPlanServiceImpl.java index 373eac33..66268c56 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMixingProductionPlanServiceImpl.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMixingProductionPlanServiceImpl.java @@ -103,6 +103,8 @@ public class MesXslMixingProductionPlanServiceImpl private void rebuildRawMaterialDemandPlan(List savedPlans) { jdbcTemplate.update("DELETE FROM mes_xsl_raw_material_demand_plan"); + jdbcTemplate.update("DELETE FROM mes_xsl_auto_small_material_demand_plan"); + jdbcTemplate.update("DELETE FROM mes_xsl_manual_small_material_demand_plan"); if (CollectionUtils.isEmpty(savedPlans)) { return; } @@ -184,6 +186,8 @@ public class MesXslMixingProductionPlanServiceImpl } Map aggregateMap = new HashMap<>(); + Map autoAggregateMap = new HashMap<>(); + Map manualAggregateMap = new HashMap<>(); for (MesXslMixingProductionPlan plan : validPlans) { String specId = specIdByName.get(StringUtils.trimToEmpty(plan.getFormulaName())); if (StringUtils.isBlank(specId)) { @@ -241,19 +245,39 @@ public class MesXslMixingProductionPlanServiceImpl StringUtils.defaultString(erpCode), rawMaterialName)); aggregate.demandWeight = aggregate.demandWeight.add(demandWeight); + + String smallKey = + String.join( + "|", + planDateKey, + StringUtils.defaultString(plan.getMachineId()), + StringUtils.defaultString(plan.getMachineName()), + rawMaterialName); + boolean isAutoKind = StringUtils.contains(StringUtils.trimToEmpty(detail.getMaterialKind()), "自动"); + Map targetSmallMap = isAutoKind ? autoAggregateMap : manualAggregateMap; + SmallDemandAggregate smallAggregate = + targetSmallMap.computeIfAbsent( + smallKey, + k -> + new SmallDemandAggregate( + plan.getPlanDate(), + StringUtils.trimToNull(plan.getMachineId()), + StringUtils.trimToNull(plan.getMachineName()), + rawMaterialName)); + smallAggregate.demandWeight = smallAggregate.demandWeight.add(demandWeight); } } - if (aggregateMap.isEmpty()) { + if (aggregateMap.isEmpty() && autoAggregateMap.isEmpty() && manualAggregateMap.isEmpty()) { return; } - List batchArgs = new ArrayList<>(); + List rawBatchArgs = new ArrayList<>(); Date now = new Date(); for (RawDemandAggregate aggregate : aggregateMap.values()) { if (aggregate.demandWeight.compareTo(BigDecimal.ZERO) <= 0) { continue; } - batchArgs.add( + rawBatchArgs.add( new Object[] { IdWorker.getIdStr(), aggregate.machineId, @@ -270,14 +294,53 @@ public class MesXslMixingProductionPlanServiceImpl CommonConstant.DEL_FLAG_0 }); } - if (batchArgs.isEmpty()) { - return; + if (!rawBatchArgs.isEmpty()) { + jdbcTemplate.batchUpdate( + "INSERT INTO mes_xsl_raw_material_demand_plan " + + "(id, machine_id, machine_name, material_id, erp_code, raw_material_name, demand_weight, standard_weight, actual_weight, plan_date, create_time, update_time, del_flag) " + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + rawBatchArgs); } - jdbcTemplate.batchUpdate( - "INSERT INTO mes_xsl_raw_material_demand_plan " - + "(id, machine_id, machine_name, material_id, erp_code, raw_material_name, demand_weight, standard_weight, actual_weight, plan_date, create_time, update_time, del_flag) " - + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", - batchArgs); + + List autoBatchArgs = buildSmallDemandBatchArgs(autoAggregateMap, now); + if (!autoBatchArgs.isEmpty()) { + jdbcTemplate.batchUpdate( + "INSERT INTO mes_xsl_auto_small_material_demand_plan " + + "(id, stat_date, machine_id, machine_name, raw_material_name, demand_weight, create_time, update_time, del_flag) " + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", + autoBatchArgs); + } + List manualBatchArgs = buildSmallDemandBatchArgs(manualAggregateMap, now); + if (!manualBatchArgs.isEmpty()) { + jdbcTemplate.batchUpdate( + "INSERT INTO mes_xsl_manual_small_material_demand_plan " + + "(id, stat_date, machine_id, machine_name, raw_material_name, demand_weight, create_time, update_time, del_flag) " + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", + manualBatchArgs); + } + } + + private List buildSmallDemandBatchArgs( + Map aggregateMap, Date now) { + List batchArgs = new ArrayList<>(); + for (SmallDemandAggregate aggregate : aggregateMap.values()) { + if (aggregate.demandWeight.compareTo(BigDecimal.ZERO) <= 0) { + continue; + } + batchArgs.add( + new Object[] { + IdWorker.getIdStr(), + aggregate.statDate, + aggregate.machineId, + aggregate.machineName, + aggregate.rawMaterialName, + aggregate.demandWeight, + now, + now, + CommonConstant.DEL_FLAG_0 + }); + } + return batchArgs; } private static class RawDemandAggregate { @@ -305,6 +368,22 @@ public class MesXslMixingProductionPlanServiceImpl } } + private static class SmallDemandAggregate { + private final Date statDate; + private final String machineId; + private final String machineName; + private final String rawMaterialName; + private BigDecimal demandWeight = BigDecimal.ZERO; + + private SmallDemandAggregate( + Date statDate, String machineId, String machineName, String rawMaterialName) { + this.statDate = statDate; + this.machineId = machineId; + this.machineName = machineName; + this.rawMaterialName = rawMaterialName; + } + } + private boolean hasBusinessData(MesXslMixingProductionPlan row) { return StringUtils.isNotBlank(row.getMachineId()) || StringUtils.isNotBlank(row.getPlanId()) diff --git a/jeecg-boot/jeecg-module-device-sync/src/main/java/org/jeecg/modules/device/sync/agent/DeviceAgentCommand.java b/jeecg-boot/jeecg-module-device-sync/src/main/java/org/jeecg/modules/device/sync/agent/DeviceAgentCommand.java new file mode 100644 index 00000000..17f640a7 --- /dev/null +++ b/jeecg-boot/jeecg-module-device-sync/src/main/java/org/jeecg/modules/device/sync/agent/DeviceAgentCommand.java @@ -0,0 +1,34 @@ +package org.jeecg.modules.device.sync.agent; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +/** + * MES → 桌面设备库命令。 + */ +@Data +public class DeviceAgentCommand implements Serializable { + private static final long serialVersionUID = 1L; + + public static final String CMD_QUERY = "EQ_QUERY"; + public static final String CMD_WRITE = "EQ_WRITE"; + public static final String CMD_SYNC_PULL = "EQ_SYNC_PULL"; + public static final String CMD_SYNC_PUSH = "EQ_SYNC_PUSH"; + public static final String CMD_TEST = "EQ_TEST"; + + private String requestId; + private String cmd; + private String deviceId; + /** 白名单表名(可选,与 sql 二选一或同时校验) */ + private String table; + /** 已登记 sqlKey 或受限 SQL(桌面端白名单校验) */ + private String sql; + private String sqlKey; + private List params; + private Map payload; + private Integer timeoutMs; + private Long sentAt; +} diff --git a/jeecg-boot/jeecg-module-device-sync/src/main/java/org/jeecg/modules/device/sync/agent/DeviceAgentCommandService.java b/jeecg-boot/jeecg-module-device-sync/src/main/java/org/jeecg/modules/device/sync/agent/DeviceAgentCommandService.java new file mode 100644 index 00000000..442d9ab2 --- /dev/null +++ b/jeecg-boot/jeecg-module-device-sync/src/main/java/org/jeecg/modules/device/sync/agent/DeviceAgentCommandService.java @@ -0,0 +1,166 @@ +package org.jeecg.modules.device.sync.agent; + +import com.alibaba.fastjson.JSON; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * 设备代理命令总线:下发 EQ_* 并等待回执。 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class DeviceAgentCommandService { + + public static final int DEFAULT_TIMEOUT_MS = 30_000; + + private final SimpMessagingTemplate messagingTemplate; + private final DeviceAgentSessionRegistry sessionRegistry; + + private final ConcurrentHashMap> pending = new ConcurrentHashMap<>(); + + public DeviceAgentResult sendAndWait(DeviceAgentCommand command) { + if (command == null) { + throw new IllegalArgumentException("command 不能为空"); + } + if (StringUtils.isBlank(command.getRequestId())) { + command.setRequestId(UUID.randomUUID().toString().replace("-", "")); + } + if (StringUtils.isBlank(command.getDeviceId())) { + DeviceAgentSession picked = sessionRegistry.pickOnlineAgent(null); + if (picked == null) { + throw new IllegalStateException("无在线桌面设备代理,无法访问设备库"); + } + command.setDeviceId(picked.getDeviceId()); + } + if (!sessionRegistry.isOnline(command.getDeviceId())) { + throw new IllegalStateException("桌面设备代理不在线: " + command.getDeviceId()); + } + int timeout = command.getTimeoutMs() != null && command.getTimeoutMs() > 0 + ? command.getTimeoutMs() : DEFAULT_TIMEOUT_MS; + command.setSentAt(System.currentTimeMillis()); + + CompletableFuture future = new CompletableFuture<>(); + CompletableFuture previous = pending.putIfAbsent(command.getRequestId(), future); + if (previous != null) { + throw new IllegalStateException("重复的 requestId: " + command.getRequestId()); + } + + try { + String destination = "/topic/device/" + command.getDeviceId() + "/cmd"; + messagingTemplate.convertAndSend(destination, JSON.toJSONString(command)); + log.info("已下发设备库命令 cmd={} requestId={} deviceId={}", + command.getCmd(), command.getRequestId(), command.getDeviceId()); + return future.get(timeout, TimeUnit.MILLISECONDS); + } catch (TimeoutException e) { + pending.remove(command.getRequestId(), future); + throw new IllegalStateException("等待桌面设备库回执超时(" + timeout + "ms), requestId=" + command.getRequestId()); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + pending.remove(command.getRequestId(), future); + throw new IllegalStateException("等待桌面设备库回执被中断", e); + } catch (Exception e) { + pending.remove(command.getRequestId(), future); + if (e instanceof IllegalStateException) { + throw (IllegalStateException) e; + } + throw new IllegalStateException("下发设备库命令失败: " + e.getMessage(), e); + } + } + + public void completeResult(DeviceAgentResult result) { + if (result == null || StringUtils.isBlank(result.getRequestId())) { + return; + } + if (StringUtils.isNotBlank(result.getDeviceId())) { + sessionRegistry.registerOrHeartbeat( + result.getDeviceId(), + null, + result.getDbConnected(), + result.getDbMessage()); + } + CompletableFuture future = pending.remove(result.getRequestId()); + if (future != null) { + future.complete(result); + } else { + log.debug("收到无等待方的设备库回执 requestId={}", result.getRequestId()); + } + } + + public DeviceAgentResult query(String deviceId, String sql, List params, Integer timeoutMs) { + DeviceAgentCommand cmd = new DeviceAgentCommand(); + cmd.setCmd(DeviceAgentCommand.CMD_QUERY); + cmd.setDeviceId(deviceId); + cmd.setSql(sql); + cmd.setParams(params); + cmd.setTimeoutMs(timeoutMs); + DeviceAgentResult result = sendAndWait(cmd); + ensureSuccess(result); + return result; + } + + public DeviceAgentResult write(String deviceId, String sql, List params, Integer timeoutMs) { + DeviceAgentCommand cmd = new DeviceAgentCommand(); + cmd.setCmd(DeviceAgentCommand.CMD_WRITE); + cmd.setDeviceId(deviceId); + cmd.setSql(sql); + cmd.setParams(params); + cmd.setTimeoutMs(timeoutMs); + DeviceAgentResult result = sendAndWait(cmd); + ensureSuccess(result); + return result; + } + + public DeviceAgentResult test(String deviceId, Integer timeoutMs) { + DeviceAgentCommand cmd = new DeviceAgentCommand(); + cmd.setCmd(DeviceAgentCommand.CMD_TEST); + cmd.setDeviceId(deviceId); + cmd.setTimeoutMs(timeoutMs != null ? timeoutMs : 10_000); + return sendAndWait(cmd); + } + + public DeviceAgentResult syncPull(String deviceId, String table, String sql, List params, Integer timeoutMs) { + DeviceAgentCommand cmd = new DeviceAgentCommand(); + cmd.setCmd(DeviceAgentCommand.CMD_SYNC_PULL); + cmd.setDeviceId(deviceId); + cmd.setTable(table); + cmd.setSql(sql); + cmd.setParams(params); + cmd.setTimeoutMs(timeoutMs != null ? timeoutMs : 60_000); + DeviceAgentResult result = sendAndWait(cmd); + ensureSuccess(result); + return result; + } + + public DeviceAgentResult syncPush(String deviceId, String table, Map payload, Integer timeoutMs) { + DeviceAgentCommand cmd = new DeviceAgentCommand(); + cmd.setCmd(DeviceAgentCommand.CMD_SYNC_PUSH); + cmd.setDeviceId(deviceId); + cmd.setTable(table); + cmd.setPayload(payload); + cmd.setTimeoutMs(timeoutMs != null ? timeoutMs : 60_000); + DeviceAgentResult result = sendAndWait(cmd); + ensureSuccess(result); + return result; + } + + private void ensureSuccess(DeviceAgentResult result) { + if (result == null) { + throw new IllegalStateException("桌面设备库无回执"); + } + if (!result.isSuccess()) { + throw new IllegalStateException(StringUtils.defaultIfBlank(result.getError(), "桌面设备库执行失败")); + } + } +} diff --git a/jeecg-boot/jeecg-module-device-sync/src/main/java/org/jeecg/modules/device/sync/agent/DeviceAgentResult.java b/jeecg-boot/jeecg-module-device-sync/src/main/java/org/jeecg/modules/device/sync/agent/DeviceAgentResult.java new file mode 100644 index 00000000..43f16291 --- /dev/null +++ b/jeecg-boot/jeecg-module-device-sync/src/main/java/org/jeecg/modules/device/sync/agent/DeviceAgentResult.java @@ -0,0 +1,30 @@ +package org.jeecg.modules.device.sync.agent; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +/** + * 桌面 → MES 设备库命令回执。 + */ +@Data +public class DeviceAgentResult implements Serializable { + private static final long serialVersionUID = 1L; + + private String requestId; + private String deviceId; + private String cmd; + private boolean success; + private String error; + private Integer affected; + private List> rows; + private Boolean dbConnected; + private String dbMessage; + private Long respondedAt; + /** 分块回传时使用 */ + private Integer chunkIndex; + private Integer chunkTotal; + private Boolean lastChunk; +} diff --git a/jeecg-boot/jeecg-module-device-sync/src/main/java/org/jeecg/modules/device/sync/agent/DeviceAgentSession.java b/jeecg-boot/jeecg-module-device-sync/src/main/java/org/jeecg/modules/device/sync/agent/DeviceAgentSession.java new file mode 100644 index 00000000..7161e6b7 --- /dev/null +++ b/jeecg-boot/jeecg-module-device-sync/src/main/java/org/jeecg/modules/device/sync/agent/DeviceAgentSession.java @@ -0,0 +1,22 @@ +package org.jeecg.modules.device.sync.agent; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 桌面设备代理在线会话(内存)。 + */ +@Data +public class DeviceAgentSession implements Serializable { + private static final long serialVersionUID = 1L; + + private String deviceId; + private String hostName; + private boolean online; + /** 设备库(SQL Server)是否连通 */ + private boolean dbConnected; + private String dbMessage; + private long lastSeenAt; + private long registeredAt; +} diff --git a/jeecg-boot/jeecg-module-device-sync/src/main/java/org/jeecg/modules/device/sync/agent/DeviceAgentSessionRegistry.java b/jeecg-boot/jeecg-module-device-sync/src/main/java/org/jeecg/modules/device/sync/agent/DeviceAgentSessionRegistry.java new file mode 100644 index 00000000..aecdb1c6 --- /dev/null +++ b/jeecg-boot/jeecg-module-device-sync/src/main/java/org/jeecg/modules/device/sync/agent/DeviceAgentSessionRegistry.java @@ -0,0 +1,121 @@ +package org.jeecg.modules.device.sync.agent; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 桌面设备代理会话注册表。 + */ +@Slf4j +@Component +public class DeviceAgentSessionRegistry { + + private static final long ONLINE_TTL_MS = 60_000L; + + private final ConcurrentHashMap sessions = new ConcurrentHashMap<>(); + + public void registerOrHeartbeat(String deviceId, String hostName, Boolean dbConnected, String dbMessage) { + if (StringUtils.isBlank(deviceId)) { + return; + } + long now = System.currentTimeMillis(); + sessions.compute(deviceId, (id, old) -> { + DeviceAgentSession s = old == null ? new DeviceAgentSession() : old; + if (old == null) { + s.setRegisteredAt(now); + } + s.setDeviceId(deviceId); + if (StringUtils.isNotBlank(hostName)) { + s.setHostName(hostName); + } + s.setOnline(true); + if (dbConnected != null) { + s.setDbConnected(dbConnected); + } + if (dbMessage != null) { + s.setDbMessage(dbMessage); + } + s.setLastSeenAt(now); + return s; + }); + } + + public void markOffline(String deviceId) { + if (StringUtils.isBlank(deviceId)) { + return; + } + DeviceAgentSession s = sessions.get(deviceId); + if (s != null) { + s.setOnline(false); + s.setLastSeenAt(System.currentTimeMillis()); + } + } + + public DeviceAgentSession get(String deviceId) { + if (StringUtils.isBlank(deviceId)) { + return null; + } + DeviceAgentSession s = sessions.get(deviceId); + if (s == null) { + return null; + } + refreshOnlineFlag(s); + return s; + } + + public boolean isOnline(String deviceId) { + DeviceAgentSession s = get(deviceId); + return s != null && s.isOnline(); + } + + /** + * 选取一个在线代理:优先指定 deviceId,否则取最近心跳的在线设备。 + */ + public DeviceAgentSession pickOnlineAgent(String preferredDeviceId) { + if (StringUtils.isNotBlank(preferredDeviceId)) { + DeviceAgentSession preferred = get(preferredDeviceId); + if (preferred != null && preferred.isOnline()) { + return preferred; + } + } + return listOnline().stream() + .max(Comparator.comparingLong(DeviceAgentSession::getLastSeenAt)) + .orElse(null); + } + + public List listOnline() { + List list = new ArrayList<>(); + for (Map.Entry e : sessions.entrySet()) { + DeviceAgentSession s = e.getValue(); + refreshOnlineFlag(s); + if (s.isOnline()) { + list.add(s); + } + } + list.sort(Comparator.comparingLong(DeviceAgentSession::getLastSeenAt).reversed()); + return list; + } + + public List listAll() { + List list = new ArrayList<>(sessions.values()); + list.forEach(this::refreshOnlineFlag); + list.sort(Comparator.comparingLong(DeviceAgentSession::getLastSeenAt).reversed()); + return list; + } + + private void refreshOnlineFlag(DeviceAgentSession s) { + if (s == null) { + return; + } + if (System.currentTimeMillis() - s.getLastSeenAt() > ONLINE_TTL_MS) { + s.setOnline(false); + } + } +} diff --git a/jeecg-boot/jeecg-module-device-sync/src/main/java/org/jeecg/modules/device/sync/controller/DeviceWebSocketController.java b/jeecg-boot/jeecg-module-device-sync/src/main/java/org/jeecg/modules/device/sync/controller/DeviceWebSocketController.java index eb672dd5..fd6eb17b 100644 --- a/jeecg-boot/jeecg-module-device-sync/src/main/java/org/jeecg/modules/device/sync/controller/DeviceWebSocketController.java +++ b/jeecg-boot/jeecg-module-device-sync/src/main/java/org/jeecg/modules/device/sync/controller/DeviceWebSocketController.java @@ -1,20 +1,29 @@ package org.jeecg.modules.device.sync.controller; +import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.jeecg.common.api.vo.Result; +import org.jeecg.modules.device.sync.agent.DeviceAgentCommand; +import org.jeecg.modules.device.sync.agent.DeviceAgentCommandService; +import org.jeecg.modules.device.sync.agent.DeviceAgentResult; +import org.jeecg.modules.device.sync.agent.DeviceAgentSession; +import org.jeecg.modules.device.sync.agent.DeviceAgentSessionRegistry; import org.jeecg.modules.device.sync.entity.DeviceStatus; import org.jeecg.modules.device.sync.mapper.DeviceStatusMapper; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; @@ -29,6 +38,8 @@ public class DeviceWebSocketController { private final DeviceStatusMapper deviceStatusMapper; private final SimpMessagingTemplate messagingTemplate; + private final DeviceAgentSessionRegistry agentSessionRegistry; + private final DeviceAgentCommandService agentCommandService; @MessageMapping("/device/status") public void receiveDeviceStatus(DeviceStatus payload) { @@ -70,6 +81,8 @@ public class DeviceWebSocketController { if (deviceId.isBlank()) { return; } + // 心跳同时刷新设备代理在线状态 + agentSessionRegistry.registerOrHeartbeat(deviceId, null, null, null); Map pong = new HashMap<>(); pong.put("cmd", "PONG_DEVICE"); pong.put("deviceId", deviceId); @@ -91,6 +104,8 @@ public class DeviceWebSocketController { commandPayload.put("sentAt", System.currentTimeMillis()); messagingTemplate.convertAndSendToUser(deviceId, "/queue/command", commandPayload); + // 匿名桌面同样订阅 /topic/device/{id}/cmd + messagingTemplate.convertAndSend("/topic/device/" + deviceId + "/cmd", commandJson); log.info("下发设备指令成功, deviceId={}", deviceId); Map result = new HashMap<>(); @@ -98,4 +113,87 @@ public class DeviceWebSocketController { result.put("queued", true); return Result.OK(result); } + + //update-begin---author:cursor ---date:2026-07-20 for:【设备库桥接】代理状态/回执/在线列表----------- + /** + * 桌面代理上报在线与设备库连通状态。 + * destination: /app/device/agent/status + */ + @MessageMapping("/device/agent/status") + public void receiveAgentStatus(@org.springframework.messaging.handler.annotation.Payload(required = false) Map payload) { + if (payload == null) { + return; + } + String deviceId = String.valueOf(payload.getOrDefault("deviceId", "")).trim(); + if (deviceId.isBlank()) { + return; + } + String hostName = payload.get("hostName") == null ? null : String.valueOf(payload.get("hostName")); + Boolean dbConnected = null; + if (payload.get("dbConnected") instanceof Boolean) { + dbConnected = (Boolean) payload.get("dbConnected"); + } else if (payload.get("dbConnected") != null) { + dbConnected = Boolean.parseBoolean(String.valueOf(payload.get("dbConnected"))); + } + String dbMessage = payload.get("dbMessage") == null ? null : String.valueOf(payload.get("dbMessage")); + agentSessionRegistry.registerOrHeartbeat(deviceId, hostName, dbConnected, dbMessage); + messagingTemplate.convertAndSend("/topic/device/" + deviceId + "/status", payload); + log.debug("设备代理状态已更新 deviceId={} dbConnected={}", deviceId, dbConnected); + } + + /** + * 桌面代理回传设备库命令结果。 + * destination: /app/device/agent/result + */ + @MessageMapping("/device/agent/result") + public void receiveAgentResult(@org.springframework.messaging.handler.annotation.Payload(required = false) Map payload) { + if (payload == null) { + return; + } + DeviceAgentResult result = JSON.parseObject(JSON.toJSONString(payload), DeviceAgentResult.class); + agentCommandService.completeResult(result); + } + + @GetMapping("/agent/online") + public Result> listOnlineAgents() { + return Result.OK(agentSessionRegistry.listOnline()); + } + + @GetMapping("/agent/list") + public Result> listAgents() { + return Result.OK(agentSessionRegistry.listAll()); + } + + @PostMapping("/agent/test") + public Result testAgentDb(@RequestBody(required = false) Map body) { + String deviceId = body == null ? null : String.valueOf(body.getOrDefault("deviceId", "")).trim(); + if (deviceId == null || deviceId.isBlank()) { + DeviceAgentSession picked = agentSessionRegistry.pickOnlineAgent(null); + if (picked == null) { + return Result.error("无在线桌面代理"); + } + deviceId = picked.getDeviceId(); + } + try { + return Result.OK(agentCommandService.test(deviceId, 10_000)); + } catch (Exception e) { + return Result.error(e.getMessage()); + } + } + + @PostMapping("/agent/command") + public Result sendAgentCommand(@RequestBody DeviceAgentCommand command) { + try { + return Result.OK(agentCommandService.sendAndWait(command)); + } catch (Exception e) { + return Result.error(e.getMessage()); + } + } + + @GetMapping("/agent/status") + public Result agentStatus(@RequestParam(name = "deviceId", required = false) String deviceId) { + DeviceAgentSession session = agentSessionRegistry.pickOnlineAgent(deviceId); + return Result.OK(session); + } + //update-end---author:cursor ---date:2026-07-20 for:【设备库桥接】代理状态/回执/在线列表----------- } diff --git a/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_164__mes_xsl_mcs_db_config_agent.sql b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_164__mes_xsl_mcs_db_config_agent.sql new file mode 100644 index 00000000..b4b63a39 --- /dev/null +++ b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_164__mes_xsl_mcs_db_config_agent.sql @@ -0,0 +1,4 @@ +-- 【设备库桥接】中间库配置增加桌面代理访问模式 +ALTER TABLE `mes_xsl_mcs_db_config` + ADD COLUMN `access_mode` varchar(32) DEFAULT 'DESKTOP_AGENT' COMMENT '访问模式 DESKTOP_AGENT/DIRECT' AFTER `remark`, + ADD COLUMN `agent_device_id` varchar(100) DEFAULT NULL COMMENT '绑定桌面代理deviceId' AFTER `access_mode`; diff --git a/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_165__mes_xsl_mill_action_act_mill.sql b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_165__mes_xsl_mill_action_act_mill.sql new file mode 100644 index 00000000..2dee9a54 --- /dev/null +++ b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_165__mes_xsl_mill_action_act_mill.sql @@ -0,0 +1,105 @@ +-- 【MES上辅机】开炼机动作 MCSToMES_Act_Mill → mes_xsl_mill_action +-- 1. MES 接收表 +-- 2. 采集配置(FULL / 30秒 / GUID 匹配)+ 字段映射 +-- 3. 中间库原表菜单 + 接收表菜单 + admin 授权 + +SET NAMES utf8mb4; + +-- ===================== 1. MES 接收表 ===================== +CREATE TABLE IF NOT EXISTS `mes_xsl_mill_action` ( + `id` varchar(32) NOT NULL COMMENT '主键', + `guid` varchar(50) NOT NULL COMMENT '源表GUID(匹配键)', + `act_addr` int DEFAULT NULL COMMENT '动作地址', + `act_name` varchar(50) DEFAULT NULL COMMENT '动作名称', + `act_name_en` varchar(50) DEFAULT NULL COMMENT '动作名称(英)', + `act_memo` varchar(20) DEFAULT NULL COMMENT '动作备注', + `act_repaddr` int DEFAULT NULL COMMENT '动作关联地址', + `write_time` datetime DEFAULT NULL COMMENT '写入时间(源)', + `read_time` datetime DEFAULT NULL COMMENT '读取时间(源)', + `rw_flag` int DEFAULT NULL COMMENT '读写标识', + `tenant_id` int DEFAULT '0' COMMENT '租户', + `sys_org_code` varchar(64) DEFAULT NULL COMMENT '部门', + `create_by` varchar(50) DEFAULT NULL COMMENT '创建人', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(50) DEFAULT NULL COMMENT '更新人', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `del_flag` int DEFAULT '0' COMMENT '删除标记(0正常1删除)', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_mma_guid` (`guid`), + KEY `idx_mma_act_name` (`act_name`), + KEY `idx_mma_write_time` (`write_time`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES开炼机动作接收表(采集自MCSToMES_Act_Mill)'; + +-- ===================== 2. 采集配置 + 字段映射 ===================== +INSERT INTO `mes_xsl_mcs_sync_config` + (`id`, `biz_type`, `config_name`, `biz_name`, `source_table`, `source_table_comment`, + `target_table`, `target_table_comment`, `interval_seconds`, `status`, `sync_mode`, + `remark`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`) +SELECT '1900000000000000880', 'MILL_ACT', '开炼机动作采集', '开炼机动作', 'MCSToMES_Act_Mill', '开炼机动作', + 'mes_xsl_mill_action', 'MES开炼机动作接收表', 30, '0', 'FULL', + '开炼机动作 FULL 按GUID Upsert,默认30秒', 0, 'admin', NOW(), 'admin', NOW(), 0 +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `mes_xsl_mcs_sync_config` WHERE `biz_type` = 'MILL_ACT' AND `tenant_id` = 0); + +INSERT INTO `mes_xsl_mcs_sync_field` + (`id`, `config_id`, `source_field`, `source_field_comment`, `source_field_type`, + `target_field`, `target_field_comment`, `match_key`, `sort_no`, + `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`) +SELECT * FROM ( + SELECT '1900000000000000881' id, '1900000000000000880' config_id, 'GUID' sf, 'GUID' sfc, 'varchar' sft, 'guid' tf, '源表GUID' tfc, '1' mk, 1 sn, 0 tid, 'admin' cb, NOW() ct, 'admin' ub, NOW() ut, 0 df + UNION ALL SELECT '1900000000000000882', '1900000000000000880', 'act_addr', '动作地址', 'int', 'act_addr', '动作地址', '0', 2, 0, 'admin', NOW(), 'admin', NOW(), 0 + UNION ALL SELECT '1900000000000000883', '1900000000000000880', 'act_name', '动作名称', 'varchar', 'act_name', '动作名称', '0', 3, 0, 'admin', NOW(), 'admin', NOW(), 0 + UNION ALL SELECT '1900000000000000884', '1900000000000000880', 'act_name_en', '动作名称(英)', 'varchar', 'act_name_en', '动作名称(英)', '0', 4, 0, 'admin', NOW(), 'admin', NOW(), 0 + UNION ALL SELECT '1900000000000000885', '1900000000000000880', 'act_memo', '动作备注', 'varchar', 'act_memo', '动作备注', '0', 5, 0, 'admin', NOW(), 'admin', NOW(), 0 + UNION ALL SELECT '1900000000000000886', '1900000000000000880', 'act_Repaddr', '动作关联地址', 'int', 'act_repaddr', '动作关联地址', '0', 6, 0, 'admin', NOW(), 'admin', NOW(), 0 + UNION ALL SELECT '1900000000000000887', '1900000000000000880', 'WriteTime', '写入时间', 'datetime', 'write_time', '写入时间', '0', 7, 0, 'admin', NOW(), 'admin', NOW(), 0 + UNION ALL SELECT '1900000000000000888', '1900000000000000880', 'ReadTime', '读取时间', 'datetime', 'read_time', '读取时间', '0', 8, 0, 'admin', NOW(), 'admin', NOW(), 0 + UNION ALL SELECT '1900000000000000889', '1900000000000000880', 'RW_Flag', '读写标识', 'int', 'rw_flag', '读写标识', '0', 9, 0, 'admin', NOW(), 'admin', NOW(), 0 +) t +WHERE NOT EXISTS (SELECT 1 FROM `mes_xsl_mcs_sync_field` WHERE `config_id` = '1900000000000000880'); + +-- ===================== 3. 中间库原表菜单(上辅机数据) ===================== +INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`, `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, `hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `rule_flag`, `status`, `internal_or_external`) +SELECT '1900000000000000890', '1900000000000000830', '开炼机动作', '/xslmesMcs/mcsToMesActMill', 'xslmesMcs/mcsToMesActMill/index', 1, NULL, NULL, 1, NULL, '0', 5.50, 0, 'ant-design:apartment-outlined', 1, 1, 0, 0, 'MCSToMES_Act_Mill 开炼机动作中间表', 'admin', NOW(), 'admin', NOW(), 0, 0, '1', 0 +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000890'); + +INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`, `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, `hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `rule_flag`, `status`, `internal_or_external`) +SELECT '1900000000000000891', '1900000000000000890', '采集设置', NULL, NULL, 0, NULL, NULL, 2, 'xslmes:mcsSyncConfig:setting', '1', 1.00, 0, NULL, 1, 0, 0, 0, NULL, 'admin', NOW(), 'admin', NOW(), 0, 0, '1', 0 +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000891'); + +INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`, `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, `hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `rule_flag`, `status`, `internal_or_external`) +SELECT '1900000000000000892', '1900000000000000890', '导出', NULL, NULL, 0, NULL, NULL, 2, 'xslmes:mcsToMesActMill:exportXls', '1', 2.00, 0, NULL, 1, 0, 0, 0, NULL, 'admin', NOW(), 'admin', NOW(), 0, 0, '1', 0 +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000892'); + +-- ===================== 4. MES 接收表菜单(技术管理) ===================== +UPDATE `sys_permission` +SET `is_leaf` = 0, `update_time` = NOW() +WHERE `id` = '1900000000000000810' AND `is_leaf` = 1; + +INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`, `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, `hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `rule_flag`, `status`, `internal_or_external`) +SELECT '1900000000000000894', '1900000000000000810', '开炼机动作接收', '/xslmes/mesXslMillAction', 'xslmes/mesXslMillAction/MesXslMillActionList', 1, 'MesXslMillActionList', NULL, 1, NULL, '0', 1.10, 0, 'ant-design:cluster-outlined', 1, 1, 0, 0, 'mes_xsl_mill_action 开炼机动作接收表', 'admin', NOW(), 'admin', NOW(), 0, 0, '1', 0 +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000894'); + +INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`, `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, `hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `rule_flag`, `status`, `internal_or_external`) +SELECT '1900000000000000895', '1900000000000000894', '采集设置', NULL, NULL, 0, NULL, NULL, 2, 'xslmes:mcsSyncConfig:setting', '1', 1.00, 0, NULL, 1, 0, 0, 0, NULL, 'admin', NOW(), 'admin', NOW(), 0, 0, '1', 0 +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000895'); + +INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`, `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, `hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `rule_flag`, `status`, `internal_or_external`) +SELECT '1900000000000000896', '1900000000000000894', '导出', NULL, NULL, 0, NULL, NULL, 2, 'xslmes:mes_xsl_mill_action:exportXls', '1', 2.00, 0, NULL, 1, 0, 0, 0, NULL, 'admin', NOW(), 'admin', NOW(), 0, 0, '1', 0 +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000896'); + +-- admin 授权 +INSERT INTO `sys_role_permission` (`id`, `role_id`, `permission_id`, `data_rule_ids`, `operate_date`, `operate_ip`) +SELECT REPLACE(UUID(), '-', ''), r.id, p.id, NULL, NOW(), '127.0.0.1' +FROM `sys_role` r +JOIN ( + SELECT id FROM `sys_permission` + WHERE id IN ( + '1900000000000000890','1900000000000000891','1900000000000000892', + '1900000000000000894','1900000000000000895','1900000000000000896' + ) +) p ON 1 = 1 +WHERE r.`role_code` = 'admin' + AND NOT EXISTS ( + SELECT 1 FROM `sys_role_permission` rp + WHERE rp.`role_id` = r.id AND rp.`permission_id` = p.id + ); diff --git a/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_166__hide_mcs_act_mill_web_menu.sql b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_166__hide_mcs_act_mill_web_menu.sql new file mode 100644 index 00000000..5fed0047 --- /dev/null +++ b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_166__hide_mcs_act_mill_web_menu.sql @@ -0,0 +1,14 @@ +-- 开炼机动作中间库原表改由桌面端展示,隐藏 MES Web 中间库菜单 +SET NAMES utf8mb4; + +UPDATE `sys_permission` +SET `del_flag` = 1, `status` = '0', `update_time` = NOW() +WHERE `id` IN ('1900000000000000890', '1900000000000000891', '1900000000000000892') + AND `del_flag` = 0; + +-- 接收表菜单名称保持「开炼机动作接收」,仅查 MySQL 接收数据 +UPDATE `sys_permission` +SET `name` = '开炼机动作接收', + `description` = 'mes_xsl_mill_action 开炼机动作接收表(仅展示采集入库数据)', + `update_time` = NOW() +WHERE `id` = '1900000000000000894'; diff --git a/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_167__restore_raw_material_inspect_std_menu.sql b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_167__restore_raw_material_inspect_std_menu.sql new file mode 100644 index 00000000..71a73945 --- /dev/null +++ b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_167__restore_raw_material_inspect_std_menu.sql @@ -0,0 +1,74 @@ +-- 修复原材料检验标准菜单(避开已被人工小料占用的 0730/0750 等 ID) +SET NAMES utf8mb4; + +-- 1) 新建页面菜单:MES原料仓储 下,与送检记录同级 +INSERT INTO `sys_permission` ( + `id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`, + `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, + `hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, + `del_flag`, `rule_flag`, `status`, `internal_or_external` +) +SELECT + '1900000000000000897', '1900000000000000700', '原材料检验标准', '/mes/rawMaterialInspectStd', + 'mes/rawmaterialinspectstd/index', 1, NULL, NULL, + 1, NULL, '0', 21.50, 0, 'ant-design:safety-outlined', 1, 1, + 0, 0, 'MES原材料检验标准(送检记录匹配用)', 'admin', NOW(), 'admin', NOW(), + 0, 0, '1', 0 +FROM DUAL +WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000897'); + +-- 2) 按钮权限改挂到新菜单(从误挂的人工小料计划维护下挪回) +UPDATE `sys_permission` +SET `parent_id` = '1900000000000000897', `update_time` = NOW() +WHERE `id` IN ( + '1900000000000000732','1900000000000000733','1900000000000000734', + '1900000000000000735','1900000000000000736','1900000000000000737' +) +AND `perms` LIKE 'mes:mes_raw_material_inspect_std:%'; + +-- 3) 添加按钮 +INSERT INTO `sys_permission` ( + `id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`, + `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, + `hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, + `del_flag`, `rule_flag`, `status`, `internal_or_external` +) +SELECT + '1900000000000000898', '1900000000000000897', '添加', NULL, NULL, 0, NULL, NULL, + 2, 'mes:mes_raw_material_inspect_std:add', '1', 1.00, 0, NULL, 1, 0, + 0, 0, NULL, 'admin', NOW(), 'admin', NOW(), + 0, 0, '1', 0 +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000898'); + +-- 4) admin 授权 +INSERT INTO `sys_role_permission` (`id`, `role_id`, `permission_id`, `data_rule_ids`, `operate_date`, `operate_ip`) +SELECT REPLACE(UUID(), '-', ''), r.id, p.id, NULL, NOW(), '127.0.0.1' +FROM `sys_role` r +JOIN ( + SELECT id FROM `sys_permission` + WHERE id IN ( + '1900000000000000897','1900000000000000898', + '1900000000000000732','1900000000000000733','1900000000000000734', + '1900000000000000735','1900000000000000736','1900000000000000737' + ) +) p ON 1 = 1 +WHERE r.`role_code` = 'admin' + AND NOT EXISTS ( + SELECT 1 FROM `sys_role_permission` rp + WHERE rp.`role_id` = r.id AND rp.`permission_id` = p.id + ); + +-- 5) 打印实体映射 +INSERT INTO `print_biz_perm_entity` (`perm_id`, `entity_class`) +SELECT '1900000000000000897', 'org.jeecg.modules.mes.material.entity.MesRawMaterialInspectStd' +FROM DUAL +WHERE NOT EXISTS ( + SELECT 1 FROM `print_biz_perm_entity` + WHERE `perm_id` = '1900000000000000897' + AND `entity_class` = 'org.jeecg.modules.mes.material.entity.MesRawMaterialInspectStd' +); + +SELECT p.id, pp.name AS parent_name, p.name, p.url, p.component +FROM sys_permission p +LEFT JOIN sys_permission pp ON pp.id = p.parent_id +WHERE p.id = '1900000000000000897' OR p.parent_id = '1900000000000000897'; diff --git a/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_168__restore_raw_material_log_menus.sql b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_168__restore_raw_material_log_menus.sql new file mode 100644 index 00000000..d490efc4 --- /dev/null +++ b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_168__restore_raw_material_log_menus.sql @@ -0,0 +1,123 @@ +-- 补回被需求计划菜单 ID 占用挤掉的两个日志菜单(新 ID,不改动现有需求计划) +-- 1) 原材料卡片修改日志 +-- 2) 原料入场删除日志 +SET NAMES utf8mb4; + +-- ===================== 1. 原材料卡片修改日志 ===================== +INSERT INTO `sys_permission` ( + `id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`, + `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, + `hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, + `del_flag`, `rule_flag`, `status`, `internal_or_external` +) +SELECT + '1900000000000000899', '1900000000000000700', '原材料卡片修改日志', + '/xslmes/mesXslRawMaterialCardEditLog', + 'xslmes/mesXslRawMaterialCardEditLog/MesXslRawMaterialCardEditLogList', + 1, NULL, NULL, 1, NULL, '0', 13.00, 0, 'ant-design:history-outlined', 1, 1, + 0, 0, '记录 Web/桌面端编辑原材料卡片剩余量与库区', 'admin', NOW(), 'admin', NOW(), + 0, 0, '1', 0 +FROM DUAL +WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000899') + AND NOT EXISTS ( + SELECT 1 FROM `sys_permission` + WHERE `del_flag` = 0 AND `menu_type` = 1 AND `name` = '原材料卡片修改日志' + ); + +INSERT INTO `sys_permission` ( + `id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`, + `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, + `hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, + `del_flag`, `rule_flag`, `status`, `internal_or_external` +) +SELECT + '1900000000000000900', '1900000000000000899', '查询', NULL, NULL, 0, NULL, NULL, + 2, 'xslmes:mes_xsl_raw_material_card_edit_log:list', '1', 1.00, 0, NULL, 1, 0, + 0, 0, NULL, 'admin', NOW(), 'admin', NOW(), 0, 0, '1', 0 +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000900'); + +-- 原 0712 仍是卡片修改日志导出权限,挪到新菜单下 +UPDATE `sys_permission` +SET `parent_id` = '1900000000000000899', `name` = '导出', `update_time` = NOW() +WHERE `id` = '1900000000000000712' + AND `perms` = 'xslmes:mes_xsl_raw_material_card_edit_log:exportXls'; + +-- 若 0712 已被改成别的权限,则新建导出按钮 +INSERT INTO `sys_permission` ( + `id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`, + `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, + `hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, + `del_flag`, `rule_flag`, `status`, `internal_or_external` +) +SELECT + '1900000000000000901', '1900000000000000899', '导出', NULL, NULL, 0, NULL, NULL, + 2, 'xslmes:mes_xsl_raw_material_card_edit_log:exportXls', '1', 2.00, 0, NULL, 1, 0, + 0, 0, NULL, 'admin', NOW(), 'admin', NOW(), 0, 0, '1', 0 +FROM DUAL +WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000901') + AND NOT EXISTS ( + SELECT 1 FROM `sys_permission` + WHERE `parent_id` = '1900000000000000899' + AND `perms` = 'xslmes:mes_xsl_raw_material_card_edit_log:exportXls' + ); + +-- ===================== 2. 原料入场删除日志 ===================== +INSERT INTO `sys_permission` ( + `id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`, + `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, + `hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, + `del_flag`, `rule_flag`, `status`, `internal_or_external` +) +SELECT + '1900000000000000902', '1900000000000000700', '原料入场删除日志', + '/xslmes/mesXslRawMaterialEntryDeleteLog', + 'xslmes/mesXslRawMaterialEntryDeleteLog/MesXslRawMaterialEntryDeleteLogList', + 1, NULL, NULL, 1, NULL, '0', 14.00, 0, 'ant-design:delete-row-outlined', 1, 1, + 0, 0, '查询已逻辑删除的原料入场记录', 'admin', NOW(), 'admin', NOW(), + 0, 0, '1', 0 +FROM DUAL +WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000902') + AND NOT EXISTS ( + SELECT 1 FROM `sys_permission` + WHERE `del_flag` = 0 AND `menu_type` = 1 AND `name` = '原料入场删除日志' + ); + +INSERT INTO `sys_permission` ( + `id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`, + `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, + `hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, + `del_flag`, `rule_flag`, `status`, `internal_or_external` +) +SELECT + '1900000000000000903', '1900000000000000902', '查询', NULL, NULL, 0, NULL, NULL, + 2, 'xslmes:mes_xsl_raw_material_entry_delete_log:list', '1', 1.00, 0, NULL, 1, 0, + 0, 0, NULL, 'admin', NOW(), 'admin', NOW(), 0, 0, '1', 0 +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000903'); + +-- ===================== 3. admin 授权 ===================== +INSERT INTO `sys_role_permission` (`id`, `role_id`, `permission_id`, `data_rule_ids`, `operate_date`, `operate_ip`) +SELECT REPLACE(UUID(), '-', ''), r.id, p.id, NULL, NOW(), '127.0.0.1' +FROM `sys_role` r +JOIN ( + SELECT id FROM `sys_permission` + WHERE id IN ( + '1900000000000000899','1900000000000000900','1900000000000000901', + '1900000000000000712', + '1900000000000000902','1900000000000000903' + ) +) p ON 1 = 1 +WHERE r.`role_code` = 'admin' + AND NOT EXISTS ( + SELECT 1 FROM `sys_role_permission` rp + WHERE rp.`role_id` = r.id AND rp.`permission_id` = p.id + ); + +SELECT p.id, pp.name AS parent_name, p.name, p.url, p.component, p.perms +FROM sys_permission p +LEFT JOIN sys_permission pp ON pp.id = p.parent_id +WHERE p.id IN ( + '1900000000000000899','1900000000000000900','1900000000000000901','1900000000000000712', + '1900000000000000902','1900000000000000903' +) +OR p.parent_id IN ('1900000000000000899','1900000000000000902') +ORDER BY p.id; diff --git a/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_169__fix_misparented_menu_buttons.sql b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_169__fix_misparented_menu_buttons.sql new file mode 100644 index 00000000..589955a3 --- /dev/null +++ b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_169__fix_misparented_menu_buttons.sql @@ -0,0 +1,149 @@ +-- 修复挂错父菜单的按钮,并补回被 ID 占用挤掉的页面 +-- A) 胶料小料锁定日志(0215 被设备对应部位占用) +-- B) 清理工序管理/设备类别「新增」按钮上误写的页面 url/component +-- C) 原材料检验项目(导入按钮 0067 误挂在工序「新增」下) +SET NAMES utf8mb4; + +-- ===================== A. 胶料小料锁定日志 ===================== +INSERT INTO `sys_permission` ( + `id`, `parent_id`, `name`, `url`, `component`, `component_name`, `is_route`, `redirect`, + `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, + `hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, + `del_flag`, `rule_flag`, `status`, `internal_or_external` +) +SELECT + '1900000000000000904', '1860000000000000162', '胶料小料锁定日志', + '/xslmes/mesXslRubberSmallLockLog', + 'xslmes/mesXslRubberSmallLockLog/MesXslRubberSmallLockLogList', + 'MesXslRubberSmallLockLogList', 1, NULL, + 1, NULL, '0', 7.00, 0, 'ant-design:file-text-outlined', 1, 1, + 0, 0, 'MES胶料小料锁定日志', 'admin', NOW(), 'admin', NOW(), + 0, 0, '1', 0 +FROM DUAL +WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000904') + AND NOT EXISTS ( + SELECT 1 FROM `sys_permission` + WHERE `del_flag` = 0 AND `menu_type` = 1 AND `name` = '胶料小料锁定日志' + ); + +-- 锁定日志按钮从「设备对应部位」挪回 +UPDATE `sys_permission` +SET `parent_id` = '1900000000000000904', `update_time` = NOW() +WHERE `id` IN ( + '1860000000000000217','1860000000000000218','1860000000000000219', + '1860000000000000220','1860000000000000221' +) +AND `perms` LIKE 'mes:mes_xsl_rubber_small_lock_log:%'; + +-- 补「新增」(原 0216 已被设备对应部位导出占用) +INSERT INTO `sys_permission` ( + `id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`, + `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, + `hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, + `del_flag`, `rule_flag`, `status`, `internal_or_external` +) +SELECT + '1900000000000000905', '1900000000000000904', '新增', NULL, NULL, 0, NULL, NULL, + 2, 'mes:mes_xsl_rubber_small_lock_log:add', '1', 1.00, 0, NULL, 1, 0, + 0, 0, NULL, 'admin', NOW(), 'admin', NOW(), 0, 0, '1', 0 +FROM DUAL +WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000905') + AND NOT EXISTS ( + SELECT 1 FROM `sys_permission` + WHERE `parent_id` = '1900000000000000904' + AND `perms` = 'mes:mes_xsl_rubber_small_lock_log:add' + ); + +-- ===================== B. 清理按钮上误写的页面路由 ===================== +UPDATE `sys_permission` +SET `url` = NULL, `component` = NULL, `component_name` = NULL, `is_route` = 0, `update_time` = NOW() +WHERE `id` IN ('1860000000000000061', '1860000000000000071') + AND `menu_type` = 2; + +-- ===================== C. 原材料检验项目 ===================== +INSERT INTO `sys_permission` ( + `id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`, + `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, + `hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, + `del_flag`, `rule_flag`, `status`, `internal_or_external` +) +SELECT + '1900000000000000906', '1900000000000000700', '原材料检验项目', + '/mes/rawMaterialInspectItem', 'mes/rawmaterialinspectitem/index', + 1, NULL, NULL, 1, NULL, '0', 21.40, 0, 'ant-design:check-square-outlined', 1, 1, + 0, 0, 'MES原材料检验项目', 'admin', NOW(), 'admin', NOW(), + 0, 0, '1', 0 +FROM DUAL +WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000906') + AND NOT EXISTS ( + SELECT 1 FROM `sys_permission` + WHERE `del_flag` = 0 AND `menu_type` = 1 AND `name` = '原材料检验项目' + ); + +-- 导入按钮从工序「新增」下挪到检验项目 +UPDATE `sys_permission` +SET `parent_id` = '1900000000000000906', `name` = '导入', `update_time` = NOW() +WHERE `id` = '1860000000000000067' + AND `perms` = 'mes:mes_raw_material_inspect_item:importExcel'; + +INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `create_by`, `create_time`) +SELECT '1900000000000000907', '1900000000000000906', '新增', 2, 'mes:mes_raw_material_inspect_item:add', '1', 1.00, 0, 1, 0, '1', 0, 'admin', NOW() +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000907'); + +INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `create_by`, `create_time`) +SELECT '1900000000000000908', '1900000000000000906', '编辑', 2, 'mes:mes_raw_material_inspect_item:edit', '1', 2.00, 0, 1, 0, '1', 0, 'admin', NOW() +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000908'); + +INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `create_by`, `create_time`) +SELECT '1900000000000000909', '1900000000000000906', '删除', 2, 'mes:mes_raw_material_inspect_item:delete', '1', 3.00, 0, 1, 0, '1', 0, 'admin', NOW() +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000909'); + +INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `create_by`, `create_time`) +SELECT '1900000000000000910', '1900000000000000906', '批量删除', 2, 'mes:mes_raw_material_inspect_item:deleteBatch', '1', 4.00, 0, 1, 0, '1', 0, 'admin', NOW() +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000910'); + +INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `create_by`, `create_time`) +SELECT '1900000000000000911', '1900000000000000906', '导出', 2, 'mes:mes_raw_material_inspect_item:exportXls', '1', 5.00, 0, 1, 0, '1', 0, 'admin', NOW() +FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000911'); + +-- ===================== D. admin 授权 ===================== +INSERT INTO `sys_role_permission` (`id`, `role_id`, `permission_id`, `data_rule_ids`, `operate_date`, `operate_ip`) +SELECT REPLACE(UUID(), '-', ''), r.id, p.id, NULL, NOW(), '127.0.0.1' +FROM `sys_role` r +JOIN ( + SELECT id FROM `sys_permission` + WHERE id IN ( + '1900000000000000904','1900000000000000905', + '1860000000000000217','1860000000000000218','1860000000000000219', + '1860000000000000220','1860000000000000221', + '1900000000000000906','1860000000000000067', + '1900000000000000907','1900000000000000908','1900000000000000909', + '1900000000000000910','1900000000000000911' + ) +) p ON 1 = 1 +WHERE r.`role_code` = 'admin' + AND NOT EXISTS ( + SELECT 1 FROM `sys_role_permission` rp + WHERE rp.`role_id` = r.id AND rp.`permission_id` = p.id + ); + +-- 校验输出 +SELECT 'lock_log' AS kind, p.id, pp.name AS parent_name, p.name, p.perms, p.url +FROM sys_permission p +LEFT JOIN sys_permission pp ON pp.id = p.parent_id +WHERE p.id = '1900000000000000904' OR p.parent_id = '1900000000000000904' +UNION ALL +SELECT 'inspect_item', p.id, pp.name, p.name, p.perms, p.url +FROM sys_permission p +LEFT JOIN sys_permission pp ON pp.id = p.parent_id +WHERE p.id = '1900000000000000906' OR p.parent_id = '1900000000000000906' +UNION ALL +SELECT 'cleaned_btn', p.id, pp.name, p.name, p.perms, p.url +FROM sys_permission p +LEFT JOIN sys_permission pp ON pp.id = p.parent_id +WHERE p.id IN ('1860000000000000061','1860000000000000071') +UNION ALL +SELECT 'equip_part', p.id, pp.name, p.name, p.perms, p.url +FROM sys_permission p +LEFT JOIN sys_permission pp ON pp.id = p.parent_id +WHERE p.id = '1860000000000000215' OR p.parent_id = '1860000000000000215'; diff --git a/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_170__grant_raw_material_inspect_role_perms.sql b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_170__grant_raw_material_inspect_role_perms.sql new file mode 100644 index 00000000..94f8f8f4 --- /dev/null +++ b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_170__grant_raw_material_inspect_role_perms.sql @@ -0,0 +1,17 @@ +-- 原材料检验项目/标准:补授权给 admin/system/vue3/zuhuadmin(菜单恢复后按钮权限未同步到各角色) +SET NAMES utf8mb4; + +INSERT INTO `sys_role_permission` (`id`, `role_id`, `permission_id`, `operate_date`, `operate_ip`) +SELECT REPLACE(UUID(), '-', ''), r.id, p.id, NOW(), '127.0.0.1' +FROM `sys_role` r +CROSS JOIN `sys_permission` p +WHERE r.role_code IN ('admin', 'system', 'vue3', 'zuhuadmin') + AND ( + p.perms LIKE 'mes:mes_raw_material_inspect_item:%' + OR p.perms LIKE 'mes:mes_raw_material_inspect_std:%' + OR p.id IN ('1900000000000000897', '1900000000000000906') + ) + AND NOT EXISTS ( + SELECT 1 FROM `sys_role_permission` rp + WHERE rp.role_id = r.id AND rp.permission_id = p.id + ); diff --git a/jeecgboot-vue3/package.json b/jeecgboot-vue3/package.json index 5944ea23..b4ea6926 100644 --- a/jeecgboot-vue3/package.json +++ b/jeecgboot-vue3/package.json @@ -34,7 +34,7 @@ "@traptitech/markdown-it-katex": "^3.6.0", "@vant/area-data": "^1.5.2", "@vue/shared": "^3.5.22", - "@vueuse/core": "^10.11.1", + "@vueuse/core": "^10.11.1", "@zxcvbn-ts/core": "^3.0.4", "ant-design-vue": "^4.2.6", "axios": "^1.12.2", diff --git a/jeecgboot-vue3/src/views/system/appconfig/McsDbConfig.data.ts b/jeecgboot-vue3/src/views/system/appconfig/McsDbConfig.data.ts index 213b9d37..15ddf856 100644 --- a/jeecgboot-vue3/src/views/system/appconfig/McsDbConfig.data.ts +++ b/jeecgboot-vue3/src/views/system/appconfig/McsDbConfig.data.ts @@ -3,12 +3,35 @@ import { FormSchema } from '/@/components/Form'; export const mcsDbConfigFormSchema: FormSchema[] = [ { label: 'id', field: 'id', component: 'Input', show: false }, { label: 'tenantId', field: 'tenantId', component: 'InputNumber', show: false }, + { + label: '访问模式', + field: 'accessMode', + component: 'Select', + defaultValue: 'DESKTOP_AGENT', + required: true, + helpMessage: '桌面代理:由厂区桌面端直连设备库;直连:MES 服务器 JDBC(仅调试)', + componentProps: { + options: [ + { label: '桌面代理(推荐)', value: 'DESKTOP_AGENT' }, + { label: '服务端直连', value: 'DIRECT' }, + ], + }, + }, + { + label: '绑定桌面代理 ID', + field: 'agentDeviceId', + component: 'Input', + helpMessage: '与桌面端「设备 ID」一致;留空则自动选用当前在线代理', + componentProps: { placeholder: '例如工控机计算机名' }, + ifShow: ({ values }) => values.accessMode !== 'DIRECT', + }, { label: '服务器地址', field: 'serverHost', component: 'Input', required: true, componentProps: { placeholder: 'IP 或域名,例如 192.168.1.10 或 xxx.vicp.fun' }, + ifShow: ({ values }) => values.accessMode === 'DIRECT', }, { label: '端口', @@ -17,6 +40,7 @@ export const mcsDbConfigFormSchema: FormSchema[] = [ defaultValue: 1433, required: true, componentProps: { min: 1, max: 65535, style: { width: '100%' } }, + ifShow: ({ values }) => values.accessMode === 'DIRECT', }, { label: '数据库名', @@ -24,6 +48,7 @@ export const mcsDbConfigFormSchema: FormSchema[] = [ component: 'Input', defaultValue: 'MES_ShareDB', required: true, + ifShow: ({ values }) => values.accessMode === 'DIRECT', }, { label: '用户名', @@ -31,12 +56,14 @@ export const mcsDbConfigFormSchema: FormSchema[] = [ component: 'Input', required: true, componentProps: { placeholder: '例如 sa' }, + ifShow: ({ values }) => values.accessMode === 'DIRECT', }, { label: '密码', field: 'dbPassword', component: 'InputPassword', componentProps: { placeholder: '编辑时留空表示不修改密码' }, + ifShow: ({ values }) => values.accessMode === 'DIRECT', }, { label: '读取开关', @@ -68,7 +95,7 @@ export const mcsDbConfigFormSchema: FormSchema[] = [ label: '启用连接', field: 'status', component: 'Switch', - helpMessage: '开启后立即连接中间库并热刷新数据源,无需重启后端', + helpMessage: '开启后立即生效(桌面代理模式无需 MES 直连 SQL Server)', componentProps: { checkedChildren: '启用', checkedValue: 1, @@ -83,6 +110,7 @@ export const mcsDbConfigFormSchema: FormSchema[] = [ component: 'InputNumber', defaultValue: 120, componentProps: { min: 10, max: 600, style: { width: '100%' } }, + ifShow: ({ values }) => values.accessMode === 'DIRECT', }, { label: '连接超时(毫秒)', @@ -90,6 +118,7 @@ export const mcsDbConfigFormSchema: FormSchema[] = [ component: 'InputNumber', defaultValue: 120000, componentProps: { min: 5000, max: 600000, style: { width: '100%' } }, + ifShow: ({ values }) => values.accessMode === 'DIRECT', }, { label: '备注', diff --git a/jeecgboot-vue3/src/views/system/appconfig/McsDbConfigModal.vue b/jeecgboot-vue3/src/views/system/appconfig/McsDbConfigModal.vue index b6c745fb..7eb939d9 100644 --- a/jeecgboot-vue3/src/views/system/appconfig/McsDbConfigModal.vue +++ b/jeecgboot-vue3/src/views/system/appconfig/McsDbConfigModal.vue @@ -33,15 +33,15 @@ const values = await getMcsDbConfig({ tenantId }); setModalProps({ confirmLoading: false }); if (values) { - await setFieldsValue({ ...values, dbPassword: '' }); + await setFieldsValue({ ...values, dbPassword: '', accessMode: values.accessMode || 'DESKTOP_AGENT' }); } else { - await setFieldsValue({ tenantId, readEnabled: 1, writeEnabled: 1, status: 0, serverPort: 1433, dbName: 'MES_ShareDB' }); + await setFieldsValue({ tenantId, readEnabled: 1, writeEnabled: 1, status: 0, serverPort: 1433, dbName: 'MES_ShareDB', accessMode: 'DESKTOP_AGENT' }); } }); async function handleSubmit() { const values = await validate(); - if (!values.id && !values.dbPassword) { + if (!values.id && values.accessMode === 'DIRECT' && !values.dbPassword) { createMessage.warning('请输入数据库密码'); return; } diff --git a/jeecgboot-vue3/src/views/system/appconfig/ThirdAppMcsDbConfigForm.vue b/jeecgboot-vue3/src/views/system/appconfig/ThirdAppMcsDbConfigForm.vue index 69f2d468..1991dfe1 100644 --- a/jeecgboot-vue3/src/views/system/appconfig/ThirdAppMcsDbConfigForm.vue +++ b/jeecgboot-vue3/src/views/system/appconfig/ThirdAppMcsDbConfigForm.vue @@ -7,8 +7,8 @@
1.配置说明
- 在此配置 SQL Server 中间库(MES_ShareDB)连接信息。保存后立即生效,无需重启后端,也无需修改 application.yml。 - 读取开关控制 MCS→MES 方向数据查询;写入开关控制 MES→MCS 方向数据写入。 + 推荐使用桌面代理模式:厂区桌面端直连设备 SQL Server,MES 通过 STOMP 命令读写。 + 也可切换为服务端直连(仅调试)。读取/写入开关仍控制 MCS↔MES 方向。
@@ -22,18 +22,32 @@
+
访问模式
+
+ {{ runtimeStatus.agentMode ? '桌面代理' : '服务端直连' }} +
+
+
+
桌面代理
+
+ {{ runtimeStatus.agentOnline ? '在线' : '离线' }} + {{ runtimeStatus.agentDbConnected ? '库已连通' : '库未连通' }} + {{ runtimeStatus.agentHostName || configData.agentDeviceId }} +
+
+
服务器
-
+
数据库
-
+
用户名
@@ -52,14 +66,14 @@
-
连接状态
+
配置状态
- {{ runtimeStatus.dbConfigActive ? '已启用数据库配置' : '使用 yml 默认配置' }} + {{ runtimeStatus.dbConfigActive ? '已启用' : '未启用/回退 yml' }}
编辑配置 - 连接测试 + {{ runtimeStatus.agentMode ? '请求桌面探测' : '连接测试' }} 删除配置
@@ -97,9 +111,12 @@ const connStatus = computed(() => { const s = runtimeStatus.value; if (!configData.value.id) { - return '尚未保存数据库配置,当前使用 application.yml 中的 sqlserver_mcs 默认连接。'; + return '尚未保存配置。推荐使用桌面代理模式:桌面端连接设备库后,MES 即可读写。'; } - return `读取:${s.readEnabled ? '开' : '关'} | 写入:${s.writeEnabled ? '开' : '关'} | 连接:${s.dbConfigActive ? '已启用数据库配置' : '使用 yml 默认配置'}`; + const mode = s.agentMode ? '桌面代理' : '服务端直连'; + const agent = s.agentOnline ? '代理在线' : '代理离线'; + const db = s.agentDbConnected ? '库连通' : '库未连通'; + return `模式:${mode} | ${agent} | ${db} | 读取:${s.readEnabled ? '开' : '关'} | 写入:${s.writeEnabled ? '开' : '关'}${s.agentDbMessage ? ' | ' + s.agentDbMessage : ''}`; }); async function reload() { diff --git a/jeecgboot-vue3/src/views/xslmes/mesXslAutoSmallMaterialDemandPlan/MesXslAutoSmallMaterialDemandPlan.data.ts b/jeecgboot-vue3/src/views/xslmes/mesXslAutoSmallMaterialDemandPlan/MesXslAutoSmallMaterialDemandPlan.data.ts index 225c8e51..16506723 100644 --- a/jeecgboot-vue3/src/views/xslmes/mesXslAutoSmallMaterialDemandPlan/MesXslAutoSmallMaterialDemandPlan.data.ts +++ b/jeecgboot-vue3/src/views/xslmes/mesXslAutoSmallMaterialDemandPlan/MesXslAutoSmallMaterialDemandPlan.data.ts @@ -1,13 +1,18 @@ import { BasicColumn, FormSchema } from '/@/components/Table'; export const baseColumns: BasicColumn[] = [ + { title: '日期', align: 'center', dataIndex: 'statDate', width: 140 }, { title: '原材料名称', align: 'center', dataIndex: 'rawMaterialName', width: 260, ellipsis: true }, { title: '需求重量(KG)', align: 'center', dataIndex: 'demandWeight', width: 180 }, ]; -export const machineColumns: BasicColumn[] = [{ title: '机台', align: 'center', dataIndex: 'machineName', width: 180 }, ...baseColumns]; +export const machineColumns: BasicColumn[] = [ + baseColumns[0], + { title: '机台', align: 'center', dataIndex: 'machineName', width: 180 }, + ...baseColumns.slice(1), +]; -export const searchFormSchema: FormSchema[] = [ +export const baseSearchFormSchema: FormSchema[] = [ { label: '日期', field: 'statDate', @@ -18,6 +23,12 @@ export const searchFormSchema: FormSchema[] = [ { label: '原材料名称', field: 'rawMaterialName', component: 'JInput', colProps: { span: 6 } }, ]; +export const machineSearchFormSchema: FormSchema[] = [ + baseSearchFormSchema[0], + { label: '机台', field: 'machineName', component: 'JInput', colProps: { span: 6 } }, + ...baseSearchFormSchema.slice(1), +]; + export const superQuerySchema = { machineName: { title: '机台', order: 0, view: 'text' }, statDate: { title: '日期', order: 1, view: 'date' }, diff --git a/jeecgboot-vue3/src/views/xslmes/mesXslAutoSmallMaterialDemandPlan/MesXslAutoSmallMaterialDemandPlanList.vue b/jeecgboot-vue3/src/views/xslmes/mesXslAutoSmallMaterialDemandPlan/MesXslAutoSmallMaterialDemandPlanList.vue index 7c4941f8..f26dcac9 100644 --- a/jeecgboot-vue3/src/views/xslmes/mesXslAutoSmallMaterialDemandPlan/MesXslAutoSmallMaterialDemandPlanList.vue +++ b/jeecgboot-vue3/src/views/xslmes/mesXslAutoSmallMaterialDemandPlan/MesXslAutoSmallMaterialDemandPlanList.vue @@ -25,7 +25,8 @@ import { baseColumns, machineColumns, - searchFormSchema, + baseSearchFormSchema, + machineSearchFormSchema, superQuerySchema, } from './MesXslAutoSmallMaterialDemandPlan.data'; import { list, getExportUrl } from './MesXslAutoSmallMaterialDemandPlan.api'; @@ -40,7 +41,7 @@ columns: baseColumns, canResize: true, formConfig: { - schemas: searchFormSchema, + schemas: baseSearchFormSchema, autoSubmitOnEnter: true, showAdvancedButton: true, }, @@ -57,20 +58,33 @@ }, }); - const [registerTable, { reload, setColumns }] = tableContext; + const [registerTable, { reload, setColumns, setProps }] = tableContext; const superQueryConfig = reactive(superQuerySchema); onMounted(() => { applyColumns(); + applySearchSchemas(); }); function applyColumns() { setColumns(groupByMachine.value ? machineColumns : baseColumns); } + function applySearchSchemas() { + setProps({ + formConfig: { + schemas: groupByMachine.value ? machineSearchFormSchema : baseSearchFormSchema, + }, + }); + } + function onGroupByMachineChange() { queryParam.groupByMachine = groupByMachine.value ? 1 : 0; + if (!groupByMachine.value) { + delete queryParam.machineName; + } applyColumns(); + applySearchSchemas(); reload(); } diff --git a/jeecgboot-vue3/src/views/xslmes/mesXslManualSmallMaterialDemandPlan/MesXslManualSmallMaterialDemandPlan.data.ts b/jeecgboot-vue3/src/views/xslmes/mesXslManualSmallMaterialDemandPlan/MesXslManualSmallMaterialDemandPlan.data.ts index 225c8e51..16506723 100644 --- a/jeecgboot-vue3/src/views/xslmes/mesXslManualSmallMaterialDemandPlan/MesXslManualSmallMaterialDemandPlan.data.ts +++ b/jeecgboot-vue3/src/views/xslmes/mesXslManualSmallMaterialDemandPlan/MesXslManualSmallMaterialDemandPlan.data.ts @@ -1,13 +1,18 @@ import { BasicColumn, FormSchema } from '/@/components/Table'; export const baseColumns: BasicColumn[] = [ + { title: '日期', align: 'center', dataIndex: 'statDate', width: 140 }, { title: '原材料名称', align: 'center', dataIndex: 'rawMaterialName', width: 260, ellipsis: true }, { title: '需求重量(KG)', align: 'center', dataIndex: 'demandWeight', width: 180 }, ]; -export const machineColumns: BasicColumn[] = [{ title: '机台', align: 'center', dataIndex: 'machineName', width: 180 }, ...baseColumns]; +export const machineColumns: BasicColumn[] = [ + baseColumns[0], + { title: '机台', align: 'center', dataIndex: 'machineName', width: 180 }, + ...baseColumns.slice(1), +]; -export const searchFormSchema: FormSchema[] = [ +export const baseSearchFormSchema: FormSchema[] = [ { label: '日期', field: 'statDate', @@ -18,6 +23,12 @@ export const searchFormSchema: FormSchema[] = [ { label: '原材料名称', field: 'rawMaterialName', component: 'JInput', colProps: { span: 6 } }, ]; +export const machineSearchFormSchema: FormSchema[] = [ + baseSearchFormSchema[0], + { label: '机台', field: 'machineName', component: 'JInput', colProps: { span: 6 } }, + ...baseSearchFormSchema.slice(1), +]; + export const superQuerySchema = { machineName: { title: '机台', order: 0, view: 'text' }, statDate: { title: '日期', order: 1, view: 'date' }, diff --git a/jeecgboot-vue3/src/views/xslmes/mesXslManualSmallMaterialDemandPlan/MesXslManualSmallMaterialDemandPlanList.vue b/jeecgboot-vue3/src/views/xslmes/mesXslManualSmallMaterialDemandPlan/MesXslManualSmallMaterialDemandPlanList.vue index 8fae95f1..a8084552 100644 --- a/jeecgboot-vue3/src/views/xslmes/mesXslManualSmallMaterialDemandPlan/MesXslManualSmallMaterialDemandPlanList.vue +++ b/jeecgboot-vue3/src/views/xslmes/mesXslManualSmallMaterialDemandPlan/MesXslManualSmallMaterialDemandPlanList.vue @@ -25,7 +25,8 @@ import { baseColumns, machineColumns, - searchFormSchema, + baseSearchFormSchema, + machineSearchFormSchema, superQuerySchema, } from './MesXslManualSmallMaterialDemandPlan.data'; import { list, getExportUrl } from './MesXslManualSmallMaterialDemandPlan.api'; @@ -40,7 +41,7 @@ columns: baseColumns, canResize: true, formConfig: { - schemas: searchFormSchema, + schemas: baseSearchFormSchema, autoSubmitOnEnter: true, showAdvancedButton: true, }, @@ -57,20 +58,33 @@ }, }); - const [registerTable, { reload, setColumns }] = tableContext; + const [registerTable, { reload, setColumns, setProps }] = tableContext; const superQueryConfig = reactive(superQuerySchema); onMounted(() => { applyColumns(); + applySearchSchemas(); }); function applyColumns() { setColumns(groupByMachine.value ? machineColumns : baseColumns); } + function applySearchSchemas() { + setProps({ + formConfig: { + schemas: groupByMachine.value ? machineSearchFormSchema : baseSearchFormSchema, + }, + }); + } + function onGroupByMachineChange() { queryParam.groupByMachine = groupByMachine.value ? 1 : 0; + if (!groupByMachine.value) { + delete queryParam.machineName; + } applyColumns(); + applySearchSchemas(); reload(); } diff --git a/jeecgboot-vue3/src/views/xslmes/mesXslMillAction/MesXslMillAction.api.ts b/jeecgboot-vue3/src/views/xslmes/mesXslMillAction/MesXslMillAction.api.ts new file mode 100644 index 00000000..2a5d6119 --- /dev/null +++ b/jeecgboot-vue3/src/views/xslmes/mesXslMillAction/MesXslMillAction.api.ts @@ -0,0 +1,13 @@ +import { defHttp } from '/@/utils/http/axios'; + +enum Api { + list = '/xslmes/mesXslMillAction/list', + queryById = '/xslmes/mesXslMillAction/queryById', + exportXls = '/xslmes/mesXslMillAction/exportXls', +} + +export const getExportUrl = Api.exportXls; + +export const list = (params) => defHttp.get({ url: Api.list, params }); + +export const queryById = (params) => defHttp.get({ url: Api.queryById, params }); diff --git a/jeecgboot-vue3/src/views/xslmes/mesXslMillAction/MesXslMillAction.data.ts b/jeecgboot-vue3/src/views/xslmes/mesXslMillAction/MesXslMillAction.data.ts new file mode 100644 index 00000000..a2767a29 --- /dev/null +++ b/jeecgboot-vue3/src/views/xslmes/mesXslMillAction/MesXslMillAction.data.ts @@ -0,0 +1,33 @@ +import { BasicColumn, FormSchema } from '/@/components/Table'; + +export const columns: BasicColumn[] = [ + { title: 'GUID', align: 'center', dataIndex: 'guid', width: 280 }, + { title: '动作地址', align: 'center', dataIndex: 'actAddr', width: 100 }, + { title: '动作名称', align: 'center', dataIndex: 'actName', width: 140 }, + { title: '动作名称(英)', align: 'center', dataIndex: 'actNameEn', width: 140 }, + { title: '动作备注', align: 'center', dataIndex: 'actMemo', width: 120 }, + { title: '关联地址', align: 'center', dataIndex: 'actRepaddr', width: 100 }, + { title: '写入时间', align: 'center', dataIndex: 'writeTime', width: 170 }, + { title: '读写标识', align: 'center', dataIndex: 'rwFlag', width: 90 }, + { title: '更新时间', align: 'center', dataIndex: 'updateTime', width: 170 }, +]; + +export const searchFormSchema: FormSchema[] = [ + { label: '动作名称', field: 'actName', component: 'Input', colProps: { span: 6 } }, + { label: '动作地址', field: 'actAddr', component: 'InputNumber', colProps: { span: 6 } }, + { label: 'GUID', field: 'guid', component: 'Input', colProps: { span: 6 } }, +]; + +export const formSchema: FormSchema[] = [ + { label: '', field: 'id', component: 'Input', show: false }, + { label: 'GUID', field: 'guid', component: 'Input', componentProps: { disabled: true }, colProps: { span: 24 } }, + { label: '动作地址', field: 'actAddr', component: 'InputNumber', componentProps: { disabled: true }, colProps: { span: 12 } }, + { label: '关联地址', field: 'actRepaddr', component: 'InputNumber', componentProps: { disabled: true }, colProps: { span: 12 } }, + { label: '动作名称', field: 'actName', component: 'Input', componentProps: { disabled: true }, colProps: { span: 12 } }, + { label: '动作名称(英)', field: 'actNameEn', component: 'Input', componentProps: { disabled: true }, colProps: { span: 12 } }, + { label: '动作备注', field: 'actMemo', component: 'Input', componentProps: { disabled: true }, colProps: { span: 24 } }, + { label: '写入时间', field: 'writeTime', component: 'Input', componentProps: { disabled: true }, colProps: { span: 12 } }, + { label: '读取时间', field: 'readTime', component: 'Input', componentProps: { disabled: true }, colProps: { span: 12 } }, + { label: '读写标识', field: 'rwFlag', component: 'InputNumber', componentProps: { disabled: true }, colProps: { span: 12 } }, + { label: '更新时间', field: 'updateTime', component: 'Input', componentProps: { disabled: true }, colProps: { span: 12 } }, +]; diff --git a/jeecgboot-vue3/src/views/xslmes/mesXslMillAction/MesXslMillActionList.vue b/jeecgboot-vue3/src/views/xslmes/mesXslMillAction/MesXslMillActionList.vue new file mode 100644 index 00000000..5a66e75d --- /dev/null +++ b/jeecgboot-vue3/src/views/xslmes/mesXslMillAction/MesXslMillActionList.vue @@ -0,0 +1,81 @@ + + + + + diff --git a/jeecgboot-vue3/src/views/xslmes/mesXslMillAction/components/MesXslMillActionModal.vue b/jeecgboot-vue3/src/views/xslmes/mesXslMillAction/components/MesXslMillActionModal.vue new file mode 100644 index 00000000..1574dce0 --- /dev/null +++ b/jeecgboot-vue3/src/views/xslmes/mesXslMillAction/components/MesXslMillActionModal.vue @@ -0,0 +1,30 @@ + + + diff --git a/jeecgboot-vue3/src/views/xslmes/mesXslRawMaterialDemandPlan/MesXslRawMaterialDemandPlan.data.ts b/jeecgboot-vue3/src/views/xslmes/mesXslRawMaterialDemandPlan/MesXslRawMaterialDemandPlan.data.ts index ea838df8..0930c4fb 100644 --- a/jeecgboot-vue3/src/views/xslmes/mesXslRawMaterialDemandPlan/MesXslRawMaterialDemandPlan.data.ts +++ b/jeecgboot-vue3/src/views/xslmes/mesXslRawMaterialDemandPlan/MesXslRawMaterialDemandPlan.data.ts @@ -10,16 +10,23 @@ export const baseColumns: BasicColumn[] = [ ]; export const machineColumns: BasicColumn[] = [ + baseColumns[0], { title: '机台', align: 'center', dataIndex: 'machineName', width: 160 }, - ...baseColumns, + ...baseColumns.slice(1), ]; -export const searchFormSchema: FormSchema[] = [ +export const baseSearchFormSchema: FormSchema[] = [ { label: '计划日期', field: 'planDate', component: 'DatePicker', colProps: { span: 6 } }, { label: 'ERP编号', field: 'erpCode', component: 'JInput', colProps: { span: 6 } }, { label: '原材料名称', field: 'rawMaterialName', component: 'JInput', colProps: { span: 6 } }, ]; +export const machineSearchFormSchema: FormSchema[] = [ + baseSearchFormSchema[0], + { label: '机台', field: 'machineName', component: 'JInput', colProps: { span: 6 } }, + ...baseSearchFormSchema.slice(1), +]; + export const superQuerySchema = { planDate: { title: '计划日期', order: 0, view: 'date' }, machineName: { title: '机台', order: 1, view: 'text' }, diff --git a/jeecgboot-vue3/src/views/xslmes/mesXslRawMaterialDemandPlan/MesXslRawMaterialDemandPlanList.vue b/jeecgboot-vue3/src/views/xslmes/mesXslRawMaterialDemandPlan/MesXslRawMaterialDemandPlanList.vue index e2fb2c10..1401ef1d 100644 --- a/jeecgboot-vue3/src/views/xslmes/mesXslRawMaterialDemandPlan/MesXslRawMaterialDemandPlanList.vue +++ b/jeecgboot-vue3/src/views/xslmes/mesXslRawMaterialDemandPlan/MesXslRawMaterialDemandPlanList.vue @@ -25,7 +25,8 @@ import { baseColumns, machineColumns, - searchFormSchema, + baseSearchFormSchema, + machineSearchFormSchema, superQuerySchema, } from './MesXslRawMaterialDemandPlan.data'; import { list, getExportUrl } from './MesXslRawMaterialDemandPlan.api'; @@ -40,7 +41,7 @@ columns: baseColumns, canResize: true, formConfig: { - schemas: searchFormSchema, + schemas: baseSearchFormSchema, autoSubmitOnEnter: true, showAdvancedButton: true, }, @@ -57,20 +58,33 @@ }, }); - const [registerTable, { reload, setColumns }] = tableContext; + const [registerTable, { reload, setColumns, setProps }] = tableContext; const superQueryConfig = reactive(superQuerySchema); onMounted(() => { applyColumns(); + applySearchSchemas(); }); function applyColumns() { setColumns(groupByMachine.value ? machineColumns : baseColumns); } + function applySearchSchemas() { + setProps({ + formConfig: { + schemas: groupByMachine.value ? machineSearchFormSchema : baseSearchFormSchema, + }, + }); + } + function onGroupByMachineChange() { queryParam.groupByMachine = groupByMachine.value ? 1 : 0; + if (!groupByMachine.value) { + delete queryParam.machineName; + } applyColumns(); + applySearchSchemas(); reload(); } diff --git a/jeecgboot-vue3/src/views/xslmesMcs/mcsToMesActMill/McsToMesActMill.api.ts b/jeecgboot-vue3/src/views/xslmesMcs/mcsToMesActMill/McsToMesActMill.api.ts new file mode 100644 index 00000000..eef2b9cb --- /dev/null +++ b/jeecgboot-vue3/src/views/xslmesMcs/mcsToMesActMill/McsToMesActMill.api.ts @@ -0,0 +1,13 @@ +import { defHttp } from '/@/utils/http/axios'; + +enum Api { + list = '/xslmes/mcs/mcsToMesActMill/list', + queryById = '/xslmes/mcs/mcsToMesActMill/queryById', + exportXls = '/xslmes/mcs/mcsToMesActMill/exportXls', +} + +export const getExportUrl = Api.exportXls; + +export const list = (params) => defHttp.get({ url: Api.list, params }); + +export const queryById = (params) => defHttp.get({ url: Api.queryById, params }); diff --git a/jeecgboot-vue3/src/views/xslmesMcs/mcsToMesActMill/McsToMesActMill.data.ts b/jeecgboot-vue3/src/views/xslmesMcs/mcsToMesActMill/McsToMesActMill.data.ts new file mode 100644 index 00000000..6e830996 --- /dev/null +++ b/jeecgboot-vue3/src/views/xslmesMcs/mcsToMesActMill/McsToMesActMill.data.ts @@ -0,0 +1,29 @@ +import { BasicColumn, FormSchema } from '/@/components/Table'; + +export const columns: BasicColumn[] = [ + { title: 'GUID', align: 'center', dataIndex: 'guid', width: 280 }, + { title: '动作地址', align: 'center', dataIndex: 'actAddr', width: 100 }, + { title: '动作名称', align: 'center', dataIndex: 'actName', width: 140 }, + { title: '动作名称(英)', align: 'center', dataIndex: 'actNameEn', width: 140 }, + { title: '动作备注', align: 'center', dataIndex: 'actMemo', width: 120 }, + { title: '关联地址', align: 'center', dataIndex: 'actRepaddr', width: 100 }, + { title: '写入时间', align: 'center', dataIndex: 'writeTime', width: 170 }, + { title: '读写标识', align: 'center', dataIndex: 'rwFlag', width: 90 }, +]; + +export const searchFormSchema: FormSchema[] = [ + { label: '动作名称', field: 'actName', component: 'JInput', colProps: { span: 6 } }, + { label: '动作地址', field: 'actAddr', component: 'InputNumber', colProps: { span: 6 } }, +]; + +export const formSchema: FormSchema[] = [ + { label: 'GUID', field: 'guid', component: 'Input', componentProps: { disabled: true }, colProps: { span: 24 } }, + { label: '动作地址', field: 'actAddr', component: 'InputNumber', componentProps: { disabled: true }, colProps: { span: 12 } }, + { label: '关联地址', field: 'actRepaddr', component: 'InputNumber', componentProps: { disabled: true }, colProps: { span: 12 } }, + { label: '动作名称', field: 'actName', component: 'Input', componentProps: { disabled: true }, colProps: { span: 12 } }, + { label: '动作名称(英)', field: 'actNameEn', component: 'Input', componentProps: { disabled: true }, colProps: { span: 12 } }, + { label: '动作备注', field: 'actMemo', component: 'Input', componentProps: { disabled: true }, colProps: { span: 24 } }, + { label: '写入时间', field: 'writeTime', component: 'Input', componentProps: { disabled: true }, colProps: { span: 12 } }, + { label: '读取时间', field: 'readTime', component: 'Input', componentProps: { disabled: true }, colProps: { span: 12 } }, + { label: '读写标识', field: 'rwFlag', component: 'InputNumber', componentProps: { disabled: true }, colProps: { span: 12 } }, +]; diff --git a/jeecgboot-vue3/src/views/xslmesMcs/mcsToMesActMill/components/McsToMesActMillModal.vue b/jeecgboot-vue3/src/views/xslmesMcs/mcsToMesActMill/components/McsToMesActMillModal.vue new file mode 100644 index 00000000..d99e7424 --- /dev/null +++ b/jeecgboot-vue3/src/views/xslmesMcs/mcsToMesActMill/components/McsToMesActMillModal.vue @@ -0,0 +1,30 @@ + + + diff --git a/jeecgboot-vue3/src/views/xslmesMcs/mcsToMesActMill/index.vue b/jeecgboot-vue3/src/views/xslmesMcs/mcsToMesActMill/index.vue new file mode 100644 index 00000000..2133388e --- /dev/null +++ b/jeecgboot-vue3/src/views/xslmesMcs/mcsToMesActMill/index.vue @@ -0,0 +1,71 @@ + + + diff --git a/yy-admin-master/YY.Admin.Core/Core/Services/IEquipmentDbStompSender.cs b/yy-admin-master/YY.Admin.Core/Core/Services/IEquipmentDbStompSender.cs new file mode 100644 index 00000000..8e88e754 --- /dev/null +++ b/yy-admin-master/YY.Admin.Core/Core/Services/IEquipmentDbStompSender.cs @@ -0,0 +1,11 @@ +namespace YY.Admin.Core.Services; + +/// +/// 设备库代理 STOMP 发送扩展(由 StompWebSocketService 实现)。 +/// +public interface IEquipmentDbStompSender +{ + Task SendAgentStatusAsync(object status, CancellationToken cancellationToken = default); + Task SendAgentResultAsync(object result, CancellationToken cancellationToken = default); + string CurrentDeviceId { get; } +} diff --git a/yy-admin-master/YY.Admin.Core/SeedData/SysConfigSeedData.cs b/yy-admin-master/YY.Admin.Core/SeedData/SysConfigSeedData.cs index f3083247..60398216 100644 --- a/yy-admin-master/YY.Admin.Core/SeedData/SysConfigSeedData.cs +++ b/yy-admin-master/YY.Admin.Core/SeedData/SysConfigSeedData.cs @@ -32,7 +32,7 @@ public class SysConfigSeedData : ISqlSugarEntitySeedData new SysConfig{ Id=1300000000191, Name="RefreshToken过期时间", Code=ConfigConst.SysRefreshTokenExpire, Value="20160", SysFlag=YesNoEnum.Y, Remark="刷新Token过期时间(分钟)(一般 refresh_token 的有效时间 > 2 * access_token 的有效时间)", OrderNo=110, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysConfig{ Id=1300000000192, Name="会话续期阈值", Code=ConfigConst.SysTokenIdleExtendMinutes, Value="20", SysFlag=YesNoEnum.Y, Remark="桌面端:用户有操作时,若会话剩余不足该分钟数则续期为「Token过期时间」整段", OrderNo=115, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysConfig{ Id=1300000000193, Name="登录状态检查间隔", Code=ConfigConst.SysTokenCheckIntervalMinutes, Value="1", SysFlag=YesNoEnum.Y, Remark="桌面端:定时检查登录是否过期的间隔(分钟)", OrderNo=118, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, - new SysConfig{ Id=1300000000194, Name="登录永不过期", Code=ConfigConst.SysTokenNeverExpire, Value="False", SysFlag=YesNoEnum.Y, Remark="桌面端:开启后不触发登录过期提示与自动踢回登录页", OrderNo=119, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, + new SysConfig{ Id=1300000000194, Name="登录永不过期", Code=ConfigConst.SysTokenNeverExpire, Value="True", SysFlag=YesNoEnum.Y, Remark="桌面端:开启后不触发登录过期提示与自动踢回登录页", OrderNo=119, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysConfig{ Id=1300000000201, Name="发送异常日志邮件", Code=ConfigConst.SysErrorMail, Value="False", SysFlag=YesNoEnum.Y, Remark="是否发送异常日志邮件", OrderNo=120, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysConfig{ Id=1300000000211, Name="域登录验证", Code=ConfigConst.SysDomainLogin, Value="False", SysFlag=YesNoEnum.Y, Remark="是否开启域登录验证", OrderNo=130, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysConfig{ Id=1300000000221, Name="数据校验日志", Code=ConfigConst.SysValidationLog, Value="True", SysFlag=YesNoEnum.Y, Remark="是否数据校验日志", OrderNo=140, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, diff --git a/yy-admin-master/YY.Admin.Core/SeedData/SysMenuSeedData.cs b/yy-admin-master/YY.Admin.Core/SeedData/SysMenuSeedData.cs index c19295a9..87cd8396 100644 --- a/yy-admin-master/YY.Admin.Core/SeedData/SysMenuSeedData.cs +++ b/yy-admin-master/YY.Admin.Core/SeedData/SysMenuSeedData.cs @@ -54,6 +54,21 @@ public class SysMenuSeedData : ISqlSugarEntitySeedData new SysMenu{ Id=1300150011201, Pid=1300150000101, Title="胶料快检记录", Path="/xslmes/rubberQuickTestRecord", Name="rubberQuickTestRecord", Component="RubberQuickTestRecordListView", Icon="", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=113 }, // 密炼计划 new SysMenu{ Id=1300150011401, Pid=1300150000101, Title="密炼计划", Path="/xslmes/mesXslMixingProductionPlan", Name="mesXslMixingProductionPlan", Component="MixingProductionPlanListView", Icon="", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=114 }, + // 开炼机动作中间表(设备库 MCSToMES_Act_Mill 只读) + new SysMenu{ Id=1300150011501, Pid=1300150000101, Title="开炼机动作中间表", Path="/xslmes/mcsActMill", Name="mcsActMill", Component="McsActMillListView", Icon="", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=115 }, + + #endregion + + #region 上辅机mes功能 + + new SysMenu{ Id=1300160000101, Pid=0, Title="上辅机mes功能", Path="/upperMes", Name="upperMes", Component="Layout", Icon="", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=9500 }, + new SysMenu{ Id=1300160010101, Pid=1300160000101, Title="交接班", Path="/xslmes/shiftHandover", Name="shiftHandover", Component="ShiftHandoverListView", Icon="", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 }, + new SysMenu{ Id=1300160010201, Pid=1300160000101, Title="bom验证", Path="/xslmes/bomValidation", Name="bomValidation", Component="BomValidationListView", Icon="", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=101 }, + new SysMenu{ Id=1300160010301, Pid=1300160000101, Title="配方维护", Path="/xslmes/formulaMaintain", Name="formulaMaintain", Component="FormulaMaintainListView", Icon="", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=102 }, + new SysMenu{ Id=1300160010401, Pid=1300160000101, Title="计划管理", Path="/xslmes/planManage", Name="planManage", Component="PlanManageListView", Icon="", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=103 }, + new SysMenu{ Id=1300160010501, Pid=1300160000101, Title="报表查询", Path="/xslmes/reportQuery", Name="reportQuery", Component="ReportQueryListView", Icon="", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=104 }, + new SysMenu{ Id=1300160010601, Pid=1300160000101, Title="设备停机记录", Path="/xslmes/equipmentDowntime", Name="equipmentDowntime", Component="EquipmentDowntimeListView", Icon="", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=105 }, + new SysMenu{ Id=1300160010701, Pid=1300160000101, Title="生产日志", Path="/xslmes/productionLog", Name="productionLog", Component="ProductionLogListView", Icon="", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=106 }, #endregion diff --git a/yy-admin-master/YY.Admin.Core/SeedData/SysTenantMenuSeedData.cs b/yy-admin-master/YY.Admin.Core/SeedData/SysTenantMenuSeedData.cs index cc3bb0bb..e791e035 100644 --- a/yy-admin-master/YY.Admin.Core/SeedData/SysTenantMenuSeedData.cs +++ b/yy-admin-master/YY.Admin.Core/SeedData/SysTenantMenuSeedData.cs @@ -35,6 +35,15 @@ public class SysTenantMenuSeedData : ISqlSugarEntitySeedData new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300150011201}, new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300150011301}, new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300150011401}, + new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300150011501}, + new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300160000101}, + new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300160010101}, + new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300160010201}, + new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300160010301}, + new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300160010401}, + new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300160010501}, + new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300160010601}, + new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300160010701}, new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300200012101}, new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300200012111}, new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300200012121}, diff --git a/yy-admin-master/YY.Admin.Services/Configuration/appsettings.json b/yy-admin-master/YY.Admin.Services/Configuration/appsettings.json index 0d646546..6bef26a8 100644 --- a/yy-admin-master/YY.Admin.Services/Configuration/appsettings.json +++ b/yy-admin-master/YY.Admin.Services/Configuration/appsettings.json @@ -95,5 +95,15 @@ "DefaultTenantId": 1002, // 自动创建本地用户时使用的默认租户ID "Captcha": "", // 如启用登录验证码,在此传入验证码 "CheckKey": "" // 如启用登录验证码,在此传入验证码key + }, + "EquipmentDb": { + "Enabled": true, + "DeviceId": "", + "ServerHost": "43.108.8.218", + "ServerPort": 1433, + "DbName": "MESNAC_MCS_ShareDB", + "Username": "sa", + "Password": "xsl@123456", + "ConnectTimeoutSeconds": 15 } } diff --git a/yy-admin-master/YY.Admin.Services/Service/Auth/SysAuthService.cs b/yy-admin-master/YY.Admin.Services/Service/Auth/SysAuthService.cs index 310964f0..c71af1a8 100644 --- a/yy-admin-master/YY.Admin.Services/Service/Auth/SysAuthService.cs +++ b/yy-admin-master/YY.Admin.Services/Service/Auth/SysAuthService.cs @@ -2331,11 +2331,23 @@ namespace YY.Admin.Services.Service.Auth } /// - /// 是否开启永不过期 + /// 是否开启永不过期(缺省或读失败时视为开启,避免误踢登录) /// private async Task getSysTokenNeverExpireAsync() { - return await _sysConfigService.GetConfigValue(ConfigConst.SysTokenNeverExpire); + try + { + var raw = await _sysConfigService.GetConfigValue(ConfigConst.SysTokenNeverExpire); + if (string.IsNullOrWhiteSpace(raw)) + return true; + if (bool.TryParse(raw, out var b)) + return b; + return raw.Trim() is "1" or "Y" or "y" or "yes" or "YES" or "on" or "ON"; + } + catch + { + return true; + } } diff --git a/yy-admin-master/YY.Admin.Services/Service/Config/SysConfigService.cs b/yy-admin-master/YY.Admin.Services/Service/Config/SysConfigService.cs index 31388229..3d246a7d 100644 --- a/yy-admin-master/YY.Admin.Services/Service/Config/SysConfigService.cs +++ b/yy-admin-master/YY.Admin.Services/Service/Config/SysConfigService.cs @@ -27,7 +27,37 @@ namespace YY.Admin.Services.Service.Config _sysCacheService.Set($"{CacheConst.KeyConfig}{code}", value); } if (string.IsNullOrWhiteSpace(value)) return default; - return (T)Convert.ChangeType(value, typeof(T)); + + // bool:Convert.ChangeType 对 "1"/"Y" 等会抛异常,统一稳健解析 + var targetType = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T); + if (targetType == typeof(bool)) + { + var b = ParseConfigBool(value); + return (T)(object)b; + } + + return (T)Convert.ChangeType(value, targetType); + } + + private static bool ParseConfigBool(string value) + { + if (bool.TryParse(value, out var b)) + return b; + switch (value.Trim().ToLowerInvariant()) + { + case "1": + case "y": + case "yes": + case "on": + return true; + case "0": + case "n": + case "no": + case "off": + return false; + default: + return false; + } } /// diff --git a/yy-admin-master/YY.Admin.Services/Service/EquipmentDb/EquipmentDbCommandHandler.cs b/yy-admin-master/YY.Admin.Services/Service/EquipmentDb/EquipmentDbCommandHandler.cs new file mode 100644 index 00000000..4f271e6e --- /dev/null +++ b/yy-admin-master/YY.Admin.Services/Service/EquipmentDb/EquipmentDbCommandHandler.cs @@ -0,0 +1,486 @@ +using System.Net.Http; +using System.Net.Http.Json; +using System.Text; +using System.Text.Json; +using System.Text.RegularExpressions; +using Microsoft.Data.SqlClient; +using Microsoft.Extensions.Configuration; +using Prism.Events; +using YY.Admin.Core.Events; +using YY.Admin.Core.Services; + +namespace YY.Admin.Services.Service.EquipmentDb; + +/// +/// 订阅 MES 下发的 EQ_* 命令,执行设备库 SQL 并回传结果。 +/// +public sealed class EquipmentDbCommandHandler +{ + private static readonly JsonSerializerOptions JsonOptions = new() + { + PropertyNameCaseInsensitive = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }; + + private readonly IEquipmentDbConnectionFactory _connectionFactory; + private readonly IHttpClientFactory _httpClientFactory; + private readonly IConfiguration _configuration; + private readonly ISignalRService _signalRService; + private readonly HashSet _processedRequestIds = new(); + private readonly object _dedupeLock = new(); + + public EquipmentDbCommandHandler( + IEventAggregator eventAggregator, + IEquipmentDbConnectionFactory connectionFactory, + IHttpClientFactory httpClientFactory, + IConfiguration configuration, + ISignalRService signalRService) + { + _connectionFactory = connectionFactory; + _httpClientFactory = httpClientFactory; + _configuration = configuration; + _signalRService = signalRService; + + eventAggregator.GetEvent() + .Subscribe(OnRemoteCommand, ThreadOption.BackgroundThread); + } + + private void OnRemoteCommand(RemoteCommandPayload payload) + { + if (payload == null || string.IsNullOrWhiteSpace(payload.CommandJson)) + { + return; + } + try + { + using var doc = JsonDocument.Parse(payload.CommandJson); + if (!doc.RootElement.TryGetProperty("cmd", out var cmdEl)) + { + return; + } + var cmd = cmdEl.GetString() ?? string.Empty; + if (!cmd.StartsWith("EQ_", StringComparison.OrdinalIgnoreCase)) + { + return; + } + var request = JsonSerializer.Deserialize(payload.CommandJson, JsonOptions); + if (request == null) + { + return; + } + _ = Task.Run(() => HandleAsync(request, CancellationToken.None)); + } + catch + { + // ignore non-EQ payloads + } + } + + public async Task HandleAsync(EquipmentDbCommandDto request, CancellationToken cancellationToken) + { + if (request == null || string.IsNullOrWhiteSpace(request.RequestId)) + { + return; + } + lock (_dedupeLock) + { + if (!_processedRequestIds.Add(request.RequestId)) + { + return; + } + if (_processedRequestIds.Count > 2000) + { + _processedRequestIds.Clear(); + _processedRequestIds.Add(request.RequestId); + } + } + + var result = new EquipmentDbResultDto + { + RequestId = request.RequestId, + DeviceId = ResolveDeviceId(request.DeviceId), + Cmd = request.Cmd, + RespondedAt = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() + }; + + try + { + switch ((request.Cmd ?? string.Empty).ToUpperInvariant()) + { + case "EQ_TEST": + await HandleTestAsync(result, cancellationToken).ConfigureAwait(false); + break; + case "EQ_QUERY": + case "EQ_SYNC_PULL": + await HandleQueryAsync(request, result, cancellationToken).ConfigureAwait(false); + break; + case "EQ_WRITE": + await HandleWriteAsync(request, result, cancellationToken).ConfigureAwait(false); + break; + case "EQ_SYNC_PUSH": + await HandleSyncPushAsync(request, result, cancellationToken).ConfigureAwait(false); + break; + default: + result.Success = false; + result.Error = "未知命令: " + request.Cmd; + break; + } + } + catch (Exception ex) + { + result.Success = false; + result.Error = ex.Message; + result.DbConnected = false; + result.DbMessage = ex.Message; + } + + await SendResultAsync(result, cancellationToken).ConfigureAwait(false); + } + + private async Task HandleTestAsync(EquipmentDbResultDto result, CancellationToken cancellationToken) + { + var (ok, message) = await _connectionFactory.TestAsync(cancellationToken).ConfigureAwait(false); + result.Success = ok; + result.DbConnected = ok; + result.DbMessage = message; + result.Error = ok ? null : message; + await ReportStatusAsync(ok, message, cancellationToken).ConfigureAwait(false); + } + + private async Task HandleQueryAsync(EquipmentDbCommandDto request, EquipmentDbResultDto result, CancellationToken cancellationToken) + { + EquipmentDbWhitelist.ValidateSql(request.Sql, request.Table); + await using var conn = _connectionFactory.CreateConnection(); + await conn.OpenAsync(cancellationToken).ConfigureAwait(false); + await using var cmd = conn.CreateCommand(); + cmd.CommandText = request.Sql!; + cmd.CommandTimeout = 60; + BindParams(cmd, request.Params); + var rows = new List>(); + await using var reader = await cmd.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false); + while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false)) + { + var row = new Dictionary(StringComparer.OrdinalIgnoreCase); + for (var i = 0; i < reader.FieldCount; i++) + { + object? val = reader.IsDBNull(i) ? null : reader.GetValue(i); + if (val is DateTime dt) + { + val = dt.ToString("yyyy-MM-dd HH:mm:ss"); + } + row[reader.GetName(i)] = val; + } + rows.Add(row); + } + result.Success = true; + result.Rows = rows; + result.Affected = rows.Count; + result.DbConnected = true; + result.DbMessage = "ok"; + } + + private async Task HandleWriteAsync(EquipmentDbCommandDto request, EquipmentDbResultDto result, CancellationToken cancellationToken) + { + EquipmentDbWhitelist.ValidateSql(request.Sql, request.Table); + await using var conn = _connectionFactory.CreateConnection(); + await conn.OpenAsync(cancellationToken).ConfigureAwait(false); + await using var cmd = conn.CreateCommand(); + cmd.CommandText = request.Sql!; + cmd.CommandTimeout = 60; + BindParams(cmd, request.Params); + var affected = await cmd.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false); + result.Success = true; + result.Affected = affected; + result.DbConnected = true; + result.DbMessage = "ok"; + } + + private async Task HandleSyncPushAsync(EquipmentDbCommandDto request, EquipmentDbResultDto result, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(request.Table) || !EquipmentDbWhitelist.IsAllowedTable(request.Table)) + { + throw new InvalidOperationException("EQ_SYNC_PUSH 表不在白名单"); + } + if (request.Payload == null || request.Payload.Count == 0) + { + throw new InvalidOperationException("EQ_SYNC_PUSH payload 为空"); + } + + request.Payload.TryGetValue("mode", out var modeObj); + var mode = Convert.ToString(modeObj)?.ToLowerInvariant() ?? "insert"; + var rows = ExtractRows(request.Payload); + if (rows.Count == 0) + { + result.Success = true; + result.Affected = 0; + result.DbConnected = true; + return; + } + + List? keys = null; + if (request.Payload.TryGetValue("keys", out var keysObj) && keysObj != null) + { + keys = JsonSerializer.Deserialize>(JsonSerializer.Serialize(keysObj), JsonOptions); + } + + await using var conn = _connectionFactory.CreateConnection(); + await conn.OpenAsync(cancellationToken).ConfigureAwait(false); + var affected = 0; + foreach (var row in rows) + { + if (mode == "upsert" && keys != null && keys.Count > 0) + { + affected += await UpsertRowAsync(conn, request.Table!, row, keys, cancellationToken).ConfigureAwait(false); + } + else if (mode == "update" && keys != null && keys.Count > 0) + { + affected += await UpdateRowAsync(conn, request.Table!, row, keys, cancellationToken).ConfigureAwait(false); + } + else + { + affected += await InsertRowAsync(conn, request.Table!, row, cancellationToken).ConfigureAwait(false); + } + } + result.Success = true; + result.Affected = affected; + result.DbConnected = true; + result.DbMessage = "ok"; + } + + private static List> ExtractRows(Dictionary payload) + { + if (!payload.TryGetValue("rows", out var rowsObj) || rowsObj == null) + { + throw new InvalidOperationException("EQ_SYNC_PUSH 缺少 rows"); + } + if (rowsObj is JsonElement je) + { + return JsonSerializer.Deserialize>>(je.GetRawText(), JsonOptions) + ?? new List>(); + } + return JsonSerializer.Deserialize>>(JsonSerializer.Serialize(rowsObj), JsonOptions) + ?? new List>(); + } + + private static async Task InsertRowAsync( + SqlConnection conn, string table, Dictionary row, CancellationToken cancellationToken) + { + var cols = row.Keys.ToList(); + foreach (var c in cols) + { + if (!Regex.IsMatch(c, @"^[A-Za-z0-9_]+$")) + { + throw new InvalidOperationException("非法列名: " + c); + } + } + var colList = string.Join(",", cols.Select(c => "[" + c + "]")); + var paramList = string.Join(",", cols.Select((_, i) => "@p" + i)); + await using var cmd = conn.CreateCommand(); + cmd.CommandText = $"INSERT INTO [{table}] ({colList}) VALUES ({paramList})"; + for (var i = 0; i < cols.Count; i++) + { + cmd.Parameters.AddWithValue("@p" + i, ToDbValue(row[cols[i]])); + } + return await cmd.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false); + } + + private static async Task UpdateRowAsync( + SqlConnection conn, string table, Dictionary row, List keys, CancellationToken cancellationToken) + { + var setCols = row.Keys.Where(k => !keys.Contains(k, StringComparer.OrdinalIgnoreCase)).ToList(); + if (setCols.Count == 0) + { + return 0; + } + var sets = string.Join(",", setCols.Select((c, i) => $"[{c}]=@s{i}")); + var wheres = string.Join(" AND ", keys.Select((k, i) => $"[{k}]=@k{i}")); + await using var cmd = conn.CreateCommand(); + cmd.CommandText = $"UPDATE [{table}] SET {sets} WHERE {wheres}"; + for (var i = 0; i < setCols.Count; i++) + { + cmd.Parameters.AddWithValue("@s" + i, ToDbValue(GetIgnoreCase(row, setCols[i]))); + } + for (var i = 0; i < keys.Count; i++) + { + cmd.Parameters.AddWithValue("@k" + i, ToDbValue(GetIgnoreCase(row, keys[i]))); + } + return await cmd.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false); + } + + private static async Task UpsertRowAsync( + SqlConnection conn, string table, Dictionary row, List keys, CancellationToken cancellationToken) + { + var updated = await UpdateRowAsync(conn, table, row, keys, cancellationToken).ConfigureAwait(false); + if (updated > 0) + { + return updated; + } + return await InsertRowAsync(conn, table, row, cancellationToken).ConfigureAwait(false); + } + + private static object? GetIgnoreCase(Dictionary row, string key) + { + if (row.TryGetValue(key, out var v)) + { + return v; + } + foreach (var kv in row) + { + if (string.Equals(kv.Key, key, StringComparison.OrdinalIgnoreCase)) + { + return kv.Value; + } + } + return null; + } + + private static object ToDbValue(object? value) + { + if (value == null) + { + return DBNull.Value; + } + if (value is JsonElement je) + { + return je.ValueKind switch + { + JsonValueKind.Null => DBNull.Value, + JsonValueKind.String => je.GetString() ?? (object)DBNull.Value, + JsonValueKind.Number => je.TryGetInt64(out var l) ? l : je.GetDouble(), + JsonValueKind.True => true, + JsonValueKind.False => false, + _ => je.ToString() + }; + } + return value; + } + + private static void BindParams(SqlCommand cmd, List? parameters) + { + if (parameters == null) + { + return; + } + for (var i = 0; i < parameters.Count; i++) + { + cmd.Parameters.AddWithValue("@p" + i, ToDbValue(parameters[i])); + } + if (cmd.CommandText.Contains('?')) + { + var sb = new StringBuilder(); + var idx = 0; + foreach (var ch in cmd.CommandText) + { + if (ch == '?') + { + sb.Append("@p").Append(idx++); + } + else + { + sb.Append(ch); + } + } + cmd.CommandText = sb.ToString(); + } + } + + private async Task SendResultAsync(EquipmentDbResultDto result, CancellationToken cancellationToken) + { + var json = JsonSerializer.Serialize(result, JsonOptions); + var useRest = json.Length > 200_000 || (result.Rows?.Count ?? 0) > 500; + if (useRest) + { + try + { + var client = _httpClientFactory.CreateClient("JeecgApi"); + result.LastChunk = true; + result.ChunkIndex = 0; + result.ChunkTotal = 1; + var resp = await client.PostAsJsonAsync("/xslmes/deviceAgent/anon/resultChunk", result, cancellationToken) + .ConfigureAwait(false); + resp.EnsureSuccessStatusCode(); + return; + } + catch + { + // fall through + } + } + + if (_signalRService is IEquipmentDbStompSender sender) + { + await sender.SendAgentResultAsync(result, cancellationToken).ConfigureAwait(false); + } + } + + public async Task ReportStatusAsync(bool dbConnected, string? dbMessage, CancellationToken cancellationToken = default) + { + var deviceId = ResolveDeviceId(null); + var status = new Dictionary + { + ["deviceId"] = deviceId, + ["hostName"] = Environment.MachineName, + ["dbConnected"] = dbConnected, + ["dbMessage"] = dbMessage, + ["ts"] = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() + }; + if (_signalRService is IEquipmentDbStompSender sender) + { + await sender.SendAgentStatusAsync(status, cancellationToken).ConfigureAwait(false); + } + try + { + var client = _httpClientFactory.CreateClient("JeecgApi"); + await client.PostAsJsonAsync("/xslmes/deviceAgent/anon/heartbeat", status, cancellationToken).ConfigureAwait(false); + } + catch + { + // ignore + } + } + + private string ResolveDeviceId(string? fromRequest) + { + if (!string.IsNullOrWhiteSpace(fromRequest)) + { + return fromRequest!; + } + var settings = _connectionFactory.CurrentSettings; + if (!string.IsNullOrWhiteSpace(settings.DeviceId)) + { + return settings.DeviceId; + } + return _configuration.GetValue("EquipmentDb:DeviceId") + ?? Environment.MachineName; + } +} + +public sealed class EquipmentDbCommandDto +{ + public string? RequestId { get; set; } + public string? Cmd { get; set; } + public string? DeviceId { get; set; } + public string? Table { get; set; } + public string? Sql { get; set; } + public string? SqlKey { get; set; } + public List? Params { get; set; } + public Dictionary? Payload { get; set; } + public int? TimeoutMs { get; set; } +} + +public sealed class EquipmentDbResultDto +{ + public string? RequestId { get; set; } + public string? DeviceId { get; set; } + public string? Cmd { get; set; } + public bool Success { get; set; } + public string? Error { get; set; } + public int? Affected { get; set; } + public List>? Rows { get; set; } + public bool? DbConnected { get; set; } + public string? DbMessage { get; set; } + public long? RespondedAt { get; set; } + public int? ChunkIndex { get; set; } + public int? ChunkTotal { get; set; } + public bool? LastChunk { get; set; } +} diff --git a/yy-admin-master/YY.Admin.Services/Service/EquipmentDb/EquipmentDbConnectionFactory.cs b/yy-admin-master/YY.Admin.Services/Service/EquipmentDb/EquipmentDbConnectionFactory.cs new file mode 100644 index 00000000..d1d66d57 --- /dev/null +++ b/yy-admin-master/YY.Admin.Services/Service/EquipmentDb/EquipmentDbConnectionFactory.cs @@ -0,0 +1,51 @@ +using Microsoft.Data.SqlClient; + +namespace YY.Admin.Services.Service.EquipmentDb; + +public interface IEquipmentDbConnectionFactory +{ + SqlConnection CreateConnection(); + Task<(bool Ok, string Message)> TestAsync(CancellationToken cancellationToken = default); + EquipmentDbSettingsStore.EquipmentDbSettings CurrentSettings { get; } + void Reload(); +} + +public sealed class EquipmentDbConnectionFactory : IEquipmentDbConnectionFactory +{ + private EquipmentDbSettingsStore.EquipmentDbSettings _settings = EquipmentDbSettingsStore.Load(); + + public EquipmentDbSettingsStore.EquipmentDbSettings CurrentSettings => _settings; + + public void Reload() + { + _settings = EquipmentDbSettingsStore.Load(); + } + + public SqlConnection CreateConnection() + { + if (!_settings.Enabled) + { + throw new InvalidOperationException("设备库连接未启用"); + } + return new SqlConnection(EquipmentDbSettingsStore.BuildConnectionString(_settings)); + } + + public async Task<(bool Ok, string Message)> TestAsync(CancellationToken cancellationToken = default) + { + try + { + Reload(); + await using var conn = CreateConnection(); + await conn.OpenAsync(cancellationToken).ConfigureAwait(false); + await using var cmd = conn.CreateCommand(); + cmd.CommandText = "SELECT 1"; + cmd.CommandTimeout = Math.Max(5, _settings.ConnectTimeoutSeconds); + var scalar = await cmd.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false); + return (true, "连接成功: " + scalar); + } + catch (Exception ex) + { + return (false, ex.Message); + } + } +} diff --git a/yy-admin-master/YY.Admin.Services/Service/EquipmentDb/EquipmentDbSettingsStore.cs b/yy-admin-master/YY.Admin.Services/Service/EquipmentDb/EquipmentDbSettingsStore.cs new file mode 100644 index 00000000..c8e6a580 --- /dev/null +++ b/yy-admin-master/YY.Admin.Services/Service/EquipmentDb/EquipmentDbSettingsStore.cs @@ -0,0 +1,110 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.IO; +using YY.Admin.Core.Util; + +namespace YY.Admin.Services.Service.EquipmentDb; + +/// +/// 设备库(SQL Server 中间库)本地连接配置。 +/// +public static class EquipmentDbSettingsStore +{ + public class EquipmentDbSettings + { + public bool Enabled { get; set; } = true; + public string DeviceId { get; set; } = string.Empty; + public string ServerHost { get; set; } = "127.0.0.1"; + public int ServerPort { get; set; } = 1433; + public string DbName { get; set; } = "MES_ShareDB"; + public string Username { get; set; } = "sa"; + public string Password { get; set; } = string.Empty; + public int ConnectTimeoutSeconds { get; set; } = 15; + } + + public static string GetUserConfigPath() + { + var dir = AppWritablePaths.EnsureDirectoryExists(AppWritablePaths.ConfigurationDirectory); + return Path.Combine(dir, "equipment-db.json"); + } + + public static EquipmentDbSettings Load() + { + var path = GetUserConfigPath(); + if (!File.Exists(path)) + { + return new EquipmentDbSettings + { + DeviceId = Environment.MachineName + }; + } + try + { + var settings = JsonConvert.DeserializeObject(File.ReadAllText(path)) + ?? new EquipmentDbSettings(); + if (string.IsNullOrWhiteSpace(settings.DeviceId)) + { + settings.DeviceId = Environment.MachineName; + } + return settings; + } + catch + { + return new EquipmentDbSettings { DeviceId = Environment.MachineName }; + } + } + + public static void Save(EquipmentDbSettings settings) + { + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + if (string.IsNullOrWhiteSpace(settings.DeviceId)) + { + settings.DeviceId = Environment.MachineName; + } + var path = GetUserConfigPath(); + File.WriteAllText(path, JsonConvert.SerializeObject(settings, Formatting.Indented)); + } + + public static string BuildConnectionString(EquipmentDbSettings s) + { + var timeout = s.ConnectTimeoutSeconds > 0 ? s.ConnectTimeoutSeconds : 15; + return $"Server={s.ServerHost},{s.ServerPort};Database={s.DbName};User Id={s.Username};Password={s.Password};" + + $"Encrypt=False;TrustServerCertificate=True;Connect Timeout={timeout};"; + } + + /// + /// 从 bundled appsettings 的 EquipmentDb 节点合并默认值(若有)。 + /// + public static EquipmentDbSettings LoadMergedWithAppSettings(JObject? root) + { + var settings = Load(); + if (root?["EquipmentDb"] is not JObject eq) + { + return settings; + } + if (string.IsNullOrWhiteSpace(settings.ServerHost) || settings.ServerHost == "127.0.0.1") + { + settings.ServerHost = eq.Value("ServerHost") ?? settings.ServerHost; + } + if (settings.ServerPort == 1433 && eq.Value("ServerPort") is int p) + { + settings.ServerPort = p; + } + if (string.IsNullOrWhiteSpace(settings.DbName) || settings.DbName == "MES_ShareDB") + { + settings.DbName = eq.Value("DbName") ?? settings.DbName; + } + if (string.IsNullOrWhiteSpace(settings.Username) || settings.Username == "sa") + { + settings.Username = eq.Value("Username") ?? settings.Username; + } + if (string.IsNullOrWhiteSpace(settings.Password)) + { + settings.Password = eq.Value("Password") ?? settings.Password; + } + return settings; + } +} diff --git a/yy-admin-master/YY.Admin.Services/Service/EquipmentDb/EquipmentDbWhitelist.cs b/yy-admin-master/YY.Admin.Services/Service/EquipmentDb/EquipmentDbWhitelist.cs new file mode 100644 index 00000000..2217eb35 --- /dev/null +++ b/yy-admin-master/YY.Admin.Services/Service/EquipmentDb/EquipmentDbWhitelist.cs @@ -0,0 +1,78 @@ +using System.Text.RegularExpressions; + +namespace YY.Admin.Services.Service.EquipmentDb; + +/// +/// 设备库 SQL 白名单:仅允许访问 MCS 中间表白名单表。 +/// +public static class EquipmentDbWhitelist +{ + private static readonly HashSet AllowedTables = new(StringComparer.OrdinalIgnoreCase) + { + "MCSToMES_Alarm", "MCSToMES_MixAlarm", "MCSToMES_MixAct", "MCSToMES_Act_Mill", "MCSToMES_MixBatch", + "MCSToMES_MixCon", "MCSToMES_MixCurve", "MCSToMES_MixStep", "MCSToMES_MixWeight", + "MCSToMES_MixExePlan", "MCSToMES_BinToMater", "MCSToMES_AutoXLLog", "MCSToMES_CheckScaleLog", + "MESToMCS_Material", "MESToMCS_MixPlan", "MESToMCS_Recipe", + "MESToMCS_Recipe_MixStep", "MESToMCS_Recipe_Weight" + }; + + private static readonly Regex Dangerous = new( + @"\b(DROP|ALTER|TRUNCATE|EXEC|EXECUTE|xp_|sp_|INTO\s+OUTFILE|OPENROWSET|OPENDATASOURCE)\b", + RegexOptions.IgnoreCase | RegexOptions.Compiled); + + private static readonly Regex TableRef = new( + @"\b(?:FROM|INTO|UPDATE|JOIN)\s+\[?(?[A-Za-z0-9_]+)\]?", + RegexOptions.IgnoreCase | RegexOptions.Compiled); + + public static bool IsAllowedTable(string? table) + { + if (string.IsNullOrWhiteSpace(table)) + { + return false; + } + return AllowedTables.Contains(table.Trim()); + } + + public static void ValidateSql(string? sql, string? tableHint = null) + { + if (string.IsNullOrWhiteSpace(sql)) + { + throw new InvalidOperationException("SQL 不能为空"); + } + if (Dangerous.IsMatch(sql)) + { + throw new InvalidOperationException("SQL 包含禁止关键字"); + } + var trimmed = sql.TrimStart(); + var isSelect = trimmed.StartsWith("SELECT", StringComparison.OrdinalIgnoreCase); + var isInsert = trimmed.StartsWith("INSERT", StringComparison.OrdinalIgnoreCase); + var isUpdate = trimmed.StartsWith("UPDATE", StringComparison.OrdinalIgnoreCase); + var isDelete = trimmed.StartsWith("DELETE", StringComparison.OrdinalIgnoreCase); + if (!(isSelect || isInsert || isUpdate || isDelete)) + { + throw new InvalidOperationException("仅允许 SELECT/INSERT/UPDATE/DELETE"); + } + + if (!string.IsNullOrWhiteSpace(tableHint) && !IsAllowedTable(tableHint)) + { + throw new InvalidOperationException($"表不在白名单: {tableHint}"); + } + + foreach (Match m in TableRef.Matches(sql)) + { + var t = m.Groups["t"].Value; + if (string.Equals(t, "TOP", StringComparison.OrdinalIgnoreCase) + || string.Equals(t, "SELECT", StringComparison.OrdinalIgnoreCase) + || string.Equals(t, "SET", StringComparison.OrdinalIgnoreCase) + || string.Equals(t, "VALUES", StringComparison.OrdinalIgnoreCase) + || string.Equals(t, "WHERE", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + if (!IsAllowedTable(t)) + { + throw new InvalidOperationException($"SQL 引用了未授权表: {t}"); + } + } + } +} diff --git a/yy-admin-master/YY.Admin.Services/Service/EquipmentDb/McsActMillService.cs b/yy-admin-master/YY.Admin.Services/Service/EquipmentDb/McsActMillService.cs new file mode 100644 index 00000000..d84b8401 --- /dev/null +++ b/yy-admin-master/YY.Admin.Services/Service/EquipmentDb/McsActMillService.cs @@ -0,0 +1,125 @@ +using System.Data; +using Microsoft.Data.SqlClient; + +namespace YY.Admin.Services.Service.EquipmentDb; + +/// +/// 开炼机动作中间表 MCSToMES_Act_Mill 只读查询(直连设备库)。 +/// +public interface IMcsActMillService +{ + Task<(IReadOnlyList Records, long Total)> PageAsync( + int pageNo, + int pageSize, + string? actName = null, + int? actAddr = null, + CancellationToken cancellationToken = default); +} + +public sealed class McsActMillRow +{ + public string? Guid { get; set; } + public int? ActAddr { get; set; } + public string? ActName { get; set; } + public string? ActNameEn { get; set; } + public string? ActMemo { get; set; } + public int? ActRepaddr { get; set; } + public DateTime? WriteTime { get; set; } + public DateTime? ReadTime { get; set; } + public int? RwFlag { get; set; } +} + +public sealed class McsActMillService : IMcsActMillService +{ + private const string TableName = "MCSToMES_Act_Mill"; + private readonly IEquipmentDbConnectionFactory _factory; + + public McsActMillService(IEquipmentDbConnectionFactory factory) + { + _factory = factory; + } + + public async Task<(IReadOnlyList Records, long Total)> PageAsync( + int pageNo, + int pageSize, + string? actName = null, + int? actAddr = null, + CancellationToken cancellationToken = default) + { + if (pageNo < 1) pageNo = 1; + if (pageSize < 1) pageSize = 20; + + _factory.Reload(); + await using var conn = _factory.CreateConnection(); + await conn.OpenAsync(cancellationToken).ConfigureAwait(false); + + var where = new List(); + var parameters = new List(); + if (!string.IsNullOrWhiteSpace(actName)) + { + where.Add("act_name LIKE @actName"); + parameters.Add(new SqlParameter("@actName", SqlDbType.NVarChar, 50) { Value = "%" + actName.Trim() + "%" }); + } + if (actAddr.HasValue) + { + where.Add("act_addr = @actAddr"); + parameters.Add(new SqlParameter("@actAddr", SqlDbType.Int) { Value = actAddr.Value }); + } + var whereSql = where.Count == 0 ? "" : " WHERE " + string.Join(" AND ", where); + + var countSql = $"SELECT COUNT(1) FROM [{TableName}]{whereSql}"; + EquipmentDbWhitelist.ValidateSql(countSql, TableName); + long total; + await using (var countCmd = conn.CreateCommand()) + { + countCmd.CommandText = countSql; + countCmd.Parameters.AddRange(CloneParams(parameters)); + var scalar = await countCmd.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false); + total = Convert.ToInt64(scalar ?? 0); + } + + var offset = (pageNo - 1) * pageSize; + var listSql = $@" +SELECT [GUID], [act_addr], [act_name], [act_name_en], [act_memo], [act_Repaddr], [WriteTime], [ReadTime], [RW_Flag] +FROM [{TableName}]{whereSql} +ORDER BY [WriteTime] DESC, [GUID] +OFFSET @offset ROWS FETCH NEXT @pageSize ROWS ONLY"; + EquipmentDbWhitelist.ValidateSql(listSql, TableName); + + var rows = new List(); + await using (var listCmd = conn.CreateCommand()) + { + listCmd.CommandText = listSql; + listCmd.Parameters.AddRange(CloneParams(parameters)); + listCmd.Parameters.Add(new SqlParameter("@offset", SqlDbType.Int) { Value = offset }); + listCmd.Parameters.Add(new SqlParameter("@pageSize", SqlDbType.Int) { Value = pageSize }); + await using var reader = await listCmd.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false); + while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false)) + { + rows.Add(new McsActMillRow + { + Guid = reader["GUID"] as string, + ActAddr = reader["act_addr"] == DBNull.Value ? null : Convert.ToInt32(reader["act_addr"]), + ActName = reader["act_name"] as string, + ActNameEn = reader["act_name_en"] as string, + ActMemo = reader["act_memo"] as string, + ActRepaddr = reader["act_Repaddr"] == DBNull.Value ? null : Convert.ToInt32(reader["act_Repaddr"]), + WriteTime = reader["WriteTime"] == DBNull.Value ? null : Convert.ToDateTime(reader["WriteTime"]), + ReadTime = reader["ReadTime"] == DBNull.Value ? null : Convert.ToDateTime(reader["ReadTime"]), + RwFlag = reader["RW_Flag"] == DBNull.Value ? null : Convert.ToInt32(reader["RW_Flag"]), + }); + } + } + + return (rows, total); + } + + private static SqlParameter[] CloneParams(List source) + { + return source.Select(p => new SqlParameter(p.ParameterName, p.SqlDbType) + { + Size = p.Size, + Value = p.Value ?? DBNull.Value + }).ToArray(); + } +} diff --git a/yy-admin-master/YY.Admin.Services/YY.Admin.Services.csproj b/yy-admin-master/YY.Admin.Services/YY.Admin.Services.csproj index d9924e04..428f79de 100644 --- a/yy-admin-master/YY.Admin.Services/YY.Admin.Services.csproj +++ b/yy-admin-master/YY.Admin.Services/YY.Admin.Services.csproj @@ -20,6 +20,7 @@ + diff --git a/yy-admin-master/YY.Admin/Infrastructure/Hubs/StompWebSocketService.cs b/yy-admin-master/YY.Admin/Infrastructure/Hubs/StompWebSocketService.cs index d5a809da..4963f242 100644 --- a/yy-admin-master/YY.Admin/Infrastructure/Hubs/StompWebSocketService.cs +++ b/yy-admin-master/YY.Admin/Infrastructure/Hubs/StompWebSocketService.cs @@ -9,10 +9,11 @@ using YY.Admin.Core.Services; using YY.Admin.Core.Session; using YY.Admin.Helper; using YY.Admin.Infrastructure.Storage; +using YY.Admin.Services.Service.EquipmentDb; namespace YY.Admin.Infrastructure.Hubs; -public class StompWebSocketService : ISignalRService +public class StompWebSocketService : ISignalRService, IEquipmentDbStompSender { // STOMP heart-beat: send \n every 10 s, declare we want to receive every 10 s private const int HeartbeatMs = 10_000; @@ -188,6 +189,11 @@ public class StompWebSocketService : ISignalRService BuildSubscribeFrame("sub-device-pong", $"/topic/device/{_deviceId}/pong"), cancellationToken).ConfigureAwait(false); + // 设备库命令通道(匿名/登录均订阅) + await SendFrameAsync( + BuildSubscribeFrame("sub-device-eq-cmd", $"/topic/device/{_deviceId}/cmd"), + cancellationToken).ConfigureAwait(false); + if (!anonymous && !string.IsNullOrWhiteSpace(_token)) { await SendFrameAsync( @@ -207,6 +213,25 @@ public class StompWebSocketService : ISignalRService _networkMonitor.SetStompTransportOnline(true); _ = Task.Run(() => ReceiveLoopAsync(cts.Token), cts.Token); + // 连接成功后上报设备代理状态 + _ = Task.Run(async () => + { + try + { + await SendAgentStatusAsync(new + { + deviceId = _deviceId, + hostName = Environment.MachineName, + dbConnected = false, + dbMessage = "pending", + ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() + }, CancellationToken.None).ConfigureAwait(false); + } + catch + { + // ignore + } + }); return; } catch @@ -232,6 +257,38 @@ public class StompWebSocketService : ISignalRService await SendFrameAsync(frame, cancellationToken).ConfigureAwait(false); } + public string CurrentDeviceId => _deviceId; + + public async Task SendAgentStatusAsync(object status, CancellationToken cancellationToken = default) + { + if (IsDisconnectedByUser() || _socket == null || _socket.State != WebSocketState.Open) + { + return; + } + var json = System.Text.Json.JsonSerializer.Serialize(status); + var frame = "SEND\n" + + "destination:/app/device/agent/status\n" + + "content-type:application/json\n" + + $"content-length:{Encoding.UTF8.GetByteCount(json)}\n\n" + + $"{json}\0"; + await SendFrameAsync(frame, cancellationToken).ConfigureAwait(false); + } + + public async Task SendAgentResultAsync(object result, CancellationToken cancellationToken = default) + { + if (IsDisconnectedByUser() || _socket == null || _socket.State != WebSocketState.Open) + { + return; + } + var json = System.Text.Json.JsonSerializer.Serialize(result); + var frame = "SEND\n" + + "destination:/app/device/agent/result\n" + + "content-type:application/json\n" + + $"content-length:{Encoding.UTF8.GetByteCount(json)}\n\n" + + $"{json}\0"; + await SendFrameAsync(frame, cancellationToken).ConfigureAwait(false); + } + public async Task DisconnectAsync(CancellationToken cancellationToken = default) { var cts = Interlocked.Exchange(ref _connectionCts, null); @@ -437,8 +494,29 @@ public class StompWebSocketService : ISignalRService return "ws://" + baseUrl["http://".Length..] + "/ws/device"; } - private static string ResolveDeviceId(string token) + private string ResolveDeviceId(string token) { + try + { + var fromConfig = EquipmentDbSettingsStore.Load().DeviceId; + if (!string.IsNullOrWhiteSpace(fromConfig)) + { + return fromConfig.Trim(); + } + } + catch + { + // ignore + } + var configured = _configuration.GetValue("EquipmentDb:DeviceId"); + if (!string.IsNullOrWhiteSpace(configured)) + { + return configured.Trim(); + } + if (!string.IsNullOrWhiteSpace(Environment.MachineName)) + { + return Environment.MachineName; + } try { var parts = token.Split('.'); diff --git a/yy-admin-master/YY.Admin/Module/NavigationExtensions.cs b/yy-admin-master/YY.Admin/Module/NavigationExtensions.cs index b96bc61a..9f580f7b 100644 --- a/yy-admin-master/YY.Admin/Module/NavigationExtensions.cs +++ b/yy-admin-master/YY.Admin/Module/NavigationExtensions.cs @@ -21,6 +21,8 @@ using YY.Admin.Views.MixerMaterialTareStrategy; using YY.Admin.Views.RubberQuickTest; using YY.Admin.Views.RubberQuickTestStd; using YY.Admin.Views.MixingProductionPlan; +using YY.Admin.Views.McsActMill; +using YY.Admin.Views.UpperMes; namespace YY.Admin { @@ -105,6 +107,15 @@ namespace YY.Admin containerRegistry.RegisterForNavigation(); // 密炼计划(只读) containerRegistry.RegisterForNavigation(); + containerRegistry.RegisterForNavigation(); + // 上辅机mes功能(占位) + containerRegistry.RegisterForNavigation(); + containerRegistry.RegisterForNavigation(); + containerRegistry.RegisterForNavigation(); + containerRegistry.RegisterForNavigation(); + containerRegistry.RegisterForNavigation(); + containerRegistry.RegisterForNavigation(); + containerRegistry.RegisterForNavigation(); // 打印设置 containerRegistry.RegisterForNavigation(); // 打印模板列表 diff --git a/yy-admin-master/YY.Admin/Module/SyncModule.cs b/yy-admin-master/YY.Admin/Module/SyncModule.cs index 45a81f89..f4c2dc63 100644 --- a/yy-admin-master/YY.Admin/Module/SyncModule.cs +++ b/yy-admin-master/YY.Admin/Module/SyncModule.cs @@ -30,6 +30,7 @@ using YY.Admin.Services.Service.Print; using YY.Admin.Services.Service.RubberQuickTest; using YY.Admin.Services.Service.RubberQuickTestStd; using YY.Admin.Services.Service.MixingProductionPlan; +using YY.Admin.Services.Service.EquipmentDb; namespace YY.Admin.Module; @@ -103,6 +104,11 @@ public class SyncModule : IModule containerRegistry.RegisterSingleton(); containerRegistry.RegisterSingleton(); + // 设备库代理(厂区 SQL Server 直连 + EQ_* 命令执行) + containerRegistry.RegisterSingleton(); + containerRegistry.RegisterSingleton(); + containerRegistry.RegisterSingleton(); + var serviceCollection = new ServiceCollection(); serviceCollection.AddTransient(); serviceCollection.AddHttpClient("JeecgApi", (sp, client) => @@ -179,6 +185,21 @@ public class SyncModule : IModule _ = containerProvider.Resolve(); // 密炼计划只读同步协调器 _ = containerProvider.Resolve(); + // 设备库命令处理器(订阅 EQ_*) + var eqHandler = containerProvider.Resolve(); + _ = Task.Run(async () => + { + try + { + var factory = containerProvider.Resolve(); + var (ok, msg) = await factory.TestAsync(CancellationToken.None).ConfigureAwait(false); + await eqHandler.ReportStatusAsync(ok, msg, CancellationToken.None).ConfigureAwait(false); + } + catch (Exception ex) + { + await eqHandler.ReportStatusAsync(false, ex.Message, CancellationToken.None).ConfigureAwait(false); + } + }); } private static IAsyncPolicy GetRetryPolicy() diff --git a/yy-admin-master/YY.Admin/ViewModels/Base/BaseViewModel.cs b/yy-admin-master/YY.Admin/ViewModels/Base/BaseViewModel.cs index c5861d0f..1cc6e519 100644 --- a/yy-admin-master/YY.Admin/ViewModels/Base/BaseViewModel.cs +++ b/yy-admin-master/YY.Admin/ViewModels/Base/BaseViewModel.cs @@ -110,33 +110,69 @@ namespace YY.Admin.ViewModels _tokenCheckTimer.Interval = TimeSpan.FromMinutes(minutes); } + /// 登录永不过期缓存,避免定时器在 UI 线程同步读库失败后回落到 30 分钟过期。 + private static bool? _tokenNeverExpireCached; + /// /// 是否开启登录永不过期 /// private static bool IsTokenNeverExpireEnabled() { + if (_tokenNeverExpireCached.HasValue) + return _tokenNeverExpireCached.Value; + try { var cfg = Prism.Ioc.ContainerLocator.Current.Resolve(); - return cfg.GetConfigValue(ConfigConst.SysTokenNeverExpire).GetAwaiter().GetResult(); + // 用 string 读取再解析,避免 Convert.ChangeType(bool) 异常被吞掉后误判为未开启 + var raw = cfg.GetConfigValue(ConfigConst.SysTokenNeverExpire).GetAwaiter().GetResult(); + var enabled = ParseNeverExpireFlag(raw); + _tokenNeverExpireCached = enabled; + return enabled; } catch { - return false; + // 读配置失败时按「永不过期」处理,避免误踢回登录页 + return true; } } + private static bool ParseNeverExpireFlag(string? raw) + { + if (string.IsNullOrWhiteSpace(raw)) + return true; + if (bool.TryParse(raw, out var b)) + return b; + return raw.Trim() is "1" or "Y" or "y" or "yes" or "YES" or "on" or "ON"; + } + /// - /// 登录设置保存后刷新检查间隔(已启动定时器时立即生效) + /// 登录设置保存后刷新检查间隔 / 永不过期策略(已启动定时器时立即生效) /// public static void RefreshTokenCheckIntervalFromConfig() { + _tokenNeverExpireCached = null; + if (IsTokenNeverExpireEnabled()) + { + StopTokenCheckTimer(); + return; + } + ApplyTokenCheckIntervalFromConfig(); + if (_tokenCheckTimer != null && !_tokenCheckTimer.IsEnabled && UserContext?.Token != null) + _tokenCheckTimer.Start(); } // 启动定时器的方法 public static void StartTokenCheckTimer() { + _tokenNeverExpireCached = null; + if (IsTokenNeverExpireEnabled()) + { + StopTokenCheckTimer(); + return; + } + if (_tokenCheckTimer == null) { _tokenCheckTimer = new DispatcherTimer(); diff --git a/yy-admin-master/YY.Admin/ViewModels/Control/MenuTreeViewModel.cs b/yy-admin-master/YY.Admin/ViewModels/Control/MenuTreeViewModel.cs index 96c56d63..f4c9da48 100644 --- a/yy-admin-master/YY.Admin/ViewModels/Control/MenuTreeViewModel.cs +++ b/yy-admin-master/YY.Admin/ViewModels/Control/MenuTreeViewModel.cs @@ -170,6 +170,33 @@ namespace YY.Admin.ViewModels.Control ["/xslmes/mesXslMixingProductionPlan"] = "MixingProductionPlanListView", ["mesXslMixingProductionPlan"] = "MixingProductionPlanListView", + ["McsActMillListView"] = "McsActMillListView", + ["/xslmes/mcsActMill"] = "McsActMillListView", + ["mcsActMill"] = "McsActMillListView", + + // 上辅机mes功能(占位) + ["ShiftHandoverListView"] = "ShiftHandoverListView", + ["/xslmes/shiftHandover"] = "ShiftHandoverListView", + ["shiftHandover"] = "ShiftHandoverListView", + ["BomValidationListView"] = "BomValidationListView", + ["/xslmes/bomValidation"] = "BomValidationListView", + ["bomValidation"] = "BomValidationListView", + ["FormulaMaintainListView"] = "FormulaMaintainListView", + ["/xslmes/formulaMaintain"] = "FormulaMaintainListView", + ["formulaMaintain"] = "FormulaMaintainListView", + ["PlanManageListView"] = "PlanManageListView", + ["/xslmes/planManage"] = "PlanManageListView", + ["planManage"] = "PlanManageListView", + ["ReportQueryListView"] = "ReportQueryListView", + ["/xslmes/reportQuery"] = "ReportQueryListView", + ["reportQuery"] = "ReportQueryListView", + ["EquipmentDowntimeListView"] = "EquipmentDowntimeListView", + ["/xslmes/equipmentDowntime"] = "EquipmentDowntimeListView", + ["equipmentDowntime"] = "EquipmentDowntimeListView", + ["ProductionLogListView"] = "ProductionLogListView", + ["/xslmes/productionLog"] = "ProductionLogListView", + ["productionLog"] = "ProductionLogListView", + // 已实现页面:打印设置 ["PrintSettingsView"] = "PrintSettingsView", ["/system/printSettings"] = "PrintSettingsView", diff --git a/yy-admin-master/YY.Admin/ViewModels/Dialogs/ServerSettingsDialogViewModel.cs b/yy-admin-master/YY.Admin/ViewModels/Dialogs/ServerSettingsDialogViewModel.cs index f484305e..1d05c3c9 100644 --- a/yy-admin-master/YY.Admin/ViewModels/Dialogs/ServerSettingsDialogViewModel.cs +++ b/yy-admin-master/YY.Admin/ViewModels/Dialogs/ServerSettingsDialogViewModel.cs @@ -1,4 +1,5 @@ using YY.Admin.Helper; +using YY.Admin.Services.Service.EquipmentDb; namespace YY.Admin.ViewModels.Dialogs { @@ -45,9 +46,6 @@ namespace YY.Admin.ViewModels.Dialogs } private bool _disconnectConnection; - /// - /// 是否断开连接(true=断开,false=连接) - /// public bool DisconnectConnection { get => _disconnectConnection; @@ -60,11 +58,50 @@ namespace YY.Admin.ViewModels.Dialogs } } - /// - /// 连接参数是否可编辑(勾选断开连接后禁用)。 - /// public bool IsConnectionFieldsEnabled => !DisconnectConnection; + private string _eqDeviceId = string.Empty; + public string EqDeviceId + { + get => _eqDeviceId; + set => SetProperty(ref _eqDeviceId, value); + } + + private string _eqServerHost = "127.0.0.1"; + public string EqServerHost + { + get => _eqServerHost; + set => SetProperty(ref _eqServerHost, value); + } + + private int _eqServerPort = 1433; + public int EqServerPort + { + get => _eqServerPort; + set => SetProperty(ref _eqServerPort, value); + } + + private string _eqDbName = "MES_ShareDB"; + public string EqDbName + { + get => _eqDbName; + set => SetProperty(ref _eqDbName, value); + } + + private string _eqUsername = "sa"; + public string EqUsername + { + get => _eqUsername; + set => SetProperty(ref _eqUsername, value); + } + + private string _eqPassword = string.Empty; + public string EqPassword + { + get => _eqPassword; + set => SetProperty(ref _eqPassword, value); + } + public DelegateCommand SaveCommand { get; } public DelegateCommand CancelCommand { get; } public DialogCloseListener RequestClose { get; private set; } @@ -91,6 +128,14 @@ namespace YY.Admin.ViewModels.Dialogs BasePath = string.IsNullOrWhiteSpace(settings.BasePath) ? "/jeecg-boot" : settings.BasePath; DisconnectConnection = settings.DisconnectConnection; ErrorMessage = string.Empty; + + var eq = EquipmentDbSettingsStore.Load(); + EqDeviceId = string.IsNullOrWhiteSpace(eq.DeviceId) ? Environment.MachineName : eq.DeviceId; + EqServerHost = eq.ServerHost; + EqServerPort = eq.ServerPort; + EqDbName = eq.DbName; + EqUsername = eq.Username; + EqPassword = eq.Password; } private void Save() @@ -106,6 +151,11 @@ namespace YY.Admin.ViewModels.Dialogs ErrorMessage = "端口号必须在 1-65535 之间"; return; } + if (EqServerPort <= 0 || EqServerPort > 65535) + { + ErrorMessage = "设备库端口号必须在 1-65535 之间"; + return; + } try { @@ -127,6 +177,19 @@ namespace YY.Admin.ViewModels.Dialogs WebSocketPath = DefaultWebSocketPath, DisconnectConnection = DisconnectConnection }); + + EquipmentDbSettingsStore.Save(new EquipmentDbSettingsStore.EquipmentDbSettings + { + Enabled = true, + DeviceId = string.IsNullOrWhiteSpace(EqDeviceId) ? Environment.MachineName : EqDeviceId.Trim(), + ServerHost = EqServerHost?.Trim() ?? "127.0.0.1", + ServerPort = EqServerPort, + DbName = EqDbName?.Trim() ?? "MES_ShareDB", + Username = EqUsername?.Trim() ?? "sa", + Password = EqPassword ?? string.Empty, + ConnectTimeoutSeconds = 15 + }); + RequestClose.Invoke(new DialogResult(ButtonResult.OK)); } catch (Exception ex) diff --git a/yy-admin-master/YY.Admin/ViewModels/McsActMill/McsActMillListViewModel.cs b/yy-admin-master/YY.Admin/ViewModels/McsActMill/McsActMillListViewModel.cs new file mode 100644 index 00000000..fce00994 --- /dev/null +++ b/yy-admin-master/YY.Admin/ViewModels/McsActMill/McsActMillListViewModel.cs @@ -0,0 +1,100 @@ +using System.Collections.ObjectModel; +using System.Diagnostics; +using HandyControl.Controls; +using YY.Admin.Core; +using YY.Admin.Core.Helper; +using YY.Admin.Services.Service.EquipmentDb; + +namespace YY.Admin.ViewModels.McsActMill; + +public class McsActMillListViewModel : BaseViewModel +{ + private readonly IMcsActMillService _service; + + private ObservableCollection _items = new(); + public ObservableCollection Items + { + get => _items; + set => SetProperty(ref _items, value); + } + + private long _total; + public long Total { get => _total; set => SetProperty(ref _total, value); } + + private int _pageNo = 1; + public int PageNo { get => _pageNo; set => SetProperty(ref _pageNo, value); } + + private int _pageSize = 20; + public int PageSize { get => _pageSize; set => SetProperty(ref _pageSize, value); } + + private string? _filterActName; + public string? FilterActName { get => _filterActName; set => SetProperty(ref _filterActName, value); } + + private string? _filterActAddrText; + public string? FilterActAddrText { get => _filterActAddrText; set => SetProperty(ref _filterActAddrText, value); } + + public DelegateCommand SearchCommand { get; } + public DelegateCommand ResetCommand { get; } + public DelegateCommand PrevPageCommand { get; } + public DelegateCommand NextPageCommand { get; } + + public McsActMillListViewModel( + IMcsActMillService service, + IContainerExtension container, + IRegionManager regionManager) : base(container, regionManager) + { + _service = service; + + SearchCommand = new DelegateCommand(async () => { PageNo = 1; await LoadAsync(); }); + ResetCommand = new DelegateCommand(async () => + { + FilterActName = null; + FilterActAddrText = null; + PageNo = 1; + await LoadAsync(); + }); + PrevPageCommand = new DelegateCommand(async () => { if (PageNo > 1) { PageNo--; await LoadAsync(); } }); + NextPageCommand = new DelegateCommand(async () => { if ((long)PageNo * PageSize < Total) { PageNo++; await LoadAsync(); } }); + + _ = InitializeAsync(); + } + + private async Task InitializeAsync() + { + try + { + await UIHelper.WaitForRenderAsync(); + await LoadAsync(); + } + catch (Exception ex) + { + Debug.WriteLine($"开炼机动作中间表初始化失败: {ex.Message}"); + } + } + + public async Task LoadAsync() + { + try + { + IsLoading = true; + int? actAddr = null; + if (!string.IsNullOrWhiteSpace(FilterActAddrText) + && int.TryParse(FilterActAddrText.Trim(), out var parsed)) + { + actAddr = parsed; + } + + var result = await _service.PageAsync(PageNo, PageSize, FilterActName, actAddr); + Items = new ObservableCollection(result.Records); + Total = result.Total; + } + catch (Exception ex) + { + Growl.Error($"加载开炼机动作中间表失败:{ex.Message}"); + } + finally + { + IsLoading = false; + } + } +} diff --git a/yy-admin-master/YY.Admin/ViewModels/UpperMes/BomValidationListViewModel.cs b/yy-admin-master/YY.Admin/ViewModels/UpperMes/BomValidationListViewModel.cs new file mode 100644 index 00000000..b945e83b --- /dev/null +++ b/yy-admin-master/YY.Admin/ViewModels/UpperMes/BomValidationListViewModel.cs @@ -0,0 +1,13 @@ +using Prism.Ioc; +using Prism.Navigation.Regions; + +namespace YY.Admin.ViewModels.UpperMes; + +public class BomValidationListViewModel : BaseViewModel +{ + public BomValidationListViewModel(IContainerExtension container, IRegionManager regionManager) + : base(container, regionManager) + { + Title = "bom验证"; + } +} diff --git a/yy-admin-master/YY.Admin/ViewModels/UpperMes/EquipmentDowntimeListViewModel.cs b/yy-admin-master/YY.Admin/ViewModels/UpperMes/EquipmentDowntimeListViewModel.cs new file mode 100644 index 00000000..2c6d9c67 --- /dev/null +++ b/yy-admin-master/YY.Admin/ViewModels/UpperMes/EquipmentDowntimeListViewModel.cs @@ -0,0 +1,13 @@ +using Prism.Ioc; +using Prism.Navigation.Regions; + +namespace YY.Admin.ViewModels.UpperMes; + +public class EquipmentDowntimeListViewModel : BaseViewModel +{ + public EquipmentDowntimeListViewModel(IContainerExtension container, IRegionManager regionManager) + : base(container, regionManager) + { + Title = "设备停机记录"; + } +} diff --git a/yy-admin-master/YY.Admin/ViewModels/UpperMes/FormulaMaintainListViewModel.cs b/yy-admin-master/YY.Admin/ViewModels/UpperMes/FormulaMaintainListViewModel.cs new file mode 100644 index 00000000..9a93f447 --- /dev/null +++ b/yy-admin-master/YY.Admin/ViewModels/UpperMes/FormulaMaintainListViewModel.cs @@ -0,0 +1,13 @@ +using Prism.Ioc; +using Prism.Navigation.Regions; + +namespace YY.Admin.ViewModels.UpperMes; + +public class FormulaMaintainListViewModel : BaseViewModel +{ + public FormulaMaintainListViewModel(IContainerExtension container, IRegionManager regionManager) + : base(container, regionManager) + { + Title = "配方维护"; + } +} diff --git a/yy-admin-master/YY.Admin/ViewModels/UpperMes/PlanManageListViewModel.cs b/yy-admin-master/YY.Admin/ViewModels/UpperMes/PlanManageListViewModel.cs new file mode 100644 index 00000000..1342040a --- /dev/null +++ b/yy-admin-master/YY.Admin/ViewModels/UpperMes/PlanManageListViewModel.cs @@ -0,0 +1,13 @@ +using Prism.Ioc; +using Prism.Navigation.Regions; + +namespace YY.Admin.ViewModels.UpperMes; + +public class PlanManageListViewModel : BaseViewModel +{ + public PlanManageListViewModel(IContainerExtension container, IRegionManager regionManager) + : base(container, regionManager) + { + Title = "计划管理"; + } +} diff --git a/yy-admin-master/YY.Admin/ViewModels/UpperMes/ProductionLogListViewModel.cs b/yy-admin-master/YY.Admin/ViewModels/UpperMes/ProductionLogListViewModel.cs new file mode 100644 index 00000000..894b5ff9 --- /dev/null +++ b/yy-admin-master/YY.Admin/ViewModels/UpperMes/ProductionLogListViewModel.cs @@ -0,0 +1,13 @@ +using Prism.Ioc; +using Prism.Navigation.Regions; + +namespace YY.Admin.ViewModels.UpperMes; + +public class ProductionLogListViewModel : BaseViewModel +{ + public ProductionLogListViewModel(IContainerExtension container, IRegionManager regionManager) + : base(container, regionManager) + { + Title = "生产日志"; + } +} diff --git a/yy-admin-master/YY.Admin/ViewModels/UpperMes/ReportQueryListViewModel.cs b/yy-admin-master/YY.Admin/ViewModels/UpperMes/ReportQueryListViewModel.cs new file mode 100644 index 00000000..3a6b5ed9 --- /dev/null +++ b/yy-admin-master/YY.Admin/ViewModels/UpperMes/ReportQueryListViewModel.cs @@ -0,0 +1,13 @@ +using Prism.Ioc; +using Prism.Navigation.Regions; + +namespace YY.Admin.ViewModels.UpperMes; + +public class ReportQueryListViewModel : BaseViewModel +{ + public ReportQueryListViewModel(IContainerExtension container, IRegionManager regionManager) + : base(container, regionManager) + { + Title = "报表查询"; + } +} diff --git a/yy-admin-master/YY.Admin/ViewModels/UpperMes/ShiftHandoverListViewModel.cs b/yy-admin-master/YY.Admin/ViewModels/UpperMes/ShiftHandoverListViewModel.cs new file mode 100644 index 00000000..e84715d9 --- /dev/null +++ b/yy-admin-master/YY.Admin/ViewModels/UpperMes/ShiftHandoverListViewModel.cs @@ -0,0 +1,13 @@ +using Prism.Ioc; +using Prism.Navigation.Regions; + +namespace YY.Admin.ViewModels.UpperMes; + +public class ShiftHandoverListViewModel : BaseViewModel +{ + public ShiftHandoverListViewModel(IContainerExtension container, IRegionManager regionManager) + : base(container, regionManager) + { + Title = "交接班"; + } +} diff --git a/yy-admin-master/YY.Admin/Views/Dialogs/ServerSettingsDialogView.xaml b/yy-admin-master/YY.Admin/Views/Dialogs/ServerSettingsDialogView.xaml index 2f2bc058..18554425 100644 --- a/yy-admin-master/YY.Admin/Views/Dialogs/ServerSettingsDialogView.xaml +++ b/yy-admin-master/YY.Admin/Views/Dialogs/ServerSettingsDialogView.xaml @@ -38,6 +38,20 @@ Style="{StaticResource ToggleButtonSwitch}"/> + + + + + + + + + + + + + + diff --git a/yy-admin-master/YY.Admin/Views/Dialogs/ServerSettingsDialogView.xaml.cs b/yy-admin-master/YY.Admin/Views/Dialogs/ServerSettingsDialogView.xaml.cs index d5e2b3dd..27ad0e47 100644 --- a/yy-admin-master/YY.Admin/Views/Dialogs/ServerSettingsDialogView.xaml.cs +++ b/yy-admin-master/YY.Admin/Views/Dialogs/ServerSettingsDialogView.xaml.cs @@ -1,4 +1,6 @@ +using System.Windows; using System.Windows.Controls; +using YY.Admin.ViewModels.Dialogs; namespace YY.Admin.Views.Dialogs { @@ -10,6 +12,23 @@ namespace YY.Admin.Views.Dialogs public ServerSettingsDialogView() { InitializeComponent(); + DataContextChanged += OnDataContextChanged; + } + + private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e) + { + if (DataContext is ServerSettingsDialogViewModel vm && !string.IsNullOrEmpty(vm.EqPassword)) + { + EqPasswordBox.Password = vm.EqPassword; + } + } + + private void EqPasswordBox_OnPasswordChanged(object sender, RoutedEventArgs e) + { + if (DataContext is ServerSettingsDialogViewModel vm) + { + vm.EqPassword = EqPasswordBox.Password; + } } } } diff --git a/yy-admin-master/YY.Admin/Views/McsActMill/McsActMillListView.xaml b/yy-admin-master/YY.Admin/Views/McsActMill/McsActMillListView.xaml new file mode 100644 index 00000000..1a8b9372 --- /dev/null +++ b/yy-admin-master/YY.Admin/Views/McsActMill/McsActMillListView.xaml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +