Pārlūkot izejas kodu

对接搅拌焊,机加工件绑定,优化整形逻辑

sunxianglong 2 mēneši atpakaļ
vecāks
revīzija
6e14d97dd1

+ 43 - 0
src/main/java/com/jeesite/modules/FswModbusTest.java

@@ -0,0 +1,43 @@
+package com.jeesite.modules;
+
+import com.jeesite.modules.scheduling.FswModbusScheduling;
+import org.springframework.boot.SpringApplication;
+import org.springframework.context.ConfigurableApplicationContext;
+
+/**
+ * 直接调用 {@link FswModbusScheduling} 定时任务联调。
+ *
+ * 右键运行 main:会启动 Spring 容器,拿到定时任务 Bean,再调用 runOnce()。
+ * 走完整逻辑(质量校验 + 落库 + Modbus 读写),不受 mes.fsw.modbus.enabled 开关限制。
+ *
+ * 运行前请确认 application.yml 中:
+ *   mes.fsw.modbus.host / port / unitId / oprno
+ */
+public class FswModbusTest {
+
+	/** 循环次数(每次间隔 1 秒);设为 1 则只跑一轮 */
+	private static final int LOOP_TIMES = 5;
+
+	public static void main(String[] args) {
+		System.out.println("========== 启动 Spring,调用 FswModbusScheduling ==========");
+		ConfigurableApplicationContext ctx = SpringApplication.run(Application.class, args);
+		try {
+			FswModbusScheduling task = ctx.getBean(FswModbusScheduling.class);
+			for (int i = 1; i <= LOOP_TIMES; i++) {
+				System.out.println();
+				System.out.println(">>> 第 " + i + "/" + LOOP_TIMES + " 轮,调用 runOnce()");
+				task.runOnce();
+				if (i < LOOP_TIMES) {
+					Thread.sleep(1000L);
+				}
+			}
+		} catch (Exception e) {
+			System.err.println("调用失败: " + e.getMessage());
+			e.printStackTrace();
+		} finally {
+			ctx.close();
+			System.out.println();
+			System.out.println("========== 结束 ==========");
+		}
+	}
+}

+ 15 - 0
src/main/java/com/jeesite/modules/mes/dao/MesOrderSnJjDao.java

@@ -0,0 +1,15 @@
+package com.jeesite.modules.mes.dao;
+
+import com.jeesite.common.dao.CrudDao;
+import com.jeesite.common.mybatis.annotation.MyBatisDao;
+import com.jeesite.modules.mes.entity.MesOrderSnJj;
+
+/**
+ * 机加订单工件绑定DAO接口
+ * @author sxl
+ * @version 2026-08-14
+ */
+@MyBatisDao
+public interface MesOrderSnJjDao extends CrudDao<MesOrderSnJj> {
+	
+}

+ 1 - 5
src/main/java/com/jeesite/modules/mes/dao/MesProductScreenDao.java

@@ -4,11 +4,7 @@ import com.jeesite.common.dao.CrudDao;
 import com.jeesite.common.mybatis.annotation.MyBatisDao;
 import com.jeesite.modules.mes.entity.MesProductScreen;
 
