☰
基于Java的智能电表采集系统毕业设计源码详解与部署指南
2026/9/28 12:10:18 网站建设 项目流程

简介:一套基于Java实现的智能电表采集系统完整毕业设计项目,面向电力信息化方向的高校本科生及需要快速搭建同类系统的开发者。项目围绕远程抄表、实时监控、用电数据分析等核心场景,提供从数据库设计到前后端联调的可运行方案,可作为课程设计参考或二次开发基础。压缩包共47个文件,约1.75MB,以Java源码(15个java、16个class)为主,辅以3个依赖jar包、数据库字典及流程说明docx、配置文件xml与classpath、项目说明README.md,以及errorLog.txt排错记录,结构上区分src、bin、lib和配置文档,便于对照学习与部署。已有72人学习,适合需要完整参考毕业设计源码、快速理解智能电表采集业务逻辑并扩展功能的读者。

1. 基于 Java 的智能电表采集系统:一份能跑起来的毕业设计源码

做电力集抄相关课题的人,对“智能电表采集系统”这名字不会陌生。简单说,这是一套用 Java 写好的电表数据采集完整工程,压缩包里包含源码、配置说明文档、数据库字典、错误日志和一张流程示意图。它能按周期到电表上抄回电压、电流、电量等数据,写入 MySQL 或 Oracle,再做阈值判断和页面展示。这套资源对两类人最有用:一是要做 Java 课程设计或毕业设计、需要“有代码、有文档、能演示”的学生;二是刚接触电力采集业务的开发,想找一份能跑的工程当参照。下面我按工程结构、配置部署、核心代码、避坑记录依次拆开讲。

2. 解压后先看什么:MeterCollection 工程结构与数据库字典的阅读顺序

很多人拿到压缩包第一反应是翻开 src 目录找 Main 方法,我建议反过来:先读 README 和两篇 docx,再看 flow.jpg,最后回到代码。这套资料的信息层级很明确——文档定边界、流程定顺序、代码定细节。倒着读容易在包结构里迷路,尤其是 bin 目录和 src 目录并存,新手很容易把编译产物当成源码翻半天。

2.1 文件清单与阅读顺序

解压后你会看到下面这些文件,我把每个文件的作用和阅读优先级列出来:

文件/目录作用阅读优先级
code 配置说明.docx部署步骤与参数设置说明先读
采集软件数据库字典及流程.docx表结构、字段含义与业务流程先读
README.md工程简介与运行入口说明先读
flow.jpg采集处理流程示意图第二
MeterCollectionEclipse Java 工程,src 为源代码最后
config.xml运行时配置:采集周期、串口参数、数据库连接结合部署读
2017_10_24_errorLog.txt一次实操产生的运行日志排错与进阶用

两篇 docx 篇幅不大,但价值很高。配置说明 docx 回答的是“怎么跑起来”,数据库字典 docx 回答的是“数据存在哪、长什么样”。flow.jpg 则是整个系统的运行主线,建议把它打印出来或者放在旁边,阅读代码时随时对照。

2.2 从 .project 与 .classpath 识别工程类型

MeterCollection 目录下没有 pom.xml,但能看到 .classpath、.settings、.project 三个文件,这是典型的 Eclipse 导出 Java 工程,不是 Maven 工程。lib 目录里放着依赖的 jar 包,说明当时是用手动方式管理依赖的,没有走中央仓库。

导入方式很简单:Eclipse 里选择 File -> Import -> Existing Projects into Workspace,选中 MeterCollection 目录即可。JDK 选 1.7 或 1.8 都能编译,具体看源码里用了什么语法。bin 目录是编译输出目录,改代码要去 src 下改,改完重新编译,bin 里的 class 会被覆盖。

.classpath 文件里记录了源码目录、输出目录和依赖 jar 的引用路径。如果导入后报“项目不可运行”或类找不到,先检查 .classpath 里引用的 jar 路径是否指向 lib 目录下的实际文件。有些导出的工程在迁移后会带上绝对路径,这时候需要手动修正 .classpath 中的路径。我一般会直接打开 .classpath 看一眼,确认所有引用都是相对路径或实际存在的文件。

2.3 数据库字典与 flow.jpg 的对照阅读

