ソースを参照

滚筒线接口,FSW接口优化,订单绑定优化

sunxianglong 2 ヶ月 前
親
コミット
1d2ade0e58

+ 123 - 21
src/main/java/com/jeesite/modules/FswModbusTest.java

@@ -1,43 +1,145 @@
 package com.jeesite.modules;
 
-import com.jeesite.modules.scheduling.FswModbusScheduling;
+import com.github.xingshuangs.iot.protocol.modbus.service.ModbusTcp;
+import com.jeesite.common.config.Global;
+import com.jeesite.common.lang.StringUtils;
+import com.jeesite.modules.mes.entity.MesProductFsw;
+import com.jeesite.modules.mes.service.MesProductFswService;
 import org.springframework.boot.SpringApplication;
 import org.springframework.context.ConfigurableApplicationContext;
 
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+
 /**
- * 直接调用 {@link FswModbusScheduling} 定时任务联调。
- *
- * 右键运行 main:会启动 Spring 容器,拿到定时任务 Bean,再调用 runOnce()。
- * 走完整逻辑(质量校验 + 落库 + Modbus 读写),不受 mes.fsw.modbus.enabled 开关限制。
- *
- * 运行前请确认 application.yml 中:
- *   mes.fsw.modbus.host / port / unitId / oprno
+ * FSW Modbus 直采测试:不看 4740 / 3600 / 质量校验,直接读点位并写入 mes_product_fsw。
+ * <p>
+ * 右键运行 main 即可。
  */
 public class FswModbusTest {
 
-	/** 循环次数(每次间隔 1 秒);设为 1 则只跑一轮 */
-	private static final int LOOP_TIMES = 5;
+	// ========== 点位 ==========
+	private static final int ADDR_SN_START = 3620;
+	private static final int SN_REGISTER_COUNT = 60;
+	private static final int ADDR_FEED_SPEED = 3740;
+	private static final int ADDR_FEED_RATE = 3780;
+	private static final int ADDR_SPINDLE_SPEED = 3820;
+	private static final int ADDR_SPINDLE_RATE = 3860;
+	private static final int ADDR_WELD_PRESSURE = 4040;
+	private static final int ADDR_WELD_OFFSET = 4590;
+	private static final int ADDR_B_AXIS_ANGLE = 4742;
+
+	/** 落库固定产线 / 工位 */
+	private static final String LINE_SN = "XT";
+	private static final String OPRNO = "OP230B";
+
+	/**
+	 * 指定工件码;为空则读 3620,仍为空则用 FSW_TEST_时间戳(保证一定能落库)
+	 */
+	private static final String FORCE_SN = "";
 
 	public static void main(String[] args) {
-		System.out.println("========== 启动 Spring,调用 FswModbusScheduling ==========");
+		System.out.println("========== FSW 直采(无视前置条件)→ mes_product_fsw ==========");
 		ConfigurableApplicationContext ctx = SpringApplication.run(Application.class, args);
+		ModbusTcp plc = null;
 		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);
-				}
+			MesProductFswService fswService = ctx.getBean(MesProductFswService.class);
+
+			String host = Global.getConfig("mes.fsw.modbus.host");
+			if (StringUtils.isBlank(host)) {
+				host = "10.0.20.220";
 			}