-/**
- * ´óÆÁչʾÅäÖÃDAO½Ó¿Ú
- * @author mes
- * @version 2026-08-12
- */
+
 @MyBatisDao
 public interface MesProductScreenDao extends CrudDao<MesProductScreen> {
 

+ 57 - 0
src/main/java/com/jeesite/modules/mes/entity/MesOrderSnJj.java

@@ -0,0 +1,57 @@
+package com.jeesite.modules.mes.entity;
+
+import javax.validation.constraints.Size;
+
+import com.jeesite.common.entity.DataEntity;
+import com.jeesite.common.mybatis.annotation.Column;
+import com.jeesite.common.mybatis.annotation.Table;
+import com.jeesite.common.mybatis.mapper.query.QueryType;
+
+/**
+ * 机加订单工件绑定Entity
+ * @author sxl
+ * @version 2026-08-14
+ */
+@Table(name="mes_order_sn_jj", alias="a", label="机加订单工件绑定信息", columns={
+		@Column(name="id", attrName="id", label="id", isPK=true),
+		@Column(name="order_jj", attrName="orderJj", label="机加工订单号"),
+		@Column(name="sn", attrName="sn", label="工件码"),
+		@Column(name="create_by", attrName="createBy", label="创建人", isUpdate=false, isQuery=false),
+		@Column(name="create_date", attrName="createDate", label="创建时间", isUpdate=false, isQuery=false, isUpdateForce=true),
+		@Column(name="update_by", attrName="updateBy", label="更新人", isQuery=false),
+		@Column(name="update_date", attrName="updateDate", label="更新时间", isQuery=false, isUpdateForce=true),
+	}, orderBy="a.update_date DESC"
+)
+public class MesOrderSnJj extends DataEntity<MesOrderSnJj> {
+	
+	private static final long serialVersionUID = 1L;
+	private String orderJj;		// 机加工订单号
+	private String sn;		// 工件码
+
+	public MesOrderSnJj() {
+		this(null);
+	}
+	
+	public MesOrderSnJj(String id){
+		super(id);
+	}
+	
+	@Size(min=0, max=64, message="机加工订单号长度不能超过 64 个字符")
+	public String getOrderJj() {
+		return orderJj;
+	}
+
+	public void setOrderJj(String orderJj) {
+		this.orderJj = orderJj;
+	}
+	
+	@Size(min=0, max=64, message="工件码长度不能超过 64 个字符")
+	public String getSn() {
+		return sn;
+	}
+
+	public void setSn(String sn) {
+		this.sn = sn;
+	}
+	
+}

+ 81 - 0
src/main/java/com/jeesite/modules/mes/service/MesOrderSnJjService.java

@@ -0,0 +1,81 @@
+package com.jeesite.modules.mes.service;
+
+import java.util.List;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.jeesite.common.entity.Page;
+import com.jeesite.common.service.CrudService;
+import com.jeesite.modules.mes.entity.MesOrderSnJj;
+import com.jeesite.modules.mes.dao.MesOrderSnJjDao;
+
+/**
+ * 机加订单工件绑定Service
+ * @author sxl
+ * @version 2026-08-14
+ */
+@Service
+public class MesOrderSnJjService extends CrudService<MesOrderSnJjDao, MesOrderSnJj> {
+	
+	/**
+	 * 获取单条数据
+	 * @param mesOrderSnJj
+	 * @return
+	 */
+	@Override
+	public MesOrderSnJj get(MesOrderSnJj mesOrderSnJj) {
+		return super.get(mesOrderSnJj);
+	}
+	
+	/**
+	 * 查询分页数据
+	 * @param mesOrderSnJj 查询条件
+	 * @param mesOrderSnJj.page 分页对象
+	 * @return
+	 */
+	@Override
+	public Page<MesOrderSnJj> findPage(MesOrderSnJj mesOrderSnJj) {
+		return super.findPage(mesOrderSnJj);
+	}
+	
+	/**
+	 * 查询列表数据
+	 * @param mesOrderSnJj
+	 * @return
+	 */
+	@Override
+	public List<MesOrderSnJj> findList(MesOrderSnJj mesOrderSnJj) {
+		return super.findList(mesOrderSnJj);
+	}
+	
+	/**
+	 * 保存数据(插入或更新)
+	 * @param mesOrderSnJj
+	 */
+	@Override
+	@Transactional
+	public void save(MesOrderSnJj mesOrderSnJj) {
+		super.save(mesOrderSnJj);
+	}
+	
+	/**
+	 * 更新状态
+	 * @param mesOrderSnJj
+	 */
+	@Override
+	@Transactional
+	public void updateStatus(MesOrderSnJj mesOrderSnJj) {
+		super.updateStatus(mesOrderSnJj);
+	}
+	
+	/**
+	 * 删除数据
+	 * @param mesOrderSnJj
+	 */
+	@Override
+	@Transactional
+	public void delete(MesOrderSnJj mesOrderSnJj) {
+		super.delete(mesOrderSnJj);
+	}
+	
+}

+ 0 - 45
src/main/java/com/jeesite/modules/mes/service/MesProductRecordService.java

@@ -734,11 +734,6 @@ public class MesProductRecordService extends CrudService<MesProductRecordDao, Me
 			}
 		}
 