数据库字典 docx 里描述的核心表一般有三张:电表档案表、采集数据表、采集任务表。电表档案表存表号、通信地址、安装日期;采集数据表存每个采集周期的电压、电流、功率、电量;任务表存每一次采集任务的开始时间、结束时间和执行状态。字段命名通常带 meter_、collect_ 前缀,电量字段用 DECIMAL(12,2),时间字段用 DATETIME,是最稳妥的设计方式。

flow.jpg 的阅读顺序是从左上角开始沿主线走:读配置 -> 加载电表档案 -> 按周期发起采集 -> 解析协议帧 -> 写数据库 -> 比对阈值 -> 更新任务状态。把数据库字典里的每张表和流程图里的节点对应起来,再去看代码会轻松很多。比如 config.xml 里改了采集周期,任务表里的时间间隔就会变化,这个联动关系在流程图里体现得很直接,但代码里要追踪两条调用链才能定位到。

3. 部署与配置:config.xml 参数、MySQL 初始化和启动验证

这套系统的运行配置全部集中在 config.xml 里,数据库连接、采集周期、串口参数都在这一个文件里改。我按“环境准备 -> 参数解读 -> 建库准备 -> 启动验证”四步走。这一步很多人会翻车,多数是版本问题和路径问题。

3.1 环境准备:JDK 8 与 MySQL 5.7 的搭配

源码是 2017 年前后的写法,语法停留在 Java 7/8 时代。用 JDK 8 编译运行最稳,太高版本反而可能因为语法兼容性问题出岔子。装完 JDK 后的第一件事是配置 JAVA_HOME,命令行执行 java -version 必须能正常输出版本信息。很多人机器上装了多个 JDK,导致编译时用的 1.6、运行时用的 1.8,这种错乱很隐蔽,建议在环境变量里把 JAVA_HOME 指到 JDK 8 的安装目录,再把 PATH 里其他 java.exe 路径清理干净。

数据库部分,配置说明里写的是 MySQL 或 Oracle 二选一。我用 MySQL 5.7 复现过完整流程,需要注意的是 JDBC 驱动 jar 必须和数据库版本匹配。MySQL 5.x 配 5.x 驱动,MySQL 8 以上配 8.x 驱动,交叉使用会出现认证方式不兼容和时区报错,后面避坑章节会专门展开。

3.2 config.xml 参数逐项拆解

config.xml 是运行时最核心的文件,结构类似下面这样:

<config> <collect interval="900" timeout="3000" threads="4"/> <protocol baudrate="9600" databits="8" stopbits="1" parity="0"/> <database host="127.0.0.1" port="3306" name="meter_db" user="root" password="123456"/> <threshold voltageLow="198" voltageHigh="235" currentHigh="60"/> </config>

这里每个参数都有实际影响,不是摆设:

参数含义修改建议
interval采集周期,单位秒,900 即 15 分钟一轮演示时可改成 30 秒,生产环境 300-900
timeout单次通信超时,单位毫秒现场总线不稳定时调大到 5000
threads并行采集线程数串口场景建议不超过 4
baudrate串口波特率,电表常用 9600与电表一致,否则通信失败
databits/stopbits/parity数据位、停止位、校验位电表默认 8/1/无校验
host/port/name/user/password数据库连接信息按本机 MySQL 配置修改
voltageLow/voltageHigh电压告警阈值,单位 V220V 系统取 198-235
currentHigh电流告警阈值,单位 A按现场负载能力设置

三个参数组分别对应流程图里“读配置 -> 发起采集 -> 写数据库”三个环节。改完 config.xml 后需要重启程序才能生效。interval 改短后,任务表里能看到采集记录密集出现,这是验证参数生效最直观的方式。

3.3 数据库初始化与电表档案数据准备

建库时优先用 utf8mb4 字符集,避免中文乱码:

