| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558 |
- 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;
- }
- }
|