+			int port = parseInt(Global.getConfig("mes.fsw.modbus.port"), 502);
+			int unitId = parseInt(Global.getConfig("mes.fsw.modbus.unitId"), 1);
+
+			System.out.println("连接 Modbus " + host + ":" + port + " unitId=" + unitId);
+			plc = new ModbusTcp(unitId, host, port);
+
+			String sn = StringUtils.isNotBlank(FORCE_SN) ? FORCE_SN.trim() : readWorkpieceCode(plc);
+			if (StringUtils.isBlank(sn)) {
+				sn = "FSW_TEST_" + System.currentTimeMillis();
+				System.out.println("3620 无工件码,使用临时 sn=" + sn);
+			} else {
+				System.out.println("工件码 sn=" + sn);
+			}
+
+			short feedSpeed = plc.readInt16(ADDR_FEED_SPEED);
+			short feedRate = plc.readInt16(ADDR_FEED_RATE);
+			short spindleSpeed = plc.readInt16(ADDR_SPINDLE_SPEED);
+			short spindleRate = plc.readInt16(ADDR_SPINDLE_RATE);
+			short pressure = plc.readInt16(ADDR_WELD_PRESSURE);
+			short pyl = plc.readInt16(ADDR_WELD_OFFSET);
+			short jd = plc.readInt16(ADDR_B_AXIS_ANGLE);
+
+			System.out.println("进给速度(3740)=" + feedSpeed);
+			System.out.println("进给倍率(3780)=" + feedRate);
+			System.out.println("主轴转速(3820)=" + spindleSpeed);
+			System.out.println("主轴倍率(3860)=" + spindleRate);
+			System.out.println("焊接压力(4040)=" + pressure);
+			System.out.println("偏移量(4590)=" + pyl);
+			System.out.println("B轴角度(4742)=" + jd);
+
+			MesProductFsw entity = new MesProductFsw();
+			entity.setSn(sn);
+			entity.setOprno(OPRNO);
+			entity.setLineSn(LINE_SN);
+			entity.setFeedSpeed(String.valueOf(feedSpeed));
+			entity.setFeedRate(String.valueOf(feedRate));
+			entity.setSpindleSpeed(String.valueOf(spindleSpeed));
+			entity.setSpindleRate(String.valueOf(spindleRate));
+			entity.setPressure(String.valueOf(pressure));
+			entity.setPyl(String.valueOf(pyl));
+			entity.setJd(String.valueOf(jd));
+			entity.setCreateDate(new Date());
+			fswService.save(entity);
+
+			System.out.println(">>> 已保存 mes_product_fsw id=" + entity.getId()
+					+ " sn=" + entity.getSn()
+					+ " oprno=" + entity.getOprno()
+					+ " lineSn=" + entity.getLineSn());
 		} catch (Exception e) {
-			System.err.println("调用失败: " + e.getMessage());
+			System.err.println("直采失败: " + e.getMessage());
 			e.printStackTrace();
 		} finally {
+			if (plc != null) {
+				try {
+					plc.close();
+				} catch (Exception ignore) {
+				}
+			}
 			ctx.close();
-			System.out.println();
 			System.out.println("========== 结束 ==========");
 		}
 	}
+
+	private static String readWorkpieceCode(ModbusTcp plc) {
+		byte[] bytes = plc.readHoldRegister(ADDR_SN_START, SN_REGISTER_COUNT);
+		if (bytes == null || bytes.length == 0) {
+			return null;
+		}
+		String raw = new String(bytes, StandardCharsets.US_ASCII);
+		StringBuilder sb = new StringBuilder(raw.length());
+		for (int i = 0; i < raw.length(); i++) {
+			char c = raw.charAt(i);
+			if (c == 0 || c == ' ') {
+				continue;
+			}
+			if (c >= 32 && c < 127) {
+				sb.append(c);
+			}
+		}
+		String sn = sb.toString().trim();
+		return sn.isEmpty() ? null : sn;
+	}
+
+	private static int parseInt(String val, int defaultVal) {
+		if (StringUtils.isBlank(val)) {
+			return defaultVal;
+		}
+		try {
+			return Integer.parseInt(val.trim());
+		} catch (Exception e) {
+			return defaultVal;
+		}
+	}
 }

+ 110 - 29
src/main/java/com/jeesite/modules/scheduling/FswModbusScheduling.java

@@ -1,6 +1,5 @@
 package com.jeesite.modules.scheduling;
 
-import com.github.xingshuangs.iot.protocol.common.buff.EByteBuffFormat;
 import com.github.xingshuangs.iot.protocol.modbus.service.ModbusTcp;
 import com.jeesite.common.config.Global;
 import com.jeesite.common.lang.ObjectUtils;
