|
|
@@ -0,0 +1,558 @@
|
|
|
+package com.mes.ui;
|
|
|
+
|
|
|
+import com.github.s7connector.api.DaveArea;
|
|
|
+import com.github.s7connector.api.factory.S7ConnectorFactory;
|
|
|
+import com.github.xingshuangs.iot.protocol.s7.service.S7PLC;
|
|
|
+import com.mes.util.JdbcUtils;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+
|
|
|
+import javax.swing.*;
|
|
|
+import java.math.BigInteger;
|
|
|
+import java.text.DecimalFormat;
|
|
|
+
|
|
|
+public class S7Util {
|
|
|
+
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(S7Util.class);
|
|
|
+
|
|
|
+ private static final int DB9051 = 9051;
|
|
|
+ private static final int DB9051_LEN = 38;
|
|
|
+ private static final int DB9052 = 9052;
|
|
|
+
|
|
|
+ /** 连接失败后的重试间隔 */
|
|
|
+ private static final long RECONNECT_COOLDOWN_MS = 5000L;
|
|
|
+ private static volatile long nextReconnectAt = 0L;
|
|
|
+ private static volatile boolean connected = false;
|
|
|
+ /** 实测本设备 slot=2 可通;成功后优先复用 */
|
|
|
+ private static volatile int lastSuccessSlot = 2;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 监控线程入口:读 DB9051 一次,刷新界面并执行业务状态机
|
|
|
+ */
|
|
|
+ public static void pollDeviceCycle() {
|
|
|
+ try {
|
|
|
+ if (!ensureConnected()) {
|
|
|
+ SwingUtilities.invokeLater(() -> MesClient.updatePlcStatus(false));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ SwingUtilities.invokeLater(() -> MesClient.updatePlcStatus(true));
|
|
|
+ byte[] db9051;
|
|
|
+ try {
|
|
|
+ db9051 = MesClient.s7Connector.read(DaveArea.DB, DB9051, DB9051_LEN, 0);
|
|
|
+ } catch (Exception e) {
|
|
|
+ markBroken("读DB9051", e);
|
|
|
+ SwingUtilities.invokeLater(() -> MesClient.updatePlcStatus(false));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (db9051 == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ updateRealtimeDisplay(db9051);
|
|
|
+ getDeviceState(db9051);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[PLC异常] pollDeviceCycle", e);
|
|
|
+ markBroken("pollDeviceCycle", e);
|
|
|
+ SwingUtilities.invokeLater(() -> MesClient.updatePlcStatus(false));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static synchronized boolean isConnected() {
|
|
|
+ return connected && MesClient.s7Connector != null;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static synchronized void disconnect() {
|
|
|
+ connected = false;
|
|
|
+ if (MesClient.s7Connector != null) {
|
|
|
+ try {
|
|
|
+ MesClient.s7Connector.close();
|
|
|
+ } catch (Exception ignored) {
|
|
|
+ }
|
|
|
+ MesClient.s7Connector = null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static synchronized boolean connect() {
|
|
|
+ disconnect();
|
|
|
+ // Unexpected function code 常见于 rack/slot 不匹配:
|
|
|
+ // 旧版默认 slot=2;S7-1200 常用 slot=1。按历史成功值优先,再依次尝试。
|
|
|
+ int[] slots;
|
|
|
+ if (lastSuccessSlot == 1) {
|
|
|
+ slots = new int[]{1, 2};
|
|
|
+ } else if (lastSuccessSlot == 2) {
|
|
|
+ slots = new int[]{2, 1};
|
|
|
+ } else {
|
|
|
+ slots = new int[]{2, 1};
|
|
|
+ }
|
|
|
+
|
|
|
+ Exception lastError = null;
|
|
|
+ for (int slot : slots) {
|
|
|
+ try {
|
|
|
+ log.info("[PLC连接] 尝试 {} rack=0 slot={} port=102 timeout=5000", MesClient.plcUrl, slot);
|
|
|
+ MesClient.s7Connector = S7ConnectorFactory
|
|
|
+ .buildTCPConnector()
|
|
|
+ .withHost(MesClient.plcUrl)
|
|
|
+ .withPort(102)
|
|
|
+ .withRack(0)
|
|
|
+ .withSlot(slot)
|
|
|
+ .withTimeout(5000)
|
|
|
+ .build();
|
|
|
+ byte[] probe = MesClient.s7Connector.read(DaveArea.DB, DB9051, DB9051_LEN, 0);
|
|
|
+ if (probe == null || probe.length < DB9051_LEN) {
|
|
|
+ throw new IllegalStateException("探测读取DB9051返回空数据");
|
|
|
+ }
|
|
|
+ connected = true;
|
|
|
+ lastSuccessSlot = slot;
|
|
|
+ nextReconnectAt = 0L;
|
|
|
+ log.info("[PLC连接] 连接成功 {} rack=0 slot={}", MesClient.plcUrl, slot);
|
|
|
+ return true;
|
|
|
+ } catch (Exception e) {
|
|
|
+ lastError = e;
|
|
|
+ log.error("[PLC连接] rack=0 slot={} 失败: {}", slot, rootCause(e));
|
|
|
+ disconnect();
|
|
|
+ try {
|
|
|
+ Thread.sleep(800);
|
|
|
+ } catch (InterruptedException ie) {
|
|
|
+ Thread.currentThread().interrupt();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ log.error("[PLC连接] 全部 slot 尝试失败 {}: {}", MesClient.plcUrl,
|
|
|
+ lastError == null ? "" : rootCause(lastError));
|
|
|
+ nextReconnectAt = System.currentTimeMillis() + RECONNECT_COOLDOWN_MS;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 确保可用连接:半开连接探测失败时强制断开再重连。
|
|
|
+ */
|
|
|
+ public static synchronized boolean ensureConnected() {
|
|
|
+ if (connected && MesClient.s7Connector != null) {
|
|
|
+ try {
|
|
|
+ // 心跳探测只读1字节,减轻通讯负担
|
|
|
+ byte[] probe = MesClient.s7Connector.read(DaveArea.DB, DB9051, 1, 0);
|
|
|
+ if (probe != null && probe.length >= 1) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ log.info("[PLC连接] 探测读取异常,准备重连");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.info("[PLC连接] 连接已失效,准备重连: {}", rootCause(e));
|
|
|
+ }
|
|
|
+ disconnect();
|
|
|
+ }
|
|
|
+ if (System.currentTimeMillis() < nextReconnectAt) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return connect();
|
|
|
+ }
|
|
|
+
|
|
|
+ private static synchronized void markBroken(String action, Exception e) {
|
|
|
+ log.info("[PLC] {} 失败,释放连接: {}", action, rootCause(e));
|
|
|
+ disconnect();
|
|
|
+ nextReconnectAt = System.currentTimeMillis() + RECONNECT_COOLDOWN_MS;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String rootCause(Throwable e) {
|
|
|
+ if (e == null) {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ Throwable cur = e;
|
|
|
+ while (cur.getCause() != null && cur.getCause() != cur) {
|
|
|
+ cur = cur.getCause();
|
|
|
+ }
|
|
|
+ if (cur == e) {
|
|
|
+ return e.getClass().getSimpleName() + ": " + e.getMessage();
|
|
|
+ }
|
|
|
+ return e.getClass().getSimpleName() + "(" + e.getMessage() + ") <- "
|
|
|
+ + cur.getClass().getSimpleName() + ": " + cur.getMessage();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 监听状态
|
|
|
+ public static void getDeviceState(byte[] db9051){
|
|
|
+ try {
|
|
|
+ if (MesClient.s7Connector == null || db9051 == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (MesClient.tjFlag == 3) {
|
|
|
+ if (MesClient.tjStatus == 0) {
|
|
|
+ MesClient.tjStatus = 1;
|
|
|
+ log.info("[流程] 焊接完成,复位允许启动并提交MES总结果 OK,工件={}",
|
|
|
+ MesClient.product_sn.getText());
|
|
|
+ sendAllowStart(false);
|
|
|
+ Boolean ret = DataUtil.sendQuality(
|
|
|
+ MesClient.nettyClient, MesClient.product_sn.getText(), "OK", MesClient.user20);
|
|
|
+ if (ret) {
|
|
|
+ log.info("[MES写] MQDW 提交成功,工件={}", MesClient.product_sn.getText());
|
|
|
+ MesClient.resetScanA();
|
|
|
+ MesClient.scan_type = 1;
|
|
|
+ MesClient.scanBarcode();
|
|
|
+ MesClient.setMenuStatus("结果提交成功,请扫下一件", 0);
|
|
|
+ } else {
|
|
|
+ log.warn("[MES写] MQDW 提交失败,工件={},等待重试", MesClient.product_sn.getText());
|
|
|
+ MesClient.tjStatus = 0;
|
|
|
+ MesClient.setMenuStatus(MesClient.tjFlagTextErr, -1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if (isWaitingProcess()) {
|
|
|
+ if (isWeldingComplete(db9051)) {
|
|
|
+ log.info("[流程] 检测到焊接完成 DB9051.DBX36.0=true,tjFlag: {} -> 3", MesClient.tjFlag);
|
|
|
+ MesClient.tjFlag = 3;
|
|
|
+ MesClient.tjStatus = 0;
|
|
|
+ MesClient.status_menu.setText(MesClient.tjFlagText3);
|
|
|
+ }
|
|
|
+ } else if (MesClient.tjFlag == 0) {
|
|
|
+ if (getAllowStart()) {
|
|
|
+ log.info("[流程] 未扫码状态检测到允许启动仍为true,执行复位");
|
|
|
+ sendAllowStart(false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }catch (Exception e){
|
|
|
+ log.error("[流程异常] getDeviceState", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /** @deprecated 请使用 pollDeviceCycle */
|
|
|
+ public static void getDeviceState(){
|
|
|
+ byte[] db9051 = readDb9051Raw(true);
|
|
|
+ if (db9051 != null) {
|
|
|
+ getDeviceState(db9051);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 实时刷新 DB9051 主轴转速、进给速度、压力值到界面
|
|
|
+ */
|
|
|
+ public static void updateRealtimeDisplay(byte[] data) {
|
|
|
+ try {
|
|
|
+ if (data == null || MesClient.spindleSpeedLabel == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ final String spindle = formatS7Val(subBytes(data, 0, 4));
|
|
|
+ final String feed = formatS7Val(subBytes(data, 4, 4));
|
|
|
+ final String pressure = formatS7Val(subBytes(data, 12, 4));
|
|
|
+
|
|
|
+ saveFswParamIfProcessing(spindle, feed, pressure);
|
|
|
+
|
|
|
+ SwingUtilities.invokeLater(() ->
|
|
|
+ MesClient.updatePlcDisplay(spindle, feed, pressure));
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[PLC异常] updateRealtimeDisplay", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /** @deprecated 请使用 pollDeviceCycle */
|
|
|
+ public static void updateRealtimeDisplay() {
|
|
|
+ byte[] data = readDb9051Raw(true);
|
|
|
+ updateRealtimeDisplay(data);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static byte[] readDb9051Raw(boolean withLog) {
|
|
|
+ try {
|
|
|
+ if (!ensureConnected()) {
|
|
|
+ if (withLog) {
|
|
|
+ log.warn("[PLC读] DB9051 失败:S7未连接");
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ byte[] data = MesClient.s7Connector.read(DaveArea.DB, DB9051, DB9051_LEN, 0);
|
|
|
+ if (withLog) {
|
|
|
+ log.info(formatDb9051Log(data));
|
|
|
+ }
|
|
|
+ return data;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[PLC读] DB9051 异常", e);
|
|
|
+ markBroken("读DB9051", e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ static String formatDb9051Log(byte[] data) {
|
|
|
+ if (data == null) {
|
|
|
+ return "[PLC读] DB9051 数据为空";
|
|
|
+ }
|
|
|
+ return String.format(
|
|
|
+ "[PLC读] DB9051 | 主轴转速=%s(HEX:%s) 进给速度=%s(HEX:%s) 压力值=%s(HEX:%s) | " +
|
|
|
+ "焊接长度1=%s 2=%s 3=%s 4=%s 5=%s | 焊接完成(DBX36.0)=%s 出站OK(DBB37)=%d | tjFlag=%d work_status=%d",
|
|
|
+ formatS7Val(subBytes(data, 0, 4)), bytesToHex(subBytes(data, 0, 4)),
|
|
|
+ formatS7Val(subBytes(data, 4, 4)), bytesToHex(subBytes(data, 4, 4)),
|
|
|
+ formatS7Val(subBytes(data, 12, 4)), bytesToHex(subBytes(data, 12, 4)),
|
|
|
+ formatS7Val(subBytes(data, 16, 4)),
|
|
|
+ formatS7Val(subBytes(data, 20, 4)),
|
|
|
+ formatS7Val(subBytes(data, 24, 4)),
|
|
|
+ formatS7Val(subBytes(data, 28, 4)),
|
|
|
+ formatS7Val(subBytes(data, 32, 4)),
|
|
|
+ isWeldingComplete(data),
|
|
|
+ data[37] & 0xFF,
|
|
|
+ MesClient.tjFlag,
|
|
|
+ MesClient.work_status
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 等待加工中(质量合格、尚未收到焊接完成),不含 tjFlag=3 提交阶段 */
|
|
|
+ private static boolean isWaitingProcess() {
|
|
|
+ return MesClient.tjFlag == 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static boolean isMeaningfulParam(String value) {
|
|
|
+ if (value == null || value.trim().isEmpty() || "--".equals(value.trim())) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ float v = Float.parseFloat(value.trim());
|
|
|
+ return !Float.isNaN(v) && !Float.isInfinite(v) && Math.abs(v) > 1e-4f;
|
|
|
+ } catch (Exception e) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void saveFswParamIfProcessing(String spindleSpeed, String feedRate, String pressure) {
|
|
|
+ if (!isWaitingProcess()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ String sn = MesClient.product_sn.getText();
|
|
|
+ if (sn == null || sn.trim().isEmpty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 设备未启动前三个参数多为0,等任一非0再开始存,避免无用数据
|
|
|
+ if (!MesClient.paramRecordingStarted) {
|
|
|
+ if (isMeaningfulParam(spindleSpeed) || isMeaningfulParam(feedRate) || isMeaningfulParam(pressure)) {
|
|
|
+ MesClient.paramRecordingStarted = true;
|
|
|
+ log.info("[过程参数] 检测到有效数据,开始缓存 sn={}", sn.trim());
|
|
|
+ } else {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ MesClient.getUser();
|
|
|
+ boolean saved = JdbcUtils.insertFswParam(
|
|
|
+ MesClient.mes_gw,
|
|
|
+ MesClient.mes_line_sn,
|
|
|
+ sn.trim(),
|
|
|
+ spindleSpeed,
|
|
|
+ feedRate,
|
|
|
+ pressure,
|
|
|
+ MesClient.user20.trim()
|
|
|
+ );
|
|
|
+ if (saved) {
|
|
|
+ log.debug("[过程参数] 本地缓存 sn={} 主轴={} 进给={} 压力={}",
|
|
|
+ sn.trim(), spindleSpeed, feedRate, pressure);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /** DB9051.DBX36.0 焊接完成(BOOL) */
|
|
|
+ private static boolean isWeldingComplete(byte[] data) {
|
|
|
+ if (data.length <= 36) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return (data[36] & (1 << 0)) != 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static byte[] subBytes(byte[] src, int offset, int length) {
|
|
|
+ byte[] dest = new byte[length];
|
|
|
+ System.arraycopy(src, offset, dest, 0, length);
|
|
|
+ return dest;
|
|
|
+ }
|
|
|
+
|
|
|
+ //屏蔽mes交互 如果设备禁止启动 则启动设备
|
|
|
+ public static void getDeviceShield(){
|
|
|
+ try {
|
|
|
+ if (!getAllowStart()) {
|
|
|
+ log.info("[MES屏蔽] 强制发送允许启动 DB9052.DBX0.0=true");
|
|
|
+ sendAllowStart(true);
|
|
|
+ }
|
|
|
+ }catch (Exception e){
|
|
|
+ log.error("[MES屏蔽异常] getDeviceShield", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 监听急停
|
|
|
+ public static void checkStop(){
|
|
|
+ try {
|
|
|
+ if (!ensureConnected()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ byte[] bytes = MesClient.s7Connector.read(DaveArea.DB, DB9052, 1, 0);
|
|
|
+ log.info(formatDb9052Log(bytes[0], "读-急停检测"));
|
|
|
+
|
|
|
+ int jtstatus = getBit(bytes[0], 7);
|
|
|
+ log.info("[PLC读] DB9052.DBX0.7 急停状态={}", jtstatus);
|
|
|
+
|
|
|
+ if(jtstatus == 0){
|
|
|
+ switchEnable(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ if(MesClient.tjFlag == 2 && jtstatus == 0){
|
|
|
+ MesClient.tjResult = "NG";
|
|
|
+ MesClient.finish_ok_bt.setEnabled(true);
|
|
|
+ MesClient.finish_ng_bt.setEnabled(true);
|
|
|
+ }
|
|
|
+ }catch (Exception e){
|
|
|
+ log.error("[PLC异常] checkStop", e);
|
|
|
+ markBroken("checkStop", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static int getBit(byte b, int n) {
|
|
|
+ String binaryString = Integer.toBinaryString(b & 0xFF);
|
|
|
+ binaryString = String.format("%8s", binaryString).replace(' ', '0');
|
|
|
+ return binaryString.charAt(n) == '1' ? 1 : 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String formatDb9052Log(byte b, String action) {
|
|
|
+ boolean allowStart = (b & (1 << 0)) != 0;
|
|
|
+ return String.format("[PLC%s] DB9052.DBB0=0x%02X DBX0.0(允许启动)=%s DBX0.7(急停)=%d",
|
|
|
+ action, b & 0xFF, allowStart, getBit(b, 7));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 转大端排序
|
|
|
+ private static byte[] xtob( byte[] bytes){
|
|
|
+ byte[] reversedBytes = new byte[bytes.length];
|
|
|
+ System.arraycopy(bytes, 0, reversedBytes, 0, bytes.length);
|
|
|
+ for (int i = 0; i < bytes.length / 2; i++) {
|
|
|
+ byte temp = reversedBytes[i];
|
|
|
+ reversedBytes[i] = reversedBytes[reversedBytes.length - 1 - i];
|
|
|
+ reversedBytes[reversedBytes.length - 1 - i] = temp;
|
|
|
+ }
|
|
|
+ return reversedBytes;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String bytesToHex(byte[] bytes) {
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ for (byte b : bytes) {
|
|
|
+ sb.append(String.format("%02X", b & 0xFF));
|
|
|
+ }
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 格式化 S7 REAL(大端 IEEE754,不做字节反转)
|
|
|
+ private static float parseS7Real(byte[] val) {
|
|
|
+ if (val == null || val.length < 4) {
|
|
|
+ return 0f;
|
|
|
+ }
|
|
|
+ int bits = ((val[0] & 0xFF) << 24)
|
|
|
+ | ((val[1] & 0xFF) << 16)
|
|
|
+ | ((val[2] & 0xFF) << 8)
|
|
|
+ | (val[3] & 0xFF);
|
|
|
+ return Float.intBitsToFloat(bits);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String formatS7Val(byte[] val){
|
|
|
+ float floatValue = parseS7Real(val);
|
|
|
+ if (Float.isNaN(floatValue) || Float.isInfinite(floatValue)) {
|
|
|
+ return "0.0";
|
|
|
+ }
|
|
|
+ DecimalFormat df = new DecimalFormat("#.#");
|
|
|
+ df.setMaximumFractionDigits(1);
|
|
|
+ return df.format(floatValue);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 禁止/启用启动
|
|
|
+ public static Boolean switchEnable(int state){
|
|
|
+ try{
|
|
|
+ log.info("[PLC写] switchEnable state={} -> DB9052.DBX0.0={}", state, state == 1);
|
|
|
+ sendAllowStart(state == 1);
|
|
|
+ return true;
|
|
|
+ }catch (Exception e){
|
|
|
+ log.error("[PLC异常] switchEnable", e);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取 DB9052.DBX0.0 允许启动状态
|
|
|
+ public static boolean getAllowStart() {
|
|
|
+ try {
|
|
|
+ if (!ensureConnected()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ byte[] data = MesClient.s7Connector.read(DaveArea.DB, DB9052, 1, 0);
|
|
|
+ log.info(formatDb9052Log(data[0], "读-允许启动"));
|
|
|
+ return (data[0] & (1 << 0)) != 0;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[PLC读] DB9052 允许启动 异常", e);
|
|
|
+ markBroken("读允许启动", e);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取允许运行状态
|
|
|
+ public static Integer getSwitchEnable(){
|
|
|
+ try{
|
|
|
+ if (!ensureConnected()) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ byte[] data = MesClient.s7Connector.read(DaveArea.DB, DB9052, 1, 0);
|
|
|
+ log.info(formatDb9052Log(data[0], "读-整字节"));
|
|
|
+ return new BigInteger(xtob(data)).intValue();
|
|
|
+ }catch (Exception e){
|
|
|
+ log.error("[PLC读] DB9052 整字节 异常", e);
|
|
|
+ markBroken("读DB9052", e);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /** DB9052.DBX0.0 允许启动信号:true=允许,false=禁止/复位 */
|
|
|
+ public static boolean sendAllowStart(boolean allow) {
|
|
|
+ log.info("[PLC写] 请求设置 DB9052.DBX0.0 允许启动={}", allow);
|
|
|
+ return writeBit(DB9052, 0, 0, allow);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 兼容旧接口:true=禁止启动,false=允许启动
|
|
|
+ */
|
|
|
+ public static boolean sendStartSignal(boolean forbidStart) {
|
|
|
+ return sendAllowStart(!forbidStart);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static boolean writeBit(int dbNumber, int byteIndex, int bitIndex, boolean value) {
|
|
|
+ try {
|
|
|
+ if (!ensureConnected()) {
|
|
|
+ log.warn("[PLC写] DB{}.DBX{}.{} 失败:S7未连接", dbNumber, byteIndex, bitIndex);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ byte[] data = MesClient.s7Connector.read(DaveArea.DB, dbNumber, 1, byteIndex);
|
|
|
+ byte oldByte = data[0];
|
|
|
+ boolean oldBit = (oldByte & (1 << bitIndex)) != 0;
|
|
|
+ byte b = oldByte;
|
|
|
+
|
|
|
+ if (value) {
|
|
|
+ b = (byte)(b | (1 << bitIndex));
|
|
|
+ } else {
|
|
|
+ b = (byte)(b & ~(1 << bitIndex));
|
|
|
+ }
|
|
|
+
|
|
|
+ byte[] toWrite = new byte[]{b};
|
|
|
+ MesClient.s7Connector.write(DaveArea.DB, dbNumber, byteIndex, toWrite);
|
|
|
+
|
|
|
+ log.info("[PLC写] DB{}.DBX{}.{} {} -> {} | DBB{}: 0x{} -> 0x{}",
|
|
|
+ dbNumber, byteIndex, bitIndex, oldBit, value,
|
|
|
+ byteIndex,
|
|
|
+ String.format("%02X", oldByte & 0xFF),
|
|
|
+ String.format("%02X", b & 0xFF));
|
|
|
+
|
|
|
+ if (dbNumber == DB9052 && byteIndex == 0 && bitIndex == 0) {
|
|
|
+ log.info("[PLC写] DB9052.DBX0.0 允许启动信号已设为 {}", value);
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[PLC写] DB{}.DBX{}.{} 异常", dbNumber, byteIndex, bitIndex, e);
|
|
|
+ markBroken("写位", e);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取运行的程序名
|
|
|
+ public static String getProgramName(S7PLC s7PLC){
|
|
|
+ String programName = "";
|
|
|
+ try{
|
|
|
+ programName = s7PLC.readProgramName();
|
|
|
+ log.info("[PLC读] 当前程序名={}", programName);
|
|
|
+ }catch (Exception e){
|
|
|
+ log.error("[PLC读] 程序名异常", e);
|
|
|
+ programName = "";
|
|
|
+ }
|
|
|
+ return programName;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|