大族打标机 TCP 工具类优先设计 + 追溯打标业务落地

news2026/4/14 18:18:48
本文按工程实施顺序组织大族 TCP 客户端工具类源码追溯打标业务源码IP、端口、模板名动态配置方案含建表 SQL。一、大族打标机 TCP 工具类1.1 协议约定大族打标常见指令ASCII初始化模板$Initialize_模板名下发数据$Data_二维码,明码1,明码2...启动打标$MarkStart_通讯帧格式帧头0x02指令体ASCII帧尾0x03即0x02 ASCII(command) 0x03这部分协议看起来很简单但它是整个系统稳定性的基础。实际项目中90% 的“打标失败”问题都发生在这一层要么是帧格式不正确要么是字符编码不一致要么是设备响应超时没有被正确识别。所以建议在工具类里把协议收发固定下来业务层不要重复拼装帧避免不同模块各自实现导致线上行为不一致。下面给出完整工具类源码。该类定位为“纯通讯层”不包含任何工位、规则、流水号等业务逻辑便于在不同项目中复用。这样拆分的价值是当你未来更换设备型号、增加中间层例如网关服务、或者要做指令重试机制时只需要调整这个工具类不需要改业务服务的代码结构。1.2 工具类源码package blog.demo.marking.tcp; import lombok.extern.slf4j.Slf4j; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketTimeoutException; import java.nio.charset.StandardCharsets; /** * 大族打标 TCP 通讯工具类 * * 约定 * 1) 指令文本不含头尾 * 2) 工具类统一封装 0x02/0x03 帧 * 3) 每次命令短连接简单稳定便于排障 * 4) 此类只负责“设备通讯”不做业务规则判断单一职责 * * 设计说明 * - 业务层只需要调用 doMarking()无需感知底层帧协议 * - 遇到设备或网络异常时直接抛 RuntimeException 给上层统一兜底 * - 如需长连接优化可在本类基础上演进不影响业务层调用签名 * * author liwj */ Slf4j public class HansLaserTcpClient { private static final byte HEAD 0x02; private static final byte TAIL 0x03; private final String host; private final int port; private final int connectTimeoutMs; private final int readTimeoutMs; /** * param host 大族打标机 IP * param port 大族打标机端口 * param connectTimeoutMs 建连超时毫秒 * param readTimeoutMs 读响应超时毫秒 * author liwj */ public HansLaserTcpClient(String host, int port, int connectTimeoutMs, int readTimeoutMs) { if (host null || host.trim().isEmpty()) { throw new IllegalArgumentException(host 不能为空); } if (port 0) { throw new IllegalArgumentException(port 非法); } this.host host.trim(); this.port port; this.connectTimeoutMs Math.max(connectTimeoutMs, 1000); this.readTimeoutMs Math.max(readTimeoutMs, 1000); } /** * 打标流程初始化模板 - 下发数据 - 启动打标 * * param templateName 模板名大族软件中的模板 * param qrCode 二维码内容 * param plainSegments 明码分段 * return true 成功 * author liwj */ public boolean doMarking(String templateName, String qrCode, String[] plainSegments) { // 第一步初始化模板告诉设备“这次按哪个版式打标” if (!initialize(templateName)) { throw new RuntimeException(初始化模板失败: templateName); } // 第二步下发二维码和明码文本按模板对象顺序 if (!sendData(qrCode, plainSegments)) { throw new RuntimeException(下发数据失败); } // 第三步触发打标动作 if (!markStart()) { throw new RuntimeException(启动打标失败); } return true; } /** * 初始化大族模板 * * param templateName 模板名 * return true: 设备返回 $Initialize_OK * author liwj */ public boolean initialize(String templateName) { if (templateName null || templateName.trim().isEmpty()) { throw new IllegalArgumentException(templateName 不能为空); } String resp sendCommand($Initialize_ templateName.trim()); return $Initialize_OK.equals(resp); } /** * 下发二维码和明码数据 * * param qrCode 二维码内容 * param plainSegments 明码分段数组顺序需与模板文本对象顺序一致 * return true: 设备返回 $Receive_OK 或 $Data_OK * author liwj */ public boolean sendData(String qrCode, String[] plainSegments) { String[] segments (plainSegments null || plainSegments.length 0) ? new String[]{} : plainSegments; StringBuilder cmd new StringBuilder($Data_); cmd.append(qrCode null ? : qrCode); for (String seg : segments) { cmd.append(,).append(seg null ? : seg); } String resp sendCommand(cmd.toString()); return $Receive_OK.equals(resp) || $Data_OK.equals(resp); } /** * 启动打标 * * return true: 设备返回 $MarkStart_OK 前缀 * author liwj */ public boolean markStart() { String resp sendCommand($MarkStart_); return resp ! null resp.startsWith($MarkStart_OK); } /** * 发送单条指令并解析响应 * * param command 指令文本不含头尾 * return 响应文本不含头尾 * author liwj */ public String sendCommand(String command) { Socket socket null; OutputStream outputStream null; InputStream inputStream null; try { // 1) 建立 TCP 连接 socket new Socket(); socket.connect(new InetSocketAddress(host, port), connectTimeoutMs); socket.setSoTimeout(readTimeoutMs); // 2) 获取输入输出流 outputStream socket.getOutputStream(); inputStream socket.getInputStream(); // 3) 协议组帧后发送0x02 ASCII 0x03 outputStream.write(buildFrame(command)); outputStream.flush(); // 4) 读取响应并解帧 byte[] buffer new byte[2048]; int len inputStream.read(buffer); if (len 0) { return null; } return parseFrame(buffer, len); } catch (SocketTimeoutException e) { throw new RuntimeException(打标机通信超时, e); } catch (IOException e) { throw new RuntimeException(打标机通信异常: e.getMessage(), e); } finally { closeQuietly(inputStream); closeQuietly(outputStream); closeQuietly(socket); } } private byte[] buildFrame(String command) { // 设备协议要求 ASCII 编码文本 byte[] body command.getBytes(StandardCharsets.US_ASCII); byte[] frame new byte[body.length 2]; frame[0] HEAD; System.arraycopy(body, 0, frame, 1, body.length); frame[frame.length - 1] TAIL; return frame; } private String parseFrame(byte[] buffer, int len) { // 防御性校验非标准帧直接返回 null交由上层判失败 if (len 2 || buffer[0] ! HEAD || buffer[len - 1] ! TAIL) { return null; } return new String(buffer, 1, len - 2, StandardCharsets.US_ASCII); } private void closeQuietly(AutoCloseable closeable) { if (closeable ! null) { try { closeable.close(); } catch (Exception e) { log.debug(close ignore, e); } } } }二、追溯打标业务如何调用工具类业务分层说明ControllerPDA 调preview、submitService根据工位/工件类型/规则选模板、生成码、拆明码Repository查规则、查工位、写事件、占流水TCP Client只负责和大族通讯。这里的重点是“职责边界”要清晰业务层负责“打什么”规则、模板、流水、明码分段通讯层负责“怎么发”TCP 连接、组帧、收响应。只要这个边界保持住后续维护成本会明显下降。2.1 业务核心代码示例package blog.demo.marking.biz; import blog.demo.marking.tcp.HansLaserTcpClient; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; Service RequiredArgsConstructor public class TraceMarkingService { private final DeviceConfigService deviceConfigService; private final MarkingRuleService markingRuleService; private final MarkingSerialService markingSerialService; private final TraceEventService traceEventService; /** * 真实追溯打标提交 * * param stationCode 工位编码决定 OP10/OP50 * param workpieceType 工件类型1/2/3 * return 打标二维码 * author liwj */ public String submit(String stationCode, String workpieceType) { boolean op50 stationCode ! null stationCode.toUpperCase().contains(OP50); // 1) 查工位绑定设备配置IP/端口/超时 DeviceTcpConfig device deviceConfigService.getDeviceByStation(stationCode); // 2) 查打标规则物料、备用码、生产阶段、版本标识等 MarkingRule rule markingRuleService.getLatestRuleByWorkpieceType(workpieceType); // 3) 占流水并发安全 int seq markingSerialService.claimNextSeq(rule.getTemplateBizKey(), rule.getSpareCode()); String serialNo SerialNoUtil.buildTodaySerial(seq); // 比如 T260413A0001 // 4) 组二维码/明码 String productTypeCode ProductTypeMapper.toProductTypeCode(workpieceType); // DZK/GXJ/ZDJ String qrCode serialNo productTypeCode rule.getProductionPhase(); String plainCode qrCode; // 5) 按工位解析模板 String templateName op50 ? deviceConfigService.getOp50TemplateBySpareCode(device.getDeviceCode(), rule.getSpareCode()) : deviceConfigService.getOp10TemplateByProductType(device.getDeviceCode(), productTypeCode); // 6) 明码拆分BTZ11/BTZ22/BTZ33 String[] plainSegments PlainSplitUtil.splitBySpareCode(rule.getSpareCode(), plainCode); // 7) 调大族 TCP 客户端 HansLaserTcpClient client new HansLaserTcpClient( device.getIp(), device.getPort(), device.getConnectTimeoutMs(), device.getReadTimeoutMs()); client.doMarking(templateName, qrCode, plainSegments); // 8) 记录追溯事件 traceEventService.saveMarkingEvent(stationCode, qrCode, plainCode, rule.getRuleId()); return qrCode; } }这段业务代码覆盖追溯打标核心动作占流水按工位和产品选模板打标成功后写追溯事件与设备通讯逻辑彻底解耦。另外在真实产线里建议把“打标请求号requestId”贯穿日志。这样一旦现场反馈某一件产品异常可以从业务日志快速关联到 TCP 指令日志和设备响应日志排查效率会高很多。三、动态配置IP、端口、模板名不要写死推荐最小可用模型为 3 张表设备连接表IP、端口、超时工位-设备绑定表模板映射表OP10 产品类型 / OP50 备用码动态配置的本质不只是“方便改参数”而是把“部署信息”和“业务逻辑”分离开。对于多产线、多设备、多机型场景这一点非常关键同一套程序可以在不同车间复用只需要调整配置数据即可。3.1 设备连接表CREATE TABLE marking_device_tcp_config ( id varchar(32) NOT NULL COMMENT 主键, device_code varchar(64) NOT NULL COMMENT 设备编码, device_name varchar(128) DEFAULT NULL COMMENT 设备名称, ip varchar(64) NOT NULL COMMENT 打标机IP, port int NOT NULL COMMENT 打标机端口, connect_timeout_ms int NOT NULL DEFAULT 15000 COMMENT 连接超时ms, read_timeout_ms int NOT NULL DEFAULT 300000 COMMENT 读超时ms, enabled tinyint(1) NOT NULL DEFAULT 1 COMMENT 是否启用, del_flag tinyint(1) NOT NULL DEFAULT 0 COMMENT 删除标记, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_device_code (device_code) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT打标机TCP连接配置;3.2 工位绑定设备表CREATE TABLE marking_station_device_bind ( id varchar(32) NOT NULL, station_code varchar(64) NOT NULL COMMENT 工位编码, device_code varchar(64) NOT NULL COMMENT 设备编码, priority int NOT NULL DEFAULT 1 COMMENT 优先级数值越小越优先, enabled tinyint(1) NOT NULL DEFAULT 1, del_flag tinyint(1) NOT NULL DEFAULT 0, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_station_code (station_code) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT工位-打标机绑定;3.3 模板映射表核心CREATE TABLE marking_template_mapping ( id varchar(32) NOT NULL, device_code varchar(64) NOT NULL COMMENT 设备编码, mapping_type varchar(32) NOT NULL COMMENT 映射类型OP10_PRODUCT_TYPE / OP50_SPARE_CODE, mapping_key varchar(64) NOT NULL COMMENT 映射键DZK/GXJ/ZDJ 或 BTZ11/BTZ22/BTZ33, template_name varchar(128) NOT NULL COMMENT 大族模板名, enabled tinyint(1) NOT NULL DEFAULT 1, del_flag tinyint(1) NOT NULL DEFAULT 0, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_device_type_key (device_code, mapping_type, mapping_key) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT打标模板映射;四、动态配置读取策略为了兼顾性能和可维护性建议使用“本地缓存 定时刷新”请求先读本地缓存低延迟缓存 miss 再查库并回填缓存每 30~60 秒定时全量刷新管理端改配置后可主动触发刷新接口。如果你们现场对配置变更实时性要求很高可以把“定时刷新”改成“消息通知刷新”例如 MQ 或配置中心回调。但无论采用哪种方式都建议保留“兜底查库”能力避免缓存异常导致整条链路不可用。伪代码DeviceTcpConfig getDeviceByStation(String stationCode) { DeviceTcpConfig cfg cache.get(stationCode); if (cfg ! null) { return cfg; } cfg mapper.selectByStation(stationCode); if (cfg null) { throw new BizException(工位未绑定打标设备: stationCode); } cache.put(stationCode, cfg); return cfg; }五、文档结构说明本文结构即实际落地顺序工具类解决设备通讯问题业务服务解决追溯流程问题动态配置解决运维和扩展问题。六、常见问题简版初始化模板失败模板名不对或设备里没这个模板。通信超时IP/端口不通、设备未监听、超时过小。明码错位BTZ11/22/33 分段规则与模板文本对象顺序不一致。OP10/OP50 串机工位绑定设备配置错误。补充一个实践经验在首次联调时建议先只测initialize和sendData确认模板和文本对象正确后再执行markStart。这样可以减少误打标对现场物料造成的干扰。七、总结在产线追溯场景里打标不是“能发指令就行”而是“设备通讯 业务规则 可运维配置”三件事同时成立。把 TCP 客户端先抽出来再把业务落地最后做动态配置这套架构就能长期跑得稳、改得动、排得了障。从工程角度看这套设计有三个长期收益稳定性收益协议层统一封装减少重复实现造成的偶发故障维护性收益业务规则和设备参数分离修改影响范围可控扩展性收益未来新增设备、工位、模板映射时无需重构主流程。如果你的目标是“先上线再可持续迭代”这种分层方式是非常稳妥的落地路径。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2517264.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…