@@ -94,7 +93,10 @@ public class FswModbusScheduling {
 
 	private final AtomicBoolean running = new AtomicBoolean(false);
 	private volatile ModbusTcp modbusTcp;
+	/** 质量校验去重:同一工件码不重复校验 */
 	private volatile String lastCheckedSn;
+	/** 最近一次非空工件码(采集时 PLC 可能已清空 3620,用此兜底) */
+	private volatile String lastKnownSn;
 
 	/**
 	 * 每秒执行:工件码质量校验 + 过程参数采集握手
@@ -135,14 +137,67 @@ public class FswModbusScheduling {
 	}
 
 	/**
+	 * 强制采集并落库(供测试):不检查 4740 / 3600,直接读过程参数写入 mes_product_fsw。
+	 *
+	 * @param snOverride     指定工件码;为空则从 3620 读取,仍为空则用最近缓存
+	 * @param clearFlag3600  落库成功后是否将 3600 置 0
+	 * @param updateStation  是否同步将 OP230B 工位结果置 OK
+	 * @return true=过程参数已写入表
+	 */
+	public boolean collectAndSaveNow(String snOverride, boolean clearFlag3600, boolean updateStation) {
+		if (!running.compareAndSet(false, true)) {
+			logger.warn("FSW Modbus上一轮尚未结束,跳过强制采集");
+			return false;
+		}
+		try {
+			ModbusTcp plc = getClient();
+			String sn = StringUtils.isNotBlank(snOverride) ? snOverride.trim() : readWorkpieceCode(plc);
+			if (StringUtils.isBlank(sn)) {
+				sn = lastKnownSn;
+			}
+			if (StringUtils.isBlank(sn)) {
+				logger.error("强制采集失败:无工件码(可在测试类 FORCE_SN 指定)");
+				return false;
+			}
+			lastKnownSn = sn;
+
+			short feedSpeed = plc.readInt16(ADDR_FEED_SPEED);
+			short feedRate = plc.readInt16(ADDR_FEED_RATE);
+			short spindleSpeed = plc.readInt16(ADDR_SPINDLE_SPEED);
+			short spindleRate = plc.readInt16(ADDR_SPINDLE_RATE);
+			short weldPressure = plc.readInt16(ADDR_WELD_PRESSURE);
+			short weldOffset = plc.readInt16(ADDR_WELD_OFFSET);
+			short bAxisAngle = plc.readInt16(ADDR_B_AXIS_ANGLE);
+
+			logger.info("FSW强制采集 sn={} 进给速度={} 进给倍率={} 主轴转速={} 主轴倍率={} 焊接压力={} 偏移量={} B轴角度={}",
+					sn, feedSpeed, feedRate, spindleSpeed, spindleRate, weldPressure, weldOffset, bAxisAngle);
+
+			boolean ok = saveProcessData(sn, feedSpeed, feedRate, spindleSpeed, spindleRate,
+					weldPressure, weldOffset, bAxisAngle, updateStation);
+			if (ok && clearFlag3600) {
+				plc.writeInt16(ADDR_COLLECT_FLAG, (short) 0);
+				logger.info("FSW强制采集完成,已将 3600 置 0 sn={}", sn);
+			}
+			return ok;
+		} catch (Exception e) {
+			logger.error("FSW强制采集失败:{}", e.getMessage(), e);
+			closeClient();
+			return false;
+		} finally {
+			running.set(false);
+		}
+	}
+
+	/**
 	 * 读取工件码 → 质量校验 → 写回 3610(1合格 / 2不合格)
 	 */
 	private void handleQualityCheck(ModbusTcp plc) {
 		String sn = readWorkpieceCode(plc);
 		if (StringUtils.isBlank(sn)) {
-			lastCheckedSn = null;
+			// 不清空 lastKnownSn:采集触发时寄存器可能已被 PLC 清掉
 			return;
 		}
+		lastKnownSn = sn;
 		if (sn.equals(lastCheckedSn)) {
 			return;
 		}
@@ -154,7 +209,7 @@ public class FswModbusScheduling {
 	}
 
 	/**
-	 * 3600=1 时采集过程参数,完成后置 0
+	 * 3600=1 时采集过程参数;仅保存成功后才置 0,避免空工件码时丢数却清握手位
 	 */
 	private void handleCollectTrigger(ModbusTcp plc) {
 		Short flag = plc.readInt16(ADDR_COLLECT_FLAG);
@@ -163,19 +218,33 @@ public class FswModbusScheduling {
 		}
 
 		String sn = readWorkpieceCode(plc);
-		float feedSpeed = plc.readFloat32(ADDR_FEED_SPEED, EByteBuffFormat.BA_DC);
-		float feedRate = plc.readFloat32(ADDR_FEED_RATE, EByteBuffFormat.BA_DC);
-		float spindleSpeed = plc.readFloat32(ADDR_SPINDLE_SPEED, EByteBuffFormat.BA_DC);
-		float spindleRate = plc.readFloat32(ADDR_SPINDLE_RATE, EByteBuffFormat.BA_DC);
-		float weldPressure = plc.readFloat32(ADDR_WELD_PRESSURE, EByteBuffFormat.BA_DC);
-		float weldOffset = plc.readFloat32(ADDR_WELD_OFFSET, EByteBuffFormat.BA_DC);
-		float bAxisAngle = plc.readFloat32(ADDR_B_AXIS_ANGLE, EByteBuffFormat.BA_DC);
+		if (StringUtils.isBlank(sn)) {
+			sn = lastKnownSn;
+			logger.warn("FSW过程采集:3620 工件码为空,使用最近工件码 sn={}", sn);
+		}
+		if (StringUtils.isBlank(sn)) {
+			logger.error("FSW过程采集失败:无工件码,保持 3600=1 待重试");
+			return;
+		}
+		lastKnownSn = sn;
+
+		short feedSpeed = plc.readInt16(ADDR_FEED_SPEED);
+		short feedRate = plc.readInt16(ADDR_FEED_RATE);
+		short spindleSpeed = plc.readInt16(ADDR_SPINDLE_SPEED);
+		short spindleRate = plc.readInt16(ADDR_SPINDLE_RATE);
+		short weldPressure = plc.readInt16(ADDR_WELD_PRESSURE);
+		short weldOffset = plc.readInt16(ADDR_WELD_OFFSET);
+		short bAxisAngle = plc.readInt16(ADDR_B_AXIS_ANGLE);
 
 		logger.info("FSW过程采集 sn={} 进给速度={} 进给倍率={} 主轴转速={} 主轴倍率={} 焊接压力={} 偏移量={} B轴角度={}",
 				sn, feedSpeed, feedRate, spindleSpeed, spindleRate, weldPressure, weldOffset, bAxisAngle);
 
-		saveProcessData(sn, feedSpeed, feedRate, spindleSpeed, spindleRate, weldPressure, weldOffset, bAxisAngle);
-		plc.writeInt16(ADDR_COLLECT_FLAG, (short) 0);
+		if (saveProcessData(sn, feedSpeed, feedRate, spindleSpeed, spindleRate, weldPressure, weldOffset, bAxisAngle, true)) {
+			plc.writeInt16(ADDR_COLLECT_FLAG, (short) 0);
+			logger.info("FSW过程采集完成,已将 3600 置 0 sn={}", sn);
+		} else {
+			logger.error("FSW过程参数保存失败,保持 3600=1 待重试 sn={}", sn);
+		}
 	}
 
 	private boolean doQualityCheck(String sn) {
@@ -225,34 +294,50 @@ public class FswModbusScheduling {
 		return line.getSn();
 	}
 
-	private void saveProcessData(String sn, float feedSpeed, float feedRate,
-			float spindleSpeed, float spindleRate, float weldPressure, float weldOffset, float bAxisAngle) {
+	/**
+	 * @param updateStation true 时同步提交 OP230B 工位结果为 OK
+	 * @return true=过程参数已落库(工位结果更新失败不影响返回 true)
+	 */
+	private boolean saveProcessData(String sn, short feedSpeed, short feedRate,
+			short spindleSpeed, short spindleRate, short weldPressure, short weldOffset, short bAxisAngle,
+			boolean updateStation) {
 		if (StringUtils.isBlank(sn)) {
-			return;
+			logger.error("FSW过程参数保存跳过:工件码为空");
+			return false;
 		}
 		try {
 			MesProductFsw entity = new MesProductFsw();
 			entity.setSn(sn);
 			entity.setOprno(FIXED_OPRNO);
 			entity.setLineSn(FIXED_LINE_SN);
-			entity.setFeedSpeed(fmt(feedSpeed));
-			entity.setFeedRate(fmt(feedRate));
-			entity.setSpindleSpeed(fmt(spindleSpeed));
-			entity.setSpindleRate(fmt(spindleRate));
-			entity.setPressure(fmt(weldPressure));
-			entity.setPyl(fmt(weldOffset));
-			entity.setJd(fmt(bAxisAngle));
+			entity.setFeedSpeed(String.valueOf(feedSpeed));
+			entity.setFeedRate(String.valueOf(feedRate));
+			entity.setSpindleSpeed(String.valueOf(spindleSpeed));
+			entity.setSpindleRate(String.valueOf(spindleRate));
+			entity.setPressure(String.valueOf(weldPressure));
+			entity.setPyl(String.valueOf(weldOffset));
+			entity.setJd(String.valueOf(bAxisAngle));
 			mesProductFswService.save(entity);
+			logger.info("FSW过程参数已写入 mes_product_fsw sn={} oprno={}", sn, FIXED_OPRNO);
+		} catch (Exception e) {
+			logger.error("FSW过程参数保存失败 sn={}:{}", sn, e.getMessage(), e);
+			return false;
+		}
 
-			// 保存完成后提交工位总结果为 OK
+		if (!updateStation) {
+			return true;
+		}
+		try {
 			List<ParamsResp> params = new ArrayList<>();
 			List<BindMaterialResp> bmlists = new ArrayList<>();
 			mesProductRecordService.updateRecordResult(sn, FIXED_OPRNO, "100000", "", "OK",
 					"", params, FIXED_LINE_SN, true, bmlists, null);
-			logger.info("FSW过程参数已保存并更新工位结果 OK sn={} oprno={}", sn, FIXED_OPRNO);
+			logger.info("FSW已更新工位结果 OK sn={} oprno={}", sn, FIXED_OPRNO);
 		} catch (Exception e) {
-			logger.error("FSW过程参数保存失败 sn={}:{}", sn, e.getMessage());
+			// 过程参数已落库,工位结果失败单独记日志,不阻断清 3600
+			logger.error("FSW更新工位结果失败 sn={}:{}", sn, e.getMessage(), e);
 		}
+		return true;
 	}
 
 	/**
@@ -319,8 +404,4 @@ public class FswModbusScheduling {
 			return defaultVal;
 		}
 	}
-
-	private static String fmt(float val) {
-		return String.format("%.3f", val);
-	}
 }