-		// OP090 工位结果提交 OK 时,同步将 OP100 工位结果改为 OK
-		if ("OP090".equals(oprno) && "OK".equals(content) && "100000".equals(craft)) {
-			syncOp100ResultOk(sn, lineSn, userCode);
-		}
-
 		// 是否更新工位时间
 		if(optime){
 			String sns = sn;
@@ -841,46 +836,6 @@ public class MesProductRecordService extends CrudService<MesProductRecordDao, Me
 
 	}
 
-	/**
-	 * OP090 提交 OK 后,将同工件 OP100(含 OP100A/B/C)工位结果改为 OK
-	 */
-	private void syncOp100ResultOk(String sn, String lineSn, String userCode) {
-		MesProductRecord query = new MesProductRecord();
-		query.setSn(sn);
-		query.setLineSn(lineSn);
-		query.setCraft("100000");
-		query.getSqlMap().getWhere().and("a.oprno", QueryType.RIGHT_LIKE, "OP100");
-		List<MesProductRecord> op100List = findList(query);
-		if (ListUtils.isEmpty(op100List)) {
-			return;
-		}
-		for (MesProductRecord record : op100List) {
-			String oldContent = record.getContent();
-			if ("OK".equals(oldContent)) {
-				continue;
-			}
-			record.setContent("OK");
-			record.setSource("0");
-			if (!StringUtils.isEmpty(userCode)) {
-				record.setUpdateBy(userCode);
-			}
-			super.update(record);
-
-			MesProductModifyRecord modifyRecord = new MesProductModifyRecord();
-			modifyRecord.setOprno(record.getOprno());
-			modifyRecord.setSn(record.getSn());
-			modifyRecord.setCraft(record.getCraft());
-			modifyRecord.setLineSn(record.getLineSn());
-			modifyRecord.setContent("OK");
-			modifyRecord.setContentOld(oldContent);
-			modifyRecord.setSource("0");
-			modifyRecord.setMaterielSn(record.getMaterielSn());
-			modifyRecord.setCreateBy(userCode);
-			modifyRecord.setUpdateBy(userCode);
-			mesProductModifyRecordService.save(modifyRecord);
-		}
-	}
-
 	// 获取模板的ID
 	public String getTemplateItemId(String cateSn,String oldoprno,String craft){
 		String tid = "";

+ 46 - 5
src/main/java/com/jeesite/modules/mes/web/MesOrderSnController.java

@@ -13,8 +13,12 @@ import com.jeesite.modules.erp.entity.MesProductOrder;
 import com.jeesite.modules.erp.service.MesProductOrderService;
 import com.jeesite.modules.mes.entity.MesLine;
 import com.jeesite.modules.mes.entity.MesProduct;
+import com.jeesite.modules.mes.resp.BindMaterialResp;
 import com.jeesite.modules.mes.resp.CommonResp;
+import com.jeesite.modules.mes.resp.ParamsResp;
+import com.jeesite.modules.mes.service.MesLineProcessMaterialService;
 import com.jeesite.modules.mes.service.MesLineService;
+import com.jeesite.modules.mes.service.MesProductRecordService;
 import com.jeesite.modules.mes.service.MesProductService;
 import com.jeesite.modules.sys.entity.User;
 import com.jeesite.modules.sys.utils.UserUtils;
@@ -23,6 +27,8 @@ import org.apache.shiro.authz.annotation.RequiresPermissions;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Controller;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.transaction.interceptor.TransactionAspectSupport;
 import org.springframework.ui.Model;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.ModelAttribute;
@@ -53,6 +59,10 @@ public class MesOrderSnController extends BaseController {
 	private MesProductService mesProductService;
 	@Autowired
 	private MesLineService mesLineService;
+	@Autowired
+	private MesProductRecordService mesProductRecordService;
+	@Autowired
+	private MesLineProcessMaterialService mesLineProcessMaterialService;
 	@Lazy
 	@Autowired
 	private MesProductRecordController mesProductRecordController;
@@ -120,12 +130,13 @@ public class MesOrderSnController extends BaseController {
 	}
 
 	/**
-	 * 生产订单绑定工件码(OP100 工位质量校验通过后方可绑定)
+	 * 生产订单绑定工件码(OP110 工位质量校验通过后方可绑定;绑定成功后提交 OP100 工位结果为 OK)
 	 * @param mesOrderSn
 	 * @return
 	 */
 	@RequestMapping(value = "bandSn")
 	@ResponseBody
+	@Transactional
 	public String bandSn(MesOrderSn mesOrderSn) {
 		if (mesOrderSn == null || StringUtils.isBlank(mesOrderSn.getOrderId())) {
 			return renderResult(Global.FALSE, text("订单编码不能为空!"));
@@ -152,15 +163,15 @@ public class MesOrderSnController extends BaseController {
 		}
 		String lineSn = lineInfo.getSn();
 
-		// OP100 质量校验(按子工位 OP100A)
+		// OP110 质量校验(按子工位 OP110A);允许本工位已加工(OK/NG)时仍可绑定订单
 		String userCode = "";
 		User user = UserUtils.getUser();
 		if (user != null && StringUtils.isNotEmpty(user.getUserCode())) {
 			userCode = user.getUserCode();
 		}
-		String ret = mesProductRecordController.checkQualityCommon("OP100A", sn, lineSn, userCode);
+		String ret = mesProductRecordController.checkQualityCommon("OP110A", sn, lineSn, userCode);
 		String checkret = ret.substring(3, 5);
-		if (!"UD".equals(checkret)) {
+		if (!"UD".equals(checkret) && !"OK".equalsIgnoreCase(checkret) && !"NG".equalsIgnoreCase(checkret)) {
 			String lx = ret.substring(0, 3);
 			return renderResult(Global.FALSE, text(ErrorMsg.getErrorMsg(checkret, lx)));
 		}
@@ -171,11 +182,41 @@ public class MesOrderSnController extends BaseController {
 		if(!list.isEmpty()){
 			return renderResult(Global.FALSE, text("该工件已和生产订单绑定!"));
 		}
-		mesOrderSnService.save(mesOrderSn);
+
+		try {
+			mesOrderSnService.save(mesOrderSn);
+			// 绑定成功后,将 OP100 工位结果提交为 OK
+			List<ParamsResp> params = new ArrayList<>();
+			List<BindMaterialResp> bmlists = mesLineProcessMaterialService.getBindMaterial("OP100A", lineSn);
+			mesProductRecordService.updateRecordResult(sn, "OP100A", "100000", mesOrderSn.getOrderId(), "OK",
+					userCode, params, lineSn, true, bmlists, null);
+		} catch (Exception e) {
+			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
+			return renderResult(Global.FALSE, text(StringUtils.isNotEmpty(e.getMessage()) ? e.getMessage() : "操作失败!"));
+		}
 		return renderResult(Global.TRUE, text("保存成功!"));
 	}
 
 	/**
+	 * 根据工件码判断是否已在生产订单工件表中存在
+	 * @param sn 工件码
+	 * @return result=true 表示已存在,false 表示不存在
+	 */
+	@RequestMapping(value = "existBySn")
+	@ResponseBody
+	public String existBySn(String sn) {
+		if (StringUtils.isBlank(sn)) {
+			return renderResult(Global.FALSE, text("工件码不能为空!"));
+		}
+		MesOrderSn query = new MesOrderSn();
+		query.setSn(sn);
+		if (mesOrderSnService.findCount(query) > 0) {
+			return renderResult(Global.TRUE, text("该工件码已存在!"));
+		}
+		return renderResult(Global.FALSE, text("该工件码不存在!"));
+	}
+
+	/**
 	 * 生产订单列表
 	 * @param mesProductOrder
 	 * @return

+ 171 - 0
src/main/java/com/jeesite/modules/mes/web/MesOrderSnJjController.java

@@ -0,0 +1,171 @@
+package com.jeesite.modules.mes.web;
+
+import java.util.List;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.jeesite.common.lang.ObjectUtils;
+import com.jeesite.common.lang.StringUtils;
+import com.jeesite.modules.mes.entity.MesLine;
+import com.jeesite.modules.mes.entity.MesProduct;
+import com.jeesite.modules.mes.service.MesLineService;
+import com.jeesite.modules.mes.service.MesProductService;
+import com.jeesite.modules.sys.entity.User;
+import com.jeesite.modules.sys.utils.UserUtils;
+import com.jeesite.modules.utils.ErrorMsg;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import com.jeesite.common.config.Global;
+import com.jeesite.common.entity.Page;
+import com.jeesite.common.web.BaseController;
+import com.jeesite.modules.mes.entity.MesOrderSnJj;
+import com.jeesite.modules.mes.service.MesOrderSnJjService;
+
+/**
+ * 机加订单工件绑定Controller
+ * @author sxl
+ * @version 2026-08-14
+ */
+@Controller
+@RequestMapping(value = "${adminPath}/mes/mesOrderSnJj")
+public class MesOrderSnJjController extends BaseController {
+
+	@Autowired
+	private MesOrderSnJjService mesOrderSnJjService;
+	@Autowired
+	private MesProductService mesProductService;
+	@Autowired
+	private MesLineService mesLineService;
+	@Lazy
+	@Autowired
+	private MesProductRecordController mesProductRecordController;
+
+	/**
+	 * 获取数据
+	 */
+	@ModelAttribute
+	public MesOrderSnJj get(String id, boolean isNewRecord) {
+		return mesOrderSnJjService.get(id, isNewRecord);
+	}
+
+	/**
+	 * 查询列表
+	 */
+	@RequiresPermissions("mes:mesOrderSnJj:view")
+	@RequestMapping(value = {"list", ""})
+	public String list(MesOrderSnJj mesOrderSnJj, Model model) {
+		model.addAttribute("mesOrderSnJj", mesOrderSnJj);
+		return "modules/mes/mesOrderSnJjList";
+	}
+
+	/**
+	 * 查询列表数据
+	 */
+	@RequiresPermissions("mes:mesOrderSnJj:view")
+	@RequestMapping(value = "listData")
+	@ResponseBody
+	public Page<MesOrderSnJj> listData(MesOrderSnJj mesOrderSnJj, HttpServletRequest request, HttpServletResponse response) {
+		mesOrderSnJj.setPage(new Page<>(request, response));
+		Page<MesOrderSnJj> page = mesOrderSnJjService.findPage(mesOrderSnJj);
+		return page;
+	}
+
+	/**
+	 * 查看编辑表单
+	 */
+	@RequiresPermissions("mes:mesOrderSnJj:view")
+	@RequestMapping(value = "form")
+	public String form(MesOrderSnJj mesOrderSnJj, Model model) {
+		model.addAttribute("mesOrderSnJj", mesOrderSnJj);
+		return "modules/mes/mesOrderSnJjForm";
+	}
+
+	/**
+	 * 保存数据
+	 */
+	@RequiresPermissions("mes:mesOrderSnJj:edit")
+	@PostMapping(value = "save")
+	@ResponseBody
+	public String save(@Validated MesOrderSnJj mesOrderSnJj) {
+		mesOrderSnJjService.save(mesOrderSnJj);
+		return renderResult(Global.TRUE, text("保存机加订单工件绑定成功!"));
+	}
+
+	/**
+	 * 删除数据
+	 */
+	@RequiresPermissions("mes:mesOrderSnJj:edit")
+	@RequestMapping(value = "delete")
+	@ResponseBody
+	public String delete(MesOrderSnJj mesOrderSnJj) {
+		mesOrderSnJjService.delete(mesOrderSnJj);
+		return renderResult(Global.TRUE, text("删除机加订单工件绑定成功!"));
+	}
+
+	/**
+	 * 机加订单绑定工件码(OP180 工位质量校验通过后方可绑定)
+	 * @param mesOrderSnJj
+	 * @return
+	 */
+	@RequestMapping(value = "bandSn")
+	@ResponseBody
+	public String bandSn(MesOrderSnJj mesOrderSnJj) {
+		if (mesOrderSnJj == null || StringUtils.isBlank(mesOrderSnJj.getOrderJj())) {
+			return renderResult(Global.FALSE, text("订单编码不能为空!"));
+		}
+		if (StringUtils.isBlank(mesOrderSnJj.getSn())) {
+			return renderResult(Global.FALSE, text("工件码不能为空!"));
+		}
+		String sn = mesOrderSnJj.getSn();
+
+		// 判定工件是否存在,并解析产线编号(质量校验需要 lineSn)
+		MesProduct mesProductQuery = new MesProduct();
+		mesProductQuery.setSn(sn);
+		List<MesProduct> productList = mesProductService.findList(mesProductQuery);
+		if (productList == null || productList.isEmpty() || ObjectUtils.isEmpty(productList.get(0))) {
+			return renderResult(Global.FALSE, text("工件不存在!"));
+		}
+		MesProduct mesProduct = productList.get(0);
+		MesLine mesLine = new MesLine();
+		mesLine.setProductCate(mesProduct.getCate());
+		mesLine.setStatus("0");
+		MesLine lineInfo = mesLineService.findInfo(mesLine);
+		if (ObjectUtils.isEmpty(lineInfo) || StringUtils.isEmpty(lineInfo.getSn())) {
+			return renderResult(Global.FALSE, text("产线信息不存在!"));
+		}
+		String lineSn = lineInfo.getSn();
+
+		// OP180 质量校验
+		String userCode = "";
+		User user = UserUtils.getUser();
+		if (user != null && StringUtils.isNotEmpty(user.getUserCode())) {
+			userCode = user.getUserCode();
+		}
+		String ret = mesProductRecordController.checkQualityCommon("OP180", sn, lineSn, userCode);
+		String checkret = ret.substring(3, 5);
+		// OP180 允许本工位已加工(OK/NG)时仍可绑定订单
+		if (!"UD".equals(checkret) && !"OK".equalsIgnoreCase(checkret) && !"NG".equalsIgnoreCase(checkret)) {
+			String lx = ret.substring(0, 3);
+			return renderResult(Global.FALSE, text(ErrorMsg.getErrorMsg(checkret, lx)));
+		}
+
+		MesOrderSnJj query = new MesOrderSnJj();
+		query.setSn(sn);
+		List<MesOrderSnJj> list = mesOrderSnJjService.findList(query);
+		if (!list.isEmpty()) {
+			return renderResult(Global.FALSE, text("该工件已和机加订单绑定!"));
+		}
+		mesOrderSnJjService.save(mesOrderSnJj);
+		return renderResult(Global.TRUE, text("保存成功!"));
+	}
+
+}

+ 18 - 1
src/main/java/com/jeesite/modules/scheduling/FswModbusScheduling.java

@@ -26,6 +26,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
  * FSW 设备 Modbus TCP 握手与过程参数采集。
  * <pre>
  * 寄存器约定:
+ *   4740  总使能(1=执行整套逻辑,其它值跳过)
  *   3600  数据采集触发(1=请求采集,MES读完后置0)
  *   3610  质量校验结果(1=合格,2=不合格)
  *   3620~3649  工件码(30个字,ASCII拼接)
@@ -51,10 +52,12 @@ public class FswModbusScheduling {
 
 	private static final Logger logger = LoggerFactory.getLogger(FswModbusScheduling.class);
 
+	/** 总开关:为 1 时才执行质量校验与过程采集 */
+	private static final int ADDR_ENABLE_FLAG = 4740;
 	private static final int ADDR_COLLECT_FLAG = 3600;
 	private static final int ADDR_QUALITY_RESULT = 3610;
 	private static final int ADDR_SN_START = 3620;
-	private static final int SN_REGISTER_COUNT = 30;
+	private static final int SN_REGISTER_COUNT = 60;
 
 	private static final int ADDR_FEED_RATE = 3740;
 	private static final int ADDR_FEED_OVERRIDE = 3780;
@@ -89,11 +92,25 @@ public class FswModbusScheduling {
 		if (StringUtils.isBlank(enabled) || !"true".equalsIgnoreCase(enabled.trim())) {
 			return;
 		}
+		runOnce();
+	}
+
+	/**
+	 * 直接执行一轮完整逻辑(供测试 / 手工触发)。
+	 * 不受 mes.fsw.modbus.enabled 开关限制,仍会检查 4740 使能点位。
+	 */
+	public void runOnce() {
 		if (!running.compareAndSet(false, true)) {
+			logger.warn("FSW Modbus上一轮尚未结束,跳过本次调用");
 			return;
 		}
 		try {
 			ModbusTcp plc = getClient();
+			Short enable = plc.readInt16(ADDR_ENABLE_FLAG);
+			if (enable == null || enable != 1) {
+				logger.info("FSW Modbus 4740={},未使能,跳过", enable);
+				return;
+			}
 			handleQualityCheck(plc);
 			handleCollectTrigger(plc);
 		} catch (Exception e) {

+ 2 - 2
src/main/resources/config/application.yml

@@ -1095,5 +1095,5 @@ mes:
       host: 10.0.20.220
       port: 502
       unitId: 1
-      oprno:          # 工位号,必填,例如 OP210A
-      lineSn:         # 产线编号,可空(空则按工件类型自动解析)
+      oprno:  OP230        # 工位号,必填,例如 OP210A
+      lineSn:   XT      # 产线编号,可空(空则按工件类型自动解析)

+ 15 - 0
src/main/resources/mappings/modules/mes/MesOrderSnJjDao.xml

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.jeesite.modules.mes.dao.MesOrderSnJjDao">
+	
+	<!-- 查询数据
+	<select id="findList" resultType="MesOrderSnJj">
+		SELECT ${sqlMap.column.toSql()}
+		FROM ${sqlMap.table.toSql()}
+		<where>
+			${sqlMap.where.toSql()}
+		</where>
+		ORDER BY ${sqlMap.order.toSql()}
+	</select> -->
+	
+</mapper>

+ 64 - 0
src/main/resources/views/modules/mes/mesOrderSnJjForm.html

@@ -0,0 +1,64 @@
+<% layout('/layouts/default.html', {title: '机加订单工件绑定管理', libs: ['validate']}){ %>
+<div class="main-content">
+	<div class="box box-main">
+		<div class="box-header with-border">
+			<div class="box-title">
+				<i class="fa icon-note"></i> ${text(mesOrderSnJj.isNewRecord ? '新增机加订单工件绑定' : '编辑机加订单工件绑定')}
+			</div>
+			<div class="box-tools pull-right">
+				<button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
+			</div>
+		</div>
+		<#form:form id="inputForm" model="${mesOrderSnJj}" action="${ctx}/mes/mesOrderSnJj/save" method="post" class="form-horizontal">
+			<div class="box-body">
+				<div class="form-unit">${text('基本信息')}</div>
+				<#form:hidden path="id"/>
+				<div class="row">
+					<div class="col-xs-6">
+						<div class="form-group">
+							<label class="control-label col-sm-4" title="">
+								<span class="required hide">*</span> ${text('机加工订单号')}:<i class="fa icon-question hide"></i></label>
+							<div class="col-sm-8">
+								<#form:input path="orderJj" maxlength="64" class="form-control"/>
+							</div>
+						</div>
+					</div>
+					<div class="col-xs-6">
+						<div class="form-group">
+							<label class="control-label col-sm-4" title="">
+								<span class="required hide">*</span> ${text('工件码')}:<i class="fa icon-question hide"></i></label>
+							<div class="col-sm-8">
+								<#form:input path="sn" maxlength="64" class="form-control"/>
+							</div>
+						</div>
+					</div>
+				</div>
+			</div>
+			<div class="box-footer">
+				<div class="row">
+					<div class="col-sm-offset-2 col-sm-10">
+						<% if (hasPermi('mes:mesOrderSnJj:edit')){ %>
+							<button type="submit" class="btn btn-sm btn-primary" id="btnSubmit"><i class="fa fa-check"></i> ${text('保 存')}</button>&nbsp;
+						<% } %>
+						<button type="button" class="btn btn-sm btn-default" id="btnCancel" onclick="js.closeCurrentTabPage()"><i class="fa fa-reply-all"></i> ${text('关 闭')}</button>
+					</div>
+				</div>
+			</div>
+		</#form:form>
+	</div>
+</div>
+<% } %>
+<script>
+$("#inputForm").validate({
+	submitHandler: function(form){
+		js.ajaxSubmitForm($(form), function(data){
+			js.showMessage(data.message);
+			if(data.result == Global.TRUE){
+				js.closeCurrentTabPage(function(contentWindow){
+					contentWindow.page();
+				});
+			}
+		}, "json");
+    }
+});
+</script>

+ 66 - 0
src/main/resources/views/modules/mes/mesOrderSnJjList.html

@@ -0,0 +1,66 @@
+<% layout('/layouts/default.html', {title: '机加订单工件绑定管理', libs: ['dataGrid']}){ %>
+<div class="main-content">
+	<div class="box box-main">
+		<div class="box-header">
+			<div class="box-title">
+				<i class="fa icon-notebook"></i> ${text('机加订单工件绑定管理')}
+			</div>
+			<div class="box-tools pull-right">
+				<a href="#" class="btn btn-default" id="btnSearch" title="${text('查询')}"><i class="fa fa-filter"></i> ${text('查询')}</a>
+				<% if(hasPermi('mes:mesOrderSnJj:edit')){ %>
+					<a href="${ctx}/mes/mesOrderSnJj/form" class="btn btn-default btnTool" title="${text('新增机加订单工件绑定')}"><i class="fa fa-plus"></i> ${text('新增')}</a>
+				<% } %>
+				<a href="#" class="btn btn-default" id="btnSetting" title="${text('设置')}"><i class="fa fa-navicon"></i></a>
+			</div>
+		</div>
+		<div class="box-body">
+			<#form:form id="searchForm" model="${mesOrderSnJj}" action="${ctx}/mes/mesOrderSnJj/listData" method="post" class="form-inline hide"
+					data-page-no="${parameter.pageNo}" data-page-size="${parameter.pageSize}" data-order-by="${parameter.orderBy}">
+				<div class="form-group">
+					<label class="control-label">${text('机加工订单号')}:</label>
+					<div class="control-inline">
+						<#form:input path="orderJj" maxlength="64" class="form-control width-120"/>
+					</div>
+				</div>
+				<div class="form-group">
+					<label class="control-label">${text('工件码')}:</label>
+					<div class="control-inline">
+						<#form:input path="sn" maxlength="64" class="form-control width-120"/>
+					</div>
+				</div>
+				<div class="form-group">
+					<button type="submit" class="btn btn-primary btn-sm">${text('查询')}</button>
+					<button type="reset" class="btn btn-default btn-sm">${text('重置')}</button>
+				</div>
+			</#form:form>
+			<table id="dataGrid"></table>
+			<div id="dataGridPage"></div>
+		</div>
+	</div>
+</div>
+<% } %>
+<script>
+// 初始化DataGrid对象
+$('#dataGrid').dataGrid({
+	searchForm: $("#searchForm"),
+	columnModel: [
+		{header:'${text("机加工订单号")}', name:'orderJj', index:'a.order_jj', width:150, align:"left", frozen:true, formatter: function(val, obj, row, act){
+			return '<a href="${ctx}/mes/mesOrderSnJj/form?id='+row.id+'" class="btnList" data-title="${text("编辑机加订单工件绑定")}">'+(val||row.id)+'</a>';
+		}},
+		{header:'${text("工件码")}', name:'sn', index:'a.sn', width:150, align:"left"},
+		{header:'${text("更新时间")}', name:'updateDate', index:'a.update_date', width:150, align:"center"},
+		{header:'${text("操作")}', name:'actions', width:120, formatter: function(val, obj, row, act){
+			var actions = [];
+			//<% if(hasPermi('mes:mesOrderSnJj:edit')){ %>
+				actions.push('<a href="${ctx}/mes/mesOrderSnJj/form?id='+row.id+'" class="btnList" title="${text("编辑机加订单工件绑定")}"><i class="fa fa-pencil"></i></a>&nbsp;');
+				actions.push('<a href="${ctx}/mes/mesOrderSnJj/delete?id='+row.id+'" class="btnList" title="${text("删除机加订单工件绑定")}" data-confirm="${text("确认要删除该机加订单工件绑定吗?")}"><i class="fa fa-trash-o"></i></a>&nbsp;');
+			//<% } %>
+			return actions.join('');
+		}}
+	],
+	// 加载成功后执行事件
+	ajaxSuccess: function(data){
+		
+	}
+});
+</script>