CREATE DATABASE meter_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE meter_db; CREATE TABLE meter_info ( meter_id VARCHAR(20) PRIMARY KEY, meter_addr VARCHAR(12) NOT NULL, meter_type VARCHAR(10), install_date DATE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE meter_data ( id BIGINT AUTO_INCREMENT PRIMARY KEY, meter_id VARCHAR(20) NOT NULL, collect_time DATETIME NOT NULL, voltage DECIMAL(10,2), current DECIMAL(10,2), power DECIMAL(10,2), energy DECIMAL(12,2), status TINYINT DEFAULT 0 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE collect_task ( task_id BIGINT AUTO_INCREMENT PRIMARY KEY, task_time DATETIME NOT NULL, meter_count INT DEFAULT 0, success_count INT DEFAULT 0, fail_count INT DEFAULT 0, task_status TINYINT DEFAULT 0 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

建完表后要往 meter_info 里插电表档案数据。meter_addr 是协议通信地址,必须和实际电表侧编码一致,否则协议解析帧到了也校验不过。没有真实电表时,可以先插几条测试数据,用串口调试工具模拟电表报文,验证采集链路。

3.4 编译启动与运行验证

配置和表都就绪后,编译启动步骤大致如下:

cd MeterCollection javac -encoding UTF-8 -cp "lib/*" -d bin src/com/collect/*.java java -cp "bin;lib/*" com.collect.Main

jar 引用顺序要注意:Windows 下类路径用分号分隔,lib 下的 jar 用通配符引进来。启动后观察控制台日志,看到“collect task started”字样说明调度线程起来了。然后查数据库:

SELECT task_id, task_time, success_count, fail_count FROM collect_task ORDER BY task_id DESC LIMIT 5;

如果有成功记录,说明整条链路已经打通。这一步我习惯把日志文件和数据库查询结果对比看:日志里每完成一轮采集,数据库 task 表就多一条记录,两边的计数要能对上。核不上就是后面要讲的解析或入库问题。

4. 核心代码实现:采集调度、645 协议解析与批量入库

源码里最值得研究的三个模块是采集调度、协议解析和数据入库。这三段逻辑决定了系统能不能按周期稳定运行、能不能正确读懂电表数据、能不能扛住循环采集的写入压力。下面按我的理解拆开讲,代码结构是这套资源里最典型的 Java 实现方式。

4.1 采集调度:用 ScheduledExecutorService 控制周期

采集任务的核心是“按固定周期反复执行”,常见做法是用 Timer 或 ScheduledExecutorService。后者在并发控制上更可靠,Java 工程里一般优先选它:

public class CollectScheduler { private ScheduledExecutorService executor; public void start(CollectConfig cfg, List<MeterInfo> meters) { executor = Executors.newScheduledThreadPool(cfg.getThreads()); CollectTask task = new CollectTask(meters, cfg); executor.scheduleAtFixedRate(task, 0, cfg.getInterval(), TimeUnit.SECONDS); } public void stop() { if (executor != null) { executor.shutdownNow(); } } }

scheduleAtFixedRate 的意思是第一次立即执行,之后每 interval 秒执行一次,和 scheduleWithFixedDelay 的区别在于:前者按固定频率触发,不关心任务本身耗时;后者是任务结束后再等 interval 秒。采集场景用 scheduleAtFixedRate 更合适,因为抄表周期必须大致均匀。但要注意,线程数 threads 不要配大,串口采集是共享总线,多线程同时写总线会造成碰撞,反而降低成功率。

4.2 协议解析:从 645 帧到业务字段

电表数据通过串口读到的是字节帧,国内电表多数遵循 DL/T 645 协议。一个典型帧的结构是:起始符 0x68、地址域、控制码、数据域、校验和、结束符 0x16。解析逻辑的核心是校验和验证和 BCD 码转换:

public class MeterProtocolParser { public static final byte FRAME_START = 0x68; public static final byte FRAME_END = 0x16; public ParseResult parse(byte[] frame, int length) { if (frame[0] != FRAME_START || frame[length - 1] != FRAME_END) { throw new ProtocolException("帧头帧尾校验失败"); } int csIndex = length - 2; byte cs = 0; for (int i = 0; i < csIndex; i++) { cs += frame[i]; } if (cs != frame[csIndex]) { throw new ProtocolException("CS 校验失败,数据可能被干扰"); } // 数据域从第 7 字节开始,BCD 编码转成十进制 int voltage = bcdToInt(frame, 7, 2); int current = bcdToInt(frame, 9, 2); return new ParseResult(voltage, current); } private int bcdToInt(byte[] frame, int offset, int len) { int result = 0; for (int i = 0; i < len; i++) { result = result * 100 + ((frame[offset + i] >> 4) & 0x0F) * 10 + (frame[offset + i] & 0x0F); } return result; } }

这段逻辑是协议层的地基。CS 校验会把帧里除校验和和结束符之外的所有字节累加起来,最终结果必须等于帧里的校验字节。实际运行中,串口线路被干扰时最容易挂在 CS 校验这一步,错误日志里看到 “CS 校验失败” 的报错,优先查通信线路而不是代码。BCD 转换要注意高低位顺序,电表返回的数据是压缩 BCD 码,每个字节表示两位十进制数,转换公式就是上面那个。

4.3 批量入库与阈值告警

采集到的数据逐条 insert 性能太差,批量提交是工程里的标准做法:

public void saveBatch(Connection conn, List<MeterData> batch) throws SQLException { String sql = "INSERT INTO meter_data(meter_id, collect_time, voltage, current, power, energy, status) " + "VALUES (?, ?, ?, ?, ?, ?, ?)"; PreparedStatement ps = conn.prepareStatement(sql); for (MeterData d : batch) { ps.setString(1, d.getMeterId()); ps.setTimestamp(2, new Timestamp(d.getCollectTime().getTime())); ps.setBigDecimal(3, d.getVoltage()); ps.setBigDecimal(4, d.getCurrent()); ps.setBigDecimal(5, d.getPower()); ps.setBigDecimal(6, d.getEnergy()); ps.setInt(7, checkThreshold(d)); ps.addBatch(); } ps.executeBatch(); }

checkThreshold 方法里就是 config.xml 里的阈值比对逻辑,电压低于 198 或高于 235 时状态置 1,电流超过 60 也置 1。这个状态位就是前端页面显示告警灯的数据来源。批量提交有两个好处:一是减少数据库交互次数,二是保证一轮采集的 N 条数据要么全进要么全不进,事务边界更清晰。注意批次大小,我一般控制在 200 条一批,太大会占用过多内存。

5. 避坑指南:编译、连接与数据采集的五个典型问题

这个项目我前前后后部署过三轮,每一轮都踩了不同的坑。挑五个最典型的写在这里,每条都是“现象 -> 原因 -> 解决”的结构,能帮你省掉至少两天的排查时间。

5.1 数据库连不上:驱动版本与时区问题

现象:启动时控制台报 Communications link failure,或者 Access denied for user,程序启动后立刻退出。

原因:多数情况是 JDBC 驱动版本和 MySQL 版本不匹配。MySQL 8 默认的 caching_sha2_password 认证方式在旧驱动下不支持,会直接拒绝连接。另外 8.x 驱动要求 JDBC URL 里带 serverTimezone 参数,不带走 UTC 时区也会报错。

解决:MySQL 5.7 就配 5.1.49 驱动,MySQL 8 就配 8.0.20 以上驱动。JDBC URL 里统一加上 characterEncoding=utf8 和 serverTimezone=Asia/Shanghai,两个参数都写上能同时解决乱码和时区问题。改完 config.xml 后重启程序验证。

5.2 config.xml 改了不生效

现象:把 interval 改成 30 秒,重启程序后采集周期没变化,日志显示的时间间隔还是旧的。

原因:程序读取 config.xml 时用的是相对路径,启动时的工作目录不在 MeterCollection 根目录,导致读到了别的位置的同名文件,或者读到了 bin 目录里的旧配置备份。

解决:先确认当前工作目录,命令行里执行 pwd(linux)或 cd(windows)。最常见的修法是把 config.xml 放到 classpath 根目录,代码里用 getResourceAsStream 读取,这样无论从哪个目录启动都能定位到。改完后再加一条启动日志,把读到的 interval 值打印出来,一眼就能看出配置有没有生效。

5.3 中文乱码:字符集三层不一致

现象:采集数据表里中文注释变成问号,或者前端页面显示乱码。

原因:MySQL 库用了 utf8mb4,但 JDBC 连接没指定字符集,连接层默认用了 latin1,数据写入时就已经编码错了。另外 docx 里提到的配置说明如果用记事本打开另存为 ANSI,程序读出来也是乱码。

解决:JDBC URL 加 characterEncoding=utf8,MySQL 的 my.ini 里保证 character-set-server=utf8mb4,建表 SQL 里也带上 CHARSET=utf8mb4。三层统一后从源头到展示都不会乱。检查方法很简单,查数据库时执行 SHOW VARIABLES LIKE 'character%'; 看连接层、服务层、数据库层的字符集是否一致。

5.4 数据重复:任务被重复调度

现象:collect_task 表里同一轮采集出现多条记录,meter_data 表里同一时间点同一块表的数据出现两条。

原因:程序异常重启后旧的调度线程没有完全销毁,两个调度器同时跑;或者 stop 方法用了 shutdown 而不是 shutdownNow,线程池里残留的任务还在执行。

解决:进程管理上保证同一时间只有一个实例在跑,部署脚本里加启动前检测,发现旧进程直接 kill。代码里 stop 方法用 shutdownNow,启动时把线程池对象声明为单例,避免多次 start 造成重复调度。排查时先看 collect_task 表里同 task_time 的记录条数,能快速确认是否重复。

5.5 电表通信超时:线路参数不对

现象:日志里大量 timeout 报错,success_count 持续为 0,但数据库连接正常。

原因:串口参数和电表不一致。波特率、数据位、停止位、校验位有一项不对,电表就不会正常应答;另外 485 总线没接终端电阻,通信距离长时信号反射也会导致超时。

解决:用串口调试工具先单独测试,确认电表返回的数据帧能正常读出来,再让程序接管。检查 config.xml 里的 protocol 参数是否和电表侧一致,电表地址 meter_addr 也必须逐位核对。通信距离超过 50 米时,在总线两端加 120 欧姆终端电阻,这个属于 485 布线的血泪经验,别看它简单,能解决大量疑难超时。

6. 进阶玩法:用 errorLog 和 flow.jpg 反推系统运行状态

压缩包里的 errorLog.txt 不是垃圾文件,它是理解这套系统运行状态最好的入口。我在复现的时候,先把 errorLog 按异常类型分组,再对照 flow.jpg 里的流程节点,很快就定位到了系统的脆弱环节。

grep -nE "ERROR|Exception|timeout" 2017_10_24_errorLog.txt | sort | uniq -c | sort -rn | head -20

这条命令能统计出日志里最频繁的异常类型。我实际跑出来的结果里,timeout 类异常占了将近一半,说明当时的串口通信稳定性是主要瓶颈,而不是数据库写入。日志里出现 ProtocolException 的位置,对应 flow.jpg 里的协议解析节点;出现 SQLException 的位置,对应写数据库节点。异常频率和流程节点对应上之后,系统瓶颈一目了然。

6.1 把 errorLog 当压测报告用

errorLog 里的每一条时间戳都有价值。按小时统计异常数量,能看到哪个时间段通信失败率最高。如果集中在某个固定时段,基本是现场电表集中上报导致总线拥塞;如果全天均匀分布,大概率是线路干扰。在做毕业设计答辩时,能说出这一层分析,比单纯展示功能要有说服力得多。

6.2 用 flow.jpg 做验证清单

flow.jpg 里的每个节点都可以转成一条验证项。我只把它的主线列出来对照测试:

  • 读配置:改 config.xml 的 interval,重启后日志打印新值
  • 加载电表档案:在 meter_info 表里删一条数据,观察采集记录里对应的表号消失
  • 发起采集:手动触发一次采集任务,collect_task 表新增记录
  • 协议解析:用串口调试工具发一帧错数据,看是否被 CS 校验拦截
  • 写数据库:查 meter_data 表,确认电压、电流字段和模拟器发送值一致
  • 阈值告警:把 voltageLow 改成 230,观察正常电压变成告警状态

这套验证方法后来成了我检查同类系统的固定动作。从那以后,每次接到采集类项目,我都强制自己先把日志按异常类型过一遍,再拿流程图当对照清单跑一轮测试,确认正常流和异常流都符合预期,才敢把系统交给业务方用。资源和文档只是起点,真正让你进步的是把每一步都验证透的习惯。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询