| | |
| | | ## data-acquisition |
| | | |
| | | 亨旺特导MES数据采集器 |
| | | 亨旺特导 MES 数据采集器(Spring Boot 2.6.3 / Java 8) |
| | | |
| | | ### 串口采集(手动监听) |
| | | - 采集方式:串口(手动监听)、Access 数据库、PostgreSQL |
| | | - 服务端口:9527(`server.port`) |
| | | - 日志目录:`logging.file-location`(默认 `D:\mes-acquistion\logs`) |
| | | - 统一返回格式:`{ "code": 0, "msg": null, "data": ... }`,`code=0` 成功,`code=1` 失败(原因在 `msg` 里) |
| | | |
| | | 串口不再随项目启动自动监听,改为手动监听:调用采集接口时开启监听,采集结束后调用关闭接口。 |
| | | ### 一、串口采集(手动监听) |
| | | |
| | | 串口不随项目启动自动监听:调用开启接口打开串口并取数,采集结束后调用关闭接口释放串口。 |
| | | |
| | | | 接口 | 说明 | |
| | | | --- | --- | |
| | | | `GET /lims/getFile?fileExtension=.serialPort` | 采集接口:调用时自动开启监听,并返回本次采集到的数据;没有数据时返回空集合,由前端轮询取数 | |
| | | | `GET /lims/openSerialPort` | 手动开启监听,并返回当前接收到的数据列表(幂等,参数一致时复用已打开的串口) | |
| | | | `GET /lims/closeSerialPort` | 手动关闭监听(幂等,采集结束后调用,避免串口一直被占用) | |
| | | | `GET /lims/serialPortStatus` | 查询监听状态:`listenName` / `listening` / `portOpen` / `dataSize` | |
| | | | `GET /collection/openSerialPort` | 开启串口监听并返回当前接收到的数据列表(没有数据时返回空集合,由前端轮询取数)。幂等:参数一致时复用已打开的串口,参数变化时按新参数重新打开,多线程并发调用也只会打开一次 | |
| | | | `GET /collection/closeSerialPort` | 关闭串口监听(幂等,未开启时调用也返回成功) | |
| | | | `GET /collection/serialPortStatus` | 查询监听状态,返回 `listenName` / `listening` / `portOpen` / `dataSize` | |
| | | |
| | | 串口参数全部由接口传入,`/lims/getFile`(`fileExtension=.serialPort` 时)和 `/lims/openSerialPort` 都支持, |
| | | 不传的字段使用默认值(`listenName` 必传)。application.yml 中的 `serialPort` 配置已不再生效,可以删除。 |
| | | #### 串口参数(全部由接口传入,不传的字段用默认值) |
| | | |
| | | | 参数 | 默认值 | 说明 | |
| | | | --- | --- | --- | |
| | | | `listenName` | 必传 | 串口名称,如 `COM12`;不传会返回“串口名称不能为空” | |
| | | | `serialPortName` | 必传 | 串口名称,如 `COM12`;不传会返回“串口名称不能为空” | |
| | | | `baudRate` | `9600` | 波特率 | |
| | | | `dataBits` | `8` | 数据位,5~8 | |
| | | | `stopBits` | `1` | 停止位,1、2、3(1.5 位) | |
| | | | `parity` | `NONE` | 校验位:`NONE`/`ODD`/`EVEN`/`MARK`/`SPACE`,也兼容 0~4 | |
| | | | `flowControl` | `NONE` | 流控:`NONE`/`RTS_CTS`/`XON_XOFF`,也兼容数字常量 | |
| | | | `parity` | `NONE` | 校验位:`NONE`/`ODD`/`EVEN`/`MARK`/`SPACE`(不区分大小写),也兼容 0~4 | |
| | | | `flowControl` | `NONE` | 流控:`NONE`/`RTS_CTS`/`XON_XOFF`(不区分大小写),也兼容数字常量 | |
| | | | `readTimeout` | `1000` | 读取超时(毫秒) | |
| | | | `endMark` | `*` | 一帧数据的结束标志,特殊字符用 URL 编码,如换行 `%0A` | |
| | | | `maxCount` | `6` | 数据缓存队列长度:队列满时插入最新一条、移除最旧一条 | |
| | | | `endMark` | `*` | 一帧数据的结束标志,特殊字符用 URL 编码传入,如换行 `%0A` | |
| | | | `maxCount` | `1` | 数据缓存队列长度:队列满时插入最新一条、移除最旧一条;默认 `1` 即只返回设备最新的一条 | |
| | | | `frameIdleMillis` | `200` | 帧空闲补帧时间(毫秒):超过该时间没有新数据,就把缓冲区剩余数据当成一帧取出;`0` 表示关闭 | |
| | | |
| | | 示例:`/lims/getFile?fileExtension=.serialPort&listenName=COM12&baudRate=19200&parity=EVEN&endMark=%0A&maxCount=6` |
| | | 调用示例: |
| | | |
| | | 参数与当前生效参数一致时复用已经打开的串口(不会重复打开);参数变化时会自动按新参数重新打开串口; |
| | | 参数非法时接口直接返回具体原因(如 `串口参数 dataBits 非法:9,取值范围 5~8`)。 |
| | | ``` |
| | | # 开启监听并取数(9600 8N1、结束标志 *,默认只取最新一条) |
| | | http://127.0.0.1:9527/collection/openSerialPort?serialPortName=COM12 |
| | | |
| | | 采集到的数据按队列缓存:插入最新的一条,超过 `maxCount` 条(默认 6 条)时移除最旧的一条, |
| | | 因此接口每次最多返回 `maxCount` 条最新的数据(按接收先后顺序);数据被取走后队列清空,重新开始缓存。 |
| | | # 19200 偶校验、以换行结尾、最多取 6 条 |
| | | http://127.0.0.1:9527/collection/openSerialPort?serialPortName=COM12&baudRate=19200&parity=EVEN&endMark=%0A&maxCount=6 |
| | | |
| | | 数据帧按 `endMark` 切分。如果设备是「每帧以某个字符开头」而不是结尾(例如发 `*0 1 0`、`*0 2 0`, |
| | | `*` 用于分隔前后两帧),那么最后一帧后面没有新的 `*` 来触发切分,只靠结束标志会漏掉最后一条; |
| | | 此时由 `frameIdleMillis`(默认 200ms)兜底:串口空闲超过该时间后,把接收缓冲区里剩下的数据当作一帧取出。 |
| | | 如果设备每帧都以结束标志结尾,缓冲区每次切分后都是空的,空闲补帧不会做任何事。 |
| | | # 查看监听状态 / 关闭串口 |
| | | http://127.0.0.1:9527/collection/serialPortStatus |
| | | http://127.0.0.1:9527/collection/closeSerialPort |
| | | ``` |
| | | |
| | | 并发说明:开启 / 关闭串口由同一把锁保护,多线程并发调用采集接口也只会打开一次串口,不会重复注册监听器; |
| | | 采集结果存放在同步集合中,多线程同时取数时同一条数据只会被一个线程取走,不会重复也不会丢失。 |
| | | 调用关闭接口后再次调用采集接口会重新开启监听,并清掉上一轮残留的半包数据和未取走的数据,保证每次采集的数据只属于本轮。 |
| | | #### 取数与切帧规则 |
| | | |
| | | - 数据按 `endMark` 切成一帧一帧; |
| | | - 切出来的数据放进队列缓存:插入最新的一条,超过 `maxCount` 条时移除最旧的一条, |
| | | 因此接口每次最多返回 `maxCount` 条最新的数据(按接收先后顺序);数据被取走后队列清空,重新开始缓存; |
| | | - 设备最后一帧如果不带结束标志(例如每帧以 `*` 开头、靠下一个 `*` 分隔前后两帧),只靠结束标志会漏掉最后一条, |
| | | 此时由 `frameIdleMillis` 兜底:串口空闲超过该时间就把接收缓冲区里剩下的数据当作一帧取出; |
| | | 如果设备每帧都以结束标志结尾,缓冲区每次切分后都是空的,空闲补帧不会做任何事; |
| | | - 参数与当前生效参数一致时复用已经打开的串口(不会重复打开);参数变化时按新参数重新打开; |
| | | - 再次开启监听时会清掉上一轮残留的半包数据和未取走的数据,保证每次采集的数据只属于本轮; |
| | | - 参数非法时接口直接返回具体原因(如 `串口参数 dataBits 非法:9,取值范围 5~8`)。 |
| | | |
| | | #### 并发说明 |
| | | |
| | | - 开启 / 关闭串口由同一把锁保护,重复调用只会真正打开一次串口,不会重复注册监听器; |
| | | - 采集结果存放在同步集合中,多线程同时取数时同一条数据只会被一个线程取走,不会重复也不会丢失; |
| | | - 监听状态使用 volatile 标记,关闭串口后回调线程不再处理数据。 |
| | | |
| | | ### 二、数据库采集 |
| | | |
| | | | 接口 | 说明 | |
| | | | --- | --- | |
| | | | `GET /collection/getAccessData` | 采集 Access 数据库(.mdb/.accdb)表数据,返回 `List<Map<String,Object>>` | |
| | | | `GET /collection/getPostgreSqlData` | 采集 PostgreSQL 表数据,返回 `List<Map<String,Object>>` | |
| | | |
| | | 参数(`DatabaseDTO`): |
| | | |
| | | | 参数 | 说明 | |
| | | | --- | --- | |
| | | | `filePath` | Access 数据库文件路径(仅 `getAccessData`) | |
| | | | `ipAddress` / `serverPort` / `databaseName` | PostgreSQL 连接信息(仅 `getPostgreSqlData`) | |
| | | | `userName` / `password` | 数据库用户名、密码;Access 的文件打开密码放在 `password`(`userName` 对 Access 文件加密无作用) | |
| | | | `tableName` | 数据表名 | |
| | | | `pointColumns` | 要采集的字段,多个用逗号分隔 | |
| | | | `mainColumn` + `batchCode` | 按批次过滤:`mainColumn = batchCode` | |
| | | | `orderColumn` / `orderRule` | 排序字段与规则(`ASC`/`DESC`,不传默认 `ASC`) | |
| | | |
| | | #### Access 读取方式(`mes.access.read-mode`) |
| | | |
| | | - `jackcess`(默认):jackcess 只读直读(`DatabaseBuilder.setReadOnly(true).setAutoSync(false)`),打开文件后顺序扫描单表、只取需要的列,不构建 HSQLDB 镜像,只取某张表几列时最快; |
| | | - `ucanaccess`:走 UCanAccess 的 SQL 路径,需要 Access SQL、链接表 `remap` 等场景时使用。 |
| | | |
| | | jackcess 模式的差异: |
| | | |
| | | - 按 `mainColumn` 过滤是整表扫描:本库文本索引的排序规则是中文 2052,jackcess 不支持(索引只读、索引游标直接抛异常),没有可用的索引查询路径;`orderColumn` 排序在 Java 侧完成; |
| | | - `OLE` 字段与原来一样返回 `null`,且不会读取它的长值; |
| | | - 时间字段统一返回 `java.sql.Timestamp`(JSON 里仍是 `yyyy-MM-dd HH:mm:ss.SSS`); |
| | | - 设置了打开密码的库会自动用 `jackcess-encrypt` 打开(密码取 `password` 参数;Access 的文件加密只有“打开密码”一个概念,`userName` 不参与,.mdw 工作组级安全性 jackcess 不支持);`ucanaccess` 模式下有 `jackcess-encrypt` 在 classpath 上,`password` 参数同样对加密库生效; |
| | | - 链接表的行为与 UCanAccess 的 `remap` 不同,遇到问题切回 `ucanaccess` 模式即可。 |
| | | |
| | | ### 三、托盘图标 |
| | | |
| | | 程序启动后任务栏右下角会出现托盘图标(`SystemTrayInitializer`),右键菜单: |
| | | |
| | | | 菜单项 | 说明 | |
| | | | --- | --- | |
| | | | 查看串口状态 | 弹出状态窗口:串口名称 / 监听状态 / 串口连接 / 缓存条数,每秒自动刷新 | |
| | | | 关闭串口 | 只关闭串口监听(不退出程序),方便现场把串口释放给别的程序用;结果用托盘气泡提示 | |
| | | | 打开日志目录 | 打开 `logging.file-location` 配置的日志目录 | |
| | | | 退出 | 先关闭 Spring 容器(触发 `@PreDestroy`:关闭串口、停止补帧线程),再结束进程 | |
| | | |
| | | - 双击托盘图标 = 查看串口状态; |
| | | - 菜单弹出在光标上方、右边缘对齐,点菜单外面 / 按 ESC / 再右键一次都会收起; |
| | | - 图标优先用 `src/main/resources/tray.png`(放一张 32x32 的 png 即可换成自己的图标),没有就用代码画的默认图标; |
| | | - 托盘依赖 AWT,启动类里已经 `setHeadless(false)`;以 Windows 服务方式运行(没有桌面会话)时托盘不显示,接口不受影响。 |
| | | |
| | | ### 四、构建与运行 |
| | | |
| | | ```bash |
| | | # 打包(产物 target/data-acquisition.jar,可直接 java -jar 运行) |
| | | mvn clean package -DskipTests |
| | | |
| | | # 运行(需要 Java 8) |
| | | java -jar target/data-acquisition.jar |
| | | ``` |
| | | |
| | | - **双击 jar 运行**:需要把 `.jar` 的默认打开方式指向 **Java 8 的 javaw.exe** |
| | | (右键 jar → 打开方式 → 选择其他应用 → 更多应用 → 在这台电脑上查找其他应用 → `<jdk8>\bin\javaw.exe` → 勾选“始终使用此应用打开 .jar 文件”)。 |
| | | 注意不要指向高版本 JDK(本项目是 Spring Boot 2.6.3,只支持 Java 8~17),也不要用压缩软件打开; |
| | | - 用 javaw 启动没有控制台,**启动失败会弹窗提示**(例如端口被占用、日志目录不可写),详细错误看 `logging.file-location` 目录下的日志; |
| | | - 退出请用托盘菜单的「退出」(会正常关闭串口);直接结束进程不会走这套清理流程。 |
| | | |
| | | ### 五、配置(application.yml) |
| | | |
| | | ```yaml |
| | | server: |
| | | port: 9527 # 服务端口 |
| | | |
| | | logging: |
| | | file-location: D:\mes-acquistion\logs # 日志目录(托盘“打开日志目录”也用它) |
| | | |
| | | #mes: |
| | | # access: |
| | | # read-mode: jackcess # Access 读取方式:jackcess(默认,直读单表)/ ucanaccess(SQL 模式) |
| | | ``` |
| | | |
| | | > 串口参数(串口名、波特率、数据位、校验位、结束标志等)全部由接口传入,配置文件里不需要串口相关配置。 |