1. 项目全景与源码导览
在动手写第一行代码之前,先把整个项目"看一遍"是很有必要的。SCADA(Supervisory Control and Data Acquisition,数据采集与监视控制)能源管理系统并不是一个单纯的"采集数据画曲线"的工具,它同时承担着现场设备接入、实时监控、能耗统计、告警联动和报表输出等多重职责。本章的目标是建立一张"项目地图":搞清楚系统要解决什么业务问题、边界划在哪里、用什么技术栈实现、源码目录如何组织、模块之间如何协作。后续所有章节的编码工作,都会落在这张地图的某个具体位置上。
一、业务目标:从"看得见"到"管得住"
能源管理系统的核心业务目标可以概括为三层递进关系。第一层是可视:把分散在配电房、车间、楼宇的电力仪表、水表、气表、温湿度传感器数据实时汇聚到统一界面,让运维人员不再依赖现场抄表。第二层是可算:基于采集到的有功功率、电流、电压等原始量,按时间维度(15分钟、小时、日、月)计算用电量、峰谷平电量、单位产品能耗、同比环比等指标。第三层是可控:当某条回路电流超限、功率因数偏低或能耗异常升高时,系统能触发告警、推送通知,并在具备条件时下发控制指令(如切除非关键负载)。
以一个典型的工业园区项目为例,假设接入 120 台多功能电表、30 台水表、20 台气体流量计,采集周期为 5 秒,那么系统每天产生的原始点位数据约为 (120+30+20)×(86400/5) ≈ 294 万条。这个量级决定了系统不能简单地"来一条存一条",而必须在采集层做批量缓冲、在存储层做分级归档。业务目标直接决定了后面的技术选型,这是本章需要建立的第一条因果链。
二、功能边界:明确"做什么"与"不做什么"
功能边界不清是项目后期返工的主要原因。本系统的功能边界可以这样界定:
- 包含:设备通信接入(Modbus TCP/RTU、IEC 104、OPC UA)、实时数据采集与缓存、实时监控画面、历史数据存储与查询、能耗统计与报表、告警产生与推送、用户权限管理、系统日志。
- 不包含:底层 PLC 逻辑控制程序、继电保护定值整定、企业 ERP 财务核算、视频监控。这些系统通过接口与本系统交换数据,但不属于本系统的实现范围。
边界之外的部分,系统只提供"接口"而非"实现"。例如与 ERP 的对接,本系统只负责按约定格式导出能耗数据文件或提供 REST 接口,不负责 ERP 侧的入库逻辑。把这条线画清楚,源码目录中就不会出现职责混乱的模块。
三、技术栈选型:为什么是这一套
技术栈的选择必须服务于前面的业务目标和数据量级。本项目的选型如下:
| 层次 | 技术选型 | 选型理由 |
|---|---|---|
| 采集层 | Python + pymodbus / asyncua | 协议库成熟,异步 IO 适合高并发轮询 |
| 通信中间件 | MQTT (EMQX) / Redis Stream | 解耦采集与处理,削峰填谷 |
| 后端服务 | Java Spring Boot / Go | 生态完善,适合长期维护的企业级服务 |
| 实时库 | Redis + InfluxDB | Redis 存最新值,InfluxDB 存时序历史 |
| 关系库 | MySQL / PostgreSQL | 存设备台账、用户、告警规则等结构化数据 |
| 前端 | Vue 3 + ECharts | 组态画面与曲线图表开发效率高 |
| 部署 | Docker Compose / K8s | 便于现场私有化部署与升级 |
需要特别说明的是"双库"设计:Redis 保存每个测点的最新值和短时缓存,供监控画面秒级刷新;InfluxDB 按时间序列保存历史数据,供趋势查询和统计计算。两者职责不重叠,避免了用关系库硬扛高频写入导致的性能瓶颈。
四、源码目录结构与模块划分
项目采用前后端分离 + 采集服务独立部署的结构。顶层目录划分如下:
scada-energy/
├── collector/ # 采集服务(Python)
│ ├── drivers/ # 协议驱动:modbus_tcp.py, iec104.py, opcua.py
│ ├── scheduler.py # 轮询调度与任务编排
│ └── publisher.py # 数据发布到 MQTT/Redis
├── server/ # 后端服务(Spring Boot)
│ ├── gateway/ # 接口网关与鉴权
│ ├── realtime/ # 实时数据处理与推送
│ ├── history/ # 历史查询与统计
│ ├── alarm/ # 告警规则引擎
│ └── report/ # 报表导出
├── web/ # 前端(Vue 3)
│ ├── views/ # 监控画面、报表、告警页
│ └── components/ # 组态元件、图表封装
├── deploy/ # docker-compose、配置模板
└── docs/ # 接口文档、点位表、部署说明
模块划分遵循"高内聚、低耦合"原则:采集服务只负责"把数据拿回来并发布出去",不关心数据如何展示;后端 realtime 模块只消费实时流并推送给前端,不直接访问设备;history 模块只处理已落库的数据。这样任何一个模块出问题,影响范围都是可控的。
五、模块协作关系全景
下面的 SVG 展示了从现场设备到最终用户的完整数据链路与模块依赖关系。图中区分了采集侧、服务侧、存储侧和展示侧四个区域,箭头方向表示数据流向,虚线表示控制/配置的反向通道。
从图中可以清楚看到,采集服务是整个系统的"入口",它通过协议驱动从现场设备读取数据,经调度器按周期组织后发布到消息中间件。后端服务从中间件消费数据,分三条路径处理:实时路径写入 Redis 并推送给前端;历史路径写入 InfluxDB 供统计查询;告警路径交给规则引擎判断。前端只与后端服务交互,不直接接触采集层和数据库。这种分层设计使得每一层都可以独立替换或扩容——例如采集服务从 Python 换成 Go,只要发布到 MQTT 的消息格式不变,后端完全无感知。
六、从全景到代码:后续章节的落点
有了这张全景图,后续章节的定位就非常清晰了:第2~5章会深入 collector 目录,讲解 Modbus 驱动如何封装、轮询任务如何调度、断线重连如何处理;第6~10章进入 server 的 realtime 和 history 模块,讨论实时数据如何缓存、电量如何按时间段积分计算;第11~15章围绕 alarm 和 report 展开;第16章之后进入前端组态与部署运维。每一章都会对应到本章目录结构中的某个具体文件或模块。
本章不涉及具体编码,但建立的项目全景认知会贯穿整个课程。请对照源码目录,在本地把项目骨架跑起来(哪怕采集服务暂时用模拟数据源),确认 MQTT、Redis、InfluxDB、MySQL 四个基础组件都能正常连接。这是后续所有章节能够顺利推进的前提。