Compare commits

...

225 Commits

Author SHA1 Message Date
5d5a7137fc Merge pull request 'dev' (#2) from dev into main
Reviewed-on: #2
2026-02-11 01:55:45 +00:00
zq
6545b4f947 修改 2026-01-28 19:26:15 +08:00
zq
8a44009c42 修改设备监控电表类型设备曲线不展示问题 2026-01-28 19:21:30 +08:00
zq
2b22b70baa 新增电表报表、收益报表导出功能 2026-01-28 15:59:30 +08:00
zq
5a86769b40 统计报表-功率曲线按照分钟展示数据 2026-01-27 19:15:55 +08:00
zq
eeccd19f0a 设备监控页面-设备告警铃铛数由查询告警点位数据改为查询设备故障告警 2026-01-27 17:54:11 +08:00
zq
d19f07c4e8 modbus本地设备数据读取失败,告警等级修改为“一般”,读到数据后自恢复 2026-01-27 17:37:42 +08:00
zq
9f2c303047 topic设备数据消息5次没有数据内容则增加告警,告警等级修改为“一般”,读到数据后自恢复 2026-01-27 17:26:11 +08:00
zq
7199677d7b 解决null值转换为字符串 "null"问题 2026-01-27 17:23:32 +08:00
zq
4b58838d5d 解决null值转换为字符串 "null"问题 2026-01-27 16:55:04 +08:00
zq
14904ea6b2 站点首页-设备告警仅存在严重、紧急告警时提醒 2026-01-27 16:49:20 +08:00
zq
5225f03195 站点首页-当日功率曲线按照分钟展示数据 2026-01-27 16:47:48 +08:00
zq
619c05e965 修改消防数据查询报错问题 2026-01-26 16:46:31 +08:00
zq
1531af2969 修改消防数据查询报错问题 2026-01-26 14:38:16 +08:00
e55cdc6504 临时修改 2026-01-25 18:47:38 +08:00
400ae98b4e 临时修改 2026-01-25 14:00:18 +08:00
zq
dd060b0abf 单体电池解析数据改动;枚举值匹配转换改动 2026-01-24 20:51:16 +08:00
6ae9126662 回滚到 19:29 2026-01-24 00:20:27 +08:00
83fae710fb 修改小错误 2026-01-23 23:46:46 +08:00
c518b9bcae mqtt转移到测试环境,生产由电动所的应用提供 mqtt 数据 2026-01-23 23:09:31 +08:00
034c682864 batteryDataProcessFromBmsc方法device_id改成 device_id+cluster_id 2026-01-23 21:05:07 +08:00
5f7604d36b 加了日志,device_id改成 device_id+cluster_id 2026-01-23 20:16:05 +08:00
zq
084201bb58 修改 2026-01-23 19:43:48 +08:00
zq
194429af2d 修改 2026-01-23 19:29:24 +08:00
zq
b3f7ca7a81 设备监控-实时运行页面曲线按照每分钟展示 2026-01-23 19:11:13 +08:00
zq
5eccb35568 修改 2026-01-23 17:38:26 +08:00
zq
c007016224 告警消息按时间倒序查询 2026-01-23 15:30:07 +08:00
zq
768dd0bead 电表设备详情其他类型展示值查询 2026-01-23 15:29:44 +08:00
zq
26c534cae9 修改 2026-01-23 11:20:05 +08:00
zq
24ba30a536 逆变器功率上限值,默认为100kW 2026-01-23 10:40:26 +08:00
zq
bb85ca1d5e 修改 2026-01-22 18:12:40 +08:00
zq
ff76a73a61 修改 2026-01-22 18:03:47 +08:00
zq
1db167f0f8 Merge branch 'dev' of http://101.43.41.9:13000/xzzn/emsback into dev 2026-01-22 17:53:43 +08:00
f592b709a7 解决报错问题 2026-01-22 16:06:52 +08:00
zq
7f025be41b 站点首页新增展示字段:昨日充电量、昨日放电量、昨日实时收入 2026-01-22 15:40:29 +08:00
8968dc7a41 Merge remote-tracking branch 'origin/dev' into dev 2026-01-22 15:08:50 +08:00
46ea4acc2c 设备列表不展示display_flg=1的数据 2026-01-22 15:08:39 +08:00
zq
04bf9fe1c1 收益报表新增一列”实际收益“,价格保留三位小数 2026-01-22 14:30:52 +08:00
zq
b4f867f796 解决查询空指针报错问题 2026-01-22 14:17:07 +08:00
zq
ecc4799afd 修改查询策略语句 2026-01-22 09:47:47 +08:00
3287cddc6a 用户信息增加 belongsite 2026-01-21 14:53:21 +08:00
11e217787b 修正 modbus 版本为 3.1.0 2026-01-20 21:58:11 +08:00
fec45dac03 修正 modbus 长连接 2026-01-20 21:22:35 +08:00
939bcbe950 修正 modbus 写入超时问题 2026-01-20 19:44:10 +08:00
12a459854e 修正 modbus 超时问题 2026-01-20 17:21:12 +08:00
a31a1a1caa 修正 modbus 超时问题 2026-01-20 17:19:35 +08:00
zq
8716d43879 修改modbus读取设备数据连接池配置 2026-01-20 10:54:14 +08:00
zq
dd0132ab2f 解决保护方案告警不触发问题 2026-01-16 17:06:31 +08:00
zq
fb64be5a5a PCS关机先向设备发送给定功率值再发送关机指令 2026-01-15 17:49:03 +08:00
zq
0076872134 解决保护方案告警不触发问题 2026-01-15 17:40:51 +08:00
zq
f05ba30f14 修改设备监控-PCS实时有功功率与总交流有功功率不一致问题 2026-01-15 15:21:26 +08:00
zq
9aa7dd9d18 运行策略测试修改 2026-01-15 14:45:28 +08:00
zq
3f4d3772b0 设备告警点位解析修改 2026-01-12 15:18:21 +08:00
zq
685a2e8687 工作状态解析格式修改 2026-01-12 13:26:40 +08:00
zq
ebc06e2a2d 运行策略防逆流逻辑修改 2026-01-12 13:25:34 +08:00
zq
2fe5d7b8a1 获取设备列表参数:站点ID不必传 2026-01-08 17:19:01 +08:00
zq
31fe793e97 开关机功能增加设备参数校验 2026-01-08 17:16:24 +08:00
zq
1eee07e040 设备配置表新增work_status字段 2026-01-08 16:46:24 +08:00
zq
566119f692 设备告警点位数据解析逻辑修改 2026-01-08 15:33:54 +08:00
zq
77a3b6e855 设备在线状态和工作状态取值调整 2026-01-07 11:27:02 +08:00
zq
a4ac11e7d9 解决策略同步数据不对和重复问题 2026-01-06 11:27:20 +08:00
zq
4f3cd8c82b 解决策略同步开始和结束时间格式解析报错 2026-01-06 10:26:39 +08:00
zq
873119a1ff 设备上送数据解析判断设备类型 2026-01-06 10:25:08 +08:00
zq
6ed7a25819 修改切面代理配置 2026-01-05 16:25:46 +08:00
zq
dbdf1a5a61 PCS开关机相关配置不在数据表维护 2026-01-05 14:23:30 +08:00
zq
2278b63ada 运行策略轮询PCS设备配置读取逻辑修改 2026-01-04 11:07:35 +08:00
zq
b032e8e073 运行策略轮询PCS设备配置读取逻辑修改 2026-01-04 11:06:03 +08:00
zq
f24072f248 PCS设备开关机逻辑修改 2026-01-04 10:40:38 +08:00
zq
041c41822e PCS设备配置功能修改 2026-01-04 09:00:10 +08:00
zq
552d471fde 策略下发日志修改 2025-12-30 15:25:59 +08:00
zq
e381ff86c8 设备监控-杭州湾电表数据不显示问题修改 2025-12-30 15:06:42 +08:00
zq
2c9cc1f1d4 运行策略轮询放电逻辑修改 2025-12-30 14:36:11 +08:00
zq
93ed334c0e 新增PCS设备配置功能 2025-12-29 18:07:30 +08:00
zq
bac66aaddb 新增PCS设备配置功能 2025-12-29 17:45:53 +08:00
zq
5456d4742e 新增运行策略轮询功能 2025-12-29 15:47:28 +08:00
zq
22415df926 没有未处理的故障告警,站点首页不显示“设备告警”提示 2025-12-26 10:58:46 +08:00
zq
9e086b77b5 运行策略页面开始和结束时间显示按照升序排序 2025-12-26 10:43:12 +08:00
zq
56613e8f70 新增PCS点位字段配置-电池簇数 2025-12-25 11:42:06 +08:00
zq
18ff0100b9 modbus读取本地设备数据增加告警逻辑 2025-12-24 16:19:04 +08:00
zq
90e9b004b4 统计报表-电表报表:计算本次与上次数据差值,累加到对应的数据类型尖、峰、平、谷里面 2025-12-23 16:12:04 +08:00
zq
4f43b043ac 统计报表-收益报表:统一逻辑,电表尖、峰、平、谷差值直接乘以对应类型电价 2025-12-23 15:43:53 +08:00
zq
eb6f44dd93 解决收益报表分页问题 2025-12-23 14:58:43 +08:00
zq
d0bab53dee 统计报表-收益报表:统一逻辑,电表尖、峰、平、谷差值直接乘以对应类型电价 2025-12-23 14:43:17 +08:00
zq
289fbc4d3a 统计报表-功率曲线:统一逻辑,储能功能取值字段secondary_total_active_power修改为取total_active_power;负荷功率=电网功率+光伏功率-储能功率 2025-12-23 14:09:17 +08:00
zq
21fe3af17c 统计报表-概率统计:统一按照电动所逻辑,从储能电表METE中取,今日总数据-昨日总数据=今日充放电 2025-12-23 13:34:44 +08:00
zq
944adf3503 解决云上-生产环境空指针报错 2025-12-23 09:50:27 +08:00
zq
15a25b5ada 新增设备点位数据转换公式字段 2025-12-22 14:02:57 +08:00
zq
289fd37e2b 解决本地端解析数据报错问题 2025-12-19 16:41:49 +08:00
zq
2cd4040431 解决设备列表页面分页展示数据不对问题 2025-12-19 15:04:04 +08:00
zq
245b3e22d6 PCS开关机功能通过modbus连接设备发送控制命令; 2025-12-19 11:50:11 +08:00
zq
ff487f1b05 集成modbus连接本地设备读取数据代码-配置文件方式读取;
PCS开关机功能通过modbus连接设备发送控制命令;
2025-12-19 11:38:04 +08:00
zq
16d414f092 平台修改意见20251120-修改上传点位清单导入报错问题,生产不产生日志 2025-12-16 12:33:55 +08:00
zq
41b846c7f9 解决生产环境接口空指针报错 2025-12-15 15:36:24 +08:00
zq
6af6232b51 解决生产环境接口空指针报错 2025-12-15 15:32:21 +08:00
zq
40e23df66a 平台修改意见20251120-设备列表查询接口增加设备类型参数 2025-12-15 14:58:33 +08:00
zq
3611e444da 平台修改意见20251120-设备列表查询接口增加设备类型参数 2025-12-15 11:07:14 +08:00
zq
7b25674a25 Merge branch 'dev' of http://101.43.41.9:13000/xzzn/emsback into dev 2025-12-15 10:55:45 +08:00
zq
d387612b07 平台修改意见20251120-设备详情查询告警点位数据报错问题 2025-12-15 10:55:40 +08:00
zq
85b370ea4c 平台修改意见20251120-点位上传根据设备ID区分 2025-12-12 14:36:18 +08:00
zq
c6fca8b939 平台修改意见20251120-新增PCS设备开关机 2025-12-12 13:25:53 +08:00
zq
e73787ee8f 平台修改意见20251120-统一PCS页面与设备列表页面的设备状态取值字段 2025-12-12 11:06:22 +08:00
zq
0e1c7f52f1 平台修改意见20251120-调整表ems_devices_setting字段running_status为device_status,与表ems_pcs_data字段device_status保持一致 2025-12-12 10:47:21 +08:00
zq
73c668709a 平台修改意见20251120-点位上传根据设备ID区分 2025-12-11 16:13:39 +08:00
zq
3488808786 平台修改意见20251120-综合查询-按天查询点位数据箱线图展示 2025-12-09 17:23:24 +08:00
zq
497e5bb8ca 去除导入的不存在类 2025-12-09 16:58:52 +08:00
zq
678e814350 平台修改意见20251120-新增EMS设备类型查询列表接口;综合查询-按天查询点位数据箱线图展示;处理EMS设备topic消息;设备类型接口新增告警数量 2025-12-09 16:47:34 +08:00
zq
5f1e621da2 平台修改意见20251120-点位图表的日数据采用箱线图展示 2025-12-09 14:28:52 +08:00
zq
b7aaf85a3f 平台修改意见20251120-点位上传新增数据枚举映射处理逻辑 2025-12-08 18:48:05 +08:00
zq
28dfb0c152 平台修改意见20251120-统计报表-收益报表 2025-12-07 18:35:32 +08:00
zq
fc5f56b3b0 平台修改意见20251120-修改电表每日充放电数据hardcode 2025-12-05 19:15:04 +08:00
zq
3313c7f9cd 平台修改意见20251120-动环设备新增字段-数据更新时间 2025-12-05 18:33:33 +08:00
zq
70c990051b 平台修改意见20251120-液冷设备新增字段-数据更新时间 2025-12-05 18:25:39 +08:00
zq
b4b78e03b1 平台修改意见20251120-BMS电池堆新增字段-数据更新时间 2025-12-05 18:16:50 +08:00
zq
3605702209 平台修改意见20251120-BMS电池簇新增字段-数据更新时间 2025-12-05 18:07:12 +08:00
zq
62340387bb 平台修改意见20251120-优化点位清单导出内容 2025-12-05 17:15:13 +08:00
zq
a31b607872 平台修改意见20251120-告警点位清单查询 2025-12-05 08:58:00 +08:00
zq
8e631b7b30 平台修改意见20251120-告警点位清单查询 2025-12-05 08:57:06 +08:00
zq
3735c4f4d8 平台修改意见20251120-设备点位匹配解析;上传点位清单修改; 2025-12-05 08:46:19 +08:00
zq
c068e7d4ab 修复站点-电动所内部实时运行查询报错问题 2025-11-30 10:26:03 +08:00
zq
b38bc36ad6 平台修改意见20251120-021_FXX_01站点设备点位初始化配置,读取配置的设备点位匹配关系,存储对应的点位数据 2025-11-29 19:54:10 +08:00
d9c0ff733a 平台修改意见20251120-新增设备点位清单上传接口 2025-11-28 11:58:45 +08:00
e87abc2807 平台修改意见20251120-设备监控类别新增动环、消防设备数据 2025-11-26 19:07:38 +08:00
a5e977c0d1 平台修改意见20251120-设备监控实时运行页面数据图按照小时展示,共用时间范围,新增时间查询条件 2025-11-25 14:23:26 +08:00
7298acc785 修改日志目录 2025-11-19 14:05:40 +08:00
55d648d12c 增加本地配置 2025-11-19 13:28:09 +08:00
36f08e5f06 漏提交-自定义注解 2025-11-19 10:05:51 +08:00
1cc7d53559 工具类 2025-11-17 12:15:07 +08:00
3753075b10 同步日志&监听处理 2025-11-17 02:47:43 +08:00
aab2b1d94e 【验收】2 运行策略配置同步 2025-11-17 02:45:35 +08:00
3ee99fa8d4 【验收】6-保护方案告警同步云端&& 【验收】7-下发操作日志同步云端 2025-11-16 22:48:14 +08:00
c62aa46fcb 【验收】3 修改 2025-11-16 22:27:53 +08:00
bfbb0ae475 【验收】3 修改 2025-11-16 21:22:09 +08:00
25e03ab028 【验收】8 本地设备切换记录日志 2025-11-16 21:00:29 +08:00
38ade0c2ed 验收3-设备保护告警同步本地 2025-11-13 00:02:47 +08:00
f1e819ba2b 生产mqtt配置 2025-11-12 23:21:04 +08:00
be4da8c58d 生产mqtt配置 2025-11-12 15:26:30 +08:00
fa280097eb 配置文件修改 2025-11-11 17:14:40 +08:00
1b5fcea5cd 配置文件修改 2025-11-11 15:42:19 +08:00
49a83fd420 验收2-同步策略运行 2025-11-11 10:37:46 +08:00
5381cd597c mqtt配置修改 2025-11-11 10:37:04 +08:00
43edc47aaa 验收5-mqtt配置 2025-11-06 19:39:53 +08:00
72c7c0c3e0 点位清单-导入导出 2025-11-06 15:32:33 +08:00
c2682f38a8 点位清单-导入导出 2025-11-05 14:58:05 +08:00
3598cb2d66 告警保护方案轮询-调整 2025-10-30 16:52:06 +08:00
82e63e28d3 告警保护方案轮询-初稿 2025-10-29 18:38:34 +08:00
614cca4297 定时任务迁移到quartz模块下 2025-10-28 14:38:18 +08:00
d83af112e7 获取指定站点下的指定设备类型的设备 2025-10-28 14:22:22 +08:00
75bd5f6bf2 单站监控-设备状态同设备列表 2025-10-28 10:20:12 +08:00
0108b4f108 task61-告警保护方案增删改查 2025-10-24 19:08:57 +08:00
4a0075b606 告警数据按设备存表 2025-10-22 17:21:43 +08:00
b776f6ff76 列表界面里面的设备类型列改成显示设备类别 2025-10-21 15:25:23 +08:00
76b30c715c 0918优化-综合查询最大最小加日期 2025-10-21 15:12:14 +08:00
e1fb6e30ac 消防数据存表 2025-10-21 13:55:06 +08:00
a338c921d3 综合查询优化 2025-10-21 12:39:14 +08:00
4ad2cafc5d 0918优化-综合查询显示曲线最大最小平均值和差值 2025-10-16 17:01:35 +08:00
d0275e62e0 电价-实时收入实时计算 2025-10-16 14:57:18 +08:00
94e18c029c 电价-实时收入实时计算 2025-10-16 14:04:13 +08:00
391ed354f8 dds-电池簇同步数据获取父类stackId 2025-10-15 17:53:12 +08:00
6716852435 设备列表-单体电池的点位清单数据 2025-10-15 16:27:22 +08:00
451b2f6766 新增设备获取父类类别的id列表 2025-10-15 16:26:21 +08:00
250318ddef 初始化设备信息存入redis 2025-10-15 15:39:18 +08:00
d468ef9941 task65-动态获取站点下设备类型 2025-10-14 17:32:16 +08:00
15b964b4ce task66-单体电池调综合查询点位曲线接口-增加簇id 2025-10-14 17:07:00 +08:00
3020f9f915 task63-点位清单增加单位列 2025-10-14 16:50:53 +08:00
609803605d 工单优化 2025-10-14 14:36:34 +08:00
aadf66d5ad 点位清单-按数值排序 2025-10-13 14:24:13 +08:00
049eaa84b7 0918电价-站点的实时总收入和实时当日收入 2025-10-13 13:00:35 +08:00
c438f50ae1 0918电价-站点的实时总收入和实时当日收入 2025-10-13 12:04:46 +08:00
eed6f839f6 0918电价-站点的实时总收入和实时当日收入 2025-10-13 11:37:03 +08:00
6ebeb48b62 0918电价-站点的实时总收入和实时当日收入 2025-10-12 19:47:55 +08:00
3454359f01 0918电表报表-奉贤每日尖峰平谷累加 2025-10-11 17:21:40 +08:00
83f8f4e293 电价配置-增加站点id 2025-10-11 16:31:32 +08:00
b79f9caa2d 电价配置-增加站点id 2025-10-11 16:28:50 +08:00
e33b26fc05 日期转换错误 2025-10-11 10:48:45 +08:00
9075878e41 电价列表-按月降序排序 2025-10-10 18:59:47 +08:00
158bc1a51d 0918优化-电表报表-尖峰平谷日差值显示 2025-10-10 17:32:15 +08:00
b861ad7593 0918优化-电表报表-尖峰平谷日差值显示 2025-10-10 16:10:37 +08:00
636a2ab73b 电价配置-列表加时间范围-暂时只取年份 2025-10-10 11:21:33 +08:00
d8d0a83c87 电价配置 2025-10-09 19:58:50 +08:00
7121fdecfa 0929-部分数据优化 2025-09-29 20:05:16 +08:00
2bbc0abc08 0918优化-告警工单逻辑优化 2025-09-29 10:14:46 +08:00
e5a2ce4c8d 0918优化-电表点位查询修改 2025-09-28 14:44:02 +08:00
f2b2741b1c 0918优化-电表取值修改 2025-09-28 10:32:02 +08:00
097709d403 0918优化-实时运行PCS最高温度数据缺失 2025-09-27 20:01:31 +08:00
33534cc500 0918优化-实时运行曲线图时间格式处理 2025-09-27 15:11:57 +08:00
ee53ab137b 0918优化-点位列表电池簇特殊处理 2025-09-26 16:43:40 +08:00
b2d5023122 0918优化-点位列表电池簇特殊处理 2025-09-26 16:37:46 +08:00
553e30c6ef 0926-奉贤液冷数据接入 2025-09-26 13:55:16 +08:00
102ecbd41c 0918优化-单站监控-实时运行曲线图pcs分开显示 2025-09-25 19:44:00 +08:00
d47d9150e6 0918优化-单站监控-实时运行曲线图取值 2025-09-25 14:53:17 +08:00
46135aa2ec 0918优化-单站监控-功率曲线默认显示前一小时数据按分钟分组 2025-09-24 22:47:09 +08:00
9874d6b1ea 0918优化-单站监控-电表数据修改 2025-09-24 14:12:37 +08:00
f7515b221b 0918优化-点位列表数据值反向搜索 2025-09-24 14:10:10 +08:00
f3b34e7fb5 0918优化-点位列表数据值反向搜索 2025-09-24 14:09:29 +08:00
2810f5e204 0918优化-点位列表数据值反向搜索 2025-09-24 14:07:54 +08:00
7cd5a9b131 0918优化-点位列表排序方式,默认升序 2025-09-23 15:04:02 +08:00
2099b94e73 0918优化-单站监控首页日期修改&点位模糊查询 2025-09-23 14:01:24 +08:00
630f77a783 奉贤 alarm topic接入 2025-09-23 14:00:22 +08:00
8e8c57cb64 奉贤 alarm topic接入 2025-09-23 13:59:45 +08:00
a5f1444984 数据20250916优化-topic订阅失败&数据空报警 2025-09-22 00:40:38 +08:00
e93f9cc6b8 数据20250916优化-点位名称模糊查询 2025-09-18 18:04:59 +08:00
8954329ccc 数据20250916优化-点位最新值+时间 2025-09-18 05:17:57 +08:00
b8d3643ba9 数据20250904优化-功率数据不计算 2025-09-16 10:53:41 +08:00
2451e00022 数据20250904优化-单站监控首页点位数据展示 2025-09-16 00:34:14 +08:00
cb39131871 数据20250904优化-单站监控首页点位数据展示 2025-09-16 00:31:58 +08:00
2bb78dc020 数据20250904优化-功率曲线取值修改 2025-09-15 14:21:27 +08:00
d807b9117d 数据20250904优化-PCS曲线和电池堆曲线返回修改 2025-09-14 22:44:10 +08:00
8a4cff0a6c 数据20250904优化-点位列表分页&点位查询 2025-09-13 21:49:33 +08:00
ecbe55cdbf 数据20250904优化-点位数据接口增加入参 2025-09-13 19:25:58 +08:00
bfdbc4f42c 数据20250904优化 2025-09-12 04:40:47 +08:00
ab6771d33d 20250905-奉贤充放电取值修改 2025-09-07 21:23:24 +08:00
2344cc458c 综合查询页面 2025-09-05 20:31:30 +08:00
aa37e2a881 综合查询页面 2025-09-05 13:45:39 +08:00
15e230babb 优化 2025-09-01 16:17:36 +08:00
7374d7708b 20250808优化-充放电逻辑修改 2025-09-01 16:10:17 +08:00
26bbe6deee 20250808优化-充放电逻辑修改 2025-08-28 14:15:19 +08:00
1a1558f8cd 20250808优化-告警优化&主路线图优化 2025-08-27 01:32:05 +08:00
d6276dc21f 20250808优化-单体电池表拆分优化 2025-08-24 17:12:42 +08:00
725ae14548 20250808优化-单体电池表拆分为分钟,时,天,月级四个表 2025-08-23 00:09:02 +08:00
b2527507e4 20250808优化-单体电池优化 2025-08-16 10:28:06 +08:00
ff058dceaf 20250808优化-单体电池优化 2025-08-13 23:41:34 +08:00
76d0634ae8 20250808优化-单站首页-策略显示 2025-08-13 18:31:14 +08:00
eb72542b67 20250808优化-故障告警 2025-08-11 19:56:51 +08:00
7927066058 20250808优化-设备监控 2025-08-11 14:49:18 +08:00
08c83c1a37 漏提交 2025-08-08 00:13:17 +08:00
289 changed files with 38253 additions and 1828 deletions

View File

@ -39,8 +39,9 @@
<!-- Mysql驱动包 --> <!-- Mysql驱动包 -->
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>com.mysql</groupId>
<artifactId>mysql-connector-java</artifactId> <artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
</dependency> </dependency>
<!-- 核心模块--> <!-- 核心模块-->

View File

@ -0,0 +1,82 @@
package com.xzzn.web.controller.ems;
import java.util.List;
import com.xzzn.ems.domain.vo.EnergyPriceVo;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import com.xzzn.common.annotation.Log;
import com.xzzn.common.core.controller.BaseController;
import com.xzzn.common.core.domain.AjaxResult;
import com.xzzn.common.enums.BusinessType;
import com.xzzn.ems.service.IEmsEnergyPriceConfigService;
import com.xzzn.common.core.page.TableDataInfo;
/**
* 电价配置Controller
*
* @author xzzn
* @date 2025-09-28
*/
@RestController
@RequestMapping("/ems/energyPriceConfig")
public class EmsEnergyPriceConfigController extends BaseController
{
@Autowired
private IEmsEnergyPriceConfigService emsEnergyPriceConfigService;
/**
* 查询电价配置列表
*/
@PreAuthorize("@ss.hasPermi('system:config:list')")
@GetMapping("/list")
public TableDataInfo list(String siteId, String startTime,String endTime)
{
List<EnergyPriceVo> list = emsEnergyPriceConfigService.selectEmsEnergyPriceConfigList(siteId,startTime,endTime);
return getDataTable2(list);
}
/**
* 获取电价配置详细信息
*/
@PreAuthorize("@ss.hasPermi('system:config:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(emsEnergyPriceConfigService.selectEmsEnergyPriceConfigById(id));
}
/**
* 新增电价配置
*/
@PreAuthorize("@ss.hasPermi('system:config:add')")
@Log(title = "电价配置", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody EnergyPriceVo priceVo)
{
return toAjax(emsEnergyPriceConfigService.insertEmsEnergyPriceConfig(priceVo));
}
/**
* 修改电价配置
*/
@PreAuthorize("@ss.hasPermi('system:config:edit')")
@Log(title = "电价配置", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody EnergyPriceVo priceVo)
{
return toAjax(emsEnergyPriceConfigService.updateEmsEnergyPriceConfig(priceVo));
}
/**
* 删除电价配置
*/
@PreAuthorize("@ss.hasPermi('system:config:remove')")
@Log(title = "电价配置", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(emsEnergyPriceConfigService.deleteEmsEnergyPriceConfigByIds(ids));
}
}

View File

@ -0,0 +1,107 @@
package com.xzzn.web.controller.ems;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import com.xzzn.ems.service.IEmsFaultProtectionPlanService;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.xzzn.common.annotation.Log;
import com.xzzn.common.core.controller.BaseController;
import com.xzzn.common.core.domain.AjaxResult;
import com.xzzn.common.enums.BusinessType;
import com.xzzn.ems.domain.EmsFaultProtectionPlan;
import com.xzzn.common.utils.poi.ExcelUtil;
import com.xzzn.common.core.page.TableDataInfo;
/**
* 故障告警保护方案Controller
*
* @author xzzn
* @date 2025-10-22
*/
@RestController
@RequestMapping("/ems/protectPlan")
public class EmsFaultProtectionPlanController extends BaseController
{
@Autowired
private IEmsFaultProtectionPlanService emsFaultProtectionPlanService;
/**
* 查询故障告警保护方案列表
*/
@PreAuthorize("@ss.hasPermi('system:plan:list')")
@GetMapping("/list")
public TableDataInfo list(EmsFaultProtectionPlan emsFaultProtectionPlan)
{
startPage();
List<EmsFaultProtectionPlan> list = emsFaultProtectionPlanService.selectEmsFaultProtectionPlanList(emsFaultProtectionPlan);
return getDataTable(list);
}
/**
* 导出故障告警保护方案列表
*/
@PreAuthorize("@ss.hasPermi('system:plan:export')")
@Log(title = "故障告警保护方案", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, EmsFaultProtectionPlan emsFaultProtectionPlan)
{
List<EmsFaultProtectionPlan> list = emsFaultProtectionPlanService.selectEmsFaultProtectionPlanList(emsFaultProtectionPlan);
ExcelUtil<EmsFaultProtectionPlan> util = new ExcelUtil<EmsFaultProtectionPlan>(EmsFaultProtectionPlan.class);
util.exportExcel(response, list, "故障告警保护方案数据");
}
/**
* 获取故障告警保护方案详细信息
*/
@PreAuthorize("@ss.hasPermi('system:plan:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(emsFaultProtectionPlanService.selectEmsFaultProtectionPlanById(id));
}
/**
* 新增故障告警保护方案
*/
@PreAuthorize("@ss.hasPermi('system:plan:add')")
@Log(title = "故障告警保护方案", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody EmsFaultProtectionPlan emsFaultProtectionPlan)
{
emsFaultProtectionPlan.setCreateBy(getUsername());
return toAjax(emsFaultProtectionPlanService.insertEmsFaultProtectionPlan(emsFaultProtectionPlan));
}
/**
* 修改故障告警保护方案
*/
@PreAuthorize("@ss.hasPermi('system:plan:edit')")
@Log(title = "故障告警保护方案", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody EmsFaultProtectionPlan emsFaultProtectionPlan)
{
emsFaultProtectionPlan.setUpdateBy(getUsername());
return toAjax(emsFaultProtectionPlanService.updateEmsFaultProtectionPlan(emsFaultProtectionPlan));
}
/**
* 删除故障告警保护方案
*/
@PreAuthorize("@ss.hasPermi('system:plan:remove')")
@Log(title = "故障告警保护方案", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(emsFaultProtectionPlanService.deleteEmsFaultProtectionPlanByIds(ids));
}
}

View File

@ -0,0 +1,78 @@
package com.xzzn.web.controller.ems;
import com.xzzn.common.core.controller.BaseController;
import com.xzzn.common.core.domain.AjaxResult;
import com.xzzn.common.enums.DeviceCategory;
import com.xzzn.ems.domain.vo.*;
import com.xzzn.ems.service.IGeneralQueryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
* 综合查询
*
*/
@RestController
@RequestMapping("/ems/generalQuery")
public class EmsGeneralQueryController extends BaseController{
@Autowired
private IGeneralQueryService iGeneralQueryService;
/**
* 获取设备枚举
*/
@GetMapping("/getAllDeviceCategory")
public AjaxResult getDeviceCategory()
{
// 获取所有枚举的信息
List<Map<String, String>> deviceCategoryList = new ArrayList<>();
for (DeviceCategory category : DeviceCategory.values()) {
Map<String, String> categoryMap = new HashMap<>();
categoryMap.put("name", category.getInfo());
categoryMap.put("code", category.getCode());
deviceCategoryList.add(categoryMap);
}
return success(deviceCategoryList);
}
/**
* 点位模糊查询
*/
@PostMapping("/pointFuzzyQuery")
public AjaxResult pointFuzzyQuery(@RequestBody PointNameRequest request)
{
return success(iGeneralQueryService.getPointNameList(request));
}
/**
* 根据点位查询点位数据变化
*/
@PostMapping("/getPointValueList")
public AjaxResult getPointValueList(@RequestBody PointNameRequest request)
{
List<GeneralQueryResponse> result = new ArrayList<>();
try {
result = iGeneralQueryService.getPointValueList(request);
} catch (Exception e) {
logger.error("<UNK>",e);
return error("报错请重试!");
}
return success(result);
}
/**
* 获取设备枚举
*/
@GetMapping("/getAllBatteryIdsBySites/{siteIds}")
public AjaxResult getAllBatteryIdsBySites(@PathVariable String[] siteIds)
{
return success(iGeneralQueryService.getAllBatteryIdsBySites(siteIds));
}
}

View File

@ -0,0 +1,106 @@
package com.xzzn.web.controller.ems;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.xzzn.common.annotation.Log;
import com.xzzn.common.core.controller.BaseController;
import com.xzzn.common.core.domain.AjaxResult;
import com.xzzn.common.enums.BusinessType;
import com.xzzn.ems.domain.EmsMqttTopicConfig;
import com.xzzn.ems.service.IEmsMqttTopicConfigService;
import com.xzzn.common.utils.poi.ExcelUtil;
import com.xzzn.common.core.page.TableDataInfo;
/**
* 站点topic配置Controller
*
* @author xzzn
* @date 2025-11-06
*/
@RestController
@RequestMapping("/ems/mqttConfig")
public class EmsMqttTopicConfigController extends BaseController
{
@Autowired
private IEmsMqttTopicConfigService emsMqttTopicConfigService;
/**
* 查询站点topic配置列表
*/
@PreAuthorize("@ss.hasPermi('system:config:list')")
@GetMapping("/list")
public TableDataInfo list(EmsMqttTopicConfig emsMqttTopicConfig)
{
startPage();
List<EmsMqttTopicConfig> list = emsMqttTopicConfigService.selectEmsMqttTopicConfigList(emsMqttTopicConfig);
return getDataTable(list);
}
/**
* 导出站点topic配置列表
*/
@PreAuthorize("@ss.hasPermi('system:config:export')")
@Log(title = "站点topic配置", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, EmsMqttTopicConfig emsMqttTopicConfig)
{
List<EmsMqttTopicConfig> list = emsMqttTopicConfigService.selectEmsMqttTopicConfigList(emsMqttTopicConfig);
ExcelUtil<EmsMqttTopicConfig> util = new ExcelUtil<EmsMqttTopicConfig>(EmsMqttTopicConfig.class);
util.exportExcel(response, list, "站点topic配置数据");
}
/**
* 获取站点topic配置详细信息
*/
@PreAuthorize("@ss.hasPermi('system:config:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(emsMqttTopicConfigService.selectEmsMqttTopicConfigById(id));
}
/**
* 新增站点topic配置
*/
@PreAuthorize("@ss.hasPermi('system:config:add')")
@Log(title = "站点topic配置", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody EmsMqttTopicConfig emsMqttTopicConfig)
{
emsMqttTopicConfig.setCreateBy(getUsername());
return toAjax(emsMqttTopicConfigService.insertEmsMqttTopicConfig(emsMqttTopicConfig));
}
/**
* 修改站点topic配置
*/
@PreAuthorize("@ss.hasPermi('system:config:edit')")
@Log(title = "站点topic配置", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody EmsMqttTopicConfig emsMqttTopicConfig)
{
emsMqttTopicConfig.setUpdateBy(getUsername());
return toAjax(emsMqttTopicConfigService.updateEmsMqttTopicConfig(emsMqttTopicConfig));
}
/**
* 删除站点topic配置
*/
@PreAuthorize("@ss.hasPermi('system:config:remove')")
@Log(title = "站点topic配置", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(emsMqttTopicConfigService.deleteEmsMqttTopicConfigByIds(ids));
}
}

View File

@ -0,0 +1,88 @@
package com.xzzn.web.controller.ems;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.xzzn.common.annotation.Log;
import com.xzzn.common.core.controller.BaseController;
import com.xzzn.common.core.domain.AjaxResult;
import com.xzzn.common.enums.BusinessType;
import com.xzzn.ems.domain.EmsPointMatch;
import com.xzzn.ems.domain.vo.DevicePointMatchExportVo;
import com.xzzn.ems.domain.vo.DevicePointMatchVo;
import com.xzzn.ems.domain.vo.ImportPointDataRequest;
import com.xzzn.ems.service.IEmsPointMatchService;
import com.xzzn.common.utils.poi.ExcelUtil;
import org.springframework.web.multipart.MultipartFile;
/**
* 点位匹配Controller
*
* @author xzzn
* @date 2025-11-04
*/
@RestController
@RequestMapping("/ems/pointMatch")
public class EmsPointMatchController extends BaseController
{
@Autowired
private IEmsPointMatchService emsPointMatchService;
/**
* 导出点位匹配列表
*/
@PreAuthorize("@ss.hasPermi('system:match:export')")
@Log(title = "点位匹配", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, EmsPointMatch emsPointMatch)
{
List<DevicePointMatchExportVo> list = emsPointMatchService.selectEmsPointMatchList(emsPointMatch);
ExcelUtil<DevicePointMatchExportVo> util = new ExcelUtil<>(DevicePointMatchExportVo.class);
util.exportExcel(response, list, "点位匹配数据");
}
/**
* 上传点位清单
* @param file
* @param updateSupport
* @return
* @throws Exception
*/
@PreAuthorize("@ss.hasPermi('system:user:import')")
@Log(title = "点位匹配", businessType = BusinessType.IMPORT)
@PostMapping("/importData")
public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception
{
ExcelUtil<EmsPointMatch> util = new ExcelUtil<EmsPointMatch>(EmsPointMatch.class);
List<EmsPointMatch> pointMatcheList = util.importExcel(file.getInputStream());
String operName = getUsername();
String message = emsPointMatchService.importPoint(pointMatcheList, updateSupport, operName);
return success(message);
}
/**
* 上传设备的点位清单
* @param request
* @return
* @throws Exception
*/
@PreAuthorize("@ss.hasPermi('system:user:import')")
@Log(title = "点位匹配", businessType = BusinessType.IMPORT)
@PostMapping("/importDataByDevice")
public void importDataByDevice(@Valid ImportPointDataRequest request, HttpServletResponse response) {
List<DevicePointMatchVo> list = emsPointMatchService.importDataByDevice(request, getUsername());
if (CollectionUtils.isNotEmpty(list)) {
ExcelUtil<DevicePointMatchVo> util = new ExcelUtil<>(DevicePointMatchVo.class);
util.exportExcel(response, list, "点位匹配数据");
}
}
}

View File

@ -1,22 +1,38 @@
package com.xzzn.web.controller.ems; package com.xzzn.web.controller.ems;
import com.xzzn.common.annotation.Log;
import com.xzzn.common.config.RuoYiConfig; import com.xzzn.common.config.RuoYiConfig;
import com.xzzn.common.core.controller.BaseController; import com.xzzn.common.core.controller.BaseController;
import com.xzzn.common.core.domain.AjaxResult; import com.xzzn.common.core.domain.AjaxResult;
import com.xzzn.common.core.page.TableDataInfo; import com.xzzn.common.core.page.TableDataInfo;
import com.xzzn.common.enums.BusinessType;
import com.xzzn.common.utils.file.FileUploadUtils; import com.xzzn.common.utils.file.FileUploadUtils;
import com.xzzn.common.utils.file.MimeTypeUtils; import com.xzzn.common.utils.file.MimeTypeUtils;
import com.xzzn.ems.domain.EmsDevicesSetting;
import com.xzzn.ems.domain.EmsSiteSetting; import com.xzzn.ems.domain.EmsSiteSetting;
import com.xzzn.ems.domain.vo.DeviceUpdateRequest;
import com.xzzn.ems.domain.vo.DevicesSettingVo;
import com.xzzn.ems.domain.vo.PointDataRequest;
import com.xzzn.ems.domain.vo.PointQueryResponse;
import com.xzzn.ems.domain.vo.SiteDeviceListVo; import com.xzzn.ems.domain.vo.SiteDeviceListVo;
import com.xzzn.ems.service.IEmsDeviceSettingService; import com.xzzn.ems.service.IEmsDeviceSettingService;
import com.xzzn.ems.service.IEmsSiteService; import com.xzzn.ems.service.IEmsSiteService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List; import java.util.List;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
/** /**
* *
* 站点配置 * 站点配置
@ -47,10 +63,12 @@ public class EmsSiteConfigController extends BaseController{
* 获取设备列表-分页 * 获取设备列表-分页
*/ */
@GetMapping("/getDeviceInfoList") @GetMapping("/getDeviceInfoList")
public TableDataInfo getDeviceInfoList(@RequestParam String siteId) public TableDataInfo getDeviceInfoList(@RequestParam(value = "siteId", required = false) String siteId,
@RequestParam(value = "deviceCategory", required = false) String deviceCategory)
{ {
startPage(); startPage();
List<SiteDeviceListVo> list = iEmsSiteService.getAllDeviceList(siteId); List<SiteDeviceListVo> list = iEmsSiteService.getAllDeviceListNoDisp(siteId, deviceCategory);
return getDataTable(list); return getDataTable(list);
} }
@ -69,7 +87,7 @@ public class EmsSiteConfigController extends BaseController{
@GetMapping("/getDeviceList") @GetMapping("/getDeviceList")
public AjaxResult getDeviceInfoList2(@RequestParam String siteId) public AjaxResult getDeviceInfoList2(@RequestParam String siteId)
{ {
return success(iEmsSiteService.getAllDeviceList(siteId)); return success(iEmsSiteService.getAllDeviceList(siteId, null));
} }
/** /**
@ -85,7 +103,7 @@ public class EmsSiteConfigController extends BaseController{
* 新增设备 * 新增设备
*/ */
@PostMapping("/addDevice") @PostMapping("/addDevice")
public AjaxResult addDevice(@RequestBody EmsDevicesSetting devicesSetting) public AjaxResult addDevice(@RequestBody DevicesSettingVo devicesSetting)
{ {
int result = iEmsDeviceSettingService.addDevice(devicesSetting); int result = iEmsDeviceSettingService.addDevice(devicesSetting);
if (result > 0) { if (result > 0) {
@ -115,7 +133,7 @@ public class EmsSiteConfigController extends BaseController{
* 修改Modbus设备配置 * 修改Modbus设备配置
*/ */
@PostMapping("/updateDevice") @PostMapping("/updateDevice")
public AjaxResult updateDevice(@RequestBody EmsDevicesSetting emsDevicesSetting) public AjaxResult updateDevice(@RequestBody DevicesSettingVo emsDevicesSetting)
{ {
int result = iEmsDeviceSettingService.updateDevice(emsDevicesSetting); int result = iEmsDeviceSettingService.updateDevice(emsDevicesSetting);
if (result > 0) { if (result > 0) {
@ -136,4 +154,52 @@ public class EmsSiteConfigController extends BaseController{
{ {
return toAjax(iEmsDeviceSettingService.deleteEmsDevicesSettingById(id)); return toAjax(iEmsDeviceSettingService.deleteEmsDevicesSettingById(id));
} }
/**
* 单个站点单个设备点位查询-点位清单
*/
@GetMapping("/getDevicePointList")
public TableDataInfo getDevicePointList(@Validated PointDataRequest request)
{
List<PointQueryResponse> result = iEmsDeviceSettingService.getSingleSiteDevicePoints(request);
return getDataTable2(result);
}
/**
* 获取指定站点下的所有设备类别
*/
@GetMapping("/getSiteAllDeviceCategory")
public AjaxResult getSiteAllDeviceCategory(String siteId)
{
return success(iEmsDeviceSettingService.getSiteAllDeviceCategory(siteId));
}
/**
* 根据设备类别获取父类的设备id
*/
@GetMapping("/getParentDeviceId")
public AjaxResult getParentDeviceId(@RequestParam String siteId, @RequestParam String deviceCategory)
{
return success(iEmsSiteService.getParentCategoryDeviceId(siteId, deviceCategory));
}
/**
* 获取指定站点下的指定设备类型的设备
*/
@GetMapping("/getDeviceListBySiteAndCategory")
public AjaxResult getDeviceListBySiteAndCategory(String siteId,String deviceCategory)
{
return success(iEmsDeviceSettingService.getDeviceListBySiteAndCategory(siteId, deviceCategory));
}
/**
* PCS设备开关机
*/
// @PreAuthorize("@ss.hasPermi('system:device:onAndOff')")
@Log(title = "开关机", businessType = BusinessType.UPDATE)
@PostMapping("/updateDeviceStatus")
public AjaxResult updateDeviceStatus(@Valid @RequestBody DeviceUpdateRequest request)
{
return success(iEmsDeviceSettingService.updateDeviceStatus(request));
}
} }

View File

@ -2,7 +2,7 @@ package com.xzzn.web.controller.ems;
import com.xzzn.common.core.controller.BaseController; import com.xzzn.common.core.controller.BaseController;
import com.xzzn.common.core.domain.AjaxResult; import com.xzzn.common.core.domain.AjaxResult;
import com.xzzn.ems.service.IEmsSiteService; import com.xzzn.ems.domain.vo.DateSearchRequest;
import com.xzzn.ems.service.IHomePageService; import com.xzzn.ems.service.IHomePageService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.RestController;
/** /**
* *
// * 站点地图 * 站点地图
* *
*/ */
@RestController @RestController
@ -31,4 +31,17 @@ public class EmsSiteMapController extends BaseController{
return success(homePageService.getSingleSiteBaseInfo(siteId)); return success(homePageService.getSingleSiteBaseInfo(siteId));
} }
/**
* 获取某个站点7天充放电数据
*/
@GetMapping("/getSevenChargeData")
public AjaxResult getSevenChargeData(DateSearchRequest request)
{
String siteId = request.getSiteId();
if(siteId == null || siteId.isEmpty()) {
return error("站点必传");
}
return success(homePageService.getSevenChargeData(request));
}
} }

View File

@ -3,14 +3,19 @@ package com.xzzn.web.controller.ems;
import com.xzzn.common.core.controller.BaseController; import com.xzzn.common.core.controller.BaseController;
import com.xzzn.common.core.domain.AjaxResult; import com.xzzn.common.core.domain.AjaxResult;
import com.xzzn.common.core.page.TableDataInfo; import com.xzzn.common.core.page.TableDataInfo;
import com.xzzn.common.utils.StringUtils;
import com.xzzn.ems.domain.vo.BMSBatteryDataList;
import com.xzzn.ems.domain.vo.BatteryDataStatsListVo; import com.xzzn.ems.domain.vo.BatteryDataStatsListVo;
import com.xzzn.ems.domain.vo.DateSearchRequest; import com.xzzn.ems.domain.vo.DateSearchRequest;
import com.xzzn.ems.domain.vo.RunningGraphRequest;
import com.xzzn.ems.domain.vo.SiteBatteryDataList;
import com.xzzn.ems.service.IEmsSiteService; import com.xzzn.ems.service.IEmsSiteService;
import com.xzzn.ems.service.IEmsStatsReportService; import com.xzzn.ems.service.IEmsStatsReportService;
import com.xzzn.ems.service.ISingleSiteService; import com.xzzn.ems.service.ISingleSiteService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List; import java.util.List;
/** /**
@ -51,27 +56,27 @@ public class EmsSiteMonitorController extends BaseController{
* 单站监控-设备监控-实时运行曲线图数据 * 单站监控-设备监控-实时运行曲线图数据
*/ */
@GetMapping("/runningGraph/storagePower") @GetMapping("/runningGraph/storagePower")
public AjaxResult getRunningGraphStorage(@RequestParam String siteId) public AjaxResult getRunningGraphStorage(RunningGraphRequest request)
{ {
return success(iSingleSiteService.getRunningGraphStorage(siteId)); return success(iSingleSiteService.getRunningGraphStorage(request));
} }
@GetMapping("/runningGraph/stackAveTemp") @GetMapping("/runningGraph/pcsMaxTemp")
public AjaxResult getRunningGraphStackTemp(@RequestParam String siteId) public AjaxResult getRunningGraphPcsMaxTemp(RunningGraphRequest request)
{ {
return success(iSingleSiteService.getRunningGraphStackTemp(siteId)); return success(iSingleSiteService.getRunningGraphPcsMaxTemp(request));
} }
@GetMapping("/runningGraph/batteryAveSoc") @GetMapping("/runningGraph/batteryAveSoc")
public AjaxResult getRunningGraphBatterySoc(@RequestParam String siteId) public AjaxResult getRunningGraphBatterySoc(RunningGraphRequest request)
{ {
return success(iSingleSiteService.getRunningGraphBatterySoc(siteId)); return success(iSingleSiteService.getRunningGraphBatterySoc(request));
} }
@GetMapping("/runningGraph/batteryAveTemp") @GetMapping("/runningGraph/batteryAveTemp")
public AjaxResult getRunningGraphBatteryTemp(@RequestParam String siteId) public AjaxResult getRunningGraphBatteryTemp(RunningGraphRequest request)
{ {
return success(iSingleSiteService.getRunningGraphBatteryTemp(siteId)); return success(iSingleSiteService.getRunningGraphBatteryTemp(request));
} }
/** /**
@ -141,11 +146,27 @@ public class EmsSiteMonitorController extends BaseController{
* 获取电池簇下面的单体电池数据 * 获取电池簇下面的单体电池数据
*/ */
@GetMapping("/getClusterDataInfoList") @GetMapping("/getClusterDataInfoList")
public TableDataInfo getClusterDataInfoList(@RequestParam String clusterDeviceId,@RequestParam String siteId) public TableDataInfo getClusterDataInfoList(@RequestParam String clusterDeviceId,@RequestParam String siteId,
@RequestParam String stackDeviceId,@RequestParam String batteryId)
{ {
startPage(); startPage();
List<BatteryDataStatsListVo> list = iSingleSiteService.getClusterDataInfoList(clusterDeviceId,siteId); SiteBatteryDataList siteBatteryDataList = new SiteBatteryDataList();
return getDataTable2(list); // 簇最大最小单体id数据
List<BMSBatteryDataList> clusterBatteryDataList = iSingleSiteService.getClusterBatteryList(siteId,stackDeviceId,clusterDeviceId);
siteBatteryDataList.setClusterList(clusterBatteryDataList);
// 单体电池数据
List<BatteryDataStatsListVo> List = iSingleSiteService.getClusterDataInfoList(clusterDeviceId,siteId,stackDeviceId,batteryId);
// 对batteryList进行分页处理
List<BatteryDataStatsListVo> batteryList = paginateList(List);
siteBatteryDataList.setBatteryList(batteryList);
// 封装分页信息
TableDataInfo pageInfo = new TableDataInfo();
pageInfo.setTotal(List.size());
pageInfo.setRows(Arrays.asList(siteBatteryDataList));
pageInfo.setCode(0);
pageInfo.setMsg("查询成功");
return pageInfo;
} }
/** /**
@ -165,4 +186,46 @@ public class EmsSiteMonitorController extends BaseController{
{ {
return success(iSingleSiteService.getAmmeterDataList(siteId)); return success(iSingleSiteService.getAmmeterDataList(siteId));
} }
/**
* 动环数据
*/
@GetMapping("/getDhDataList")
public AjaxResult getDhDataList(@RequestParam String siteId)
{
return success(iSingleSiteService.getDhDataList(siteId));
}
/**
* 消防数据
*/
@GetMapping("/getXfDataList")
public AjaxResult getXfDataList(@RequestParam String siteId)
{
return success(iSingleSiteService.getXfDataList(siteId));
}
/**
* EMS数据
*/
@GetMapping("/getEmsDataList")
public AjaxResult getEmsDataList(@RequestParam String siteId)
{
return success(iSingleSiteService.getEmsDataList(siteId));
}
/**
* 单站监控-首页-点位展示
* 储能功率、电网功率、负荷功率、光伏功率
* SOC、SOH、电池平均温度
*/
@GetMapping("/getPointData")
public AjaxResult getPointData(DateSearchRequest requestVo)
{
if (!StringUtils.isEmpty(requestVo.getSiteId())) {
return success(iSingleSiteService.getPointData(requestVo));
} else {
return error("缺少必传项");
}
}
} }

View File

@ -1,22 +1,31 @@
package com.xzzn.web.controller.ems; package com.xzzn.web.controller.ems;
import com.xzzn.common.annotation.Log;
import com.xzzn.common.core.controller.BaseController; import com.xzzn.common.core.controller.BaseController;
import com.xzzn.common.core.domain.AjaxResult; import com.xzzn.common.core.domain.AjaxResult;
import com.xzzn.common.core.page.TableDataInfo; import com.xzzn.common.core.page.TableDataInfo;
import com.xzzn.common.enums.BusinessType;
import com.xzzn.common.utils.StringUtils; import com.xzzn.common.utils.StringUtils;
import com.xzzn.ems.domain.vo.AmmeterRevenueStatisListVo;
import com.xzzn.ems.domain.vo.AmmeterStatisListVo;
import com.xzzn.ems.domain.vo.ClusterStatisListVo; import com.xzzn.ems.domain.vo.ClusterStatisListVo;
import com.xzzn.ems.domain.vo.DateSearchRequest; import com.xzzn.ems.domain.vo.DateSearchRequest;
import com.xzzn.ems.domain.vo.StatisAmmeterDateRequest; import com.xzzn.ems.domain.vo.StatisAmmeterDateRequest;
import com.xzzn.ems.domain.vo.StatisClusterDateRequest; import com.xzzn.ems.domain.vo.StatisClusterDateRequest;
import com.xzzn.ems.service.IEmsStatsReportService; import com.xzzn.ems.service.IEmsStatsReportService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/** /**
* 单站监控-统计报表 * 单站监控-统计报表
* *
@ -58,8 +67,7 @@ public class EmsStatisticalReportController extends BaseController
@GetMapping("/getPCSData") @GetMapping("/getPCSData")
public AjaxResult getPCSData(DateSearchRequest requestVo) public AjaxResult getPCSData(DateSearchRequest requestVo)
{ {
if (!StringUtils.isEmpty(requestVo.getSiteId()) && if (!StringUtils.isEmpty(requestVo.getSiteId())&&
!StringUtils.isEmpty(requestVo.getDeviceId()) &&
!StringUtils.isEmpty(requestVo.getDataType())) { !StringUtils.isEmpty(requestVo.getDataType())) {
return success(ieEmsStatsReportService.getPCSDataResult(requestVo)); return success(ieEmsStatsReportService.getPCSDataResult(requestVo));
} else { } else {
@ -74,7 +82,6 @@ public class EmsStatisticalReportController extends BaseController
public AjaxResult getStackData(DateSearchRequest requestVo) public AjaxResult getStackData(DateSearchRequest requestVo)
{ {
if (!StringUtils.isEmpty(requestVo.getSiteId()) && if (!StringUtils.isEmpty(requestVo.getSiteId()) &&
!StringUtils.isEmpty(requestVo.getDeviceId()) &&
!StringUtils.isEmpty(requestVo.getDataType())) { !StringUtils.isEmpty(requestVo.getDataType())) {
return success(ieEmsStatsReportService.getStackDataResult(requestVo)); return success(ieEmsStatsReportService.getStackDataResult(requestVo));
} else { } else {
@ -100,7 +107,7 @@ public class EmsStatisticalReportController extends BaseController
} }
/** /**
* 概率统计-获取 * 概率统计-获取站点下所有电
*/ */
@GetMapping("/getLoadNameList") @GetMapping("/getLoadNameList")
public AjaxResult getLoadNameList(String siteId) public AjaxResult getLoadNameList(String siteId)
@ -116,13 +123,44 @@ public class EmsStatisticalReportController extends BaseController
* 概率统计-电表报表 * 概率统计-电表报表
*/ */
@GetMapping("/getAmmeterData") @GetMapping("/getAmmeterData")
public AjaxResult getAmmeterData(StatisAmmeterDateRequest requestVo) public TableDataInfo getAmmeterData(StatisAmmeterDateRequest requestVo)
{ {
if (!StringUtils.isEmpty(requestVo.getDeviceId())) { startPage();
return success(ieEmsStatsReportService.getAmmeterDataResult(requestVo)); List<AmmeterStatisListVo> dataList = ieEmsStatsReportService.getAmmeterDataResult(requestVo);
} else { return getDataTable(dataList);
return error("缺少必传项"); }
}
/**
* 导出电表报表
*/
@PreAuthorize("@ss.hasPermi('system:ammeterData:export')")
@Log(title = "电表报表", businessType = BusinessType.EXPORT)
@PostMapping("/exportAmmeterData")
public void exportAmmeterData(HttpServletResponse response, StatisAmmeterDateRequest requestVo)
{
ieEmsStatsReportService.exportAmmeterData(response, requestVo);
}
/**
* 概率统计-电表收益报表
*/
@GetMapping("/getAmmeterRevenueData")
public TableDataInfo getAmmeterRevenueData(StatisAmmeterDateRequest requestVo)
{
startPage();
List<AmmeterRevenueStatisListVo> dataList = ieEmsStatsReportService.getAmmeterRevenueDataResult(requestVo);
return getDataTable(dataList);
}
/**
* 导出收益报表
*/
@PreAuthorize("@ss.hasPermi('system:ammeterRevenueData:export')")
@Log(title = "收益报表", businessType = BusinessType.EXPORT)
@PostMapping("/exportAmmeterRevenueData")
public void exportAmmeterRevenueData(HttpServletResponse response, StatisAmmeterDateRequest requestVo)
{
ieEmsStatsReportService.exportAmmeterRevenueData(response, requestVo);
} }
/** /**
@ -131,9 +169,7 @@ public class EmsStatisticalReportController extends BaseController
@GetMapping("/getPowerData") @GetMapping("/getPowerData")
public AjaxResult getPowerData(DateSearchRequest requestVo) public AjaxResult getPowerData(DateSearchRequest requestVo)
{ {
if (!StringUtils.isEmpty(requestVo.getSiteId()) if (!StringUtils.isEmpty(requestVo.getSiteId())) {
&& !StringUtils.isEmpty(requestVo.getDeviceId())
&& !StringUtils.isEmpty(requestVo.getDataType())) {
return success(ieEmsStatsReportService.getPowerDataList(requestVo)); return success(ieEmsStatsReportService.getPowerDataList(requestVo));
} else { } else {
return error("缺少必传项"); return error("缺少必传项");

View File

@ -1,27 +1,39 @@
package com.xzzn.web.controller.ems; package com.xzzn.web.controller.ems;
import com.xzzn.ems.domain.EmsMqttMessage; import com.xzzn.common.constant.RedisKeyConstants;
import com.xzzn.common.core.redis.RedisCache;
import com.xzzn.common.enums.TopicHandleType;
import com.xzzn.common.utils.StringUtils;
import com.xzzn.ems.domain.EmsMqttTopicConfig;
import com.xzzn.ems.mapper.EmsMqttTopicConfigMapper;
import com.xzzn.ems.service.IDDSDataProcessService; import com.xzzn.ems.service.IDDSDataProcessService;
import com.xzzn.ems.service.IDeviceDataProcessService;
import com.xzzn.ems.service.IEmsMqttMessageService; import com.xzzn.ems.service.IEmsMqttMessageService;
import com.xzzn.ems.service.IEmsStrategyService;
import com.xzzn.ems.service.IFXXAlarmDataProcessService;
import com.xzzn.ems.service.IFXXDataProcessService; import com.xzzn.ems.service.IFXXDataProcessService;
import com.xzzn.ems.service.IMqttSyncLogService;
import com.xzzn.framework.manager.MqttLifecycleManager; import com.xzzn.framework.manager.MqttLifecycleManager;
import com.xzzn.framework.web.service.MqttPublisher; import com.xzzn.framework.web.service.MqttPublisher;
import com.xzzn.framework.web.service.MqttSubscriber; import com.xzzn.framework.web.service.MqttSubscriber;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory; import java.util.List;
import javax.annotation.PostConstruct;
import org.apache.commons.collections4.CollectionUtils;
import org.eclipse.paho.client.mqttv3.IMqttMessageListener; import org.eclipse.paho.client.mqttv3.IMqttMessageListener;
import org.eclipse.paho.client.mqttv3.MqttException; import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage; import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
@Service @Service
public class MqttMessageController implements MqttPublisher, MqttSubscriber { public class MqttMessageController implements MqttPublisher, MqttSubscriber {
private static final Logger log = LoggerFactory.getLogger(MqttMessageController.class);
private static final Log log = LogFactory.getLog(MqttMessageController.class);
private final MqttLifecycleManager mqttLifecycleManager; private final MqttLifecycleManager mqttLifecycleManager;
@ -34,6 +46,22 @@ public class MqttMessageController implements MqttPublisher, MqttSubscriber {
@Autowired @Autowired
private IDDSDataProcessService dDSDataProcessService; private IDDSDataProcessService dDSDataProcessService;
@Autowired
private IDeviceDataProcessService deviceDataProcessService;
@Autowired
private IFXXAlarmDataProcessService fXXAlarmDataProcessService;
@Autowired
private EmsMqttTopicConfigMapper emsMqttTopicConfigMapper;
@Autowired
private IEmsStrategyService emsStrategyService;
@Autowired
private IMqttSyncLogService iMqttSyncLogService;
@Autowired
private RedisCache redisCache;
@Autowired @Autowired
public MqttMessageController(MqttLifecycleManager mqttLifecycleManager) { public MqttMessageController(MqttLifecycleManager mqttLifecycleManager) {
this.mqttLifecycleManager = mqttLifecycleManager; this.mqttLifecycleManager = mqttLifecycleManager;
@ -41,20 +69,69 @@ public class MqttMessageController implements MqttPublisher, MqttSubscriber {
@PostConstruct @PostConstruct
public void init() { public void init() {
List<EmsMqttTopicConfig> topicConfigList = emsMqttTopicConfigMapper.selectEmsMqttTopicConfigList(null);
if (CollectionUtils.isEmpty (topicConfigList)) {
log.info ("未查询到任何 MQTT 主题配置,跳过订阅");
return;
}
for (EmsMqttTopicConfig topicConfig : topicConfigList) {
String topic = topicConfig.getMqttTopic();
if (StringUtils.isEmpty(topic)) {
log.info ("主题配置 ID:" +topicConfig.getId() +"的 mqttTopic 为空,跳过订阅");
continue;
}
int qos = topicConfig.getQos() == null ? 1 : topicConfig.getQos();
if (qos < 0 || qos > 2) {
log.info ("主题:" + topic +"的 QoS值"+ qos + "不合法自动调整为1");
qos = 1;
}
IMqttMessageListener listener = getMqttListenerByTopic(topic, topicConfig.getHandleType());
subscribe(topic, qos, listener);
}
// 订阅奉贤系统状态主题 // 订阅奉贤系统状态主题
subscribe("021_FXX_01_UP", 1, this::handleDeviceData); /*subscribe("021_FXX_01_UP", 1, this::handleDeviceData);
subscribe("021_FXX_01_RECALL", 1, this::handleDeviceData); subscribe("021_FXX_01_RECALL", 1, this::handleDeviceData);
subscribe("021_FXX_01_DOWN", 1, this::handleDeviceData); subscribe("021_FXX_01_DOWN", 1, this::handleDeviceData);
subscribe("021_FXX_01", 1, this::handleSystemStatus); subscribe("021_FXX_01", 1, this::handleSystemStatus);
subscribe("021_FXX_01_ALARM_UP", 1, this::handleAlarmData);
// 订阅电动所系统状态主题 // 订阅电动所系统状态主题
subscribe("021_DDS_01_UP", 1, this::handleDeviceData); subscribe("021_DDS_01_UP", 1, this::handleDeviceData);
subscribe("021_DDS_01_RECALL", 1, this::handleDeviceData); subscribe("021_DDS_01_RECALL", 1, this::handleDeviceData);
subscribe("021_DDS_01_DOWN", 1, this::handleDeviceData); subscribe("021_DDS_01_DOWN", 1, this::handleDeviceData);
subscribe("021_DDS_01", 1, this::handleSystemStatus); subscribe("021_DDS_01", 1, this::handleSystemStatus);*/
} }
private IMqttMessageListener getMqttListenerByTopic(String topic, String handleType) {
TopicHandleType topicHandleType = TopicHandleType.getEnumByCode(handleType);
switch (topicHandleType) {
case DEVICE:
return this::handleDeviceData;
case DEVICE_ALARM:
return this::handleAlarmData;
case STRATEGY:
if (topic.equals("FAULT_PROTECTION_PLAN_UP")) {
return this::handleFaultProtPlanData;
} else if (topic.equals("FAULT_ALARM_RECORD_UP")) {
return this::handleFaultAlarmData;
} else if (topic.equals("FAULT_PLAN_ISSUE_UP")) {
return this::handleFaultPlanIssueData;
} else if (topic.equals("DEVICE_CHANGE_LOG_UP")) {
return this::handleDeviceChangeLogData;
} else {
return this::handleStrategyData;
}
default:
log.warn("Unknown handle type: " + handleType + ", using default handler");
return this::handleSystemStatus;
}
}
// 处理系统状态消息 // 处理系统状态消息
private void handleSystemStatus(String topic, MqttMessage message) { private void handleSystemStatus(String topic, MqttMessage message) {
String payload = new String(message.getPayload()); String payload = new String(message.getPayload());
@ -70,21 +147,122 @@ public class MqttMessageController implements MqttPublisher, MqttSubscriber {
// 处理设备数据 // 处理设备数据
private void handleDeviceData(String topic, MqttMessage message) { private void handleDeviceData(String topic, MqttMessage message) {
String payload = new String(message.getPayload());
log.error("[DEVICE] data: " + payload);
try {
// 业务处理逻辑
// if (topic.startsWith("021_DDS")) {
// dDSDataProcessService.handleDdsData(payload);
// } else if (topic.startsWith("021_FXX")) {
// fXXDataProcessService.handleFxData(payload);
// }
deviceDataProcessService.handleDeviceData(payload, getSiteIdByTopic(topic));
emsMqttMessageService.insertMqttOriginalMessage(topic, payload);
} catch (Exception e) {
log.error("Failed to process device data message: " + e.getMessage(), e);
}
}
// 处理告警数据
private void handleAlarmData(String topic, MqttMessage message) {
String payload = new String(message.getPayload()); String payload = new String(message.getPayload());
System.out.println("[DEVICE] data: " + payload); System.out.println("[DEVICE] data: " + payload);
try { try {
// 业务处理逻辑 // 业务处理逻辑
if (topic.startsWith("021_DDS")) { // if (topic.startsWith("021_FXX")) {
dDSDataProcessService.handleDdsData(payload); // fXXAlarmDataProcessService.handleFxAlarmData(payload);
} else if (topic.startsWith("021_FXX")) { // }
fXXDataProcessService.handleFxData(payload); deviceDataProcessService.handleAlarmData(payload, getSiteIdByTopic(topic));
}
emsMqttMessageService.insertMqttOriginalMessage(topic, payload);
} catch (Exception e) {
e.printStackTrace();
log.error("Failed to process device alarm data message: " + e.getMessage(), e);
}
}
private String getSiteIdByTopic(String topic) {
String siteId = redisCache.getCacheObject(RedisKeyConstants.SITE_ID + topic);
if (StringUtils.isEmpty(siteId)) {
EmsMqttTopicConfig topicConfig = emsMqttTopicConfigMapper.selectOneByTopic(topic);
siteId = topicConfig.getSiteId();
redisCache.setCacheObject(RedisKeyConstants.SITE_ID + topic, siteId);
}
log.info("当前处理数据站点:" + siteId + ",topic: " + topic);
return siteId;
}
// 处理运行策略数据:云端-本地
private void handleStrategyData(String topic, MqttMessage message) {
String payload = new String(message.getPayload());
System.out.println("[处理运行策略数据] data: " + payload);
try {
// 业务处理逻辑
iMqttSyncLogService.handleMqttStrategyData(payload);
emsMqttMessageService.insertMqttOriginalMessage(topic,payload); emsMqttMessageService.insertMqttOriginalMessage(topic,payload);
} catch (Exception e) { } catch (Exception e) {
log.error("Failed to process system status message: " + e.getMessage(), e); log.error("Failed to process strategy data message: " + e.getMessage(), e);
} }
}
// 处理设备保护告警策略数据:云端-本地
private void handleFaultProtPlanData(String topic, MqttMessage message) {
String payload = new String(message.getPayload());
System.out.println("[处理设备保护告警策略数据] data: " + payload);
try {
// 业务处理逻辑
iMqttSyncLogService.handleMqttPlanData(payload);
emsMqttMessageService.insertMqttOriginalMessage(topic,payload);
} catch (Exception e) {
log.error("Failed to process fault plan data message: " + e.getMessage(), e);
}
}
// 处理保护策略告警信息:本地-云端
private void handleFaultAlarmData(String topic, MqttMessage message) {
String payload = new String(message.getPayload());
System.out.println("[处理本地保护策略告警信息到云端] data: " + payload);
try {
// 业务处理逻辑
iMqttSyncLogService.handleFaultAlarmData(payload);
emsMqttMessageService.insertMqttOriginalMessage(topic,payload);
} catch (Exception e) {
log.error("Failed to process fault plan alarm data message: " + e.getMessage(), e);
}
}
// 处理保护策略下发日志:本地-云端
private void handleFaultPlanIssueData(String topic, MqttMessage message) {
String payload = new String(message.getPayload());
System.out.println("[处理本地保护策略下发日志到云端] data: " + payload);
try {
// 业务处理逻辑
iMqttSyncLogService.handleFaultPlanIssueData(payload);
emsMqttMessageService.insertMqttOriginalMessage(topic,payload);
} catch (Exception e) {
log.error("Failed to process fault plan issue log message: " + e.getMessage(), e);
}
}
// 处理设备状态变更日志:本地-云端
private void handleDeviceChangeLogData(String topic, MqttMessage message) {
String payload = new String(message.getPayload());
System.out.println("[处理本地的保护策略告警信息到云端] data: " + payload);
try {
// 业务处理逻辑
iMqttSyncLogService.handleDeviceChangeLogData(payload);
emsMqttMessageService.insertMqttOriginalMessage(topic,payload);
} catch (Exception e) {
log.error("Failed to process device change log message: " + e.getMessage(), e);
}
} }
@Override @Override
@ -114,6 +292,4 @@ public class MqttMessageController implements MqttPublisher, MqttSubscriber {
} }
} }
} }

View File

@ -1,61 +1,61 @@
# 数据源配置 ## 数据源配置
spring: #spring:
datasource: # datasource:
type: com.alibaba.druid.pool.DruidDataSource # type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver # driverClassName: com.mysql.cj.jdbc.Driver
druid: # druid:
# 主库数据源 # # 主库数据源
master: # master:
url: jdbc:mysql://122.51.194.184:13306/setri_ems?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 # url: jdbc:mysql://122.51.194.184:13306/setri_ems?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: ems # username: ems
password: 12345678 # password: 12345678
# 从库数据源 # # 从库数据源
slave: # slave:
# 从数据源开关/默认关闭 # # 从数据源开关/默认关闭
enabled: false # enabled: false
url: # url:
username: # username:
password: # password:
# 初始连接数 # # 初始连接数
initialSize: 5 # initialSize: 5
# 最小连接池数量 # # 最小连接池数量
minIdle: 10 # minIdle: 10
# 最大连接池数量 # # 最大连接池数量
maxActive: 20 # maxActive: 20
# 配置获取连接等待超时的时间 # # 配置获取连接等待超时的时间
maxWait: 60000 # maxWait: 60000
# 配置连接超时时间 # # 配置连接超时时间
connectTimeout: 30000 # connectTimeout: 30000
# 配置网络超时时间 # # 配置网络超时时间
socketTimeout: 60000 # socketTimeout: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 # # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000 # timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒 # # 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000 # minEvictableIdleTimeMillis: 300000
# 配置一个连接在池中最大生存的时间,单位是毫秒 # # 配置一个连接在池中最大生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000 # maxEvictableIdleTimeMillis: 900000
# 配置检测连接是否有效 # # 配置检测连接是否有效
validationQuery: SELECT 1 FROM DUAL # validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true # testWhileIdle: true
testOnBorrow: false # testOnBorrow: false
testOnReturn: false # testOnReturn: false
webStatFilter: # webStatFilter:
enabled: true # enabled: true
statViewServlet: # statViewServlet:
enabled: true # enabled: true
# 设置白名单,不填则允许所有访问 # # 设置白名单,不填则允许所有访问
allow: # allow:
url-pattern: /druid/* # url-pattern: /druid/*
# 控制台管理用户名和密码 # # 控制台管理用户名和密码
login-username: xzzn # login-username: xzzn
login-password: 123456 # login-password: 123456
filter: # filter:
stat: # stat:
enabled: true # enabled: true
# 慢SQL记录 # # 慢SQL记录
log-slow-sql: true # log-slow-sql: true
slow-sql-millis: 1000 # slow-sql-millis: 1000
merge-sql: true # merge-sql: true
wall: # wall:
config: # config:
multi-statement-allow: true # multi-statement-allow: true

View File

@ -0,0 +1,207 @@
# 项目相关配置
xzzn:
# 名称
name: EMS
# 版本
version: 0.0.1
# 版权年份
copyrightYear: 2025
# 文件路径 示例( Windows配置D:/xzzn/uploadPathLinux配置 /home/xzzn/uploadPath
profile: ../uploadPath
# 获取ip地址开关
addressEnabled: false
# 验证码类型 math 数字计算 char 字符验证
captchaType: math
# 开发环境配置
server:
# 服务器的HTTP端口默认为8080
port: 8089
servlet:
# 应用的访问路径
context-path: /
tomcat:
# tomcat的URI编码
uri-encoding: UTF-8
# 连接数满后的排队数默认为100
accept-count: 1000
threads:
# tomcat最大线程数默认为200
max: 800
# Tomcat启动初始化的线程数默认值10
min-spare: 100
# 日志配置
logging:
level:
com.xzzn: info
org.springframework: warn
# 用户配置
user:
password:
# 密码最大错误次数
maxRetryCount: 5
# 密码锁定时间默认10分钟
lockTime: 10
# Spring配置
spring:
# 资源信息
messages:
# 国际化资源文件路径
basename: i18n/messages
# 文件上传
servlet:
multipart:
# 单个文件大小
max-file-size: 10MB
# 设置总上传的文件大小
max-request-size: 20MB
# 服务模块
devtools:
restart:
# 热部署开关
enabled: true
# redis 配置
redis:
# 地址
host: 127.0.0.1
# 端口默认为6379
port: 6379
# 数据库索引
database: 0
# 密码
password: 12345678
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池中的最小空闲连接
min-idle: 0
# 连接池中的最大空闲连接
max-idle: 8
# 连接池的最大数据库连接数
max-active: 8
# #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# 数据源配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
# 主库数据源
master:
url: jdbc:mysql://127.0.0.1:3306/setri_ems?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: ems
password: Aa112211!
# 从库数据源
slave:
# 从数据源开关/默认关闭
enabled: false
url:
username:
password:
# 初始连接数
initialSize: 5
# 最小连接池数量
minIdle: 10
# 最大连接池数量
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置连接超时时间
connectTimeout: 30000
# 配置网络超时时间
socketTimeout: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
# 配置一个连接在池中最大生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000
# 配置检测连接是否有效
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
webStatFilter:
enabled: true
statViewServlet:
enabled: true
# 设置白名单,不填则允许所有访问
allow:
url-pattern: /druid/*
# 控制台管理用户名和密码
login-username: xzzn
login-password: 123456
filter:
stat:
enabled: true
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
# token配置
token:
# 令牌自定义标识
header: Authorization
# 令牌密钥
secret: abcdefghijklmnopqrstuvwxyz
# 令牌有效期默认30分钟
expireTime: 30
# MyBatis配置
mybatis:
# 搜索指定包别名
typeAliasesPackage: com.xzzn.**.domain
# 配置mapper的扫描找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml
# 加载全局的配置文件
configLocation: classpath:mybatis/mybatis-config.xml
# PageHelper分页插件
pagehelper:
helperDialect: mysql
supportMethodsArguments: true
params: count=countSql
# Swagger配置
swagger:
# 是否开启swagger
enabled: true
# 请求前缀
pathMapping: /dev-api
# 防止XSS攻击
xss:
# 过滤开关
enabled: true
# 排除链接(多个用逗号分隔)
excludes: /system/notice
# 匹配链接
urlPatterns: /system/*,/monitor/*,/tool/*
mqtt:
broker.url: tcp://121.5.164.6:1883
client.id: ems-cloud
username: dmbroker
password: qwer1234
connection-timeout: 15
keep-alive-interval: 30
automatic-reconnect: true
topic: 021_DDS_01_UP
siteId: 021_DDS_01
modbus:
pool:
max-total: 20
max-idle: 10
min-idle: 3
poll:
interval: "0 */5 * * * *" # 5分钟间隔
timeout: 30000 # 30秒超时

View File

@ -0,0 +1,207 @@
# 项目相关配置
xzzn:
# 名称
name: EMS
# 版本
version: 0.0.1
# 版权年份
copyrightYear: 2025
# 文件路径 示例( Windows配置D:/xzzn/uploadPathLinux配置 /home/xzzn/uploadPath
profile: /home/xzzn/uploadPath
# 获取ip地址开关
addressEnabled: false
# 验证码类型 math 数字计算 char 字符验证
captchaType: math
# 开发环境配置
server:
# 服务器的HTTP端口默认为8080
port: 8089
servlet:
# 应用的访问路径
context-path: /
tomcat:
# tomcat的URI编码
uri-encoding: UTF-8
# 连接数满后的排队数默认为100
accept-count: 1000
threads:
# tomcat最大线程数默认为200
max: 800
# Tomcat启动初始化的线程数默认值10
min-spare: 100
# 日志配置
logging:
level:
com.xzzn: info
org.springframework: warn
# 用户配置
user:
password:
# 密码最大错误次数
maxRetryCount: 5
# 密码锁定时间默认10分钟
lockTime: 10
# Spring配置
spring:
# 资源信息
messages:
# 国际化资源文件路径
basename: i18n/messages
# 文件上传
servlet:
multipart:
# 单个文件大小
max-file-size: 10MB
# 设置总上传的文件大小
max-request-size: 20MB
# 服务模块
devtools:
restart:
# 热部署开关
enabled: true
# redis 配置
redis:
# 地址
host: 172.17.0.9
# 端口默认为6379
port: 6379
# 数据库索引
database: 0
# 密码
password: 12345678
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池中的最小空闲连接
min-idle: 0
# 连接池中的最大空闲连接
max-idle: 8
# 连接池的最大数据库连接数
max-active: 8
# #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# 数据源配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
# 主库数据源
master:
url: jdbc:mysql://172.17.0.13:3306/setri_ems?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: ems
password: Aa112211!
# 从库数据源
slave:
# 从数据源开关/默认关闭
enabled: false
url:
username:
password:
# 初始连接数
initialSize: 5
# 最小连接池数量
minIdle: 10
# 最大连接池数量
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置连接超时时间
connectTimeout: 30000
# 配置网络超时时间
socketTimeout: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
# 配置一个连接在池中最大生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000
# 配置检测连接是否有效
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
webStatFilter:
enabled: true
statViewServlet:
enabled: true
# 设置白名单,不填则允许所有访问
allow:
url-pattern: /druid/*
# 控制台管理用户名和密码
login-username: xzzn
login-password: 123456
filter:
stat:
enabled: true
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
# token配置
token:
# 令牌自定义标识
header: Authorization
# 令牌密钥
secret: abcdefghijklmnopqrstuvwxyz
# 令牌有效期默认30分钟
expireTime: 30
# MyBatis配置
mybatis:
# 搜索指定包别名
typeAliasesPackage: com.xzzn.**.domain
# 配置mapper的扫描找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml
# 加载全局的配置文件
configLocation: classpath:mybatis/mybatis-config.xml
# PageHelper分页插件
pagehelper:
helperDialect: mysql
supportMethodsArguments: true
params: count=countSql
# Swagger配置
swagger:
# 是否开启swagger
enabled: true
# 请求前缀
pathMapping: /dev-api
# 防止XSS攻击
xss:
# 过滤开关
enabled: true
# 排除链接(多个用逗号分隔)
excludes: /system/notice
# 匹配链接
urlPatterns: /system/*,/monitor/*,/tool/*
mqtt:
broker.url: tcp://121.5.164.6:1883
client.id: ems-cloud
username: dmbroker
password: qwer1234
connection-timeout: 15
keep-alive-interval: 30
automatic-reconnect: true
topic:
siteId:
modbus:
pool:
max-total: 20
max-idle: 10
min-idle: 3
poll:
interval: "0 */5 * * * *" # 5分钟间隔
timeout: 30000 # 30秒超时

View File

@ -87,6 +87,66 @@ spring:
max-active: 8 max-active: 8
# #连接池最大阻塞等待时间(使用负值表示没有限制) # #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms max-wait: -1ms
# 数据源配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
# 主库数据源
master:
url: jdbc:mysql://122.51.194.184:13306/setri_ems?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: ems
password: 12345678
# 从库数据源
slave:
# 从数据源开关/默认关闭
enabled: false
url:
username:
password:
# 初始连接数
initialSize: 5
# 最小连接池数量
minIdle: 10
# 最大连接池数量
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置连接超时时间
connectTimeout: 30000
# 配置网络超时时间
socketTimeout: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
# 配置一个连接在池中最大生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000
# 配置检测连接是否有效
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
webStatFilter:
enabled: true
statViewServlet:
enabled: true
# 设置白名单,不填则允许所有访问
allow:
url-pattern: /druid/*
# 控制台管理用户名和密码
login-username: xzzn
login-password: 123456
filter:
stat:
enabled: true
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
# token配置 # token配置
token: token:
@ -136,6 +196,8 @@ mqtt:
connection-timeout: 15 connection-timeout: 15
keep-alive-interval: 30 keep-alive-interval: 30
automatic-reconnect: true automatic-reconnect: true
topic:
siteId:
modbus: modbus:
pool: pool:

View File

@ -119,6 +119,12 @@
<artifactId>javax.servlet-api</artifactId> <artifactId>javax.servlet-api</artifactId>
</dependency> </dependency>
<!-- modbus4j (保留兼容) -->
<dependency>
<groupId>com.infiniteautomation</groupId>
<artifactId>modbus4j</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -0,0 +1,11 @@
package com.xzzn.common.annotation;
import java.lang.annotation.*;
/**
* 标记调用insert后需要同步数据到其他服务器
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SyncAfterInsert {
}

View File

@ -170,4 +170,9 @@ public class Constants
*/ */
public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml", public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
"org.springframework", "org.apache", "com.xzzn.common.utils.file", "com.xzzn.common.config", "com.xzzn.generator" }; "org.springframework", "org.apache", "com.xzzn.common.utils.file", "com.xzzn.common.config", "com.xzzn.generator" };
/**
* 昨日充放电最晚数据有效期
*/
public static final Integer DATE_VALID_TIME = 1;
} }

View File

@ -42,6 +42,11 @@ public class RedisKeyConstants
*/ */
public static final String DH = "DH_"; public static final String DH = "DH_";
/**
* 消防数据 redis key
*/
public static final String XF = "XF_";
/** /**
* 电池组 redis key * 电池组 redis key
*/ */
@ -51,4 +56,70 @@ public class RedisKeyConstants
* BMSD原始数据 redis key * BMSD原始数据 redis key
*/ */
public static final String ORIGINAL_BMSD = "BMSD_"; public static final String ORIGINAL_BMSD = "BMSD_";
/**
* 冷却数据 redis key
*/
public static final String COOLING = "COOLING_";
/**
* EMS数据 redis key
*/
public static final String EMS = "EMS_";
/**
* 点位匹配数据 redis key
*/
public static final String POINT_MATCH = "POINT_MATCH_";
/**
* 点位枚举匹配数据 redis key
*/
public static final String POINT_ENUM_MATCH = "POINT_ENUM_MATCH_";
/**
* topic对应站点ID redis key
*/
public static final String SITE_ID = "SITE_ID_";
/**
* 存放单个设备同步过来的原始数据-最晚一次数据
*/
public static final String ORIGINAL_MQTT_DATA = "MQTT_";
/**
* 存放单个设备同步过来的告警点位原始数据-最晚一次数据
*/
public static final String ORIGINAL_MQTT_DATA_ALARM = "MQTT_ALARM_";
/** 存放订阅失败告警信息 */
public static final String TOPIC_FAILED_ALRAM_RECORD = "topic_failed_";
/** topic 内没有数据设备维度告警 */
public static final String TOPIC_EMPTY_ALARM_RECORD = "topic_empty_";
/** modbus读取 没有数据设备维度告警 */
public static final String MODBUS_EMPTY_ALARM_RECORD = "modbus_empty_";
/** modbus读取 设备离线告警 */
public static final String MODBUS_OFFLINE_ALARM_RECORD = "modbus_offline_";
/** 设备信息初始化 */
public static final String INIT_DEVICE_INFO = "init_device_info";
/** 告警匹配信息 */
public static final String ALARM_MATCH_INFO = "alarm_message_info";
/** 现有的告警数据 */
public static final String LATEST_ALARM_RECORD = "LATEST_ALARM_RECORD";
/** 预存电价时间配置 */
public static final String ENERGY_PRICE_TIME = "energy_price_time_";
/** dds昨日累计总收益 */
public static final String DDS_TOTAL_REVENUE = "total_revenue_";
/** fx实时总收益和当日实时收益 */
public static final String FXX_REALTIME_REVENUE = "realtime_revenue_";
/** 每个设备最新数据-设置失效时间-判断是否正常同步数据 */
public static final String SYNC_DATA= "SYNC_DATA_";
/** 每个设备最新数据-设置失效时间-判断是否正常同步数据 */
public static final String SYNC_DATA_ALARM = "SYNC_DATA_ALARM_";
} }

View File

@ -208,28 +208,69 @@ public class BaseController
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({ "rawtypes", "unchecked" })
protected TableDataInfo getDataTable2(List<?> list) protected TableDataInfo getDataTable2(List<?> list)
{ {
List<?> subList = new ArrayList<>(); // 1. 处理原列表为null的情况避免空指针
// 分页梳理 List<?> targetList = (list == null) ? Collections.emptyList() : list;
List<?> subList;
// 2. 获取分页参数
PageDomain pageDomain = TableSupport.buildPageRequest(); PageDomain pageDomain = TableSupport.buildPageRequest();
int pageNum = pageDomain.getPageNum(); int pageNum = pageDomain.getPageNum();
int pageSize = pageDomain.getPageSize(); int pageSize = pageDomain.getPageSize();
// 3. 判断分页参数是否有效pageNum和pageSize均为正数时才分页
if (pageNum > 0 && pageSize > 0) { if (pageNum > 0 && pageSize > 0) {
// 计算分页起始和结束索引 // 计算起始索引确保不小于0
int startIndex = (pageNum - 1) * pageSize; int startIndex = Math.max((pageNum - 1) * pageSize, 0);
int endIndex = Math.min(startIndex + pageSize, list.size());
// 防止越界 // 关键修复:若起始索引已超出列表大小,直接返回空列表
if (startIndex >= list.size()) { if (startIndex >= targetList.size()) {
subList = Collections.emptyList(); subList = Collections.emptyList();
} else {
// 计算结束索引(不超过列表大小,且不小于起始索引)
int endIndex = Math.min(startIndex + pageSize, targetList.size());
endIndex = Math.max(endIndex, startIndex); // 防止endIndex < startIndex
// 截取子列表转换为新ArrayList避免视图依赖
subList = new ArrayList<>(targetList.subList(startIndex, endIndex));
} }
// 截取当前页数据 } else {
subList = list.subList(startIndex, endIndex); // 分页参数无效时,返回全部数据
subList = new ArrayList<>(targetList);
} }
// 4. 封装返回结果
TableDataInfo rspData = new TableDataInfo(); TableDataInfo rspData = new TableDataInfo();
rspData.setCode(HttpStatus.SUCCESS); rspData.setCode(HttpStatus.SUCCESS);
rspData.setMsg("查询成功"); rspData.setMsg("查询成功");
rspData.setRows(subList); rspData.setRows(subList);
rspData.setTotal(list.size()); rspData.setTotal(targetList.size());
return rspData; return rspData;
} }
/**
* 通用分页工具方法
*/
protected <T> List<T> paginateList(List<T> sourceList) {
if (sourceList == null || sourceList.isEmpty()) {
return new ArrayList<>();
}
// 分页梳理
PageDomain pageDomain = TableSupport.buildPageRequest();
int pageNum = pageDomain.getPageNum();
int pageSize = pageDomain.getPageSize();
int startIndex = 0;
int endIndex = sourceList.size();
if (pageNum > 0 && pageSize > 0) {
// 计算起始索引处理页码小于1的情况
startIndex = Math.max((pageNum - 1) * pageSize, 0);
// 计算结束索引(处理超出列表长度的情况)
endIndex = Math.min(startIndex + pageSize, sourceList.size());
}
// 防止越界
if (startIndex >= sourceList.size()) {
return Collections.emptyList();
}
// 截取分页数据
return sourceList.subList(startIndex, endIndex);
}
} }

View File

@ -92,6 +92,8 @@ public class SysUser extends BaseEntity
/** 角色ID */ /** 角色ID */
private Long roleId; private Long roleId;
private String belongSite;
public SysUser() public SysUser()
{ {
@ -310,6 +312,14 @@ public class SysUser extends BaseEntity
this.roleId = roleId; this.roleId = roleId;
} }
public String getBelongSite() {
return belongSite;
}
public void setBelongSite(String belongSite) {
this.belongSite = belongSite;
}
@Override @Override
public String toString() { public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
@ -332,7 +342,7 @@ public class SysUser extends BaseEntity
.append("updateBy", getUpdateBy()) .append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime()) .append("updateTime", getUpdateTime())
.append("remark", getRemark()) .append("remark", getRemark())
.append("dept", getDept()) .append("belongSite", getBelongSite())
.toString(); .toString();
} }
} }

View File

@ -0,0 +1,111 @@
package com.xzzn.common.core.modbus;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.ip.IpParameters;
import com.xzzn.common.core.modbus.domain.DeviceConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Modbus连接管理器
* 使用长连接模式,维护连接缓存,复用已有连接
*/
@Component
public class Modbus4jConnectionManager {
private static final Logger logger = LoggerFactory.getLogger(Modbus4jConnectionManager.class);
private final ModbusFactory modbusFactory = new ModbusFactory();
private final Map<String, ModbusMaster> connectionCache = new ConcurrentHashMap<>();
/**
* 获取或创建连接(长连接模式)
*/
public ModbusMaster borrowMaster(DeviceConfig config) throws Exception {
String key = buildConnectionKey(config);
ModbusMaster master = connectionCache.get(key);
if (master == null) {
synchronized (this) {
master = connectionCache.get(key);
if (master == null) {
master = createNewConnection(config);
connectionCache.put(key, master);
logger.info("创建新Modbus长连接: {}:{}", config.getHost(), config.getPort());
}
}
}
return master;
}
/**
* 归还连接(长连接模式,不关闭)
*/
public void returnMaster(DeviceConfig config, ModbusMaster master) {
}
/**
* 废弃连接(发生异常时关闭并移除)
*/
public void invalidateMaster(DeviceConfig config, ModbusMaster master) {
String key = buildConnectionKey(config);
connectionCache.remove(key);
destroyMaster(master, config);
logger.warn("废弃并移除Modbus连接: {}:{}", config.getHost(), config.getPort());
}
private ModbusMaster createNewConnection(DeviceConfig config) throws Exception {
IpParameters params = new IpParameters();
params.setHost(config.getHost());
params.setPort(config.getPort());
params.setEncapsulated(false);
ModbusMaster master = modbusFactory.createTcpMaster(params, true);
master.init();
return master;
}
private void destroyMaster(ModbusMaster master, DeviceConfig config) {
if (master != null) {
try {
master.destroy();
logger.debug("已关闭Modbus连接: {}:{}", config.getHost(), config.getPort());
} catch (Exception e) {
logger.warn("关闭Modbus连接异常: {}:{}", config.getHost(), config.getPort(), e);
}
}
}
private String buildConnectionKey(DeviceConfig config) {
return config.getHost() + ":" + config.getPort();
}
/**
* 关闭所有连接
*/
public void closeAllConnections() {
for (Map.Entry<String, ModbusMaster> entry : connectionCache.entrySet()) {
try {
if (entry.getValue() != null) {
entry.getValue().destroy();
}
} catch (Exception e) {
logger.warn("关闭Modbus连接异常: {}", entry.getKey(), e);
}
}
connectionCache.clear();
logger.info("已关闭所有Modbus连接");
}
}

View File

@ -0,0 +1,486 @@
package com.xzzn.common.core.modbus;
import com.alibaba.fastjson2.JSON;
import com.serotonin.modbus4j.BatchRead;
import com.serotonin.modbus4j.BatchResults;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.code.DataType;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.locator.BaseLocator;
import com.serotonin.modbus4j.msg.WriteCoilRequest;
import com.serotonin.modbus4j.msg.WriteCoilResponse;
import com.serotonin.modbus4j.msg.WriteCoilsRequest;
import com.serotonin.modbus4j.msg.WriteCoilsResponse;
import com.serotonin.modbus4j.msg.WriteRegisterRequest;
import com.serotonin.modbus4j.msg.WriteRegisterResponse;
import com.serotonin.modbus4j.msg.WriteRegistersRequest;
import com.serotonin.modbus4j.msg.WriteRegistersResponse;
import com.xzzn.common.core.modbus.domain.DeviceConfig;
import com.xzzn.common.core.modbus.domain.TagConfig;
import com.xzzn.common.core.modbus.domain.WriteTagConfig;
import com.xzzn.common.core.redis.RedisCache;
import com.xzzn.common.enums.ModBusType;
import com.xzzn.common.enums.RegisterType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import static com.xzzn.common.enums.RegisterType.COIL;
import static com.xzzn.common.enums.RegisterType.DISCRETE_INPUT;
@Service
public class ModbusProcessor {
private static final Logger logger = LoggerFactory.getLogger(ModbusProcessor.class);
@Value("${modbus.read-timeout:8000}")
private int readTimeout;
@Value("${modbus.write-timeout:5000}")
private int writeTimeout;
@Value("${modbus.read-retries:1}")
private int readRetries;
@Value("${modbus.write-retries:1}")
private int writeRetries;
@Autowired
private RedisCache redisCache;
@Autowired
private Modbus4jConnectionManager connectionManager;
public boolean writeDataToDevice(DeviceConfig config) {
logger.info("writeDataToDevice: {}", JSON.toJSONString(config));
ModbusMaster master = null;
boolean result = false;
boolean hasError = false;
try {
master = connectionManager.borrowMaster(config);
master.setTimeout(writeTimeout);
master.setRetries(0);
result = writeTagValue(master, config, config.getWriteTags());
} catch (Exception e) {
logger.error("Failed to borrow connection or write to devices '{}'", config.getDeviceName(), e);
hasError = true;
}
finally {
if (master != null) {
if (hasError) {
// 发生异常时废弃连接,下次重新创建
connectionManager.invalidateMaster(config, master);
} else {
// 正常时归还连接
connectionManager.returnMaster(config, master);
}
}
}
return result;
}
public boolean writeDataToDeviceWithRetry(DeviceConfig config) {
logger.info("writeDataToDevice: {}", JSON.toJSONString(config));
ModbusMaster master = null;
boolean result;
try {
master = connectionManager.borrowMaster(config);
master.setTimeout(writeTimeout); // 设置超时时间
master.setRetries(0);
// 使用重试装饰器
ModbusMaster finalMaster = master;
result = RetryableModbusOperation.executeWithRetry(() -> {
return writeTagValue(finalMaster, config, config.getWriteTags());
}, writeRetries); // 最大重试次数由配置控制
} catch (Exception e) {
logger.error("Failed to borrow connection or write to devices '{}'", config.getDeviceName(), e);
result = false;
} finally {
if (master != null) {
connectionManager.returnMaster(config, master);
}
}
return result;
}
public boolean writeTagValue(ModbusMaster master, DeviceConfig config, List<WriteTagConfig> tags) {
tags.forEach(tag -> {
Map<Integer, RegisterType> type = ModBusType.REGISTER_TYPE;
int firstDigit = Integer.parseInt(tag.getAddress().substring(0, 1));
int address = convertAddress(tag.getAddress());
RegisterType registerType = type.get(firstDigit);
logger.info("Register type: {}, address: {}, firstDigit: {}", registerType, tag.getAddress(), firstDigit);
switch (registerType) {
case COIL: {
boolean result = writeCoilRequest(master, config.getSlaveId(), address, Boolean.parseBoolean(String.valueOf(tag.getValue())));
tag.setWrite(result);
break;
}
case HOLDING_REGISTER: {
Number value = Double.parseDouble(String.valueOf(tag.getValue()));
double doubleValue = value.doubleValue();
// 检查是否在16位有符号整数范围内
if (doubleValue < -32768 || doubleValue > 32767) {
logger.warn("Value {} out of range for 16-bit signed register at address {}", doubleValue, address);
}
boolean result = writeRegisterRequest(master, config.getSlaveId(), address, (int) doubleValue);
tag.setWrite(result);
break;
}
default:
logger.error("Unsupported register type: {}", registerType);
break;
}
});
List<WriteTagConfig> collect = tags.stream().filter(tag -> !Objects.equals(tag.isWrite(), true)).collect(Collectors.toList());
if (!collect.isEmpty()) {
return false;
}
return true;
}
public static boolean writeCoilRequest(ModbusMaster master, int slaveId, int address, boolean value) {
try {
WriteCoilRequest request = new WriteCoilRequest(slaveId, address, value);
WriteCoilResponse response = (WriteCoilResponse)master.send(request);
if (response.isException()) {
logger.info("Write coil failed: " + response.getExceptionMessage());
} else {
logger.info("Write coil successful");
return true;
}
} catch (Exception e) {
logger.error("Failed to write coil value '{}' to address '{}'", value, address, e);
}
return false;
}
public static void writeCoilsRequest(ModbusMaster master, int slaveId, int address, boolean[] values) {
try {
WriteCoilsRequest request = new WriteCoilsRequest(slaveId, address, values);
WriteCoilsResponse response = (WriteCoilsResponse)master.send(request);
if (response.isException()) {
logger.info("Write coils failed: " + response.getExceptionMessage());
} else {
logger.info("Write coils successful");
}
} catch (Exception e) {
logger.error("Failed to write coils value '{}' to address '{}'", values, address, e);
}
}
public static boolean writeRegisterRequest(ModbusMaster master, int slaveId, int address, int value) {
try {
WriteRegisterRequest request = new WriteRegisterRequest(slaveId, address, value);
WriteRegisterResponse response = (WriteRegisterResponse)master.send(request);
if (response.isException()) {
logger.info("Write register failed: " + response.getExceptionMessage());
} else {
logger.info("Write register successful");
return true;
}
} catch (Exception e) {
logger.error("Failed to write register value '{}' to address '{}'", value, address, e);
}
return false;
}
public static void writeRegistersRequest(ModbusMaster master, int slaveId, int address, short[] values) {
try {
WriteRegistersRequest request = new WriteRegistersRequest(slaveId, address, values);
WriteRegistersResponse response = (WriteRegistersResponse)master.send(request);
if (response.isException()) {
logger.info("Write registers failed: " + response.getExceptionMessage());
} else {
logger.info("Write registers successful");
}
} catch (Exception e) {
logger.error("Failed to write registers value '{}' to address '{}'", values, address, e);
}
}
public static void setCoilValue(ModbusMaster master, int slaveId, int address, int value) {
// 写入单个线圈(从站ID, 地址, 布尔值)
BaseLocator<Boolean> coilLocator = BaseLocator.coilStatus(slaveId, address);
try {
// 写入布尔值到线圈
master.setValue(coilLocator, value);
logger.info("set coil successful");
} catch (ModbusTransportException | ErrorResponseException e) {
logger.error("Failed to set coil value '{}' to address '{}'", value, address, e);
}
}
public static void setRegisterValue(ModbusMaster master, int slaveId, int address, int value) {
// 写入单个保持寄存器(从站, 地址, 16位整数)
BaseLocator<Number> holdingLocator = BaseLocator.holdingRegister(slaveId, address, DataType.TWO_BYTE_INT_SIGNED);
try {
// 写入整数值到保持寄存器
master.setValue(holdingLocator, value);
logger.info("set register successful");
} catch (ModbusTransportException | ErrorResponseException e) {
logger.error("Failed to set register value '{}' to address '{}'", value, address, e);
}
}
public ModbusMaster borrowMaster(DeviceConfig config) throws Exception {
ModbusMaster master = connectionManager.borrowMaster(config);
// 设置了Modbus通信的超时时间为5000毫秒5秒。当主设备与从设备通信时若在5秒内未收到响应则认为通信超时并抛出异常。这有助于避免长时间等待无响应的设备。
master.setTimeout(readTimeout);
master.setRetries(readRetries);
return master;
}
public Map<String, Object> readDataFromDevice(DeviceConfig config, ModbusMaster master) {
Map<String, Object> deviceData = new HashMap<>();
boolean hasError = false;
try {
BatchResults<String> results = readTagValues(master, config.getSlaveId(), config.getTags());
for (TagConfig tag : config.getTags()) {
if (Objects.equals(tag.getDataType(), "FOUR_BYTE_FLOAT_DBCA")){
Object value = results.getValue(tag.getKey());
value = convertValueToFloat(value);
deviceData.put(tag.getKey(), value);
}else {
deviceData.put(tag.getKey(), results.getValue(tag.getKey()));
}
}
} catch (Exception e) {
logger.error("Failed read from devices '{}'", config.getDeviceName(), e);
hasError = true;
}
finally {
if (master != null) {
if (hasError) {
// 发生异常时废弃连接,下次重新创建
connectionManager.invalidateMaster(config, master);
} else {
// 正常时归还连接
connectionManager.returnMaster(config, master);
}
}
}
return deviceData;
}
private Object readTagValue(ModbusMaster master, int slaveId, TagConfig tag) throws Exception {
Object value;
Map<Integer, RegisterType> type = ModBusType.REGISTER_TYPE;
Map<String, Integer> DATA_LENGTH = ModBusType.LENGTH;
int firstDigit = Integer.parseInt(tag.getAddress().substring(0, 1));
int address = 0;
int addressLength = tag.getAddress().length();
int exp = (int) Math.pow(10, addressLength-1);
if (firstDigit != 0){
int digit = Integer.parseInt(tag.getAddress());
address = digit % (exp);
}else {
address = Integer.parseInt(tag.getAddress());
}
RegisterType registerType = type.get(firstDigit);
int dataLength = DATA_LENGTH.get(tag.getDataType());
// 增加配置校验和警告
if ((Objects.equals(registerType, COIL) || Objects.equals(registerType, DISCRETE_INPUT))) {
logger.warn("Configuration warning for tag '{}': ModBusType is {} but DataType is {}. DataType should be BOOLEAN. Proceeding with BOOLEAN read.",
tag.getKey(), registerType, tag.getDataType());
}
try {
switch (registerType) {
case COIL: {
BaseLocator<Boolean> loc = BaseLocator.coilStatus(slaveId, address);
value = master.getValue(loc);
logger.info("读取线圈{}成功: {}",tag.getAddress(), value);
break;
}
case DISCRETE_INPUT: {
BaseLocator<Boolean> loc = BaseLocator.inputStatus(slaveId, address);
value = master.getValue(loc);
logger.info("读取输入{}成功: {}",tag.getAddress(), value);
break;
}
case HOLDING_REGISTER: {
if (dataLength == 28){
BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, address, 4);
value = master.getValue(locator);
//将 value 转换为二进制数据
value = convertValueToFloat(value);
}else{
BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, address, dataLength);
value = master.getValue(locator);
}
logger.info("读取保持寄存器{}成功: {}",tag.getAddress(), value);
break;
}
case INPUT_REGISTER: {
BaseLocator<Number> locator = BaseLocator.inputRegister(slaveId, address, dataLength);
value= master.getValue(locator);
logger.info("读取输入寄存器{}成功: {}",tag.getAddress(), value);
break;
}
default:
logger.error("Unsupported register type: {}", registerType);
value = null;
break;
}
}catch (Exception e){
logger.error("Failed to read tag '{}'", tag.getKey(), e);
value = null;
}
return value;
}
private BatchResults<String> readTagValues(ModbusMaster master, int slaveId, List<TagConfig> tags) throws Exception {
try {
BatchResults<String> results = sendBatchRead(master, slaveId, tags);
logBatchResults(tags, results);
return results;
} catch (Exception e){
if (isTimeoutException(e)) {
logger.warn("批量读取超时尝试降级为单点读取slaveId: {}", slaveId);
BatchResults<String> fallback = new BatchResults<>();
for (TagConfig tag : tags) {
Object value = readTagValue(master, slaveId, tag);
fallback.addResult(tag.getKey(), value);
}
logBatchResults(tags, fallback);
return fallback;
}
logger.error("Failed to read master '{}'", slaveId, e);
throw new Exception(e);
}
}
private BatchResults<String> sendBatchRead(ModbusMaster master, int slaveId, List<TagConfig> tags) throws Exception {
BatchRead<String> batch = new BatchRead<>();
tags.forEach(tag -> {
Map<Integer, RegisterType> type = ModBusType.REGISTER_TYPE;
Map<String, Integer> DATA_LENGTH = ModBusType.LENGTH;
int firstDigit = Integer.parseInt(tag.getAddress().substring(0, 1));
int address = 0;
int addressLength = tag.getAddress().length();
int exp = (int) Math.pow(10, addressLength - 1);
if (firstDigit != 0) {
int digit = Integer.parseInt(tag.getAddress());
address = digit % (exp);
} else {
address = Integer.parseInt(tag.getAddress());
}
RegisterType registerType = type.get(firstDigit);
int dataLength = DATA_LENGTH.get(tag.getDataType());
switch (registerType) {
case COIL: {
BaseLocator<Boolean> loc = BaseLocator.coilStatus(slaveId, address);
batch.addLocator(tag.getKey(), loc);
break;
}
case DISCRETE_INPUT: {
BaseLocator<Boolean> loc = BaseLocator.inputStatus(slaveId, address);
batch.addLocator(tag.getKey(), loc);
break;
}
case HOLDING_REGISTER: {
if (dataLength == 28) {
BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, address, 4);
batch.addLocator(tag.getKey(), locator);
} else {
BaseLocator<Number> loc = BaseLocator.holdingRegister(slaveId, address, dataLength);
batch.addLocator(tag.getKey(), loc);
}
break;
}
case INPUT_REGISTER: {
BaseLocator<Number> loc = BaseLocator.inputRegister(slaveId, address, dataLength);
batch.addLocator(tag.getKey(), loc);
break;
}
}
});
return master.send(batch);
}
private void logBatchResults(List<TagConfig> tags, BatchResults<String> results) {
List<String> logInfoList = new ArrayList<>();
for (TagConfig tag : tags) {
StringBuilder logInfo = new StringBuilder();
logInfo.append(tag.getAddress());
if (tag.getBit() != null) {
logInfo.append("(").append(tag.getBit()).append("):");
} else {
logInfo.append(":");
}
logInfo.append(results.getValue(tag.getKey()));
logInfoList.add(logInfo.toString());
}
logger.info("批处理读取寄存器成功: {}", JSON.toJSONString(logInfoList));
}
private boolean isTimeoutException(Throwable e) {
Throwable current = e;
while (current != null) {
if (current instanceof com.serotonin.modbus4j.sero.messaging.TimeoutException) {
return true;
}
current = current.getCause();
}
return false;
}
public static float convertValueToFloat(Object value) {
if (!(value instanceof Number)) {
throw new IllegalArgumentException("Input must be a Number");
}
int intValue = ((Number) value).intValue();
String binaryData = Integer.toBinaryString(intValue);
// 补足32位二进制字符串
if (binaryData.length() < 32) {
binaryData = String.format("%32s", binaryData).replace(' ', '0');
}
// 分割为4个字节并重组为 dcba 格式
byte[] reorderedBytes = new byte[4];
for (int i = 0; i < 4; i++) {
int start = i * 8;
String byteStr = binaryData.substring(start, start + 8);
reorderedBytes[3 - i] = (byte) Integer.parseInt(byteStr, 2);
}
// 使用 ByteBuffer 提高可读性和安全性
return Float.intBitsToFloat(
java.nio.ByteBuffer.wrap(reorderedBytes).getInt()
);
}
/** 转换寄存器地址 */
public static int convertAddress(String address) {
if (StringUtils.isBlank(address)) {
return 0;
}
int firstDigit = Integer.parseInt(address.substring(0, 1));
int addressLength = address.length();
int exp = (int) Math.pow(10, addressLength-1);
if (firstDigit != 0){
int digit = Integer.parseInt(address);
return digit % (exp);
} else {
return Integer.parseInt(address);
}
}
}

View File

@ -0,0 +1,58 @@
package com.xzzn.common.core.modbus;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.ip.IpParameters;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
public class PooledModbusMasterFactory extends BasePooledObjectFactory<ModbusMaster> {
private final String host;
private final int port;
public PooledModbusMasterFactory(String host, int port) {
this.host = host;
this.port = port;
}
@Override
public ModbusMaster create() throws Exception {
IpParameters params = new IpParameters();
params.setHost(this.host);
params.setPort(this.port);
params.setEncapsulated(false);
ModbusMaster master = new ModbusFactory().createTcpMaster(params, true);
master.init();
return master;
}
@Override
public PooledObject<ModbusMaster> wrap(ModbusMaster master) {
return new DefaultPooledObject<>(master);
}
@Override
public void destroyObject(PooledObject<ModbusMaster> p) throws Exception {
if (p.getObject() != null) {
p.getObject().destroy();
}
super.destroyObject(p);
}
@Override
public boolean validateObject(PooledObject<ModbusMaster> p) {
// testConnectivity() 方法在 modbus4j 中不存在。
// 一个真正的验证需要执行一次读操作,但这需要 slaveId而工厂是通用的不知道 slaveId。
// 因此,我们使用一个较弱的验证,只检查 master 对象是否已初始化。
// 这无法检测到被远程设备断开的连接,错误将在实际读取操作中被捕获。
if (p.getObject() == null) {
return false;
}
return p.getObject().isInitialized();
}
}

View File

@ -0,0 +1,37 @@
package com.xzzn.common.core.modbus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Modbus重试
*
*/
public class RetryableModbusOperation {
private static final Logger logger = LoggerFactory.getLogger(RetryableModbusOperation.class);
public interface ModbusOperation<T> {
T execute() throws Exception;
}
public static <T> T executeWithRetry(ModbusOperation<T> operation, int maxRetries) {
int retryCount = 0;
Exception lastException = null;
while (retryCount <= maxRetries) {
try {
return operation.execute();
} catch (Exception e) {
lastException = e;
retryCount++;
if (retryCount <= maxRetries) {
logger.info("Operation failed, retrying... (Attempt {})", retryCount);
}
}
}
logger.error("Max retries ({}) reached. Last error: {}", maxRetries,
lastException != null ? lastException.getMessage() : "Unknown");
throw new RuntimeException("Operation failed after " + maxRetries + " retries", lastException);
}
}

View File

@ -0,0 +1,106 @@
package com.xzzn.common.core.modbus.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "modbus")
public class ModbusProperties {
private PoolConfig pool = new PoolConfig();
private TimeoutConfig timeout = new TimeoutConfig();
public PoolConfig getPool() {
return pool;
}
public void setPool(PoolConfig pool) {
this.pool = pool;
}
public TimeoutConfig getTimeout() {
return timeout;
}
public void setTimeout(TimeoutConfig timeout) {
this.timeout = timeout;
}
public static class PoolConfig {
private int maxTotal = 20;
private int maxIdle = 10;
private int minIdle = 3;
private long maxWait = 5000;
private long timeBetweenEvictionRuns = 30000;
private long minEvictableIdleTime = 60000;
public int getMaxTotal() {
return maxTotal;
}
public void setMaxTotal(int maxTotal) {
this.maxTotal = maxTotal;
}
public int getMaxIdle() {
return maxIdle;
}
public void setMaxIdle(int maxIdle) {
this.maxIdle = maxIdle;
}
public int getMinIdle() {
return minIdle;
}
public void setMinIdle(int minIdle) {
this.minIdle = minIdle;
}
public long getMaxWait() {
return maxWait;
}
public void setMaxWait(long maxWait) {
this.maxWait = maxWait;
}
public long getTimeBetweenEvictionRuns() {
return timeBetweenEvictionRuns;
}
public void setTimeBetweenEvictionRuns(long timeBetweenEvictionRuns) {
this.timeBetweenEvictionRuns = timeBetweenEvictionRuns;
}
public long getMinEvictableIdleTime() {
return minEvictableIdleTime;
}
public void setMinEvictableIdleTime(long minEvictableIdleTime) {
this.minEvictableIdleTime = minEvictableIdleTime;
}
}
public static class TimeoutConfig {
private int read = 10000;
private int write = 5000;
public int getRead() {
return read;
}
public void setRead(int read) {
this.read = read;
}
public int getWrite() {
return write;
}
public void setWrite(int write) {
this.write = write;
}
}
}

View File

@ -0,0 +1,91 @@
package com.xzzn.common.core.modbus.domain;
import java.util.List;
public class DeviceConfig {
private String deviceNumber;
private String deviceName;
private String host;
private int port;
private long time;
private int slaveId;
private boolean enabled;
private List<TagConfig> tags;
private List<WriteTagConfig> writeTags;
public String getDeviceNumber() {
return deviceNumber;
}
public void setDeviceNumber(String deviceNumber) {
this.deviceNumber = deviceNumber;
}
public String getDeviceName() {
return deviceName;
}
public void setDeviceName(String deviceName) {
this.deviceName = deviceName;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public int getSlaveId() {
return slaveId;
}
public void setSlaveId(int slaveId) {
this.slaveId = slaveId;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public List<TagConfig> getTags() {
return tags;
}
public void setTags(List<TagConfig> tags) {
this.tags = tags;
}
public List<WriteTagConfig> getWriteTags() {
return writeTags;
}
public void setWriteTags(List<WriteTagConfig> writeTags) {
this.writeTags = writeTags;
}
}

View File

@ -0,0 +1,78 @@
package com.xzzn.common.core.modbus.domain;
public class TagConfig {
private String key;
private String des;
private String address;
private String dataType;
private float a;
private float k;
private float b;
private Integer bit;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getDataType() {
return dataType;
}
public void setDataType(String dataType) {
this.dataType = dataType;
}
public float getA() {
return a;
}
public void setA(float a) {
this.a = a;
}
public float getK() {
return k;
}
public void setK(float k) {
this.k = k;
}
public float getB() {
return b;
}
public void setB(float b) {
this.b = b;
}
public Integer getBit() {
return bit;
}
public void setBit(Integer bit) {
this.bit = bit;
}
}

View File

@ -0,0 +1,31 @@
package com.xzzn.common.core.modbus.domain;
public class WriteTagConfig {
private Object value;
private String address;
private boolean isWrite;
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public boolean isWrite() {
return isWrite;
}
public void setWrite(boolean write) {
isWrite = write;
}
}

View File

@ -7,10 +7,8 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations; import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
@ -243,6 +241,18 @@ public class RedisCache
return redisTemplate.opsForHash().multiGet(key, hKeys); return redisTemplate.opsForHash().multiGet(key, hKeys);
} }
/**
* 删除Hash中的多条数据
*
* @param key Redis键
* @param hKey Hash键
* @return 是否成功
*/
public boolean deleteAllCacheMapValue(final String key, final Object[] hKey)
{
return redisTemplate.opsForHash().delete(key, hKey) > 0;
}
/** /**
* 删除Hash中的某条数据 * 删除Hash中的某条数据
* *
@ -255,6 +265,18 @@ public class RedisCache
return redisTemplate.opsForHash().delete(key, hKey) > 0; return redisTemplate.opsForHash().delete(key, hKey) > 0;
} }
/**
* 批量往Hash中存入数据
*
* @param key Redis键
* @param value 值
*/
public <T> void setAllCacheMapValue(final String key, final Map<String, T> value)
{
redisTemplate.opsForHash().putAll(key, value);
}
/** /**
* 获得缓存的基本对象列表 * 获得缓存的基本对象列表
* *
@ -276,4 +298,78 @@ public class RedisCache
{ {
return redisTemplate.delete(key); return redisTemplate.delete(key);
} }
/**
* 批量缓存
* @param cacheMap
*/
public <T> void multiSet(final Map<String, String> cacheMap)
{
redisTemplate.opsForValue().multiSet(cacheMap);
}
/**
* 批量设置键值对,并为每个键设置相同的过期时间
* @param keyValueMap 键值对集合
* @param timeout 过期时间
* @param unit 时间单位
*/
public void multiSetWithExpire(Map<String, String> keyValueMap, long timeout, TimeUnit unit) {
if (keyValueMap.isEmpty()) {
return; // 空集合直接返回
}
// 使用Redis管道批量执行命令
redisTemplate.executePipelined(new SessionCallback<Object>() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
// 循环处理每个键值对
for (Map.Entry<String, String> entry : keyValueMap.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
// 1. 设置键值对
operations.opsForValue().set(key, value);
// 2. 设置过期时间
operations.expire(key, timeout, unit);
}
return null; // 管道操作无需返回值
}
});
}
/**
* 删除指定站点的所有历史测试数据缓存(最精准)
* @param testSiteId 测试数据所属的站点ID如"test-site-001"
*/
public void deleteTestCacheBySite(String testSiteId) {
System.out.println("开删除缓存redis" + testSiteId + "<UNK>");
if (testSiteId == null || testSiteId.isEmpty()) {
return;
}
// 构造键前缀测试站点ID开头的所有缓存键无论粒度和时间
String pattern = testSiteId + "_*";
deleteCacheByPattern(pattern);
}
/**
* 根据键前缀批量删除缓存(核心方法)
* @param pattern 键匹配模式支持Redis的通配符*表示任意字符,?表示单个字符)
*/
public void deleteCacheByPattern(String pattern) {
try {
// 1. 查找所有匹配的键注意keys命令在大数据量时可能阻塞Redis建议生产环境用scan
Set<String> keys = redisTemplate.keys(pattern);
if (keys == null || keys.isEmpty()) {
return;
}
// 2. 批量删除匹配的键
long deleteCount = redisTemplate.delete(keys);
System.out.println("删除缓存数据:" + deleteCount + "<UNK>");
} catch (Exception e) {
}
}
} }

View File

@ -7,8 +7,10 @@ package com.xzzn.common.enums;
*/ */
public enum AmmeterCategory public enum AmmeterCategory
{ {
CURRENT_COMB_ACTIVE("1", "当前组合有功电能"), CURRENT_FORWARD_ACTIVE("1", "当前正向有功电能"),
CURRENT_COMB_REACTIVE("2", "当前组合无功电能"), CURRENT_FORWARD_REACTIVE("2", "当前正向无功电能"),
CURRENT_REVERSE_ACTIVE("3", "当前反向有功电能"),
CURRENT_REVERSE_REACTIVE("4", "当前反向无功电能"),
A_POWER("3", "A相功率"), A_POWER("3", "A相功率"),
B_POWER("4", "B相功率"), B_POWER("4", "B相功率"),
C_POWER("5", "C相功率"); C_POWER("5", "C相功率");

View File

@ -7,7 +7,7 @@ package com.xzzn.common.enums;
*/ */
public enum CommunicationStatus public enum CommunicationStatus
{ {
OK("0", "正常"), SUSPEND("1", "通信中断") ,EXCEPTION("1", "异常"); OK("0", "正常"), SUSPEND("1", "通信中断") ,EXCEPTION("2", "异常");
private final String code; private final String code;
private final String info; private final String info;

View File

@ -0,0 +1,42 @@
package com.xzzn.common.enums;
/**
* 电量类型
*/
public enum CostType
{
PEAK("peak", ""),
HIGH("high", ""),
FLAT("flat", ""),
VALLEY("valley", ""),
;
private final String code;
private final String info;
CostType(String code, String info)
{
this.code = code;
this.info = info;
}
public String getCode()
{
return code;
}
public String getInfo()
{
return info;
}
public static CostType getEnumByCode(String code)
{
for (CostType costType : CostType.values()) {
if (costType.code.equals(code)) {
return costType;
}
}
return null;
}
}

View File

@ -1,5 +1,10 @@
package com.xzzn.common.enums; package com.xzzn.common.enums;
import org.apache.xmlbeans.impl.common.NameUtil;
import java.util.HashMap;
import java.util.Map;
/** /**
* device-设备类别 * device-设备类别
* *
@ -7,21 +12,27 @@ package com.xzzn.common.enums;
*/ */
public enum DeviceCategory public enum DeviceCategory
{ {
PCS("PCS", "PCS设备"), PCS("PCS", "PCS设备", null),
BRANCH("BRANCH", "PCS分支设备"), BRANCH("BRANCH", "PCS分支设备", PCS),
STACK("STACK", "电池堆"), STACK("STACK", "电池堆", null),
CLUSTER("CLUSTER", "电池簇"), CLUSTER("CLUSTER", "电池簇", STACK),
BATTERY("BATTERY", "单体电池"), BATTERY("BATTERY", "单体电池", CLUSTER),
AMMETER("AMMETER", "电表"), AMMETER("AMMETER", "电表", null),
COOLING("COOLING", "液体"); COOLING("COOLING", "", null),
DH("DH", "动环", null),
XF("XF", "消防", null),
BATTERY_GROUP("BATTERY_GROUP", "电池组", null),
EMS("EMS", "EMS设备", null),;
private final String code; private final String code;
private final String info; private final String info;
private final DeviceCategory parentCategory;
DeviceCategory(String code, String info) DeviceCategory(String code, String info, DeviceCategory parentCategory)
{ {
this.code = code; this.code = code;
this.info = info; this.info = info;
this.parentCategory = parentCategory;
} }
public String getCode() public String getCode()
@ -33,4 +44,43 @@ public enum DeviceCategory
{ {
return info; return info;
} }
public DeviceCategory getParentCategory()
{
return parentCategory;
}
// 缓存info与code的映射优化查询效率
private static final Map<String, String> INFO_CODE_MAP = new HashMap<>();
// 静态块初始化缓存
static {
for (DeviceCategory category : DeviceCategory.values()) {
INFO_CODE_MAP.put(category.info, category.code);
}
}
// 通过info获取code的方法
public static String getCodeByInfo(String info) {
return INFO_CODE_MAP.get(info); // 从缓存中直接获取,效率高
}
// 通过code获取父类code
// 根据字符串编码查找对应的枚举
public static DeviceCategory fromCode(String code) {
for (DeviceCategory category : values()) {
if (category.code.equalsIgnoreCase(code)) { // 忽略大小写,增强兼容性
return category;
}
}
return null;
}
public static String getInfoByCode(String code) {
for (DeviceCategory category : DeviceCategory.values()) {
if (category.code.equals(code)) {
return category.info;
}
}
return null;
}
} }

View File

@ -1,18 +1,19 @@
package com.xzzn.common.enums; package com.xzzn.common.enums;
/** /**
* pcs-设备状态 * device-通信状态
* *
* @author xzzn * @author xzzn
*/ */
public enum DeviceStatus public enum DeviceRunningStatus
{ {
ONLINE("0", "在线"), OFFLINE("1", "离线"), UNDER_REPAIR("2", "维修中"); OFFLINE("0", "离线"),
ONLINE("1", "在线");
private final String code; private final String code;
private final String info; private final String info;
DeviceStatus(String code, String info) DeviceRunningStatus(String code, String info)
{ {
this.code = code; this.code = code;
this.info = info; this.info = info;
@ -27,4 +28,5 @@ public enum DeviceStatus
{ {
return info; return info;
} }
} }

View File

@ -0,0 +1,55 @@
package com.xzzn.common.enums;
import com.serotonin.modbus4j.code.DataType;
import java.util.HashMap;
import java.util.Map;
public class ModBusType {
public static final Map<Integer, RegisterType> REGISTER_TYPE = new HashMap<Integer, RegisterType>();
public static final Map<String, Integer> LENGTH = new HashMap<String, Integer>();
static {
REGISTER_TYPE.put(0, RegisterType.COIL);
REGISTER_TYPE.put(1, RegisterType.DISCRETE_INPUT);
REGISTER_TYPE.put(4, RegisterType.HOLDING_REGISTER);
REGISTER_TYPE.put(3, RegisterType.INPUT_REGISTER);
LENGTH.put("BINARY",DataType.BINARY);
LENGTH.put("TWO_BYTE_INT_UNSIGNED",DataType.TWO_BYTE_INT_UNSIGNED);
LENGTH.put("TWO_BYTE_INT_SIGNED",DataType.TWO_BYTE_INT_SIGNED);
LENGTH.put("TWO_BYTE_INT_UNSIGNED_SWAPPED",DataType.TWO_BYTE_INT_UNSIGNED_SWAPPED);
LENGTH.put("TWO_BYTE_INT_SIGNED_SWAPPED",DataType.TWO_BYTE_INT_SIGNED_SWAPPED);
LENGTH.put("FOUR_BYTE_INT_UNSIGNED",DataType.FOUR_BYTE_INT_UNSIGNED);
LENGTH.put("FOUR_BYTE_INT_SIGNED",DataType.FOUR_BYTE_INT_SIGNED);
LENGTH.put("FOUR_BYTE_INT_UNSIGNED_SWAPPED",DataType.FOUR_BYTE_INT_UNSIGNED_SWAPPED);
LENGTH.put("FOUR_BYTE_INT_SIGNED_SWAPPED",DataType.FOUR_BYTE_INT_SIGNED_SWAPPED);
LENGTH.put("FOUR_BYTE_INT_UNSIGNED_SWAPPED_SWAPPED",DataType.FOUR_BYTE_INT_UNSIGNED_SWAPPED_SWAPPED);
LENGTH.put("FOUR_BYTE_INT_SIGNED_SWAPPED_SWAPPED",DataType.FOUR_BYTE_INT_SIGNED_SWAPPED_SWAPPED);
LENGTH.put("FOUR_BYTE_FLOAT",DataType.FOUR_BYTE_FLOAT);
LENGTH.put("FOUR_BYTE_FLOAT_SWAPPED",DataType.FOUR_BYTE_FLOAT_SWAPPED);
LENGTH.put("FOUR_BYTE_FLOAT_SWAPPED_INVERTED",DataType.FOUR_BYTE_FLOAT_SWAPPED_INVERTED);
LENGTH.put("EIGHT_BYTE_INT_UNSIGNED",DataType.EIGHT_BYTE_INT_UNSIGNED);
LENGTH.put("EIGHT_BYTE_INT_SIGNED",DataType.EIGHT_BYTE_INT_SIGNED);
LENGTH.put("EIGHT_BYTE_INT_UNSIGNED_SWAPPED",DataType.EIGHT_BYTE_INT_UNSIGNED_SWAPPED);
LENGTH.put("EIGHT_BYTE_INT_SIGNED_SWAPPED",DataType.EIGHT_BYTE_INT_SIGNED_SWAPPED);
LENGTH.put("EIGHT_BYTE_FLOAT",DataType.EIGHT_BYTE_FLOAT);
LENGTH.put("EIGHT_BYTE_FLOAT_SWAPPED",DataType.EIGHT_BYTE_FLOAT_SWAPPED);
LENGTH.put("TWO_BYTE_BCD",DataType.TWO_BYTE_BCD);
LENGTH.put("FOUR_BYTE_BCD",DataType.FOUR_BYTE_BCD);
LENGTH.put("FOUR_BYTE_BCD_SWAPPED",DataType.FOUR_BYTE_BCD_SWAPPED);
LENGTH.put("CHAR",DataType.CHAR);
LENGTH.put("VARCHAR",DataType.VARCHAR);
LENGTH.put("FOUR_BYTE_FLOAT_DBCA",28);
}
}

View File

@ -0,0 +1,45 @@
package com.xzzn.common.enums;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* 设备点位类型
*/
public enum PointType
{
YES(1, ""),
NO(0, "");
private final Integer code;
private final String info;
private static final Map<Integer, String> CODE_NAME_MAP = new HashMap<>(PointType.values().length);
static {
Arrays.stream(PointType.values()).forEach(record -> {
CODE_NAME_MAP.put(record.code, record.info);
});
}
PointType(Integer code, String info)
{
this.code = code;
this.info = info;
}
public String getInfoByCode(Integer code) {
return CODE_NAME_MAP.get(code);
}
public Integer getCode()
{
return code;
}
public String getInfo()
{
return info;
}
}

View File

@ -0,0 +1,30 @@
package com.xzzn.common.enums;
/**
* 告警保护方案状态
*
* @author xzzn
*/
public enum ProtPlanStatus
{
STOP(0L, "未启用"), RUNNING(1L, "已启用");
private final Long code;
private final String info;
ProtPlanStatus(Long code, String info)
{
this.code = code;
this.info = info;
}
public Long getCode()
{
return code;
}
public String getInfo()
{
return info;
}
}

View File

@ -0,0 +1,8 @@
package com.xzzn.common.enums;
public enum RegisterType {
COIL,
DISCRETE_INPUT,
HOLDING_REGISTER,
INPUT_REGISTER
}

View File

@ -0,0 +1,67 @@
package com.xzzn.common.enums;
/**
*
*/
public enum SiteDevice
{
/**
* 电池堆
*/
BMSD,
/**
* 电池簇
*/
BMSC,
/**
* PCS设备
*/
PCS,
/**
* 电表-总表
*/
LOAD,
/**
* 电表-光伏电表
*/
METEGF,
/**
* 电表-储能电表
*/
METE,
/**
* 电表-储能电表
*/
METE0,
/**
* 动环
*/
donghuan,
/**
* 动环
*/
DH,
/**
* 消防
*/
XF,
/**
* 中水冷却
*/
ZSLQ,
/**
* EMS
*/
EMS
}

View File

@ -0,0 +1,31 @@
package com.xzzn.common.enums;
/**
* device-设备类型
*
* @author xzzn
*/
public enum SiteEnum
{
DDS("021_DDS_01", "电动所内部"), FX("021_FXX_01", "奉贤西部污水处理厂");
private final String code;
private final String info;
SiteEnum(String code, String info)
{
this.code = code;
this.info = info;
}
public String getCode()
{
return code;
}
public String getInfo()
{
return info;
}
}

View File

@ -0,0 +1,32 @@
package com.xzzn.common.enums;
/**
* SOC限制 (%) 1 = 开0 = 关
*
* @author xzzn
*/
public enum SocLimit
{
OFF(0, ""),
ON(1, ""),
;
private final Integer code;
private final String info;
SocLimit(Integer code, String info)
{
this.code = code;
this.info = info;
}
public Integer getCode()
{
return code;
}
public String getInfo()
{
return info;
}
}

View File

@ -7,7 +7,7 @@ package com.xzzn.common.enums;
*/ */
public enum StrategyStatus public enum StrategyStatus
{ {
NOT_ENABLED("0", "未启用"), RUNNING("1", "已运行"),SUSPENDED("2", "已暂停"), DISABLE("1", "禁用"),DELETE("2", "删除"); NOT_ENABLED("0", "未启用"), RUNNING("1", "已运行"),SUSPENDED("2", "已暂停"), DISABLE("3", "禁用"),DELETE("4", "删除");
private final String code; private final String code;
private final String info; private final String info;

View File

@ -0,0 +1,30 @@
package com.xzzn.common.enums;
/**
* ticket - 工单状态
*
* @author xzzn
*/
public enum TicketStatus
{
WAITING("1", "待处理"), PROCESSING("2", "处理中"),DONE("3", "已处理");
private final String code;
private final String info;
TicketStatus(String code, String info)
{
this.code = code;
this.info = info;
}
public String getCode()
{
return code;
}
public String getInfo()
{
return info;
}
}

View File

@ -0,0 +1,39 @@
package com.xzzn.common.enums;
/**
* MQTT主题处理类型未知类型
*/
public enum TopicHandleType {
DEVICE("DEVICE", "设备数据"),
DEVICE_ALARM("DEVICE_ALARM", "设备告警数据"),
STRATEGY("STRATEGY", "策略数据"),
SYSTEM("SYSTEM", "系统数据"),
;
private final String code;
private final String info;
TopicHandleType(String code, String info) {
this.code = code;
this.info = info;
}
public String getName() {
return code;
}
public String getInfo() {
return info;
}
// 根据名称查找枚举
public static TopicHandleType getEnumByCode(String code) {
for (TopicHandleType type : values()) {
if (type.getName().equals(code)) {
return type;
}
}
// 默认返回SYSTEM
return SYSTEM;
}
}

View File

@ -7,7 +7,7 @@ package com.xzzn.common.enums;
*/ */
public enum WorkStatus public enum WorkStatus
{ {
NORMAL("0", "正常"), ABNORMAL("1", "异常"), STOP("2", "停止"); NORMAL("0", "运行"), STOP("1", "停机"), ABNORMAL("2", "故障");
private final String code; private final String code;
private final String info; private final String info;

View File

@ -0,0 +1,33 @@
package com.xzzn.common.enums;
public enum YesOrNo
{
YES(1, "", "Y"),
NO(0, "", "N");
private final Integer code;
private final String info;
private final String value;
YesOrNo(Integer code, String info, String value)
{
this.code = code;
this.info = info;
this.value = value;
}
public Integer getCode()
{
return code;
}
public String getInfo()
{
return info;
}
public String getValue()
{
return value;
}
}

View File

@ -0,0 +1,39 @@
package com.xzzn.common.utils;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DataUtils {
private static final Logger log = LoggerFactory.getLogger(DataUtils.class);
public static String getJSONFromFile(InputStream inputStream) {
BufferedReader bufferReader = null;
StringBuffer stringBuffer = new StringBuffer();
try {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
bufferReader = new BufferedReader(inputStreamReader);
stringBuffer = new StringBuffer();
String lineData = null;
while ((lineData = bufferReader.readLine()) != null) {
stringBuffer.append(lineData);
}
} catch (Exception e) {
log.warn("getJSONFromFile error", e);
} finally {
if (null != bufferReader) {
try {
bufferReader.close();
inputStream.close();
} catch (Exception e) {
log.warn("getJSONFromFile error", e);
}
}
}
return stringBuffer.toString();
}
}

View File

@ -8,9 +8,14 @@ import java.time.LocalDateTime;
import java.time.LocalTime; import java.time.LocalTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.TimeZone;
import org.apache.commons.lang3.time.DateFormatUtils; import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.logging.LogFactory;
/** /**
* 时间工具类 * 时间工具类
@ -19,6 +24,8 @@ import org.apache.commons.lang3.time.DateFormatUtils;
*/ */
public class DateUtils extends org.apache.commons.lang3.time.DateUtils public class DateUtils extends org.apache.commons.lang3.time.DateUtils
{ {
private static final org.apache.commons.logging.Log log = LogFactory.getLog(DateUtils.class);
public static String YYYY = "yyyy"; public static String YYYY = "yyyy";
public static String YYYY_MM = "yyyy-MM"; public static String YYYY_MM = "yyyy-MM";
@ -29,6 +36,13 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
public static String YYYYMMDD = "yyyyMMdd";
public static String YYYY_MM_DD_HH_MM_00 = "yyyy-MM-dd HH:mm:00";
private static final DateTimeFormatter DAY_INPUT_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private static final DateTimeFormatter MIN_HOUR_INPUT_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private static String[] parsePatterns = { private static String[] parsePatterns = {
"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
"yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
@ -145,6 +159,14 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24))); return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24)));
} }
/**
* 计算相差分钟
*/
public static int differentMinutesByMillisecond(Date date1, Date date2)
{
return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 60)));
}
/** /**
* 计算时间差 * 计算时间差
* *
@ -200,4 +222,247 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
long date = calendar.get(Calendar.DAY_OF_MONTH); // 月份从0开始所以要加1 long date = calendar.get(Calendar.DAY_OF_MONTH); // 月份从0开始所以要加1
return date; return date;
} }
// LocalDateTime 转 Date带时区
public static Date convertToDate(LocalDateTime localDateTime) {
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.systemDefault());
return Date.from(zonedDateTime.toInstant());
}
/**
* 获取昨天的日期格式为yyyy-MM-dd 23:59:59
* @return 昨天的日期字符串
*/
public static String getYesterdayDate() {
// 获取今天的日期
LocalDate today = LocalDate.now();
// 减去一天得到昨天
LocalDate yesterday = today.minusDays(1);
// 将日期转换为LocalDateTime并设置时间为23:59:59
LocalDateTime yesterdayEndTime = yesterday.atTime(23, 59, 59);
// 定义日期格式化器
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(YYYY_MM_DD_HH_MM_SS);
// 格式化并返回
return yesterdayEndTime.format(formatter);
}
/**
* 校验传入日期是否今天
* @param dataDate
* @return
*/
public static boolean checkIsToday(String dataDate) {
boolean flag = false;
Calendar calendar = Calendar.getInstance();
int todayMonth = calendar.get(Calendar.MONTH) + 1;
int todayDay = calendar.get(Calendar.DAY_OF_MONTH);
if (StringUtils.isNotEmpty(dataDate)){
String[] pcsDateArray = dataDate.split("-");
if (todayMonth == Integer.parseInt(pcsDateArray[1]) &&
todayDay == Integer.parseInt(pcsDateArray[2])) {
flag = true;
}
}
return flag;
}
/**
* 将时间转换为月份YYYY-MM
*/
public static String formatMonth(LocalDateTime time) {
return time.format(DateTimeFormatter.ofPattern("yyyy-MM"));
}
/**
* 增加 LocalDateTime ==> String
*/
public static String convertToString(LocalDateTime time) {
return time.format(DateTimeFormatter.ofPattern(YYYY_MM_DD_HH_MM_SS));
}
/**
* 将输入时间调整到下一分钟的整点秒数强制为00
* 例如2026-09-03 18:34:49 -> 2026-09-03 18:35:00
* @param timeStr 输入时间字符串支持yyyy-MM-dd HH:mm:ss格式
* @return 调整后的时间字符串yyyy-MM-dd HH:mm:00
* @throws ParseException 时间格式错误时抛出
*/
public static String adjustToNextMinute(String timeStr) throws ParseException {
// 1. 解析原始时间(使用包含秒数的格式)
SimpleDateFormat fullFormat = new SimpleDateFormat(YYYY_MM_DD_HH_MM_SS);
Date originalDate = fullFormat.parse(timeStr);
// 2. 使用Calendar调整时间
Calendar cal = Calendar.getInstance();
cal.setTime(originalDate);
// 3. 如果当前秒数大于0自动进1分钟否则保持当前分钟
if (cal.get(Calendar.SECOND) > 0) {
cal.add(Calendar.MINUTE, 1); // 分钟+1
}
// 4. 强制设置秒数和毫秒为0
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
// 5. 格式化为目标字符串
return new SimpleDateFormat(YYYY_MM_DD_HH_MM_00).format(cal.getTime());
}
/**
* 将"yyyy-MM-dd"字符串转换为Date类型的"yyyy-MM-dd 23:59:59"
*/
public static Date adjustToEndOfDay(String dateStr) {
// 1. 解析字符串为LocalDate仅日期
LocalDate localDate = LocalDate.parse(dateStr, DAY_INPUT_FORMATTER);
// 2. 补充时间为23:59:59转换为LocalDateTime
LocalDateTime localDateTime = localDate.atTime(23, 59, 59);
// 3. 转换为Date类型兼容旧API
return Date.from(
localDateTime.atZone(TimeZone.getDefault().toZoneId()) // 适配系统时区
.toInstant()
);
}
/**
* 将 "yyyy-MM-dd HH:mm:ss" 字符串转换为Date类型的"yyyy-MM-dd HH:00:00"
*/
public static Date adjustToStartOfHour(String inputTime) {
// 1. 解析输入字符串为LocalDateTime包含日期和小时、分钟
LocalDateTime inputLdt = LocalDateTime.parse(inputTime, MIN_HOUR_INPUT_FORMATTER);
// 2. 调整分钟为00秒为00保留日期和小时不变
LocalDateTime adjustedLdt = inputLdt
.withMinute(00) // 强制设为00分
.withSecond(00); // 强制设为00秒
// 3. 转换为Date类型适配旧API
return Date.from(
adjustedLdt.atZone(TimeZone.getDefault().toZoneId()) // 适配系统时区
.toInstant()
);
}
/**
* 将 "yyyy-MM-dd HH:mm:ss" 字符串转换为Date类型的"yyyy-MM-dd HH:mm:00"
*/
public static Date adjustToStartOfMinutes(String inputTime) {
// 1. 解析输入字符串为LocalDateTime包含日期和小时、分钟
LocalDateTime inputLdt = LocalDateTime.parse(inputTime, MIN_HOUR_INPUT_FORMATTER);
// 2. 调整分钟为00秒为00保留日期和小时不变
LocalDateTime adjustedLdt = inputLdt
.withSecond(00); // 强制设为00秒
// 3. 转换为Date类型适配旧API
return Date.from(
adjustedLdt.atZone(TimeZone.getDefault().toZoneId()) // 适配系统时区
.toInstant()
);
}
/**
* 将时间转换为月份YYYY-MM-DD HH:mm:00
*/
public static Date timeConvertToDate(LocalDateTime time) {
String dateStr = convertToString(time);
return dateTime(YYYY_MM_DD_HH_MM_SS,dateStr);
}
/**
* 增加 Date ==> LocalDateTime
*/
public static LocalDateTime toLocalDateTime(Date date)
{
if (date == null) {
return null;
}
// 1. Date → LocalDateTime保留原始时间
LocalDateTime dateTime = date.toInstant()
.atZone(TimeZone.getDefault().toZoneId()) // 关联时区
.toLocalDateTime();
return dateTime;
}
// 时间戳转时间
public static Date convertUpdateTime(Long updateTime) {
if (updateTime == null) {
return null;
}
// 兼容10位秒级和13位毫秒级
int length = String.valueOf(updateTime).length();
if (length == 10) { // 10位秒级 -> 转换为毫秒级
updateTime *= 1000;
} else if (length != 13) { // 既不是10位也不是13位视为非法
System.err.println("时间戳格式错误必须是10位(秒)或13位(毫秒)");
return null;
}
// 3. 转换为Date
return new Date(updateTime);
}
/**
* 获取昨天的日期格式为yyyy-MM-dd
* @return 昨天的日期字符串
*/
public static String getYesterdayDayString() {
// 获取今天的日期
LocalDate today = LocalDate.now();
// 减去一天得到昨天
LocalDate yesterday = today.minusDays(1);
// 定义日期格式化器
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(YYYY_MM_DD);
// 格式化并返回
return yesterday.format(formatter);
}
/**
* 获取日期的开始时间
* @param dateString 格式为yyyy-MM-dd的日期字符串
* @return 日期的开始时间字符串
*/
public static String getDayBeginString(String dateString) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
try {
// 解析日期字符串
LocalDate date = LocalDate.parse(dateString, formatter);
// 创建时间段的开始时间 00:00:00
LocalDateTime startTime = date.atTime(LocalTime.MIN);
return convertToString(startTime);
} catch (DateTimeParseException e) {
log.info("Error parsing date: " + e.getMessage());
}
return dateString + " 00:00:00";
}
/**
* 获取日期的结束时间
* @param dateString 格式为yyyy-MM-dd的日期字符串
* @return 日期的结束时间字符串
*/
public static String getDayEndString(String dateString) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
try {
// 解析日期字符串
LocalDate date = LocalDate.parse(dateString, formatter);
// 创建时间段的结束时间 23:59:59
LocalDateTime endTime = date.atTime(LocalTime.MAX);
return convertToString(endTime);
} catch (DateTimeParseException e) {
log.info("Error parsing date: " + e.getMessage());
}
return dateString + " 23:59:59";
}
} }

View File

@ -0,0 +1,44 @@
package com.xzzn.common.utils;
import java.util.Map;
public class MapUtils {
/**
* 从Map中获取值并转为Integer适配tinyint字段
* @param map 数据源Map
* @param key 字段名
* @return 转换后的Integer默认返回0可根据业务调整默认值
*/
public static Integer getInteger(Map<String, Object> map, String key) {
// 1. 处理Map为null或key不存在的情况
if (map == null || !map.containsKey(key)) {
return 0;
}
// 2. 获取原始值
Object value = map.get(key);
if (value == null) {
return 0;
}
// 3. 转换为Integer处理常见类型
if (value instanceof Integer) {
return (Integer) value;
} else if (value instanceof Long) {
// 若Map中存的是Long如JSON解析时数字默认转为Long
return ((Long) value).intValue();
} else if (value instanceof String) {
// 若值是字符串类型(如"1"
try {
return Integer.parseInt((String) value);
} catch (NumberFormatException e) {
// 字符串无法转数字,返回默认值
return 0;
}
} else if (value instanceof Boolean) {
// 特殊情况布尔值转整数true→1false→0
return (Boolean) value ? 1 : 0;
} else {
// 其他不支持的类型,返回默认值
return 0;
}
}
}

View File

@ -1,5 +1,8 @@
package com.xzzn.common.utils; package com.xzzn.common.utils;
import com.xzzn.common.constant.Constants;
import com.xzzn.common.core.text.StrFormatter;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@ -7,9 +10,8 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.springframework.util.AntPathMatcher; import org.springframework.util.AntPathMatcher;
import com.xzzn.common.constant.Constants;
import com.xzzn.common.core.text.StrFormatter;
/** /**
* 字符串工具类 * 字符串工具类
@ -743,6 +745,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
} }
public static String getString(Object s){ public static String getString(Object s){
if (s == null) return null;
String result; String result;
try { try {
result = String.valueOf(s); result = String.valueOf(s);
@ -751,4 +754,17 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
} }
return result; return result;
} }
// 补全三位数字(返回字符串)
public static String fillThreeDigits(String value) {
if (value == null || value.trim().isEmpty()) {
return "000";
}
try {
int num = Integer.parseInt(value.trim());
return String.format("%03d", num);
} catch (NumberFormatException e) {
return value;
}
}
} }

View File

@ -50,11 +50,7 @@
<groupId>org.eclipse.paho</groupId> <groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId> <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
</dependency> </dependency>
<!-- 轮询 -->
<dependency>
<groupId>net.wimpi</groupId>
<artifactId>j2mod</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.retry</groupId> <groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId> <artifactId>spring-retry</artifactId>

View File

@ -0,0 +1,97 @@
package com.xzzn.framework.aspectj;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xzzn.common.utils.StringUtils;
import com.xzzn.ems.domain.EmsDeviceChangeLog;
import com.xzzn.ems.domain.MqttSyncLog;
import com.xzzn.ems.mapper.EmsMqttTopicConfigMapper;
import com.xzzn.ems.mapper.MqttSyncLogMapper;
import com.xzzn.framework.web.service.MqttPublisher;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.UUID;
/**
* 设备运行状态变更日志同步
* 本地 - 云端
*/
@Aspect
@Component
public class DeviceChangeLogAspect {
private static final Log logger = LogFactory.getLog(DeviceChangeLogAspect.class);
@Autowired
private MqttPublisher mqttPublisher;
private static final ObjectMapper objectMapper = new ObjectMapper();
private static final String MQTT_TOPIC = "DEVICE_CHANGE_LOG_UP";
private static final String TABLE_NAME = "ems_device_change_log";
@Autowired
private MqttSyncLogMapper mqttSyncLogMapper;
@Autowired
private EmsMqttTopicConfigMapper emsMqttTopicConfigMapper;
// 定义切点拦截策略相关表的Mapper方法
@Pointcut("(execution(* com.xzzn.ems.mapper.EmsDeviceChangeLogMapper.insertEmsDeviceChangeLog(..)) && args(insertEntity)) ")
public void insertPointCut(EmsDeviceChangeLog insertEntity) {
logger.info("【新增设备运行状态变更日志】DeviceChangeLogAspect 实例化");
}
// 方法执行成功后发布同步消息
@AfterReturning(pointcut = "insertPointCut(insertEntity)", returning = "result")
public void afterInsert(JoinPoint joinPoint, EmsDeviceChangeLog insertEntity, Integer result) {
logger.info("【新增设备运行状态变更日志下发数据切面进入成功】");
if (result == 0 || insertEntity == null) {
return;
}
// 校验是否配置监听topic-监听则不发布
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
if (!StringUtils.isEmpty(topic)) {
return;
}
// 解析方法名获取操作类型INSERT/UPDATE/DELETE和表名
String operateType = "INSERT";
String siteId = insertEntity.getSiteId();
// 构建日志同步消息
MqttSyncLog message = createMessageObject(operateType,siteId);
try {
// 数据转换
String content = objectMapper.writeValueAsString(insertEntity);
message.setContent(content);
// mqtt同步到云端
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
} catch (Exception e) {
message.setStatus("FAIL");
message.setErrorMsg(e.getMessage());
}
// 存储同步信息
mqttSyncLogMapper.insertMqttSyncLog(message);
}
// 构建同步信息
private MqttSyncLog createMessageObject(String operateType, String siteId) {
MqttSyncLog message = new MqttSyncLog();
message.setSyncId(UUID.randomUUID().toString());
message.setOperateType(operateType);
message.setTableName(TABLE_NAME);
message.setCreateTime(new Date());
message.setTopic(MQTT_TOPIC);
message.setStatus("SUCCESS");
message.setSyncObject(siteId);
message.setTarget("CLOUD");
return message;
}
}

View File

@ -0,0 +1,104 @@
package com.xzzn.framework.aspectj;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xzzn.common.utils.StringUtils;
import com.xzzn.ems.domain.EmsAlarmRecords;
import com.xzzn.ems.domain.MqttSyncLog;
import com.xzzn.ems.mapper.EmsMqttTopicConfigMapper;
import com.xzzn.ems.mapper.MqttSyncLogMapper;
import com.xzzn.framework.web.service.MqttPublisher;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.UUID;
/**
* 本地保护告警信息同步云端
* 本地 - 云端
* @author xzzn
*/
@Aspect
@Component
public class FaultPlanAlarmAspect
{
private static final Log logger = LogFactory.getLog(FaultPlanAlarmAspect.class);
private static final ObjectMapper objectMapper = new ObjectMapper();
@Autowired
private MqttSyncLogMapper mqttSyncLogMapper;
@Autowired
private MqttPublisher mqttPublisher;
@Autowired
private EmsMqttTopicConfigMapper emsMqttTopicConfigMapper;
private static final String MQTT_TOPIC = "FAULT_ALARM_RECORD_UP";
private static final String TABLE_NAME = "ems_alarm_records";
// 切入点:拦截所有添加了@SyncAfterInsert注解的方法
@Pointcut("@annotation(com.xzzn.common.annotation.SyncAfterInsert)")
public void syncInsertPointcut() {}
// 方法执行成功后同步数据
@AfterReturning(pointcut = "syncInsertPointcut()", returning = "result")
public void afterInsert(JoinPoint joinPoint, Integer result) {
logger.info("【新增保护策略告警数据切面进入成功】");
if (result == 0) {
return;
}
// 校验是否配置监听topic-监听则不发布
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
if (!StringUtils.isEmpty(topic)) {
return;
}
// 获取参数中的EmsAlarmRecords对象
Object[] args = joinPoint.getArgs();
EmsAlarmRecords alarmRecords = null;
for (Object arg : args) {
if (arg instanceof EmsAlarmRecords) {
alarmRecords = (EmsAlarmRecords) arg;
break;
}
}
if (alarmRecords == null) {
return;
}
// 构建日志同步消息
MqttSyncLog message = createMessageObject("INSERT", alarmRecords.getSiteId());
try {
// 同步内容
String content = objectMapper.writeValueAsString(alarmRecords);
message.setContent(content);
// 发布到MQTT主题-同步到云端
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
} catch (Exception e) {
message.setStatus("FAIL");
message.setErrorMsg(e.getMessage());
}
// 存储同步信息
mqttSyncLogMapper.insertMqttSyncLog(message);
}
// 构建同步信息
private MqttSyncLog createMessageObject(String operateType, String siteId) {
MqttSyncLog message = new MqttSyncLog();
message.setSyncId(UUID.randomUUID().toString());
message.setOperateType(operateType);
message.setTableName(TABLE_NAME);
message.setCreateTime(new Date());
message.setTopic(MQTT_TOPIC);
message.setStatus("SUCCESS");
message.setSyncObject(siteId);
message.setTarget("CLOUD");
return message;
}
}

View File

@ -0,0 +1,98 @@
package com.xzzn.framework.aspectj;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xzzn.common.utils.StringUtils;
import com.xzzn.ems.domain.EmsFaultIssueLog;
import com.xzzn.ems.domain.MqttSyncLog;
import com.xzzn.ems.mapper.EmsMqttTopicConfigMapper;
import com.xzzn.ems.mapper.MqttSyncLogMapper;
import com.xzzn.framework.web.service.MqttPublisher;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* 保护告警方案下发日志同步
* 本地 - 云端
*/
@Aspect
@Component
public class FaultPlanIssueAspect {
private static final Log logger = LogFactory.getLog(FaultPlanIssueAspect.class);
@Autowired
private MqttPublisher mqttPublisher;
private static final ObjectMapper objectMapper = new ObjectMapper();
private static final String MQTT_TOPIC = "FAULT_PLAN_ISSUE_UP";
private static final String TABLE_NAME = "ems_fault_issue_log";
@Autowired
private EmsMqttTopicConfigMapper emsMqttTopicConfigMapper;
@Autowired
private MqttSyncLogMapper mqttSyncLogMapper;
// 定义切点拦截策略相关表的Mapper方法
@Pointcut("(execution(* com.xzzn.ems.mapper.EmsFaultIssueLogMapper.insertEmsFaultIssueLog(..)) && args(insertEntity)) ")
public void insertPointCut(EmsFaultIssueLog insertEntity) {
logger.info("【新增保护告警策略下发数据】FaultPlanIssueAspect 实例化");
}
// 方法执行成功后发布同步消息
@AfterReturning(pointcut = "insertPointCut(insertEntity)", returning = "result")
public void afterInsert(JoinPoint joinPoint, EmsFaultIssueLog insertEntity, Integer result) {
logger.info("【新增保护告警策略下发数据切面进入成功】");
if (result == 0 || insertEntity == null) {
return;
}
// 校验是否配置监听topic-监听则不发布
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
if (!StringUtils.isEmpty(topic)) {
return;
}
// 解析方法名获取操作类型INSERT/UPDATE/DELETE和表名
String operateType = "INSERT";
String siteId = insertEntity.getSiteId();
// 构建日志同步消息
MqttSyncLog message = createMessageObject(operateType,siteId);
try {
// 数据转换
String content = objectMapper.writeValueAsString(insertEntity);
message.setContent(content);
// mqtt同步到云端
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
} catch (Exception e) {
message.setStatus("FAIL");
message.setErrorMsg(e.getMessage());
}
// 存储同步信息
mqttSyncLogMapper.insertMqttSyncLog(message);
}
// 构建同步信息
private MqttSyncLog createMessageObject(String operateType, String siteId) {
MqttSyncLog message = new MqttSyncLog();
message.setSyncId(UUID.randomUUID().toString());
message.setOperateType(operateType);
message.setTableName(TABLE_NAME);
message.setCreateTime(new Date());
message.setTopic(MQTT_TOPIC);
message.setStatus("SUCCESS");
message.setSyncObject(siteId);
message.setTarget("CLOUD");
return message;
}
}

View File

@ -0,0 +1,277 @@
package com.xzzn.framework.aspectj;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xzzn.common.utils.StringUtils;
import com.xzzn.ems.domain.EmsFaultProtectionPlan;
import com.xzzn.ems.domain.MqttSyncLog;
import com.xzzn.ems.mapper.EmsFaultProtectionPlanMapper;
import com.xzzn.ems.mapper.EmsMqttTopicConfigMapper;
import com.xzzn.ems.mapper.MqttSyncLogMapper;
import com.xzzn.framework.web.service.MqttPublisher;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* 设备保护告警方案同步
* 云端 - 本地
*/
@Aspect
@Component
public class FaultProtPlanAspect {
private static final Log logger = LogFactory.getLog(FaultProtPlanAspect.class);
@Autowired
private MqttPublisher mqttPublisher;
private static final ObjectMapper objectMapper = new ObjectMapper();
private static final String MQTT_TOPIC = "FAULT_PROTECTION_PLAN_UP";
private static final String TABLE_NAME = "ems_fault_protection_plan";
@Autowired
private EmsMqttTopicConfigMapper emsMqttTopicConfigMapper;
@Autowired
private MqttSyncLogMapper mqttSyncLogMapper;
@Autowired
private EmsFaultProtectionPlanMapper emsFaultProtectionPlanMapper;
// 用ThreadLocal暂存删除前的对象
private ThreadLocal<EmsFaultProtectionPlan> beforeDeleteThreadLocal = new ThreadLocal<>();
@Before("execution(* com.xzzn.ems.mapper.EmsFaultProtectionPlanMapper.deleteEmsFaultProtectionPlanByIds(..)) && args(ids)")
public void beforeDelete(JoinPoint joinPoint, Long[] ids) {
// 获取删除的id
Object[] args = joinPoint.getArgs();
if (args == null || args.length == 0) {
return;
}
Long[] id = (Long[]) args[0];
// 查询删除前的数据
EmsFaultProtectionPlan faultInfo = emsFaultProtectionPlanMapper.selectEmsFaultProtectionPlanById(id[0]);
beforeDeleteThreadLocal.set(faultInfo); // 暂存
}
// 定义切点拦截策略相关表的Mapper方法
@Pointcut("(execution(* com.xzzn.ems.mapper.EmsFaultProtectionPlanMapper.insertEmsFaultProtectionPlan(..)) && args(insertEntity)) ")
public void insertPointCut(EmsFaultProtectionPlan insertEntity) {
logger.info("【新增设备保护告警】FaultProtPlanAspect 实例化");
}
@Pointcut("(execution(* com.xzzn.ems.mapper.EmsFaultProtectionPlanMapper.updateEmsFaultProtectionPlan(..)) && args(updateEntity)) ")
public void updatePointCut(EmsFaultProtectionPlan updateEntity) {
logger.info("【更新设备保护告警】FaultProtPlanAspect 实例化");
}
@Pointcut("(execution(* com.xzzn.ems.mapper.EmsFaultProtectionPlanMapper.deleteEmsFaultProtectionPlanByIds(..)) && args(ids)) ")
public void deletePointCut(Long[] ids) {
logger.info("【删除设备保护告警】FaultProtPlanAspect 实例化");
}
// 方法执行成功后发布同步消息
@AfterReturning(pointcut = "insertPointCut(insertEntity)", returning = "result")
public void afterInsert(JoinPoint joinPoint, EmsFaultProtectionPlan insertEntity, Integer result) {
logger.info("【新增设备保护告警切面进入成功】");
if (result == 0 || insertEntity == null) {
return;
}
// 校验是否配置监听topic-监听则不发布
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
if (!StringUtils.isEmpty(topic)) {
return;
}
// 解析方法名获取操作类型INSERT/UPDATE/DELETE和表名
String methodName = joinPoint.getSignature().getName();
String operateType = getOperateType(methodName);
String siteId = insertEntity.getSiteId();
// 构建日志同步消息
MqttSyncLog message = createMessageObject(operateType,siteId);
try {
// 数据转换
String content = convertEntityToJson(insertEntity);
message.setContent(content);
// 发布到MQTT主题
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
} catch (Exception e) {
message.setStatus("FAIL");
message.setErrorMsg(e.getMessage());
}
// 存储同步信息
mqttSyncLogMapper.insertMqttSyncLog(message);
}
@AfterReturning(pointcut = "updatePointCut(updateEntity)", returning = "result")
public void afterUpdate(JoinPoint joinPoint, EmsFaultProtectionPlan updateEntity, Integer result) {
logger.info("【更新设备保护告警切面进入成功】");
if (result == 0 || updateEntity == null) {
return;
}
// 校验是否配置监听topic-监听则不发布
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
if (!StringUtils.isEmpty(topic)) {
return;
}
// 解析方法名获取操作类型INSERT/UPDATE/DELETE和表名
String methodName = joinPoint.getSignature().getName();
String operateType = getOperateType(methodName);
String siteId = updateEntity.getSiteId();
// 构建日志同步消息
MqttSyncLog message = createMessageObject(operateType,siteId);
try {
// 数据转换
String content = convertEntityToJson(updateEntity);
message.setContent(content);
// 发布到MQTT主题
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
} catch (Exception e) {
message.setStatus("FAIL");
message.setErrorMsg(e.getMessage());
}
// 存储同步信息
mqttSyncLogMapper.insertMqttSyncLog(message);
}
@AfterReturning(pointcut = "deletePointCut(id)", returning = "result")
public void afterDelete(JoinPoint joinPoint, Long[] id, Integer result) {
logger.info("【删除设备保护告警切面进入成功】");
if (result == 0 || id == null) {
return;
}
// 校验是否配置监听topic-监听则不发布
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
if (!StringUtils.isEmpty(topic)) {
return;
}
// 解析方法名获取操作类型INSERT/UPDATE/DELETE和表名
String methodName = joinPoint.getSignature().getName();
String operateType = getOperateType(methodName);
// 从ThreadLocal中获取删除前的对象
EmsFaultProtectionPlan faultInfo = beforeDeleteThreadLocal.get();
if (faultInfo == null) {
return;
}
// 构建日志同步消息
MqttSyncLog message = createMessageObject(operateType,faultInfo.getSiteId());
try {
// 数据转换
String content = convertEntityToJson(faultInfo);
message.setContent(content);
// 发布到MQTT主题
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
} catch (Exception e) {
message.setStatus("FAIL");
message.setErrorMsg(e.getMessage());
}
// 存储同步信息
mqttSyncLogMapper.insertMqttSyncLog(message);
}
// 构建同步信息
private MqttSyncLog createMessageObject(String operateType, String siteId) {
MqttSyncLog message = new MqttSyncLog();
message.setSyncId(UUID.randomUUID().toString());
message.setOperateType(operateType);
message.setTableName(TABLE_NAME);
message.setCreateTime(new Date());
message.setTopic(MQTT_TOPIC);
message.setStatus("SUCCESS");
message.setSyncObject("CLOUD");
message.setTarget(siteId);
return message;
}
// 从方法名判断操作类型示例insert→INSERTupdate→UPDATEdelete→DELETE
private String getOperateType(String methodName) {
if (methodName.startsWith("insert")) return "INSERT";
if (methodName.startsWith("stop")) return "STOP";
if (methodName.startsWith("update") || methodName.startsWith("stop")) return "UPDATE";
if (methodName.startsWith("delete")) return "DELETE";
return "UNKNOWN";
}
// 从方法参数提取数据示例若参数是实体类转成Map
private Map<String, Object> extractDataFromParams(Object[] args) {
// 实际需反射获取实体类的字段和值如id、name等
Map<String, Object> data = new HashMap<>();
if (args == null || args.length == 0) {
return data;
}
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
if (arg == null) {
continue; // 跳过null参数
}
// 处理基本类型/包装类/字符串直接作为值存入key为"param0"、"param1"等)
if (isBasicType(arg.getClass())) {
String key = "param" + i; // 基本类型参数用"param0"、"param1"作为key
data.put(key, arg);
} else {
Map<String, Object> beanMap = beanToMap(arg);
data.putAll(beanMap); // 合并实体类的字段到结果Map
}
}
return data;
}
/**
* 判断是否为基本类型或包装类或字符串
*/
private boolean isBasicType(Class<?> clazz) {
return clazz.isPrimitive() // 基本类型int、long、boolean等
|| clazz == String.class // 字符串
|| Number.class.isAssignableFrom(clazz) // 数字包装类Integer、Long等
|| clazz == Boolean.class; // 布尔包装类
}
/**
* 将实体类转换为Map字段名为key字段值为value
*/
private Map<String, Object> beanToMap(Object bean) {
Map<String, Object> map = new HashMap<>();
if (bean == null) {
return map;
}
// 方式1使用BeanMap简洁高效
BeanMap beanMap = BeanMap.create(bean);
for (Object key : beanMap.keySet()) {
map.put(key.toString(), beanMap.get(key));
}
return map;
}
// 在方法中转换
public String convertEntityToJson(EmsFaultProtectionPlan insertEntity) throws Exception {
if (insertEntity == null) {
return null; // 空对象返回空JSON
}
// 将实体类转换为JSON字符串
return objectMapper.writeValueAsString(insertEntity);
}
}

View File

@ -0,0 +1,272 @@
package com.xzzn.framework.aspectj;
import com.alibaba.fastjson2.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xzzn.common.utils.StringUtils;
import com.xzzn.ems.domain.EmsStrategyRunning;
import com.xzzn.ems.domain.MqttSyncLog;
import com.xzzn.ems.domain.vo.StrategyRunningVo;
import com.xzzn.ems.mapper.EmsMqttTopicConfigMapper;
import com.xzzn.ems.mapper.EmsStrategyRunningMapper;
import com.xzzn.ems.mapper.MqttSyncLogMapper;
import com.xzzn.framework.web.service.MqttPublisher;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.stereotype.Component;
/**
* 策略运行切面同步
* 云端 - 本地
*/
@Aspect
@Component
public class StrategyRunningSyncAspect {
private static final Logger logger = LoggerFactory.getLogger(StrategyRunningSyncAspect.class);
@Autowired
private MqttPublisher mqttPublisher;
private static final ObjectMapper objectMapper = new ObjectMapper();
private static final String MQTT_TOPIC = "EMS_STRATEGY_UP";
private static final String TABLE_NAME = "ems_strategy_running";
@Autowired
private EmsMqttTopicConfigMapper emsMqttTopicConfigMapper;
@Autowired
private MqttSyncLogMapper mqttSyncLogMapper;
@Autowired
private EmsStrategyRunningMapper emsStrategyRunningMapper;
@Pointcut("(execution(* com.xzzn.ems.mapper.EmsStrategyRunningMapper.stopEmsStrategyRunning(..)) && args(id)) ")
public void stopPointCut(Long id) {
logger.info("【停止策略切面】StrategyAspect 被实例化");
}
@Pointcut("(execution(* com.xzzn.ems.mapper.EmsStrategyRunningMapper.insertEmsStrategyRunning(..)) && args(insertEntity)) ")
public void insertPointCut(EmsStrategyRunning insertEntity) {
logger.info("【新增策略切面】StrategyAspect 被实例化");
}
@Pointcut("(execution(* com.xzzn.ems.mapper.EmsStrategyRunningMapper.updateEmsStrategyRunning(..)) && args(updateEntity)) ")
public void updatePointCut(EmsStrategyRunning updateEntity) {
logger.info("【更新策略切面】StrategyAspect 被实例化");
}
// 方法执行成功后发布同步消息
@AfterReturning(pointcut = "updatePointCut(updateEntity)", returning = "result")
public void afterUpdate(JoinPoint joinPoint, EmsStrategyRunning updateEntity, Integer result) {
logger.info("【更新策略切面进入成功】");
if (result == 0 || updateEntity == null) {
return;
}
// 校验是否配置监听topic-监听则不发布
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
if (!StringUtils.isEmpty(topic)) {
return;
}
// 解析方法名获取操作类型INSERT/UPDATE/DELETE和表名
String methodName = joinPoint.getSignature().getName();
String operateType = getOperateType(methodName);
String siteId = updateEntity.getSiteId();
// 构建日志同步消息
MqttSyncLog message = createMessageObject(operateType,siteId);
try {
// 数据转换
List<StrategyRunningVo> runningVos = emsStrategyRunningMapper.getRunningList(siteId);
if (runningVos == null || runningVos.size() == 0) {
return;
}
StrategyRunningVo runningVo = runningVos.get(0);
String content = objectMapper.writeValueAsString(runningVo);
message.setContent(content);
// 发布到MQTT主题
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
} catch (Exception e) {
message.setStatus("FAIL");
message.setErrorMsg(e.getMessage());
}
// 存储同步信息
mqttSyncLogMapper.insertMqttSyncLog(message);
}
@AfterReturning(pointcut = "insertPointCut(insertEntity)", returning = "result")
public void afterInsert(JoinPoint joinPoint, EmsStrategyRunning insertEntity, Integer result) {
logger.info("【新增策略切面进入成功】");
if (result == 0 || insertEntity == null) {
return;
}
// 校验是否配置监听topic-监听则不发布
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
if (!StringUtils.isEmpty(topic)) {
return;
}
// 解析方法名获取操作类型INSERT/UPDATE/DELETE和表名
String methodName = joinPoint.getSignature().getName();
String operateType = getOperateType(methodName);
String siteId = insertEntity.getSiteId();
// 构建日志同步消息
MqttSyncLog message = createMessageObject(operateType, siteId);
try {
// 数据转换
List<StrategyRunningVo> runningVos = emsStrategyRunningMapper.getRunningList(siteId);
if (runningVos == null || runningVos.size() == 0) {
return;
}
StrategyRunningVo runningVo = runningVos.get(0);
String content = objectMapper.writeValueAsString(runningVo);
message.setContent(content);
// 发布到MQTT主题
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
} catch (Exception e) {
message.setStatus("FAIL");
message.setErrorMsg(e.getMessage());
}
// 存储同步信息
mqttSyncLogMapper.insertMqttSyncLog(message);
}
@AfterReturning(pointcut = "stopPointCut(id)", returning = "result")
public void afterStop(JoinPoint joinPoint, Long id, Integer result) {
logger.info("【停止策略切面进入成功】");
if (result == 0 || id == null) {
return;
}
// 校验是否配置监听topic-监听则不发布
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
if (!StringUtils.isEmpty(topic)) {
return;
}
// 解析方法名获取操作类型INSERT/UPDATE/DELETE和表名
String methodName = joinPoint.getSignature().getName();
String operateType = getOperateType(methodName);
EmsStrategyRunning emsStrategyRunning = emsStrategyRunningMapper.selectEmsStrategyRunningById(id);
String siteId = emsStrategyRunning==null ? null : emsStrategyRunning.getSiteId();
// 构建日志同步消息
MqttSyncLog message = createMessageObject(operateType, siteId);
try {
// 数据转换
Map<String, Object> idMap = new HashMap<>();
idMap.put("id", id); // 手动将参数值映射到"id"字段
String content = JSON.toJSONString(idMap);
message.setContent(content);
// 发布到MQTT主题
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
} catch (Exception e) {
message.setStatus("FAIL");
message.setErrorMsg(e.getMessage());
}
// 存储同步信息
mqttSyncLogMapper.insertMqttSyncLog(message);
}
// 构建同步信息
private MqttSyncLog createMessageObject(String operateType, String siteId) {
MqttSyncLog message = new MqttSyncLog();
message.setSyncId(UUID.randomUUID().toString());
message.setOperateType(operateType);
message.setTableName(TABLE_NAME);
message.setCreateTime(new Date());
message.setTopic(MQTT_TOPIC);
message.setStatus("SUCCESS");
message.setSyncObject("CLOUD");
message.setTarget(siteId);
return message;
}
// 从方法名判断操作类型示例insert→INSERTupdate→UPDATEdelete→DELETE
private String getOperateType(String methodName) {
if (methodName.startsWith("insert")) return "INSERT";
if (methodName.startsWith("stop")) return "STOP";
if (methodName.startsWith("update") || methodName.startsWith("stop")) return "UPDATE";
if (methodName.startsWith("delete")) return "DELETE";
return "UNKNOWN";
}
// 从方法参数提取数据示例若参数是实体类转成Map
private Map<String, Object> extractDataFromParams(Object[] args) {
// 实际需反射获取实体类的字段和值如id、name等
Map<String, Object> data = new HashMap<>();
if (args == null || args.length == 0) {
return data;
}
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
if (arg == null) {
continue; // 跳过null参数
}
// 处理基本类型/包装类/字符串直接作为值存入key为"param0"、"param1"等)
if (isBasicType(arg.getClass())) {
String key = "param" + i; // 基本类型参数用"param0"、"param1"作为key
data.put(key, arg);
} else {
Map<String, Object> beanMap = beanToMap(arg);
data.putAll(beanMap); // 合并实体类的字段到结果Map
}
}
return data;
}
/**
* 判断是否为基本类型或包装类或字符串
*/
private boolean isBasicType(Class<?> clazz) {
return clazz.isPrimitive() // 基本类型int、long、boolean等
|| clazz == String.class // 字符串
|| Number.class.isAssignableFrom(clazz) // 数字包装类Integer、Long等
|| clazz == Boolean.class; // 布尔包装类
}
/**
* 将实体类转换为Map字段名为key字段值为value
*/
private Map<String, Object> beanToMap(Object bean) {
Map<String, Object> map = new HashMap<>();
if (bean == null) {
return map;
}
// 方式1使用BeanMap简洁高效
BeanMap beanMap = BeanMap.create(bean);
for (Object key : beanMap.keySet()) {
map.put(key.toString(), beanMap.get(key));
}
return map;
}
// 在方法中转换
public String convertEntityToJson(EmsStrategyRunning insertEntity) throws Exception {
if (insertEntity == null) {
return null; // 空对象返回空JSON
}
// 将实体类转换为JSON字符串
return objectMapper.writeValueAsString(insertEntity);
}
}

View File

@ -0,0 +1,261 @@
package com.xzzn.framework.aspectj;
import com.alibaba.fastjson2.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xzzn.common.utils.StringUtils;
import com.xzzn.common.utils.bean.BeanUtils;
import com.xzzn.ems.domain.EmsStrategy;
import com.xzzn.ems.domain.EmsStrategyRunning;
import com.xzzn.ems.domain.EmsStrategyTemp;
import com.xzzn.ems.domain.MqttSyncLog;
import com.xzzn.ems.domain.vo.SyncStrategyTempVo;
import com.xzzn.ems.mapper.EmsMqttTopicConfigMapper;
import com.xzzn.ems.mapper.EmsStrategyMapper;
import com.xzzn.ems.mapper.EmsStrategyTempMapper;
import com.xzzn.ems.mapper.MqttSyncLogMapper;
import com.xzzn.framework.web.service.MqttPublisher;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.stereotype.Component;
/**
* 策略模板数据同步
* 云端 - 本地
*/
@Aspect
@Component
public class StrategyTempSyncAspect {
private static final Logger logger = LoggerFactory.getLogger(StrategyTempSyncAspect.class);
@Autowired
private MqttPublisher mqttPublisher;
private static final ObjectMapper objectMapper = new ObjectMapper();
private static final String MQTT_TOPIC = "EMS_STRATEGY_UP";
private static final String TABLE_NAME = "ems_strategy_temp";
@Autowired
private EmsMqttTopicConfigMapper emsMqttTopicConfigMapper;
@Autowired
private MqttSyncLogMapper mqttSyncLogMapper;
@Autowired
private EmsStrategyMapper emsStrategyMapper;
@Autowired
private EmsStrategyTempMapper emsStrategyTempMapper;
// 用ThreadLocal暂存删除前的对象
private ThreadLocal<EmsStrategyTemp> beforeDeleteThreadLocal = new ThreadLocal<>();
@Before("execution(* com.xzzn.ems.mapper.EmsStrategyTempMapper.deleteTempByTempId(..)) && args(templateId)")
public void beforeDelete(JoinPoint joinPoint, String templateId) {
// 查询删除前的数据-仅存一获取siteId
List<EmsStrategyTemp> tempList = emsStrategyTempMapper.selectStrategyTempByTempId(templateId);
if (tempList != null && tempList.size() > 0) {
beforeDeleteThreadLocal.set(tempList.get(0)); // 暂存
}
}
@Pointcut("(execution(* com.xzzn.ems.mapper.EmsStrategyTempMapper.deleteTempByTempId(..)) && args(templateId)) ")
public void deletePointCut(String templateId) {
logger.info("【删除策略模版切面】StrategyTempSyncAspect 被实例化");
}
@Pointcut("(execution(* com.xzzn.ems.mapper.EmsStrategyTempMapper.insertEmsStrategyTemp(..)) && args(insertEntity)) ")
public void insertPointCut(EmsStrategyTemp insertEntity) {
logger.info("【新增策略模版切面】StrategyTempSyncAspect 被实例化");
}
// 方法执行成功后发布同步消息
@AfterReturning(pointcut = "insertPointCut(insertEntity)", returning = "result")
public void afterInsert(JoinPoint joinPoint, EmsStrategyTemp insertEntity, Integer result) {
logger.info("【新增策略切面进入成功】");
if (result == 0 || insertEntity == null) {
return;
}
// 校验是否配置监听topic-监听则不发布
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
if (!StringUtils.isEmpty(topic)) {
return;
}
// 解析方法名获取操作类型INSERT/UPDATE/DELETE和表名
String methodName = joinPoint.getSignature().getName();
String operateType = getOperateType(methodName);
String siteId = insertEntity.getSiteId();
// 构建日志同步消息
MqttSyncLog message = createMessageObject(operateType, siteId);
try {
// 校验策略id是否存在
Long strategyId = insertEntity.getStrategyId();
if (strategyId == null) {
return;
}
// 数据转换
SyncStrategyTempVo tempVo = convertEntity(insertEntity);
String content = JSON.toJSONString(tempVo);
message.setContent(content);
// 发布到MQTT主题
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
} catch (Exception e) {
message.setStatus("FAIL");
message.setErrorMsg(e.getMessage());
}
// 存储同步信息
mqttSyncLogMapper.insertMqttSyncLog(message);
}
private SyncStrategyTempVo convertEntity(EmsStrategyTemp insertEntity) {
SyncStrategyTempVo tempVo = new SyncStrategyTempVo();
BeanUtils.copyProperties(insertEntity, tempVo);
EmsStrategy strategy = emsStrategyMapper.selectEmsStrategyById(insertEntity.getStrategyId());
if (strategy != null) {
tempVo.setStrategyName(strategy.getStrategyName());
tempVo.setStrategyType(strategy.getStrategyType());
}
return tempVo;
}
@AfterReturning(pointcut = "deletePointCut(templateId)", returning = "result")
public void afterDelete(JoinPoint joinPoint, String templateId, Integer result) {
logger.info("【删除策略模版切面进入成功】");
if (result == 0 || StringUtils.isEmpty(templateId)) {
return;
}
// 校验是否配置监听topic-监听则不发布
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
if (!StringUtils.isEmpty(topic)) {
return;
}
// 解析方法名获取操作类型INSERT/UPDATE/DELETE和表名
String methodName = joinPoint.getSignature().getName();
String operateType = getOperateType(methodName);
// 从ThreadLocal中获取删除前的对象
EmsStrategyTemp strategyTemp = beforeDeleteThreadLocal.get();
String siteId = "";
if (strategyTemp != null) {
siteId = strategyTemp.getSiteId();
}
// 构建日志同步消息
MqttSyncLog message = createMessageObject(operateType, siteId);
try {
// 数据转换
Map<String, Object> idMap = new HashMap<>();
idMap.put("templateId", templateId); // 手动将参数值映射到"id"字段
String content = JSON.toJSONString(idMap);
message.setContent(content);
// 发布到MQTT主题
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
} catch (Exception e) {
message.setStatus("FAIL");
message.setErrorMsg(e.getMessage());
}
// 存储同步信息
mqttSyncLogMapper.insertMqttSyncLog(message);
}
// 构建同步信息
private MqttSyncLog createMessageObject(String operateType, String siteId) {
MqttSyncLog message = new MqttSyncLog();
message.setSyncId(UUID.randomUUID().toString());
message.setOperateType(operateType);
message.setTableName(TABLE_NAME);
message.setCreateTime(new Date());
message.setTopic(MQTT_TOPIC);
message.setStatus("SUCCESS");
message.setSyncObject("CLOUD");
message.setTarget(siteId);
return message;
}
// 从方法名判断操作类型示例insert→INSERTupdate→UPDATEdelete→DELETE
private String getOperateType(String methodName) {
if (methodName.startsWith("insert")) return "INSERT";
if (methodName.startsWith("stop")) return "STOP";
if (methodName.startsWith("update") || methodName.startsWith("stop")) return "UPDATE";
if (methodName.startsWith("delete")) return "DELETE";
return "UNKNOWN";
}
// 从方法参数提取数据示例若参数是实体类转成Map
private Map<String, Object> extractDataFromParams(Object[] args) {
// 实际需反射获取实体类的字段和值如id、name等
Map<String, Object> data = new HashMap<>();
if (args == null || args.length == 0) {
return data;
}
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
if (arg == null) {
continue; // 跳过null参数
}
// 处理基本类型/包装类/字符串直接作为值存入key为"param0"、"param1"等)
if (isBasicType(arg.getClass())) {
String key = "param" + i; // 基本类型参数用"param0"、"param1"作为key
data.put(key, arg);
} else {
Map<String, Object> beanMap = beanToMap(arg);
data.putAll(beanMap); // 合并实体类的字段到结果Map
}
}
return data;
}
/**
* 判断是否为基本类型或包装类或字符串
*/
private boolean isBasicType(Class<?> clazz) {
return clazz.isPrimitive() // 基本类型int、long、boolean等
|| clazz == String.class // 字符串
|| Number.class.isAssignableFrom(clazz) // 数字包装类Integer、Long等
|| clazz == Boolean.class; // 布尔包装类
}
/**
* 将实体类转换为Map字段名为key字段值为value
*/
private Map<String, Object> beanToMap(Object bean) {
Map<String, Object> map = new HashMap<>();
if (bean == null) {
return map;
}
// 方式1使用BeanMap简洁高效
BeanMap beanMap = BeanMap.create(bean);
for (Object key : beanMap.keySet()) {
map.put(key.toString(), beanMap.get(key));
}
return map;
}
// 在方法中转换
public String convertEntityToJson(EmsStrategyRunning insertEntity) throws Exception {
if (insertEntity == null) {
return null; // 空对象返回空JSON
}
// 将实体类转换为JSON字符串
return objectMapper.writeValueAsString(insertEntity);
}
}

View File

@ -0,0 +1,241 @@
package com.xzzn.framework.aspectj;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xzzn.common.utils.StringUtils;
import com.xzzn.common.utils.bean.BeanUtils;
import com.xzzn.ems.domain.EmsStrategy;
import com.xzzn.ems.domain.EmsStrategyRunning;
import com.xzzn.ems.domain.EmsStrategyTimeConfig;
import com.xzzn.ems.domain.MqttSyncLog;
import com.xzzn.ems.domain.vo.SyncStrategyTimeConfigVo;
import com.xzzn.ems.mapper.EmsMqttTopicConfigMapper;
import com.xzzn.ems.mapper.EmsStrategyMapper;
import com.xzzn.ems.mapper.EmsStrategyTempMapper;
import com.xzzn.ems.mapper.MqttSyncLogMapper;
import com.xzzn.framework.web.service.MqttPublisher;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.stereotype.Component;
/**
* 策略时间配置同步
* 云端 - 本地
*/
@Aspect
@Component
public class StrategyTimeConfigSyncAspect {
private static final Logger logger = LoggerFactory.getLogger(StrategyTimeConfigSyncAspect.class);
@Autowired
private MqttPublisher mqttPublisher;
private static final ObjectMapper objectMapper = new ObjectMapper();
private static final String MQTT_TOPIC = "EMS_STRATEGY_UP";
private static final String TABLE_NAME = "ems_strategy_temp";
@Autowired
private EmsMqttTopicConfigMapper emsMqttTopicConfigMapper;
@Autowired
private MqttSyncLogMapper mqttSyncLogMapper;
@Autowired
private EmsStrategyMapper emsStrategyMapper;
@Autowired
private EmsStrategyTempMapper emsStrategyTempMapper;
@Pointcut("(execution(* com.xzzn.ems.mapper.EmsStrategyTimeConfigMapper.insertEmsStrategyTimeConfig(..)) && args(insertEntity)) ")
public void insertPointCut(EmsStrategyTimeConfig insertEntity) {
logger.info("【新增策略模版时间配置切面】StrategyTimeConfigSyncAspect 被实例化");
}
@Pointcut("(execution(* com.xzzn.ems.mapper.EmsStrategyTimeConfigMapper.updateEmsStrategyTimeConfig(..)) && args(updateEntity)) ")
public void updatePointCut(EmsStrategyTimeConfig updateEntity) {
logger.info("【更新策略模版时间配置切面】StrategyTimeConfigSyncAspect 被实例化");
}
// 方法执行成功后发布同步消息
@AfterReturning(pointcut = "insertPointCut(insertEntity)", returning = "result")
public void afterInsert(JoinPoint joinPoint, EmsStrategyTimeConfig insertEntity, Integer result) {
logger.info("【新增策略模版时间切面进入成功】");
if (result == 0 || insertEntity == null) {
return;
}
// 校验是否配置监听topic-监听则不发布
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
if (!StringUtils.isEmpty(topic)) {
return;
}
// 解析方法名获取操作类型INSERT/UPDATE/DELETE和表名
String methodName = joinPoint.getSignature().getName();
String operateType = getOperateType(methodName);
String siteId = insertEntity.getSiteId();
// 构建日志同步消息
MqttSyncLog message = createMessageObject(operateType, siteId);
try {
// 校验策略id是否存在
Long strategyId = insertEntity.getStrategyId();
if (strategyId == null) {
return;
}
// 数据转换
SyncStrategyTimeConfigVo timeConfigVo = convertEntity(insertEntity);
String content = objectMapper.writeValueAsString(timeConfigVo);
message.setContent(content);
// 发布到MQTT主题
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
} catch (Exception e) {
message.setStatus("FAIL");
message.setErrorMsg(e.getMessage());
}
// 存储同步信息
mqttSyncLogMapper.insertMqttSyncLog(message);
}
private SyncStrategyTimeConfigVo convertEntity(EmsStrategyTimeConfig insertEntity) {
SyncStrategyTimeConfigVo timeConfigVo = new SyncStrategyTimeConfigVo();
BeanUtils.copyProperties(insertEntity, timeConfigVo);
EmsStrategy strategy = emsStrategyMapper.selectEmsStrategyById(insertEntity.getStrategyId());
if (strategy != null) {
timeConfigVo.setStrategyName(strategy.getStrategyName());
timeConfigVo.setStrategyType(strategy.getStrategyType());
}
return timeConfigVo;
}
@AfterReturning(pointcut = "updatePointCut(updateEntity)", returning = "result")
public void afterUpdate(JoinPoint joinPoint, EmsStrategyTimeConfig updateEntity, Integer result) {
logger.info("【删除策略模版切面进入成功】");
if (result == 0 || updateEntity == null) {
return;
}
// 校验是否配置监听topic-监听则不发布
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
if (!StringUtils.isEmpty(topic)) {
return;
}
// 解析方法名获取操作类型INSERT/UPDATE/DELETE和表名
String methodName = joinPoint.getSignature().getName();
String operateType = getOperateType(methodName);
String siteId = updateEntity.getSiteId();
// 构建日志同步消息
MqttSyncLog message = createMessageObject(operateType, siteId);
try {
// 数据转换
SyncStrategyTimeConfigVo timeConfigVo = convertEntity(updateEntity);
String content = objectMapper.writeValueAsString(timeConfigVo);
message.setContent(content);
// 发布到MQTT主题
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
} catch (Exception e) {
message.setStatus("FAIL");
message.setErrorMsg(e.getMessage());
}
// 存储同步信息
mqttSyncLogMapper.insertMqttSyncLog(message);
}
// 构建同步信息
private MqttSyncLog createMessageObject(String operateType, String siteId) {
MqttSyncLog message = new MqttSyncLog();
message.setSyncId(UUID.randomUUID().toString());
message.setOperateType(operateType);
message.setTableName(TABLE_NAME);
message.setCreateTime(new Date());
message.setTopic(MQTT_TOPIC);
message.setStatus("SUCCESS");
message.setSyncObject("CLOUD");
message.setTarget(siteId);
return message;
}
// 从方法名判断操作类型示例insert→INSERTupdate→UPDATEdelete→DELETE
private String getOperateType(String methodName) {
if (methodName.startsWith("insert")) return "INSERT";
if (methodName.startsWith("stop")) return "STOP";
if (methodName.startsWith("update") || methodName.startsWith("stop")) return "UPDATE";
if (methodName.startsWith("delete")) return "DELETE";
return "UNKNOWN";
}
// 从方法参数提取数据示例若参数是实体类转成Map
private Map<String, Object> extractDataFromParams(Object[] args) {
// 实际需反射获取实体类的字段和值如id、name等
Map<String, Object> data = new HashMap<>();
if (args == null || args.length == 0) {
return data;
}
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
if (arg == null) {
continue; // 跳过null参数
}
// 处理基本类型/包装类/字符串直接作为值存入key为"param0"、"param1"等)
if (isBasicType(arg.getClass())) {
String key = "param" + i; // 基本类型参数用"param0"、"param1"作为key
data.put(key, arg);
} else {
Map<String, Object> beanMap = beanToMap(arg);
data.putAll(beanMap); // 合并实体类的字段到结果Map
}
}
return data;
}
/**
* 判断是否为基本类型或包装类或字符串
*/
private boolean isBasicType(Class<?> clazz) {
return clazz.isPrimitive() // 基本类型int、long、boolean等
|| clazz == String.class // 字符串
|| Number.class.isAssignableFrom(clazz) // 数字包装类Integer、Long等
|| clazz == Boolean.class; // 布尔包装类
}
/**
* 将实体类转换为Map字段名为key字段值为value
*/
private Map<String, Object> beanToMap(Object bean) {
Map<String, Object> map = new HashMap<>();
if (bean == null) {
return map;
}
// 方式1使用BeanMap简洁高效
BeanMap beanMap = BeanMap.create(bean);
for (Object key : beanMap.keySet()) {
map.put(key.toString(), beanMap.get(key));
}
return map;
}
// 在方法中转换
public String convertEntityToJson(EmsStrategyRunning insertEntity) throws Exception {
if (insertEntity == null) {
return null; // 空对象返回空JSON
}
// 将实体类转换为JSON字符串
return objectMapper.writeValueAsString(insertEntity);
}
}

View File

@ -13,8 +13,9 @@ import org.springframework.context.annotation.EnableAspectJAutoProxy;
* @author xzzn * @author xzzn
*/ */
@Configuration @Configuration
// 表示通过aop框架暴露该代理对象,AopContext能够访问 // proxyTargetClass = true表示使用cglib代理false表示jdk动态代理
@EnableAspectJAutoProxy(exposeProxy = true) // exposeProxy = true表示通过aop框架暴露该代理对象,AopContext能够访问
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
// 指定要扫描的Mapper类的包的路径 // 指定要扫描的Mapper类的包的路径
@MapperScan("com.xzzn.**.mapper") @MapperScan("com.xzzn.**.mapper")
public class ApplicationConfig public class ApplicationConfig

View File

@ -1,38 +0,0 @@
package com.xzzn.framework.config;
import com.xzzn.framework.manager.ModbusConnectionManager;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ModbusConfig {
@Value("${modbus.pool.max-total:20}")
private int maxTotal;
@Value("${modbus.pool.max-idle:10}")
private int maxIdle;
@Value("${modbus.pool.min-idle:3}")
private int minIdle;
@Value("${modbus.pool.max-wait:3000}")
private long maxWaitMillis;
@Value("${modbus.pool.time-between-eviction-runs:30000}")
private long timeBetweenEvictionRunsMillis;
@Value("${modbus.pool.min-evictable-idle-time:60000}")
private long minEvictableIdleTimeMillis;
public ModbusConnectionManager modbusConnectionManager() {
ModbusConnectionManager manager = new ModbusConnectionManager();
manager.setMaxTotal(maxTotal);
manager.setMaxIdle(maxIdle);
manager.setMinIdle(minIdle);
manager.setMaxWaitMillis(maxWaitMillis);
manager.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
manager.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
return manager;
}
}

View File

@ -1,254 +0,0 @@
package com.xzzn.framework.manager;
import com.ghgande.j2mod.modbus.net.TCPMasterConnection;
import com.xzzn.ems.domain.EmsDevicesSetting;
import com.xzzn.ems.mapper.EmsDevicesSettingMapper;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.stream.Collectors;
@Component
public class ModbusConnectionManager implements ApplicationRunner {
private static final Logger logger = LoggerFactory.getLogger(ModbusConnectionManager.class);
private final Map<Integer, ModbusConnectionWrapper> connectionPool = new ConcurrentHashMap<>();
// 连接池配置参数
private int maxTotal = 20;
private int maxIdle = 10;
private int minIdle = 3;
private long maxWaitMillis = 3000;
private long timeBetweenEvictionRunsMillis = 30000;
private long minEvictableIdleTimeMillis = 60000;
private ScheduledExecutorService scheduler;
@Autowired
private EmsDevicesSettingMapper deviceRepo;
@Override
public void run(ApplicationArguments args) throws Exception {
init();
}
public void init() {
// 启动心跳检测线程
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(this::heartbeatCheck, 1, 5, TimeUnit.MINUTES);
logger.info("Modbus连接管理器已初始化");
}
/**
* 获取连接(带自动创建和缓存)
*/
public ModbusConnectionWrapper getConnection(EmsDevicesSetting device) throws Exception {
return connectionPool.compute(Math.toIntExact(device.getId()), (id, wrapper) -> {
try {
if (wrapper == null || !wrapper.isActive()) {
if (connectionPool.size() >= maxTotal) {
evictIdleConnection();
}
logger.info("创建新连接: {}", device);
return new ModbusConnectionWrapper(createRawConnection(device));
}
wrapper.updateLastAccess();
return wrapper;
} catch (Exception e) {
throw new RuntimeException("连接创建失败: " + device.getId(), e);
}
});
}
/**
* 创建原始Modbus连接
*/
private TCPMasterConnection createRawConnection(EmsDevicesSetting device) throws Exception {
try {
InetAddress addr = InetAddress.getByName("192.168.80.100");
TCPMasterConnection connection = new TCPMasterConnection(addr);
connection.setPort(502);
connection.setTimeout(5000);
connection.connect();
return connection;
} catch (Exception e) {
logger.error("创建Modbus连接失败: {}", device, e);
throw e;
}
}
/**
* 心跳检测
*/
private void heartbeatCheck() {
logger.info("开始监控Modbus连接池状态当前连接数: {}", connectionPool.size());
// 步骤1获取所有活跃设备列表与轮询逻辑共用同一批设备
List<EmsDevicesSetting> activeDevices = null;
if (activeDevices == null || activeDevices.isEmpty()) {
logger.warn("无活跃设备,心跳检测仅清理无效连接");
}
// 步骤2清理无效连接遍历连接池移除已失效的连接
List<Integer> invalidDeviceIds = new ArrayList<>();
connectionPool.forEach((deviceId, wrapper) -> {
try {
if (!wrapper.isActive()) {
logger.info("连接{}已失效,移除连接", deviceId);
invalidDeviceIds.add(deviceId);
wrapper.close();
}
} catch (Exception e) {
logger.error("心跳检测异常: {}", deviceId, e);
}
});
// 批量移除无效连接(避免边遍历边修改)
invalidDeviceIds.forEach(connectionPool::remove);
logger.debug("移除无效连接后,连接池大小: {}", connectionPool.size());
// 步骤3补充关键设备的连接优先保障活跃设备的连接存在
if (!activeDevices.isEmpty()) {
// 3.1 先为所有活跃设备预加载连接(确保需要轮询的设备有连接)
preloadCriticalConnection(activeDevices);
// 3.2 若连接数仍不足minIdle补充额外连接可选避免连接池过小
int currentSize = connectionPool.size();
if (currentSize < minIdle) {
logger.info("连接数{}不足最小空闲数{},补充额外连接", currentSize, minIdle);
// 从活跃设备中选未创建连接的设备补充(避免重复创建)
List<EmsDevicesSetting> needMoreDevices = activeDevices.stream()
.filter(device -> !connectionPool.containsKey(Math.toIntExact(device.getId())))
.limit(minIdle - currentSize) // 只补充差额
.collect(Collectors.toList());
preloadCriticalConnection(needMoreDevices); // 复用预加载方法
}
}
}
/**
* 预加载关键连接
*/
private void preloadCriticalConnection(List<EmsDevicesSetting> devices) {
// 简化示例,不实现具体逻辑
logger.info("预加载连接: 连接池当前大小={}, 最小空闲={}", connectionPool.size(), minIdle);
devices.forEach(device -> {
try {
Integer deviceId = Math.toIntExact(device.getId());
if (!connectionPool.containsKey(deviceId)) {
getConnection(device); // 复用已有创建逻辑
}
} catch (Exception e) {
logger.warn("预加载设备{}连接失败", device.getId(), e);
}
});
}
/**
* 移除最久未使用的空闲连接
*/
private void evictIdleConnection() {
if (connectionPool.isEmpty()) {
return;
}
ModbusConnectionWrapper oldestWrapper = null;
long oldestAccessTime = Long.MAX_VALUE;
for (ModbusConnectionWrapper wrapper : connectionPool.values()) {
if (wrapper.isActive() && wrapper.getLastAccessTime() < oldestAccessTime) {
oldestAccessTime = wrapper.getLastAccessTime();
oldestWrapper = wrapper;
}
}
if (oldestWrapper != null) {
logger.info("移除空闲连接: {}", oldestWrapper.getConnection());
connectionPool.values().remove(oldestWrapper);
oldestWrapper.close();
}
}
// 移除指定设备连接
public void removeConnection(Integer deviceId) {
ModbusConnectionWrapper wrapper = connectionPool.remove(deviceId);
if (wrapper != null) {
wrapper.close(); // 双重保障,确保连接关闭
logger.info("连接池主动移除设备{}的连接", deviceId);
}
}
/**
* 判断是否应该移除空连接池
*/
private boolean shouldRemoveEmptyPool(GenericObjectPool<?> pool) {
// 可根据配置或逻辑决定是否移除空连接池
// 这里简单实现为当连接池数量超过最大值时移除
return connectionPool.size() > maxTotal;
}
/**
* 关闭连接
*/
private void closeConnection(TCPMasterConnection connection) {
try {
if (connection != null && connection.isConnected()) {
connection.close();
}
} catch (Exception e) {
logger.error("关闭Modbus连接失败", e);
}
}
// 容器销毁时关闭线程池
@PreDestroy
public void destroy() {
if (scheduler != null) {
scheduler.shutdown();
try {
if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
scheduler.shutdownNow();
}
} catch (InterruptedException e) {
scheduler.shutdownNow();
}
}
}
// Getters and Setters
public void setMaxTotal(int maxTotal) {
this.maxTotal = maxTotal;
}
public void setMaxIdle(int maxIdle) {
this.maxIdle = maxIdle;
}
public void setMinIdle(int minIdle) {
this.minIdle = minIdle;
}
public void setMaxWaitMillis(long maxWaitMillis) {
this.maxWaitMillis = maxWaitMillis;
}
public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
}
public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
}
}

View File

@ -1,81 +0,0 @@
package com.xzzn.framework.manager;
import com.ghgande.j2mod.modbus.net.SerialConnection;
import com.ghgande.j2mod.modbus.net.TCPMasterConnection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.atomic.AtomicInteger;
public class ModbusConnectionWrapper {
private static final Logger logger = LoggerFactory.getLogger(ModbusConnectionWrapper.class);
private static final AtomicInteger COUNTER = new AtomicInteger(0);
private final Object connection;
private final int connectionId;
private volatile long lastAccessTime;
private volatile boolean active = true;
public ModbusConnectionWrapper(Object connection) {
this.connection = connection;
this.connectionId = COUNTER.incrementAndGet();
this.lastAccessTime = System.currentTimeMillis();
logger.info("创建连接包装: {}", this);
}
public boolean isActive() {
if (!active) return false;
try {
// 检查连接是否物理上有效
if (connection instanceof TCPMasterConnection) {
return ((TCPMasterConnection) connection).isConnected();
} else if (connection instanceof SerialConnection) {
return ((SerialConnection) connection).isOpen();
}
} catch (Exception e) {
logger.error("连接状态检查失败: {}", connectionId, e);
return false;
}
// 默认检查空闲时间
return System.currentTimeMillis() - lastAccessTime < 300000; // 5分钟
}
public void updateLastAccess() {
this.lastAccessTime = System.currentTimeMillis();
}
public Object getConnection() {
return connection;
}
public void close() {
try {
logger.info("关闭连接: {}", this);
if (connection instanceof TCPMasterConnection) {
((TCPMasterConnection) connection).close();
} else if (connection instanceof SerialConnection) {
((SerialConnection) connection).close();
}
} catch (Exception e) {
logger.error("关闭连接失败: {}", connectionId, e);
} finally {
this.active = false;
}
}
public long getLastAccessTime() {
return lastAccessTime;
}
@Override
public String toString() {
return "ModbusConnectionWrapper{" +
"connectionId=" + connectionId +
", active=" + active +
", lastAccessTime=" + lastAccessTime +
'}';
}
}

View File

@ -1,5 +1,6 @@
package com.xzzn.framework.manager; package com.xzzn.framework.manager;
import com.xzzn.ems.service.IEmsAlarmRecordsService;
import org.eclipse.paho.client.mqttv3.*; import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -16,6 +17,7 @@ import java.util.concurrent.ConcurrentHashMap;
public class MqttLifecycleManager implements ApplicationRunner, SmartLifecycle, MqttCallback { public class MqttLifecycleManager implements ApplicationRunner, SmartLifecycle, MqttCallback {
private final MqttConnectOptions connectOptions; private final MqttConnectOptions connectOptions;
private final IEmsAlarmRecordsService iEmsAlarmRecordsService;
private MqttClient mqttClient; private MqttClient mqttClient;
private volatile boolean running = false; private volatile boolean running = false;
@ -23,8 +25,9 @@ public class MqttLifecycleManager implements ApplicationRunner, SmartLifecycle,
private final ConcurrentHashMap<String, SubscriptionInfo> subscriptions = new ConcurrentHashMap<>(); private final ConcurrentHashMap<String, SubscriptionInfo> subscriptions = new ConcurrentHashMap<>();
@Autowired @Autowired
public MqttLifecycleManager(MqttConnectOptions connectOptions) { public MqttLifecycleManager(MqttConnectOptions connectOptions, IEmsAlarmRecordsService iEmsAlarmRecordsService) {
this.connectOptions = connectOptions; this.connectOptions = connectOptions;
this.iEmsAlarmRecordsService = iEmsAlarmRecordsService;
} }
// Spring Boot 启动完成后执行 // Spring Boot 启动完成后执行
@ -107,7 +110,11 @@ public class MqttLifecycleManager implements ApplicationRunner, SmartLifecycle,
subscriptions.put(topic, new SubscriptionInfo(listener, qos)); subscriptions.put(topic, new SubscriptionInfo(listener, qos));
} catch (MqttException e) { } catch (MqttException e) {
System.err.println("Subscribe failed: " + e.getMessage()); System.err.println("Subscribe failed: " + e.getMessage());
// 订阅失败-增加告警
iEmsAlarmRecordsService.addSubFailedAlarmRecord(topic);
} }
// 订阅成功了-校验是否存在未处理或者处理中的订阅失败信息
iEmsAlarmRecordsService.checkFailedRecord(topic);
} }
// 发布方法 // 发布方法

View File

@ -1,126 +0,0 @@
package com.xzzn.framework.scheduler;
import com.xzzn.ems.domain.EmsDevicesSetting;
import com.xzzn.ems.mapper.EmsDevicesSettingMapper;
import com.xzzn.ems.mapper.EmsMqttMessageMapper;
import com.xzzn.framework.manager.ModbusConnectionManager;
import com.xzzn.framework.manager.ModbusConnectionWrapper;
import com.xzzn.framework.manager.MqttLifecycleManager;
import com.xzzn.framework.web.service.ModbusService;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
@EnableScheduling
public class ModbusPoller {
private static final Logger logger = LoggerFactory.getLogger(ModbusPoller.class);
private final MqttLifecycleManager mqttLifecycleManager;
@Autowired
private ModbusConnectionManager connectionManager;
@Autowired
private ModbusService modbusService;
@Autowired
private EmsDevicesSettingMapper deviceRepo;
@Autowired
private EmsMqttMessageMapper emsMqttMessageMapper;
@Autowired
public ModbusPoller(MqttLifecycleManager mqttLifecycleManager) {
this.mqttLifecycleManager = mqttLifecycleManager;
}
// 每5分钟触发支持cron表达式动态配置
@Scheduled(cron = "${modbus.poll.interval}")
@Async("modbusTaskExecutor")
public void pollAllDevices() {
logger.info("开始执行Modbus设备轮询...");
List<EmsDevicesSetting> activeDevices = deviceRepo.selectEmsDevicesSettingList(null);
EmsDevicesSetting device = activeDevices.get(0);
try {
processData(device,null);
} catch (Exception e) {
logger.error("调度设备{}任务失败", device.getId(), e);
}
/*
try {
pollSingleDevice(device);
} catch (Exception e) {
logger.error("调度设备{}任务失败", device.getId(), e);
}*/
/*activeDevices.forEach(device -> {
try {
CompletableFuture.runAsync(() -> pollSingleDevice(device))
.exceptionally(e -> {
logger.error("设备{}轮询异常", device.getId(), e);
return null;
});
} catch (Exception e) {
logger.error("调度设备{}任务失败", device.getId(), e);
}
});*/
}
private void pollSingleDevice(EmsDevicesSetting device) {
logger.debug("开始轮询设备: {}", device.getSiteId(), device.getDeviceName(), device.getId());
ModbusConnectionWrapper wrapper = null;
try {
// 获取连接
wrapper = connectionManager.getConnection(device);
// 读取保持寄存器
int[] data = modbusService.readHoldingRegisters(
wrapper.getConnection(),
1, //从站ID
10 // 寄存器数量
);
// 处理读取到的数据
processData(device, data);
} catch (Exception e) {
logger.error("轮询设备{}失败: {}", device.getId(), e.getMessage());
// 标记连接为无效
if (wrapper != null) {
wrapper.close();
connectionManager.removeConnection(Integer.parseInt(device.getDeviceId()));
}
throw new RuntimeException("轮询设备失败", e);
}
}
// 处理获取到的数据发到mqtt服务上
private void processData(EmsDevicesSetting device, int[] data) throws MqttException {
/*if (data == null || data.length == 0) {
logger.warn("设备{}返回空数据", device.getId());
return;
}*/
/*// 数据处理逻辑
StringBuilder sb = new StringBuilder();
sb.append("设备[").append(device.getDeviceName()).append("]数据: ");
for (int i = 0; i < data.length; i++) {
sb.append("R").append(i).append("=").append(data[i]).append(" ");
}
logger.info(sb.toString());*/
// 测试发送mqtt
/* EmsMqttMessage msg = emsMqttMessageMapper.selectEmsMqttMessageById(1L);
String dataJson = msg.getMqttMessage();
String topic = msg.getMqttTopic();
logger.info("topic" + topic);
logger.info("dataJson" + dataJson);
// 将设备数据下发到mqtt服务器上
mqttLifecycleManager.publish(topic, dataJson, 0);*/
}
}

View File

@ -1,170 +0,0 @@
package com.xzzn.framework.scheduler;
import com.alibaba.fastjson2.JSON;
import com.xzzn.common.utils.DateUtils;
import com.xzzn.common.utils.StringUtils;
import com.xzzn.ems.domain.EmsStrategyCurve;
import com.xzzn.ems.domain.EmsStrategyTemp;
import com.xzzn.ems.domain.EmsStrategyTimeConfig;
import com.xzzn.ems.domain.vo.StrategyPowerDataVo;
import com.xzzn.ems.domain.vo.StrategyRunningVo;
import com.xzzn.ems.mapper.*;
import com.xzzn.framework.manager.ModbusConnectionManager;
import com.xzzn.framework.manager.MqttLifecycleManager;
import com.xzzn.framework.web.service.ModbusService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
@Component
@EnableScheduling
public class StrategyPoller {
private static final Logger logger = LoggerFactory.getLogger(StrategyPoller.class);
private final MqttLifecycleManager mqttLifecycleManager;
@Autowired
private ModbusConnectionManager connectionManager;
@Autowired
private ModbusService modbusService;
@Autowired
private EmsDevicesSettingMapper deviceRepo;
@Autowired
private EmsMqttMessageMapper emsMqttMessageMapper;
@Autowired
private EmsStrategyRunningMapper emsStrategyRunningMapper;
@Autowired
private EmsStrategyTempMapper emsStrategyTempMapper;
@Autowired
private EmsStrategyTimeConfigMapper emsStrategyTimeConfigMapper;
@Autowired
private EmsStrategyCurveMapper emsStrategyCurveMapper;
@Autowired
public StrategyPoller(MqttLifecycleManager mqttLifecycleManager) {
this.mqttLifecycleManager = mqttLifecycleManager;
}
// 每1分钟触发支持cron表达式动态配置
@Scheduled(cron = "0 */1 * * * *")
@Async("strategyTaskExecutor")
public void pollAllDevices() {
logger.info("开始执行策略数据轮询...");
List<StrategyRunningVo> strategyRunningVoList = emsStrategyRunningMapper.getRunningList(null);
strategyRunningVoList.forEach(strategyVo -> {
try {
CompletableFuture.runAsync(() -> {
processData(strategyVo);
})
.exceptionally(e -> {
logger.error("策略{}轮询异常", strategyVo.getId(), e);
return null;
});
} catch (Exception e) {
logger.error("策略下方{}任务失败", strategyVo.getId(), e);
}
});
}
// 处理获取到的数据发到mqtt服务上
private void processData(StrategyRunningVo strategyVo) {
logger.info("策略下发数据处理开始");
// 根据运行策略获取主副策略的模板数据
Long mainStrategyId = strategyVo.getMainStrategyId();
Long auxStrategyId = strategyVo.getAuxStrategyId();
String siteId = strategyVo.getSiteId();
// 处理主策略数据
if (mainStrategyId != null && StringUtils.isNotBlank(siteId)) {
dealStrategyCurveData(mainStrategyId, siteId);
}
// 处理副策略数据
if (auxStrategyId != null && StringUtils.isNotBlank(siteId)) {
dealStrategyCurveData(auxStrategyId, siteId);
}
// 策略数据下发-下方格式暂无
logger.info("策略下发结束");
}
private void dealStrategyCurveData(Long mainStrategyId, String siteId) {
// 获取当前策略的所有模板
List<Map<String, String>> temps = emsStrategyTempMapper.getTempNameList(mainStrategyId,siteId);
if (temps != null && temps.size() > 0) {
for (Map<String, String> temp : temps) {
String tempId = temp.get("templateId");
List<EmsStrategyTimeConfig> timeConfigs = emsStrategyTimeConfigMapper.getAllTimeConfigByTempId(tempId);
if (timeConfigs != null && timeConfigs.size() > 0) {
for (EmsStrategyTimeConfig timeConfig : timeConfigs) {
EmsStrategyCurve curve = new EmsStrategyCurve();
curve.setStrategyId(mainStrategyId);
curve.setSiteId(siteId);
curve.setTemplateId(tempId);
curve.setCreateBy("system");
curve.setCreateTime(DateUtils.getNowDate());
curve.setUpdateBy("system");
curve.setUpdateTime(DateUtils.getNowDate());
// 时间设置
int month = Integer.parseInt(timeConfig.getMonth().toString());
String[] dateList= dealWithMonth(month);
curve.setMonth(Long.valueOf(month));
curve.setStartDate(DateUtils.dateTime(DateUtils.YYYY_MM_DD,dateList[0]));
curve.setEndDate(DateUtils.dateTime(DateUtils.YYYY_MM_DD,dateList[1]));
// powerData-存json格式
List<EmsStrategyTemp> powerConfig = emsStrategyTempMapper.selectStrategyTempByTempId(tempId);
List<StrategyPowerDataVo> powerDataVoList = new ArrayList<>();
for (int i = 0; i < powerConfig.size(); i++) {
EmsStrategyTemp powerTemp = powerConfig.get(i);
StrategyPowerDataVo powerDataVo = new StrategyPowerDataVo();
powerDataVo.setPowerData(powerTemp.getChargeDischargePower());
powerDataVo.setEndTime(DateUtils.parseDateToStr("HH:mm:ss",powerTemp.getEndTime()));
powerDataVo.setStartTime(DateUtils.parseDateToStr("HH:mm:ss",powerTemp.getStartTime()));
powerDataVoList.add(powerDataVo);
}
curve.setPowerData(powerDataVoList !=null ? JSON.toJSON(powerDataVoList).toString() : "");
// 记录推送记录
emsStrategyCurveMapper.insertEmsStrategyCurve(curve);
// 设置已下发
timeConfig.setIsPost(0);
emsStrategyTimeConfigMapper.updateEmsStrategyTimeConfig(timeConfig);
}
}
}
}
}
private String[] dealWithMonth(int month) {
// 获取当前年份
int currentYear = LocalDate.now().getYear();
// 创建YearMonth对象表示当年指定的月份
YearMonth yearMonth = YearMonth.of(currentYear, month);
// 获取当月的第一天和最后一天
LocalDate firstDay = yearMonth.atDay(1);
LocalDate lastDay = yearMonth.atEndOfMonth();
// 定义日期格式(年月日)
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
// 格式化日期
return new String[]{
firstDay.format(formatter),
lastDay.format(formatter)
};
}
}

View File

@ -1,101 +0,0 @@
package com.xzzn.framework.web.service;
import com.ghgande.j2mod.modbus.ModbusException;
import com.ghgande.j2mod.modbus.ModbusIOException;
import com.ghgande.j2mod.modbus.io.ModbusTCPTransaction;
import com.ghgande.j2mod.modbus.msg.ReadInputRegistersRequest;
import com.ghgande.j2mod.modbus.msg.ReadInputRegistersResponse;
import com.ghgande.j2mod.modbus.net.SerialConnection;
import com.ghgande.j2mod.modbus.net.TCPMasterConnection;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
/**
* Modbus操作服务添加重试机制
*/
@Service
public class ModbusService {
private static final Logger logger = LoggerFactory.getLogger(ModbusService.class);
@Retryable(
value = {ModbusException.class}, // 仅对自定义Modbus异常重试
maxAttempts = 3, // 最大重试3次1次原始调用 + 2次重试
backoff = @Backoff(delay = 1000, multiplier = 2) // 退避策略1s → 2s → 4s
)
@CircuitBreaker(name = "modbusOperation", fallbackMethod = "readRegistersFallback")
public int[] readHoldingRegisters(Object connection, int startAddr, int count) throws ModbusException {
try {
if (connection instanceof TCPMasterConnection) {
return readTcpRegisters((TCPMasterConnection) connection, startAddr, count);
} else if (connection instanceof SerialConnection) {
return readRtuRegisters((SerialConnection) connection, startAddr, count);
}
throw new IllegalArgumentException("不支持的连接类型: " + connection.getClass().getName());
} catch (ModbusIOException e) {
throw new ModbusException("通信故障", e);
} catch (Exception e) {
throw new ModbusException("系统错误", e);
}
}
private int[] readRtuRegisters(SerialConnection connection, int startAddr, int count) {
return null;
}
private int[] readTcpRegisters(TCPMasterConnection conn, int start, int count) throws ModbusException {
// 验证连接是否已建立
if (!conn.isConnected()) {
throw new ModbusIOException("TCP连接未建立");
}
// 使用正确的功能码03 - 读取保持寄存器ReadHoldingRegistersRequest
ReadInputRegistersRequest request = new ReadInputRegistersRequest(start, count);
ModbusTCPTransaction transaction = new ModbusTCPTransaction(conn);
transaction.setRequest(request);
// 设置超时避免长时间阻塞
transaction.setRetries(2);
try {
transaction.execute();
ReadInputRegistersResponse response = (ReadInputRegistersResponse) transaction.getResponse();
if (response == null) {
throw new ModbusException("Modbus异常响应: " + response.getMessage());
}
// 正确解析寄存器值
return parseRegisters(response);
} catch (ModbusException e) {
// 记录详细错误信息
logger.error("读取TCP寄存器失败: {}", e.getMessage());
throw e;
}
}
/**
* 解析Modbus响应中的寄存器值
*/
private int[] parseRegisters(ReadInputRegistersResponse response) {
int byteCount = response.getByteCount();
int[] result = new int[byteCount / 2];
for (int i = 0; i < result.length; i++) {
// 转换为无符号整数
result[i] = response.getRegisterValue(i) & 0xFFFF;
}
return result;
}
/**
* 熔断降级方法
*/
public int[] readRegistersFallback(Object connection, int startAddr, int count, Exception e) {
logger.warn("Modbus操作降级原因: {}),返回空数据", e.getMessage());
return new int[0];
}
}

View File

@ -34,6 +34,10 @@
<groupId>com.xzzn</groupId> <groupId>com.xzzn</groupId>
<artifactId>ems-common</artifactId> <artifactId>ems-common</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.xzzn</groupId>
<artifactId>ems-framework</artifactId>
</dependency>
</dependencies> </dependencies>

View File

@ -0,0 +1,33 @@
package com.xzzn.quartz.config;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.annotation.PreDestroy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Modbus操作执行器配置
* 所有Modbus读写操作必须通过此单线程执行器串行执行避免并发访问导致通讯故障
*/
@Configuration
public class ModbusExecutorConfig {
private final ExecutorService modbusExecutor = Executors.newSingleThreadExecutor(r -> {
Thread t = new Thread(r, "modbus-io-thread");
t.setDaemon(false);
return t;
});
@Bean(name = "modbusExecutor")
public ExecutorService modbusExecutor() {
return modbusExecutor;
}
@PreDestroy
public void shutdown() {
modbusExecutor.shutdown();
}
}

View File

@ -0,0 +1,34 @@
package com.xzzn.quartz.config;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.springframework.stereotype.Component;
@Component
public class ScheduledTask {
private ScheduledExecutorService executor = Executors.newScheduledThreadPool(10);
private final Map<String, ScheduledFuture<?>> futureMap = new ConcurrentHashMap<>();
public void startTask(String deviceId, Runnable task, long period) {
stopTask(deviceId); // 如果已有同ID任务在运行先停止
ScheduledFuture<?> future = executor.scheduleAtFixedRate(task, 0, period, TimeUnit.MILLISECONDS);
futureMap.put(deviceId, future);
}
public void stopTask(String deviceId) {
ScheduledFuture<?> future = futureMap.get(deviceId);
if (future != null && !future.isDone()) {
future.cancel(true);
}
futureMap.remove(deviceId);
}
}

View File

@ -0,0 +1,343 @@
package com.xzzn.quartz.task;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.serotonin.modbus4j.ModbusMaster;
import com.xzzn.common.constant.RedisKeyConstants;
import com.xzzn.common.core.modbus.ModbusProcessor;
import com.xzzn.common.core.modbus.domain.DeviceConfig;
import com.xzzn.common.core.redis.RedisCache;
import com.xzzn.common.enums.DeviceRunningStatus;
import com.xzzn.common.utils.DateUtils;
import com.xzzn.ems.domain.EmsDevicesSetting;
import com.xzzn.ems.mapper.EmsDevicesSettingMapper;
import com.xzzn.ems.service.IEmsAlarmRecordsService;
import com.xzzn.ems.service.impl.DeviceDataProcessServiceImpl;
import com.xzzn.framework.manager.MqttLifecycleManager;
import com.xzzn.framework.web.service.MqttPublisher;
import com.xzzn.quartz.config.ScheduledTask;
import com.xzzn.quartz.domain.SysJob;
import com.xzzn.quartz.service.ISysJobService;
import com.xzzn.quartz.util.CronUtils;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
/**
* 轮询设备-通过modbus协议读取数据
*/
@Component("modbusPoller")
public class ModbusPoller {
private static final Logger log = LoggerFactory.getLogger(ModbusPoller.class);
private final MqttLifecycleManager mqttLifecycleManager;
private final ScheduledTask scheduledTask;
private final ObjectMapper objectMapper = new ObjectMapper();
private final Map<String, Integer> deviceFailureCounts = new ConcurrentHashMap<>();
private final AtomicBoolean polling = new AtomicBoolean(false);
@Resource(name = "modbusExecutor")
private ExecutorService modbusExecutor;
@Autowired
private ModbusProcessor modbusProcessor;
@Autowired
private IEmsAlarmRecordsService iEmsAlarmRecordsService;
@Autowired
private ISysJobService iSysJobService;
@Autowired
private DeviceDataProcessServiceImpl deviceDataProcessServiceImpl;
@Autowired
private EmsDevicesSettingMapper emsDevicesSettingMapper;
@Autowired
private RedisCache redisCache;
@Autowired
private MqttPublisher mqttPublisher;
@Value("${mqtt.topic}")
private String topic;
@Value("${mqtt.siteId}")
private String siteId;
@Autowired
public ModbusPoller(MqttLifecycleManager mqttLifecycleManager, ScheduledTask scheduledTask) {
this.mqttLifecycleManager = mqttLifecycleManager;
this.scheduledTask = scheduledTask;
}
public void pollAllDevices() {
if (!polling.compareAndSet(false, true)) {
log.warn("上一次轮询尚未完成,本次轮询跳过");
return;
}
Path devicesDir = Paths.get(System.getProperty("user.dir"), "devices");
if (!Files.exists(devicesDir)) {
log.error("Devices目录不存在: {}", devicesDir);
polling.set(false);
return;
}
List<Path> jsonFiles = null;
try {
jsonFiles = Files.list(devicesDir)
.filter(path -> path.toString().endsWith(".json"))
.collect(Collectors.toList());
} catch (IOException e) {
log.error("modbusPoller.loadConfigs 获取设备配置文件失败: {}", devicesDir, e);
polling.set(false);
return;
}
// 按主机IP分组同一网关的不同端口也归为一组避免并发访问导致Connection Reset
Map<String, List<DeviceConfig>> groupedByHost = new HashMap<>();
for (Path filePath : jsonFiles) {
DeviceConfig config = null;
try {
config = objectMapper.readValue(filePath.toFile(), DeviceConfig.class);
} catch (IOException e) {
log.error("modbusPoller.loadConfigs 解析设备配置文件失败: {}", filePath, e);
continue;
}
if (config.isEnabled()) {
// 只按主机IP分组确保同一网关的所有端口串行访问
String hostKey = config.getHost();
groupedByHost.computeIfAbsent(hostKey, k -> new ArrayList<>()).add(config);
}
}
// 使用单线程 executor 串行执行所有主机的 Modbus 操作
// 将所有主机的设备按顺序串行处理,避免任何并发访问
Future<?> future = modbusExecutor.submit(() -> {
for (Map.Entry<String, List<DeviceConfig>> entry : groupedByHost.entrySet()) {
String hostKey = entry.getKey();
List<DeviceConfig> configs = entry.getValue();
for (DeviceConfig config : configs) {
try {
scheduledStart(config);
// 每次读取后等待200ms给Modbus网关足够的处理时间
Thread.sleep(200);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
log.warn("Modbus轮询被中断");
return;
} catch (Exception e) {
log.error("采集设备数据异常: {}", config.getDeviceName(), e);
}
}
log.info("采集设备数据{}轮询任务执行完成", hostKey);
}
});
try {
future.get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.warn("Modbus轮询任务等待中断");
} catch (Exception e) {
log.error("Modbus轮询任务执行异常", e);
} finally {
polling.set(false);
}
}
public void scheduledStart(DeviceConfig config) {
if (config.isEnabled()) {
log.info("Reading data from devices: {}", config.getDeviceName());
// 带重试的读取最多重试2次
Map<String, Object> data = readWithRetry(config, 2);
List<String> rawValuEmptyList = new ArrayList<>();
// 在这里处理采集到的数据空
config.getTags().forEach(tag -> {
Object rawValue = data.get(tag.getKey());
if (rawValue != null) {
float value = 0;
if (rawValue instanceof Number) {
value = ((Number) rawValue).floatValue(); // 安全地转换为 float
} else {
log.error("tag:{},无法将数据转换为数字: {}", tag.getKey(), rawValue);
}
value = tag.getA() * value * value + tag.getK() * value + tag.getB();
int intValue = (int) value;
if (tag.getBit() != null) {
log.info("tag:{},bit:{},value:{}", tag.getKey(), tag.getBit(), value);
String binary = Integer.toBinaryString(intValue);
data.put(tag.getKey(), binary);
} else {
data.put(tag.getKey(), value);
}
} else {
// data.put(tag.getKey(), rawValue);
// log.warn("tag:{},数据为空: {}", tag.getKey(), rawValue);
rawValuEmptyList.add("tag: " + tag.getKey() + ",数据为空: " + rawValue);
}
});
if (!rawValuEmptyList.isEmpty()) {
log.warn("设备 {} 数据为空: {}", config.getDeviceName(), JSON.toJSONString(rawValuEmptyList));
}
log.info("Data from {}: {}", config.getDeviceName(), data);
String deviceNumber = config.getDeviceNumber();
//处理数据并发送MQTT消息、保存Redis数据和数据入库
processingData(data, deviceNumber);
}
}
/**
* 带重试的读取方法
*/
private Map<String, Object> readWithRetry(DeviceConfig config, int maxRetries) {
Map<String, Object> data = new HashMap<>();
for (int attempt = 0; attempt <= maxRetries; attempt++) {
try {
ModbusMaster master = modbusProcessor.borrowMaster(config);
data = modbusProcessor.readDataFromDevice(config, master);
// 如果读取成功(有数据),直接返回
if (!data.isEmpty()) {
if (attempt > 0) {
log.info("设备 {} 第 {} 次重试成功", config.getDeviceName(), attempt);
}
return data;
}
// 读取返回空数据,等待后重试
if (attempt < maxRetries) {
log.warn("设备 {} 读取返回空数据等待1秒后重试 ({}/{})",
config.getDeviceName(), attempt + 1, maxRetries);
Thread.sleep(1000);
}
} catch (Exception e) {
log.error("设备 {} 读取异常 ({}/{}): {}",
config.getDeviceName(), attempt + 1, maxRetries, e.getMessage());
if (attempt < maxRetries) {
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
break;
}
}
}
}
// 所有重试都失败
log.error("设备 {} 读取失败,已重试 {} 次", config.getDeviceName(), maxRetries);
return data;
}
private void processingData(Map<String, Object> data, String deviceNumber) {
if (CollectionUtils.isEmpty(data)) {
// 增加失败计数
int failureCount = deviceFailureCounts.getOrDefault(deviceNumber, 0) + 1;
deviceFailureCounts.put(deviceNumber, failureCount);
log.warn("设备 {} 数据读取失败,当前连续失败次数: {}", deviceNumber, failureCount);
// 连续6次失败触发报警
if (failureCount >= 6) {
addDeviceOfflineRecord(siteId, deviceNumber);
log.error("设备 {} 连续 {} 次未读取到数据,触发报警", deviceNumber, failureCount);
}
return;
}
// 数据读取成功,重置计数器
deviceFailureCounts.remove(deviceNumber);
// 读取到数据后告警自恢复
deleteDeviceOfflineRecord(siteId, deviceNumber);
// 发送MQTT消息、保存Redis数据和数据入库
Long timestamp = System.currentTimeMillis();
JSONObject json = new JSONObject();
json.put("Data", data);
json.put("timestamp", timestamp);
json.put("Device", deviceNumber);
sendMqttMsg(json);
saveRedisData(json, deviceNumber);
saveDataToDatabase(data, deviceNumber, timestamp);
}
public void sendMqttMsg(JSONObject json) {
try {
mqttPublisher.publish(topic, Collections.singletonList(json).toString(), 0);
} catch (MqttException e) {
log.error("MQTT消息发布失败: {}, reason code: {}", json.toJSONString(), e.getReasonCode() ,e);
}
log.info("已发送数据: {}", json.toJSONString());
}
public void saveRedisData(JSONObject obj, String deviceNumber) {
try {
// 存放mqtt原始每个设备最晚一次数据便于后面点位获取数据
redisCache.setCacheObject(RedisKeyConstants.ORIGINAL_MQTT_DATA + siteId + "_" + deviceNumber, obj);
// 存放每次同步数据,失效时间(同同步时间)-用于判断是否正常同步数据和保护策略查询
redisCache.setCacheObject(RedisKeyConstants.SYNC_DATA + siteId + "_" + deviceNumber, obj, 1, TimeUnit.MINUTES);
log.info("数据已成功存储在Redis: {}", deviceNumber);
} catch (Exception e) {
log.error("无法在设备的Redis中存储数据: {}", deviceNumber, e);
}
}
private void saveDataToDatabase(Map<String, Object> data, String deviceNumber, Long timestamp) {
deviceDataProcessServiceImpl.processingDeviceData(siteId, deviceNumber, JSON.toJSONString(data), DateUtils.convertUpdateTime(timestamp));
}
//处理设备连接失败的情况,更新设备状态为离线,添加报警记录
private void addDeviceOfflineRecord(String siteId, String deviceNumber) {
updateDeviceStatus(siteId, deviceNumber, DeviceRunningStatus.OFFLINE.getCode());
iEmsAlarmRecordsService.addDeviceOfflineRecord(siteId, deviceNumber);
}
//处理设备读取到数据的情况,更新设备状态为在线,报警记录自恢复
private void deleteDeviceOfflineRecord(String siteId, String deviceNumber) {
updateDeviceStatus(siteId, deviceNumber, DeviceRunningStatus.ONLINE.getCode());
iEmsAlarmRecordsService.deleteDeviceOfflineRecord(siteId, deviceNumber);
}
// 更新设备状态为在线或离线
private void updateDeviceStatus(String siteId, String deviceNumber, String deviceStatus) {
EmsDevicesSetting emsDevicesSetting = emsDevicesSettingMapper.getDeviceBySiteAndDeviceId(deviceNumber, siteId);
if (emsDevicesSetting != null && !Objects.equals(emsDevicesSetting.getDeviceStatus(), deviceStatus)) {
emsDevicesSetting.setDeviceStatus(deviceStatus);
emsDevicesSettingMapper.updateEmsDevicesSetting(emsDevicesSetting);
}
}
private int getScheduledTaskInterval() {
SysJob query = new SysJob();
query.setInvokeTarget("modbusPoller.pollAllDevices");
List<SysJob> sysJobs = iSysJobService.selectJobList(query);
return Math.toIntExact(CronUtils.getNextExecutionIntervalMillis(sysJobs.get(0).getCronExpression()));
}
}

View File

@ -0,0 +1,482 @@
package com.xzzn.quartz.task;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xzzn.common.annotation.SyncAfterInsert;
import com.xzzn.common.constant.RedisKeyConstants;
import com.xzzn.common.core.redis.RedisCache;
import com.xzzn.common.enums.AlarmLevelStatus;
import com.xzzn.common.enums.AlarmStatus;
import com.xzzn.common.enums.ProtPlanStatus;
import com.xzzn.common.enums.StrategyStatus;
import com.xzzn.common.utils.StringUtils;
import com.xzzn.ems.domain.EmsAlarmRecords;
import com.xzzn.ems.domain.EmsDevicesSetting;
import com.xzzn.ems.domain.EmsFaultIssueLog;
import com.xzzn.ems.domain.EmsFaultProtectionPlan;
import com.xzzn.ems.domain.EmsStrategyRunning;
import com.xzzn.ems.domain.vo.ProtectionPlanVo;
import com.xzzn.ems.domain.vo.ProtectionSettingVo;
import com.xzzn.ems.mapper.EmsAlarmRecordsMapper;
import com.xzzn.ems.mapper.EmsDevicesSettingMapper;
import com.xzzn.ems.mapper.EmsFaultIssueLogMapper;
import com.xzzn.ems.mapper.EmsFaultProtectionPlanMapper;
import com.xzzn.ems.mapper.EmsStrategyRunningMapper;
import com.xzzn.ems.service.IEmsFaultProtectionPlanService;
import com.xzzn.common.core.modbus.ModbusProcessor;
import com.xzzn.common.core.modbus.domain.DeviceConfig;
import com.xzzn.common.core.modbus.domain.WriteTagConfig;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
/**
* 告警保护方案轮询
*
* @author xzzn
*/
@Component("protectionPlanTask")
public class ProtectionPlanTask {
private static final Logger logger = LoggerFactory.getLogger(ProtectionPlanTask.class);
@Resource(name = "scheduledExecutorService")
private ScheduledExecutorService scheduledExecutorService;
@Autowired
private IEmsFaultProtectionPlanService iEmsFaultProtectionPlanService;
@Autowired
private EmsAlarmRecordsMapper emsAlarmRecordsMapper;
@Autowired
private EmsStrategyRunningMapper emsStrategyRunningMapper;
@Autowired
private EmsFaultProtectionPlanMapper emsFaultProtectionPlanMapper;
@Autowired
private RedisCache redisCache;
private static final ObjectMapper objectMapper = new ObjectMapper();
@Autowired
private EmsDevicesSettingMapper emsDevicesSettingMapper;
@Autowired
private ModbusProcessor modbusProcessor;
@Autowired
private EmsFaultIssueLogMapper emsFaultIssueLogMapper;
public ProtectionPlanTask(IEmsFaultProtectionPlanService iEmsFaultProtectionPlanService) {
this.iEmsFaultProtectionPlanService = iEmsFaultProtectionPlanService;
}
public void pollPlanList() {
Long planId = 0L;
try {
// 获取所有方案,轮询
List<EmsFaultProtectionPlan> planList = iEmsFaultProtectionPlanService.selectEmsFaultProtectionPlanList(null);
for (EmsFaultProtectionPlan plan : planList) {
planId = plan.getId();
String siteId = plan.getSiteId();
if (StringUtils.isEmpty(siteId)) {
return;
}
// 保护前提
String protectionSettings = plan.getProtectionSettings();
final List<ProtectionSettingVo> protSettings = objectMapper.readValue(
protectionSettings,
new TypeReference<List<ProtectionSettingVo>>() {}
);
if (protSettings == null) {
return;
}
// 处理告警保护方案
boolean isHighLevel = dealWithProtectionPlan(plan, protSettings);
if (isHighLevel) {
// 触发最高故障等级-结束循环
return;
}
}
} catch (Exception e) {
logger.error("轮询失败,方案id为{}", planId, e);
}
}
// 处理告警保护方案-返回触发下发方案时是否最高等级
// 需要同步云端
@SyncAfterInsert
private boolean dealWithProtectionPlan(EmsFaultProtectionPlan plan, List<ProtectionSettingVo> protSettings) {
logger.info("<轮询保护方案> 站点:{}方案ID:{}", plan.getSiteId(), plan.getId());
boolean isHighLevel = false;
String siteId = plan.getSiteId();
final Integer isAlertAlarm = plan.getIsAlert();
final Long status = plan.getStatus();
// 看方案是否启用,走不同判断
if (Objects.equals(status, ProtPlanStatus.STOP.getCode())) {
logger.info("<方案未启用> 站点:{}方案ID:{}", siteId, plan.getId());
// 未启用,获取方案的故障值与最新数据判断是否需要下发方案
if(checkIsNeedIssuedPlan(protSettings, siteId)){
if("3".equals(plan.getFaultLevel())){
isHighLevel = true;//最高故障等级
}
// 延时
final int faultDelay = plan.getFaultDelaySeconds().intValue();
ScheduledFuture<?> delayTask = scheduledExecutorService.schedule(() -> {
// 延时后再次确认是否仍满足触发条件(防止期间状态变化)
if (checkIsNeedIssuedPlan(protSettings, siteId)) {
// 判断是否需要生成告警
if (isAlertAlarm == 1) {
logger.info("<生成告警> 方案ID:{},站点:{}", plan.getId(), siteId);
EmsAlarmRecords alarmRecords = addAlarmRecord(siteId,plan.getFaultName(),
getAlarmLevel(plan.getFaultLevel()));
emsAlarmRecordsMapper.insertEmsAlarmRecords(alarmRecords);
}
// 是否有保护方案有则通过modbus连接设备下发方案
String protPlanJson = plan.getProtectionPlan();
if (protPlanJson != null && !protPlanJson.isEmpty() && !"[]".equals(protPlanJson)) {
logger.info("<下发保护方案> 方案内容:{}", protPlanJson);
executeProtectionActions(protPlanJson,siteId,plan.getId(),plan.getFaultLevel()); // 执行Modbus指令
}
// 更新方案状态为“已启用”
logger.info("<方案已启用> 方案ID:{}", plan.getId());
plan.setStatus(ProtPlanStatus.RUNNING.getCode());
emsFaultProtectionPlanMapper.updateEmsFaultProtectionPlan(plan);
// 更新该站点策略为暂停状态
updateStrategyRunningStatus(siteId, StrategyStatus.SUSPENDED.getCode());
}
}, faultDelay, TimeUnit.SECONDS);
}
} else {
logger.info("<方案已启用> 站点:{}方案ID:{}", siteId, plan.getId());
// 已启用,则获取方案的释放值与最新数据判断是否需要取消方案
if(checkIsNeedCancelPlan(protSettings, siteId)){
// 延时,
int releaseDelay = plan.getReleaseDelaySeconds().intValue();
ScheduledFuture<?> delayTask = scheduledExecutorService.schedule(() -> {
// 判断是否已存在未处理告警,有着取消
if(isAlertAlarm == 1){
logger.info("<取消告警>");
EmsAlarmRecords emsAlarmRecords = emsAlarmRecordsMapper.getFailedRecord(siteId,
plan.getFaultName(),getAlarmLevel(plan.getFaultLevel()));
if(emsAlarmRecords != null){
emsAlarmRecords.setStatus(AlarmStatus.DONE.getCode());
emsAlarmRecordsMapper.updateEmsAlarmRecords(emsAlarmRecords);
}
}
// 更新方案状态为“未启用”
logger.info("<方案变更为未启用> 方案ID:{}", plan.getId());
plan.setStatus(ProtPlanStatus.STOP.getCode());
plan.setUpdateBy("system");
emsFaultProtectionPlanMapper.updateEmsFaultProtectionPlan(plan);
// 更新该站点策略为启用状态
updateStrategyRunningStatus(siteId, StrategyStatus.RUNNING.getCode());
}, releaseDelay, TimeUnit.SECONDS);
}
}
return isHighLevel;
}
// 下发保护方案
private void executeProtectionActions(String protPlanJson, String siteId, Long planId, Integer faultLevel){
final List<ProtectionPlanVo> protPlanList;
try {
protPlanList = objectMapper.readValue(
protPlanJson,
new TypeReference<List<ProtectionPlanVo>>() {}
);
if (protPlanList == null) {
return;
}
// 遍历保护方案
for (ProtectionPlanVo plan : protPlanList) {
if (StringUtils.isEmpty(plan.getDeviceId()) || StringUtils.isEmpty(plan.getPoint())) {
return;
}
// 给设备发送指令记录日志,并同步云端
EmsFaultIssueLog faultIssueLog = createLogEntity(plan,siteId);
faultIssueLog.setLogLevel(faultLevel);
emsFaultIssueLogMapper.insertEmsFaultIssueLog(faultIssueLog);
// 通过modbus连接设备发送数据
executeSinglePlan(plan,siteId);
}
} catch (Exception e) {
logger.error("下发保护方案失败,方案id为", planId, e);
}
}
private EmsFaultIssueLog createLogEntity(ProtectionPlanVo plan,String siteId) {
EmsFaultIssueLog faultIssueLog = new EmsFaultIssueLog();
faultIssueLog.setLogId(UUID.randomUUID().toString());
faultIssueLog.setLogTime(new Date());
faultIssueLog.setSiteId(siteId);
faultIssueLog.setDeviceId(plan.getDeviceId());
faultIssueLog.setPoint(plan.getPoint());
faultIssueLog.setValue(plan.getValue());
faultIssueLog.setCreateBy("sys");
faultIssueLog.setCreateTime(new Date());
return faultIssueLog;
}
private void executeSinglePlan(ProtectionPlanVo plan, String siteId) throws Exception {
String deviceId = plan.getDeviceId();
// 获取设备地址信息
EmsDevicesSetting device = emsDevicesSettingMapper.getDeviceBySiteAndDeviceId(deviceId, siteId);
if (device == null || StringUtils.isEmpty(device.getIpAddress()) || device.getIpPort()==null) {
logger.warn("设备信息不完整deviceId:{}", deviceId);
return;
}
// 构建设备配置
DeviceConfig config = new DeviceConfig();
config.setHost(device.getIpAddress());
config.setPort(device.getIpPort().intValue());
config.setSlaveId(device.getSlaveId().intValue());
config.setDeviceName(device.getDeviceName());
config.setDeviceNumber(device.getDeviceId());
// 构建写入标签配置
WriteTagConfig writeTag = new WriteTagConfig();
writeTag.setAddress(plan.getPoint());
writeTag.setValue(plan.getValue());
List<WriteTagConfig> writeTags = new ArrayList<>();
writeTags.add(writeTag);
config.setWriteTags(writeTags);
// 写入数据到设备
boolean success = modbusProcessor.writeDataToDeviceWithRetry(config);
if (!success) {
logger.error("写入失败,设备地址:{}", device.getIpAddress());
}
}
// 校验释放值是否取消方案
private boolean checkIsNeedCancelPlan(List<ProtectionSettingVo> protSettings, String siteId) {
BigDecimal releaseValue = BigDecimal.ZERO;
StringBuilder conditionSb = new StringBuilder();
for (int i = 0; i < protSettings.size(); i++) {
ProtectionSettingVo vo = protSettings.get(i);
String deviceId = vo.getDeviceId();
String point = vo.getPoint();
releaseValue = vo.getFaultValue();
if(StringUtils.isEmpty(deviceId) || StringUtils.isEmpty(point) || releaseValue == null
|| StringUtils.isEmpty(vo.getReleaseOperator())){
return false;
}
// 获取点位最新值
BigDecimal lastPointValue = getPointLastValue(deviceId, point, siteId);
logger.info("checkIsNeedCancelPlan 点位:{},最新值:{},比较方式:{},释放值:{}", point, lastPointValue, vo.getReleaseOperator(), releaseValue);
if(lastPointValue == null){
return false;
}
// 拼接校验语句-最新值+比较方式+故障值+与下一点位关系(最后一个条件后不加关系)
conditionSb.append(lastPointValue).append(vo.getReleaseOperator()).append(releaseValue);
if (i < protSettings.size() - 1) {
String relation = vo.getRelationNext();
conditionSb.append(" ").append(relation).append(" ");
}
}
// 执行比较语句
return executeWithParser(conditionSb.toString());
}
// 校验故障值是否需要下发方案
private boolean checkIsNeedIssuedPlan(List<ProtectionSettingVo> protSettings, String siteId) {
BigDecimal faultValue = BigDecimal.ZERO;
StringBuilder conditionSb = new StringBuilder();
for (int i = 0; i < protSettings.size(); i++) {
ProtectionSettingVo vo = protSettings.get(i);
String deviceId = vo.getDeviceId();
String point = vo.getPoint();
faultValue = vo.getFaultValue();
if(StringUtils.isEmpty(deviceId) || StringUtils.isEmpty(point) || faultValue == null
|| StringUtils.isEmpty(vo.getFaultOperator())){
return false;
}
// 获取点位最新值
BigDecimal lastPointValue = getPointLastValue(deviceId, point, siteId);
logger.info("checkIsNeedIssuedPlan 点位:{},最新值:{},比较方式:{},故障值:{}", point, lastPointValue, vo.getFaultOperator(), faultValue);
if(lastPointValue == null){
return false;
}
// 拼接校验语句-最新值+比较方式+故障值+与下一点位关系(最后一个条件后不加关系)
conditionSb.append(lastPointValue).append(vo.getFaultOperator()).append(faultValue);
if (i < protSettings.size() - 1) {
String relation = vo.getRelationNext();
conditionSb.append(" ").append(relation).append(" ");
}
}
// 执行比较语句
return executeWithParser(conditionSb.toString());
}
private BigDecimal getPointLastValue(String deviceId, String point, String siteId) {
JSONObject mqttJson = redisCache.getCacheObject(RedisKeyConstants.SYNC_DATA + siteId + "_" + deviceId);
if(mqttJson == null){
return null;
}
String jsonData = mqttJson.get("Data").toString();
if(StringUtils.isEmpty(jsonData)){
return null;
}
Map<String, Object> obj = JSON.parseObject(jsonData, new com.alibaba.fastjson2.TypeReference<Map<String, Object>>() {});
return StringUtils.getBigDecimal(obj.get(point));
}
// 更新站点策略为启用
private void updateStrategyRunningStatus(String siteId, String status) {
// 获取是否有正在运行的策略,如果有则不更改
EmsStrategyRunning query = new EmsStrategyRunning();
query.setSiteId(siteId);
query.setStatus(StrategyStatus.RUNNING.getCode().equals(status) ? StrategyStatus.SUSPENDED.getCode() : StrategyStatus.RUNNING.getCode());
List<EmsStrategyRunning> strategyRunningList = emsStrategyRunningMapper.selectEmsStrategyRunningList(query);
if (CollectionUtils.isNotEmpty(strategyRunningList)) {
// 获取已存在并且状态为:未启用和已暂停的最晚一条策略,更新为已启用
strategyRunningList.forEach(emsStrategyRunning -> {
emsStrategyRunning.setStatus(status);
emsStrategyRunningMapper.updateEmsStrategyRunning(emsStrategyRunning);
});
}
}
// 更新站点策略为启用
private void updateStrategyRunning(String siteId) {
// 获取是否有正在运行的策略,如果有则不更改
EmsStrategyRunning emsStrategyRunning = emsStrategyRunningMapper.getRunningStrategy(siteId);
if (emsStrategyRunning == null) {
// 获取已存在并且状态为:未启用和已暂停的最晚一条策略,更新为已启用
emsStrategyRunning = emsStrategyRunningMapper.getPendingStrategy(siteId);
emsStrategyRunning.setStatus(StrategyStatus.RUNNING.getCode());
emsStrategyRunningMapper.updateEmsStrategyRunning(emsStrategyRunning);
}
}
private EmsAlarmRecords addAlarmRecord(String siteId, String content,String level) {
EmsAlarmRecords emsAlarmRecords = new EmsAlarmRecords();
emsAlarmRecords.setSiteId(siteId);
emsAlarmRecords.setAlarmContent(content);
emsAlarmRecords.setAlarmLevel(level);
emsAlarmRecords.setAlarmStartTime(new Date());
emsAlarmRecords.setStatus(AlarmStatus.WAITING.getCode());
emsAlarmRecords.setDeviceType("TCP");
emsAlarmRecords.setCreateBy("system");
emsAlarmRecords.setCreateTime(new Date());
return emsAlarmRecords;
}
// 故障等级-告警等级匹配
private String getAlarmLevel(Integer faultLevel) {
if (ObjectUtils.isEmpty(faultLevel) || faultLevel < 1 || faultLevel > 3) {
logger.warn("非法故障等级:{},默认返回普通告警", faultLevel);
return AlarmLevelStatus.EMERGENCY.getCode();
}
switch (faultLevel) {
case 1: return AlarmLevelStatus.GENERAL.getCode();
case 2: return AlarmLevelStatus.SERIOUS.getCode();
case 3: return AlarmLevelStatus.EMERGENCY.getCode();
default:
logger.error("未匹配的故障等级:{}", faultLevel);
return AlarmLevelStatus.EMERGENCY.getCode();
}
}
// 自定义表达式解析器(仅支持简单运算符和逻辑关系)
public boolean executeWithParser(String conditionStr) {
if (conditionStr == null || conditionStr.isEmpty()) {
return false;
}
// 1. 拆分逻辑关系(提取 && 或 ||
List<String> logicRelations = new ArrayList<>();
Pattern logicPattern = Pattern.compile("(&&|\\|\\|)");
Matcher logicMatcher = logicPattern.matcher(conditionStr);
while (logicMatcher.find()) {
logicRelations.add(logicMatcher.group());
}
// 2. 拆分原子条件(如 "3.55>3.52"
String[] atomicConditions = logicPattern.split(conditionStr);
// 3. 解析每个原子条件并计算结果
List<Boolean> atomicResults = new ArrayList<>();
Pattern conditionPattern = Pattern.compile("(\\d+\\.?\\d*)\\s*([><]=?|==)\\s*(\\d+\\.?\\d*)");
for (String atomic : atomicConditions) {
Matcher matcher = conditionPattern.matcher(atomic.trim());
if (!matcher.matches()) {
logger.error("无效的原子条件:{}", atomic);
return false;
}
double left = Double.parseDouble(matcher.group(1)); // 左值(最新值)
String operator = matcher.group(2); // 运算符
double right = Double.parseDouble(matcher.group(3)); // 右值(故障值)
// 执行比较
boolean result;
switch (operator) {
case ">":
result = left > right;
break;
case ">=":
result = left >= right;
break;
case "<":
result = left < right;
break;
case "<=":
result = left <= right;
break;
case "==":
result = left == right;
break;
default:
result = false;
break;
}
atomicResults.add(result);
}
// 4. 组合原子结果(根据逻辑关系)
boolean finalResult = atomicResults.get(0);
for (int i = 0; i < logicRelations.size(); i++) {
String relation = logicRelations.get(i);
boolean nextResult = atomicResults.get(i+1);
if ("&&".equals(relation)) {
finalResult = finalResult && nextResult;
} else if ("||".equals(relation)) {
finalResult = finalResult || nextResult;
}
}
return finalResult;
}
}

View File

@ -0,0 +1,494 @@
package com.xzzn.quartz.task;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.xzzn.common.core.modbus.ModbusProcessor;
import com.xzzn.common.core.modbus.domain.DeviceConfig;
import com.xzzn.common.core.modbus.domain.WriteTagConfig;
import com.xzzn.common.enums.ChargeStatus;
import com.xzzn.common.enums.DeviceCategory;
import com.xzzn.common.enums.SiteDevice;
import com.xzzn.common.enums.SocLimit;
import com.xzzn.common.enums.WorkStatus;
import com.xzzn.common.utils.DateUtils;
import com.xzzn.common.utils.StringUtils;
import com.xzzn.ems.domain.EmsAmmeterData;
import com.xzzn.ems.domain.EmsBatteryStack;
import com.xzzn.ems.domain.EmsDevicesSetting;
import com.xzzn.ems.domain.EmsPcsSetting;
import com.xzzn.ems.domain.EmsStrategyLog;
import com.xzzn.ems.domain.EmsStrategyTemp;
import com.xzzn.ems.domain.EmsStrategyTimeConfig;
import com.xzzn.ems.domain.vo.StrategyRunningVo;
import com.xzzn.ems.mapper.EmsAmmeterDataMapper;
import com.xzzn.ems.mapper.EmsBatteryStackMapper;
import com.xzzn.ems.mapper.EmsDevicesSettingMapper;
import com.xzzn.ems.mapper.EmsPcsSettingMapper;
import com.xzzn.ems.mapper.EmsStrategyLogMapper;
import com.xzzn.ems.mapper.EmsStrategyRunningMapper;
import com.xzzn.ems.mapper.EmsStrategyTempMapper;
import com.xzzn.ems.mapper.EmsStrategyTimeConfigMapper;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import javax.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component("strategyPoller")
public class StrategyPoller {
private static final Logger logger = LoggerFactory.getLogger(StrategyPoller.class);
private static final ConcurrentHashMap<Long, Boolean> strategyLocks = new ConcurrentHashMap<>();
// SOC 上下限值默认为0%-100%
private static final BigDecimal SOC_DOWN = new BigDecimal(0);
private static final BigDecimal SOC_UP = new BigDecimal(100);
// 逆变器功率下限值默认为30kW
private static final BigDecimal ANTI_REVERSE_THRESHOLD = new BigDecimal(30);
// 逆变器下限值范围默认为20%
private static final BigDecimal ANTI_REVERSE_RANGE_PERCENT = new BigDecimal(20);
// 逆变器功率上限值默认为100kW
private static final BigDecimal ANTI_REVERSE_UP = new BigDecimal(100);
// PCS功率降幅默认为10%
private static final BigDecimal ANTI_REVERSE_POWER_DOWN_PERCENT = new BigDecimal(10);
@Autowired
private EmsStrategyRunningMapper emsStrategyRunningMapper;
@Autowired
private EmsStrategyTempMapper emsStrategyTempMapper;
@Autowired
private EmsStrategyTimeConfigMapper emsStrategyTimeConfigMapper;
@Autowired
private EmsBatteryStackMapper emsBatteryStackMapper;
@Autowired
private EmsDevicesSettingMapper emsDevicesMapper;
@Autowired
private EmsPcsSettingMapper emsPcsSettingMapper;
@Autowired
private EmsAmmeterDataMapper emsAmmeterDataMapper;
@Autowired
private EmsStrategyLogMapper emsStrategyLogMapper;
@Autowired
private ModbusProcessor modbusProcessor;
@Resource(name = "modbusExecutor")
private ExecutorService modbusExecutor;
public void pollAllDevices() {
logger.info("开始执行运行策略数据轮询...");
List<StrategyRunningVo> strategyRunningVoList = emsStrategyRunningMapper.getPendingPollerStrategy(null);
strategyRunningVoList.forEach(strategyVo -> {
Long strategyId = strategyVo.getId();
if (strategyLocks.putIfAbsent(strategyId, true) == null) {
// 使用共享的modbusExecutor串行执行避免与ModbusPoller并发访问导致通讯故障
modbusExecutor.submit(() -> {
try {
processData(strategyVo);
} catch (Exception e) {
logger.error("运行策略{}轮询异常", strategyVo.getId(), e);
} finally {
logger.info("运行策略{}轮询任务执行完成,释放锁", strategyVo.getId());
strategyLocks.remove(strategyId);
}
});
} else {
logger.info("策略{}已在处理中,跳过重复执行", strategyId);
}
});
}
// 处理获取到的运行策略数据modbus发送指定的命令控制设备
private void processData(StrategyRunningVo strategyVo) {
logger.info("运行策略数据处理开始");
// 根据运行策略获取主副策略的模板数据
Long mainStrategyId = strategyVo.getMainStrategyId();
Long auxStrategyId = strategyVo.getAuxStrategyId();
String siteId = strategyVo.getSiteId();
// 处理主策略数据
if (mainStrategyId != null && StringUtils.isNotBlank(siteId)) {
dealStrategyCurveData(mainStrategyId, siteId);
}
// 处理副策略数据
if (auxStrategyId != null && StringUtils.isNotBlank(siteId)) {
dealStrategyCurveData(auxStrategyId, siteId);
}
logger.info("运行策略轮询处理结束");
}
private void dealStrategyCurveData(Long strategyId, String siteId) {
// 1.获取当前策略的所有模板
List<Map<String, String>> temps = emsStrategyTempMapper.getTempNameList(strategyId, siteId);
if (CollectionUtils.isEmpty(temps)) {
logger.info("当前站点: {}, 策略: {} 没有模板数据", siteId, strategyId);
return;
}
for (Map<String, String> temp : temps) {
// 2.查询当月配置的运行策略
String tempId = temp.get("templateId");
int month = LocalDateTime.now().getMonthValue();
List<EmsStrategyTimeConfig> timeConfigs = emsStrategyTimeConfigMapper.getTimeConfigByTempIdAndMonth(tempId, month);
if (CollectionUtils.isEmpty(timeConfigs)) {
continue;
}
logger.info("当前站点: {}, 策略: {}, {}月配置模版:{}", siteId, strategyId, month, tempId);
// 3.查询当月配置的运行策略时间阶段数据
List<EmsStrategyTemp> powerConfig = emsStrategyTempMapper.selectStrategyTempByTempId(tempId);
if (CollectionUtils.isEmpty(powerConfig)) {
logger.info("当前站点: {}, 策略: {}, 模版:{} 未配置数据", siteId, strategyId, tempId);
continue;
}
// 4.遍历时间段数据,判断当前时间是否在时间段内,在时间段内的进行处理
for (EmsStrategyTemp emsStrategyTemp : powerConfig) {
if (emsStrategyTemp.getStartTime() == null || emsStrategyTemp.getEndTime() == null) {
logger.info("当前站点: {}, 策略: {}, 模版:{} 未配置时间阶段数据", siteId, strategyId, tempId);
continue;
}
// 判断当前时间是否在时间段内
if (!isTimeInRange(LocalTime.now(), emsStrategyTemp.getStartTime(), emsStrategyTemp.getEndTime())) {
logger.info("当前站点: {}, 策略: {}, 时间段:{} 不在时间段内", siteId, strategyId, emsStrategyTemp.getStartTime() + "-" + emsStrategyTemp.getEndTime());
continue;
}
// 查询PCS设备信息
EmsDevicesSetting queryDevices = new EmsDevicesSetting();
queryDevices.setSiteId(siteId);
queryDevices.setDeviceCategory(DeviceCategory.PCS.getCode());
List<EmsDevicesSetting> pcsDeviceList = emsDevicesMapper.selectEmsDevicesSettingList(queryDevices);
if (CollectionUtils.isEmpty(pcsDeviceList)) {
logger.info("当前站点: {} 未配置PCS设备", siteId);
continue;
}
// 判断SOC上下限
if (isSocInRange(emsStrategyTemp)) {
BigDecimal avgChargeDischargePower = emsStrategyTemp.getChargeDischargePower().divide(new BigDecimal(pcsDeviceList.size()));
for (EmsDevicesSetting pcsDevice : pcsDeviceList) {
EmsPcsSetting pcsSetting = emsPcsSettingMapper.selectEmsPcsSettingByDeviceId(pcsDevice.getId());
if (pcsSetting == null || pcsSetting.getClusterNum() < 1) {
logger.info("当前站点: {}, PCS设备: {} 未获取电池簇数量", siteId, pcsDevice.getDeviceId());
continue;
}
// 功率默认放大10倍平均功率值根据电池簇数量进行平均分配
avgChargeDischargePower = avgChargeDischargePower.multiply(new BigDecimal(10)).divide(new BigDecimal(pcsSetting.getClusterNum()));
// 根据充电状态,处理数据
if (ChargeStatus.CHARGING.getCode().equals(emsStrategyTemp.getChargeStatus())) {
// 发送Modbus命令控制设备-充电
sendModbusCommand(Collections.singletonList(pcsDevice), pcsSetting, ChargeStatus.CHARGING, avgChargeDischargePower, emsStrategyTemp, false, null);
} else if (ChargeStatus.DISCHARGING.getCode().equals(emsStrategyTemp.getChargeStatus())) {
boolean needAntiReverseFlow = false;
Integer powerDownType = null;
BigDecimal chargeDischargePower = avgChargeDischargePower;
// 查询策略运行日志
EmsStrategyLog lastStrategyLog = getLastStrategyLog(pcsDevice.getDeviceId(), emsStrategyTemp);
if (lastStrategyLog != null) {
// 如果当前时间段已经进入待机状态,则不处理
if (ChargeStatus.STANDBY.getCode().equals(lastStrategyLog.getChargeStatus())) {
continue;
}
chargeDischargePower = lastStrategyLog.getChargeDischargePower();
powerDownType = lastStrategyLog.getPowerDownType();
}
// 查询电网电表的正向有功功率,36kW-50kW范围内稳定运行低于36kW降功率高于50kW增加功率
EmsAmmeterData emsAmmeterData = emsAmmeterDataMapper.getLastData(emsStrategyTemp.getSiteId(), SiteDevice.LOAD.name());
if (emsAmmeterData == null || emsAmmeterData.getTotalActivePower() == null) {
logger.info("当前站点: {}, 未获取到最新电表数据", emsStrategyTemp.getSiteId());
} else {
// 判断是否需要防逆流
needAntiReverseFlow = isNeedAntiReverseFlow(emsAmmeterData.getTotalActivePower());
BigDecimal power = avgChargeDischargePower.multiply(ANTI_REVERSE_POWER_DOWN_PERCENT).divide(new BigDecimal(100));
if (needAntiReverseFlow) {
// 降功率
chargeDischargePower = chargeDischargePower.subtract(power);
powerDownType = 0;
} else {
// 判断是否需要增加功率,
if (powerDownType != null && emsAmmeterData.getTotalActivePower().compareTo(ANTI_REVERSE_UP) > 0) {
if (chargeDischargePower.compareTo(avgChargeDischargePower) == 0) {
// 功率增加到平均值则停止
continue;
}
// 增加功率
chargeDischargePower = chargeDischargePower.add(power);
powerDownType = 1;
needAntiReverseFlow = true;
}
}
}
if (BigDecimal.ZERO.compareTo(chargeDischargePower) == 0) {
// 如果已经降功率到0则设备直接待机
// 发送Modbus命令控制设备-待机
sendModbusCommand(Collections.singletonList(pcsDevice), pcsSetting, ChargeStatus.STANDBY, BigDecimal.ZERO, emsStrategyTemp, needAntiReverseFlow, powerDownType);
} else {
// 发送Modbus命令控制设备-放电
sendModbusCommand(Collections.singletonList(pcsDevice), pcsSetting, ChargeStatus.DISCHARGING, chargeDischargePower, emsStrategyTemp, needAntiReverseFlow, powerDownType);
}
} else {
// 发送Modbus命令控制设备-待机
sendModbusCommand(Collections.singletonList(pcsDevice), pcsSetting, ChargeStatus.STANDBY, BigDecimal.ZERO, emsStrategyTemp, false, null);
}
}
} else {
// 发送Modbus命令控制设备-待机
sendModbusCommand(pcsDeviceList, null, ChargeStatus.STANDBY, BigDecimal.ZERO, emsStrategyTemp, false, null);
}
}
}
}
private void saveStrategyLog(String deviceId, BigDecimal chargeDischargePower, String chargeStatus,
EmsStrategyTemp strategyTemp, boolean needAntiReverseFlow, Integer powerDownType) {
EmsStrategyLog log = new EmsStrategyLog();
log.setStrategyId(strategyTemp.getStrategyId());
log.setTemplateId(strategyTemp.getTemplateId());
log.setSiteId(strategyTemp.getSiteId());
log.setDeviceId(deviceId);
log.setStartTime(strategyTemp.getStartTime());
log.setEndTime(strategyTemp.getEndTime());
log.setChargeDischargePower(chargeDischargePower);
log.setChargeStatus(chargeStatus);
log.setExecutionDate(DateUtils.toDate(LocalDateTime.now()));
log.setAntiReverse(needAntiReverseFlow ? 1 : 0);
log.setPowerDownType(powerDownType);
emsStrategyLogMapper.insertEmsStrategyLog(log);
}
private List<EmsStrategyLog> getStrategyLog(String deviceId, String chargeStatus,
EmsStrategyTemp strategyTemp, boolean needAntiReverseFlow) {
EmsStrategyLog query = new EmsStrategyLog();
query.setStrategyId(strategyTemp.getStrategyId());
query.setTemplateId(strategyTemp.getTemplateId());
query.setSiteId(strategyTemp.getSiteId());
query.setDeviceId(deviceId);
query.setStartTime(strategyTemp.getStartTime());
query.setEndTime(strategyTemp.getEndTime());
query.setChargeStatus(chargeStatus);
query.setExecutionDate(DateUtils.toDate(LocalDateTime.now()));
query.setAntiReverse(needAntiReverseFlow ? 1 : 0);
return emsStrategyLogMapper.selectEmsStrategyLogList(query);
}
private EmsStrategyLog getLastStrategyLog(String deviceId, EmsStrategyTemp strategyTemp) {
EmsStrategyLog query = new EmsStrategyLog();
query.setStrategyId(strategyTemp.getStrategyId());
query.setTemplateId(strategyTemp.getTemplateId());
query.setSiteId(strategyTemp.getSiteId());
query.setDeviceId(deviceId);
query.setStartTime(strategyTemp.getStartTime());
query.setEndTime(strategyTemp.getEndTime());
query.setExecutionDate(DateUtils.toDate(LocalDateTime.now()));
return emsStrategyLogMapper.getLastStrategyLog(query);
}
private boolean isNeedAntiReverseFlow(BigDecimal totalActivePower) {
// 获取当前设定的防逆流阈值(30kW)
BigDecimal threshold = ANTI_REVERSE_THRESHOLD;
// 计算20%范围的上限(36kW)
BigDecimal upperLimit = threshold.multiply(ANTI_REVERSE_RANGE_PERCENT).divide(new BigDecimal(100)).add(threshold);
// 判断电网电表正向有功功率是否小于36kW(接近30kW的20%范围)
return totalActivePower.compareTo(upperLimit) < 0;
}
public List<WriteTagConfig> getSwitchDeviceWriteTags(EmsPcsSetting pcsSetting, String workStatus) {
List<WriteTagConfig> writeTags = new ArrayList<>();
BigDecimal power;
if (WorkStatus.NORMAL.getCode().equals(workStatus)) {
// 开机先发送开机指令再发送有功功率给定值
WriteTagConfig writeTag = new WriteTagConfig();
writeTag.setAddress(pcsSetting.getPointAddress());
writeTag.setValue(pcsSetting.getStartCommand());
writeTags.add(writeTag);
power = pcsSetting.getStartPower();
} else {
// 关机
power = pcsSetting.getStopPower();
}
JSONArray array = JSON.parseArray(pcsSetting.getClusterPointAddress());
for (int i = 0; i < pcsSetting.getClusterNum(); i++) {
Object clusterPointAddress = array.get(i);
WriteTagConfig clusterWriteTag = new WriteTagConfig();
clusterWriteTag.setAddress(String.valueOf(clusterPointAddress));
// 电池簇PCS有功功率给定默认置0
if (power == null) {
power = BigDecimal.ZERO;
}
clusterWriteTag.setValue(power);
writeTags.add(clusterWriteTag);
}
if (WorkStatus.STOP.getCode().equals(workStatus)) {
// 关机先发送有功功率给定值再发送关机指令
WriteTagConfig writeTag = new WriteTagConfig();
writeTag.setAddress(pcsSetting.getPointAddress());
writeTag.setValue(pcsSetting.getStopCommand());
writeTags.add(writeTag);
}
return writeTags;
}
public List<WriteTagConfig> getWriteTags(EmsPcsSetting pcsSetting, BigDecimal chargeDischargePower) {
List<WriteTagConfig> writeTags = new ArrayList<>();
JSONArray array = JSON.parseArray(pcsSetting.getClusterPointAddress());
for (int i = 0; i < pcsSetting.getClusterNum(); i++) {
Object clusterPointAddress = array.get(i);
WriteTagConfig clusterWriteTag = new WriteTagConfig();
clusterWriteTag.setAddress(String.valueOf(clusterPointAddress));
clusterWriteTag.setValue(chargeDischargePower);
writeTags.add(clusterWriteTag);
}
return writeTags;
}
public DeviceConfig getDeviceConfig(String siteId, String deviceId, EmsDevicesSetting device, EmsPcsSetting pcsSetting, BigDecimal chargeDischargePower, int writeType) {
if (Objects.isNull(pcsSetting)) {
pcsSetting = emsPcsSettingMapper.selectEmsPcsSettingByDeviceId(device.getId());
if (pcsSetting == null) {
logger.info("当前站点: {}, PCS设备: {} 未找到对应PCS配置", siteId, deviceId);
return null;
}
}
if (device.getIpPort() == null || device.getSlaveId() == null) {
logger.info("当前站点: {}, PCS设备: {} 未配置IP端口或从站号", siteId, deviceId);
return null;
}
DeviceConfig deviceConfig = new DeviceConfig();
deviceConfig.setDeviceNumber(device.getDeviceId());
deviceConfig.setDeviceName(device.getDeviceName());
deviceConfig.setSlaveId(device.getSlaveId().intValue());
deviceConfig.setHost(device.getIpAddress());
deviceConfig.setPort(device.getIpPort().intValue());
deviceConfig.setWriteTags(writeType == 0 ? getWriteTags(pcsSetting, chargeDischargePower) : getSwitchDeviceWriteTags(pcsSetting, device.getWorkStatus()));
return deviceConfig;
}
private void sendModbusCommand(List<EmsDevicesSetting> pcsDeviceList, EmsPcsSetting pcsSetting, ChargeStatus chargeStatus, BigDecimal chargeDischargePower,
EmsStrategyTemp emsStrategyTemp, boolean needAntiReverseFlow, Integer powerDownType) {
for (EmsDevicesSetting pcsDevice : pcsDeviceList) {
String siteId = pcsDevice.getSiteId();
String deviceId = pcsDevice.getDeviceId();
List<EmsStrategyLog> strategyLogList = getStrategyLog(deviceId, chargeStatus.getCode(), emsStrategyTemp, needAntiReverseFlow);
if (CollectionUtils.isNotEmpty(strategyLogList)) {
boolean isExist = true;
if (ChargeStatus.DISCHARGING.equals(chargeStatus) && needAntiReverseFlow) {
isExist = false;
}
if (isExist) {
logger.info("当前站点: {}, PCS设备: {} 当前时间段已存在策略执行记录,不再重复执行", siteId, deviceId);
continue;
}
}
// 每次操作先判断设备工作状态
if (StringUtils.isEmpty(pcsDevice.getWorkStatus()) || WorkStatus.ABNORMAL.getCode().equals(pcsDevice.getWorkStatus())) {
// 设备故障,不发送指令
continue;
} else if (WorkStatus.STOP.getCode().equals(pcsDevice.getWorkStatus())) {
// 设备停机
if (ChargeStatus.STANDBY.equals(chargeStatus)) {
// 待机,则不写入功率值
continue;
} else {
// 充、放电,则先开机设备
switchDevice(pcsDevice, pcsSetting, WorkStatus.NORMAL);
continue;
}
}
DeviceConfig deviceConfig = getDeviceConfig(siteId, deviceId, pcsDevice, pcsSetting, chargeDischargePower, 0);
if (deviceConfig == null) {
continue;
}
boolean result = modbusProcessor.writeDataToDeviceWithRetry(deviceConfig);
if (!result) {
logger.info("当前站点: {}, PCS设备: {} modbus控制设备{}指令发送失败", siteId, deviceId, chargeStatus.getInfo());
continue;
} else {
if (ChargeStatus.STANDBY.equals(chargeStatus)) {
// 待机,先写功率值,再关机
if (!switchDevice(pcsDevice, pcsSetting, WorkStatus.STOP)) {
logger.info("当前站点: {}, PCS设备: {} modbus控制设备{}指令发送失败", siteId, deviceId, WorkStatus.STOP.getInfo());
continue;
}
}
}
// 记录策略执行日志
saveStrategyLog(deviceId, chargeDischargePower, chargeStatus.getCode(), emsStrategyTemp, needAntiReverseFlow, powerDownType);
}
}
//设备开关机
private boolean switchDevice(EmsDevicesSetting pcsDevice, EmsPcsSetting pcsSetting, WorkStatus workStatus) {
String siteId = pcsDevice.getSiteId();
String deviceId = pcsDevice.getDeviceId();
pcsDevice.setWorkStatus(workStatus.getCode());
DeviceConfig deviceConfig = getDeviceConfig(siteId, deviceId, pcsDevice, pcsSetting , null, 1);
if (deviceConfig == null) {
return false;
}
boolean result = modbusProcessor.writeDataToDeviceWithRetry(deviceConfig);
if (!result) {
logger.info("当前站点: {}, PCS设备: {} modbus控制设备{}指令发送失败", siteId, deviceConfig, workStatus.getInfo());
}
return result;
}
// 判断当前时间是否在时间范围内
private static boolean isTimeInRange(LocalTime now, Date startTime, Date endTime) {
ZoneId zoneId = ZoneId.of("Asia/Shanghai");
LocalTime startLocalTime = startTime.toInstant()
.atZone(zoneId)
.toLocalTime();
LocalTime endLocalTime = endTime.toInstant()
.atZone(zoneId)
.toLocalTime();
return now.equals(startLocalTime) || (now.isAfter(startLocalTime) && now.isBefore(endLocalTime));
}
// 判断SOC上限和下限
private boolean isSocInRange(EmsStrategyTemp emsStrategyTemp) {
BigDecimal socDown = SOC_DOWN;
BigDecimal socUp = SOC_UP;
if (SocLimit.ON.getCode().equals(emsStrategyTemp.getSdcLimit())) {
socDown = emsStrategyTemp.getSdcDown();
socUp = emsStrategyTemp.getSdcUp();
}
// 查询电池堆(BMSD) SOC
EmsBatteryStack emsBatteryStack = emsBatteryStackMapper.getSiteSumStackInfo(emsStrategyTemp.getSiteId());
if (emsBatteryStack == null || emsBatteryStack.getStackSoc() == null) {
return true;
}
// 充电阶段判断SOC上限值
if (ChargeStatus.CHARGING.getCode().equals(emsStrategyTemp.getChargeStatus()) && emsBatteryStack.getStackSoc().compareTo(socUp) >= 0) {
return false;
}
// 放电阶段判断SOC下限值
if (ChargeStatus.DISCHARGING.getCode().equals(emsStrategyTemp.getChargeStatus()) && emsBatteryStack.getStackSoc().compareTo(socDown) <= 0) {
return false;
}
return true;
}
}

View File

@ -60,4 +60,25 @@ public class CronUtils
throw new IllegalArgumentException(e.getMessage()); throw new IllegalArgumentException(e.getMessage());
} }
} }
/**
* 返回当前时间到下一次执行时间间隔的毫秒
*/
public static long getNextExecutionIntervalMillis(String cronExpression)
{
try
{
CronExpression cron = new CronExpression(cronExpression);
Date now = new Date();
Date nextExecution = cron.getNextValidTimeAfter(now);
Date nextExecution2 = cron.getNextValidTimeAfter(nextExecution);
return nextExecution2.getTime() - nextExecution.getTime();
}
catch (ParseException e)
{
throw new IllegalArgumentException(e.getMessage());
}
}
} }

View File

@ -0,0 +1,117 @@
package com.xzzn.ems.domain;
import com.xzzn.common.core.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.xzzn.common.annotation.Excel;
/**
* 告警点位匹配数据对象 ems_alarm_match_data
*
* @author xzzn
* @date 2025-09-22
*/
public class EmsAlarmMatchData extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** $column.columnComment */
private Long id;
/** 告警点位 */
@Excel(name = "告警点位")
private String point;
/** 告警值 */
@Excel(name = "告警值")
private Long alarmData;
/** 告警描述 */
@Excel(name = "告警描述")
private String alarmDescription;
/** 站点id */
@Excel(name = "站点id")
private String siteId;
/** 设备类型 */
@Excel(name = "设备类型")
private String deviceCategory;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setPoint(String point)
{
this.point = point;
}
public String getPoint()
{
return point;
}
public void setAlarmData(Long alarmData)
{
this.alarmData = alarmData;
}
public Long getAlarmData()
{
return alarmData;
}
public void setAlarmDescription(String alarmDescription)
{
this.alarmDescription = alarmDescription;
}
public String getAlarmDescription()
{
return alarmDescription;
}
public void setSiteId(String siteId)
{
this.siteId = siteId;
}
public String getSiteId()
{
return siteId;
}
public void setDeviceCategory(String deviceCategory)
{
this.deviceCategory = deviceCategory;
}
public String getDeviceCategory()
{
return deviceCategory;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("point", getPoint())
.append("alarmData", getAlarmData())
.append("alarmDescription", getAlarmDescription())
.append("siteId", getSiteId())
.append("deviceCategory", getDeviceCategory())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
.toString();
}
}

View File

@ -24,8 +24,8 @@ public class EmsAlarmRecords extends BaseEntity
@Excel(name = "设备类型") @Excel(name = "设备类型")
private String deviceType; private String deviceType;
/** 告警等级 */ /** 告警等级A-提示 B-一般 C-严重 D紧急 */
@Excel(name = "告警等级") @Excel(name = "告警等级A-提示 B-一般 C-严重 D紧急")
private String alarmLevel; private String alarmLevel;
/** 告警内容 */ /** 告警内容 */
@ -42,8 +42,12 @@ public class EmsAlarmRecords extends BaseEntity
@Excel(name = "告警结束时间", width = 30, dateFormat = "yyyy-MM-dd") @Excel(name = "告警结束时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date alarmEndTime; private Date alarmEndTime;
/** 状态 */ /** 告警点位 */
@Excel(name = "状态") @Excel(name = "告警点位")
private String alarmPoint;
/** 状态:0-待处理 1-已处理 2-处理中 */
@Excel(name = "状态:0-待处理 1-已处理 2-处理中")
private String status; private String status;
/** 站点id */ /** 站点id */
@ -54,10 +58,6 @@ public class EmsAlarmRecords extends BaseEntity
@Excel(name = "设备唯一标识符") @Excel(name = "设备唯一标识符")
private String deviceId; private String deviceId;
/** 设备名称,用于标识设备 */
@Excel(name = "设备名称,用于标识设备")
private String deviceName;
/** 工单号规则T+日期+6位随机 */ /** 工单号规则T+日期+6位随机 */
@Excel(name = "工单号", readConverterExp = "规=则T+日期+6位随机") @Excel(name = "工单号", readConverterExp = "规=则T+日期+6位随机")
private String ticketNo; private String ticketNo;
@ -122,6 +122,16 @@ public class EmsAlarmRecords extends BaseEntity
return alarmEndTime; return alarmEndTime;
} }
public void setAlarmPoint(String alarmPoint)
{
this.alarmPoint = alarmPoint;
}
public String getAlarmPoint()
{
return alarmPoint;
}
public void setStatus(String status) public void setStatus(String status)
{ {
this.status = status; this.status = status;
@ -152,24 +162,16 @@ public class EmsAlarmRecords extends BaseEntity
return deviceId; return deviceId;
} }
public void setDeviceName(String deviceName) public void setTicketNo(String ticketNo)
{ {
this.deviceName = deviceName;
}
public String getDeviceName()
{
return deviceName;
}
public String getTicketNo() {
return ticketNo;
}
public void setTicketNo(String ticketNo) {
this.ticketNo = ticketNo; this.ticketNo = ticketNo;
} }
public String getTicketNo()
{
return ticketNo;
}
@Override @Override
public String toString() { public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
@ -179,6 +181,7 @@ public class EmsAlarmRecords extends BaseEntity
.append("alarmContent", getAlarmContent()) .append("alarmContent", getAlarmContent())
.append("alarmStartTime", getAlarmStartTime()) .append("alarmStartTime", getAlarmStartTime())
.append("alarmEndTime", getAlarmEndTime()) .append("alarmEndTime", getAlarmEndTime())
.append("alarmPoint", getAlarmPoint())
.append("status", getStatus()) .append("status", getStatus())
.append("createBy", getCreateBy()) .append("createBy", getCreateBy())
.append("createTime", getCreateTime()) .append("createTime", getCreateTime())
@ -187,8 +190,7 @@ public class EmsAlarmRecords extends BaseEntity
.append("remark", getRemark()) .append("remark", getRemark())
.append("siteId", getSiteId()) .append("siteId", getSiteId())
.append("deviceId", getDeviceId()) .append("deviceId", getDeviceId())
.append("deviceName", getDeviceName())
.append("ticketNo", getTicketNo()) .append("ticketNo", getTicketNo())
.toString(); .toString();
} }
} }

View File

@ -1,11 +1,15 @@
package com.xzzn.ems.domain; package com.xzzn.ems.domain;
import java.math.BigDecimal; import com.fasterxml.jackson.annotation.JsonFormat;
import com.xzzn.common.annotation.Excel;
import com.xzzn.common.core.domain.BaseEntity; import com.xzzn.common.core.domain.BaseEntity;
import com.xzzn.common.utils.StringUtils;
import java.math.BigDecimal;
import java.util.Date;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import com.xzzn.common.annotation.Excel;
/** /**
* 电池簇数据对象 ems_battery_cluster * 电池簇数据对象 ems_battery_cluster
@ -20,8 +24,13 @@ public class EmsBatteryCluster extends BaseEntity
/** */ /** */
private Long id; private Long id;
/** 工作状态0-正常 1-异常 2-停止 */ /** 数据更新时间 */
@Excel(name = "工作状态0-正常 1-异常 2-停止") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "数据更新时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date dataUpdateTime;
/** 工作状态0-运行 1-停机 2-故障 */
@Excel(name = "工作状态0-运行 1-停机 2-故障")
private String workStatus; private String workStatus;
/** 与PCS通信状态0-正常 1-通信中断 2-异常 */ /** 与PCS通信状态0-正常 1-通信中断 2-异常 */
@ -146,7 +155,7 @@ public class EmsBatteryCluster extends BaseEntity
/** 最高单体电压对应点号 */ /** 最高单体电压对应点号 */
@Excel(name = "最高单体电压对应点号") @Excel(name = "最高单体电压对应点号")
private Long maxCellVoltageId; private String maxCellVoltageId;
/** 最低单体电压 */ /** 最低单体电压 */
@Excel(name = "最低单体电压") @Excel(name = "最低单体电压")
@ -154,7 +163,7 @@ public class EmsBatteryCluster extends BaseEntity
/** 最低单体电压对应点号 */ /** 最低单体电压对应点号 */
@Excel(name = "最低单体电压对应点号") @Excel(name = "最低单体电压对应点号")
private Long minCellVoltageId; private String minCellVoltageId;
/** 最高单体温度 */ /** 最高单体温度 */
@Excel(name = "最高单体温度") @Excel(name = "最高单体温度")
@ -162,7 +171,7 @@ public class EmsBatteryCluster extends BaseEntity
/** 最高单体温度对应点号 */ /** 最高单体温度对应点号 */
@Excel(name = "最高单体温度对应点号") @Excel(name = "最高单体温度对应点号")
private Long maxCellTempId; private String maxCellTempId;
/** 最低单体温度 */ /** 最低单体温度 */
@Excel(name = "最低单体温度") @Excel(name = "最低单体温度")
@ -170,7 +179,7 @@ public class EmsBatteryCluster extends BaseEntity
/** 最低单体温度对应点号 */ /** 最低单体温度对应点号 */
@Excel(name = "最低单体温度对应点号") @Excel(name = "最低单体温度对应点号")
private Long minCellTempId; private String minCellTempId;
/** 最高单体SOC */ /** 最高单体SOC */
@Excel(name = "最高单体SOC") @Excel(name = "最高单体SOC")
@ -178,7 +187,7 @@ public class EmsBatteryCluster extends BaseEntity
/** 最高单体SOC对应点号 */ /** 最高单体SOC对应点号 */
@Excel(name = "最高单体SOC对应点号") @Excel(name = "最高单体SOC对应点号")
private Long maxCellSocId; private String maxCellSocId;
/** 最低单体SOC */ /** 最低单体SOC */
@Excel(name = "最低单体SOC") @Excel(name = "最低单体SOC")
@ -186,7 +195,7 @@ public class EmsBatteryCluster extends BaseEntity
/** 最低单体SOC对应点号 */ /** 最低单体SOC对应点号 */
@Excel(name = "最低单体SOC对应点号") @Excel(name = "最低单体SOC对应点号")
private Long minCellSocId; private String minCellSocId;
/** 最高单体SOH */ /** 最高单体SOH */
@Excel(name = "最高单体SOH") @Excel(name = "最高单体SOH")
@ -194,7 +203,7 @@ public class EmsBatteryCluster extends BaseEntity
/** 最高单体SOH对应点号 */ /** 最高单体SOH对应点号 */
@Excel(name = "最高单体SOH对应点号") @Excel(name = "最高单体SOH对应点号")
private Long maxCellSohId; private String maxCellSohId;
/** 最低单体SOH */ /** 最低单体SOH */
@Excel(name = "最低单体SOH") @Excel(name = "最低单体SOH")
@ -202,7 +211,7 @@ public class EmsBatteryCluster extends BaseEntity
/** 最低单体SOH对应点号 */ /** 最低单体SOH对应点号 */
@Excel(name = "最低单体SOH对应点号") @Excel(name = "最低单体SOH对应点号")
private Long minCellSohId; private String minCellSohId;
/** 单次累计充电电量 */ /** 单次累计充电电量 */
@Excel(name = "单次累计充电电量") @Excel(name = "单次累计充电电量")
@ -222,6 +231,14 @@ public class EmsBatteryCluster extends BaseEntity
return id; return id;
} }
public Date getDataUpdateTime() {
return dataUpdateTime;
}
public void setDataUpdateTime(Date dataUpdateTime) {
this.dataUpdateTime = dataUpdateTime;
}
public void setWorkStatus(String workStatus) public void setWorkStatus(String workStatus)
{ {
this.workStatus = workStatus; this.workStatus = workStatus;
@ -532,12 +549,12 @@ public class EmsBatteryCluster extends BaseEntity
return maxCellVoltage; return maxCellVoltage;
} }
public void setMaxCellVoltageId(Long maxCellVoltageId) public void setMaxCellVoltageId(String maxCellVoltageId)
{ {
this.maxCellVoltageId = maxCellVoltageId; this.maxCellVoltageId = StringUtils.fillThreeDigits(maxCellVoltageId);
} }
public Long getMaxCellVoltageId() public String getMaxCellVoltageId()
{ {
return maxCellVoltageId; return maxCellVoltageId;
} }
@ -552,12 +569,12 @@ public class EmsBatteryCluster extends BaseEntity
return minCellVoltage; return minCellVoltage;
} }
public void setMinCellVoltageId(Long minCellVoltageId) public void setMinCellVoltageId(String minCellVoltageId)
{ {
this.minCellVoltageId = minCellVoltageId; this.minCellVoltageId = StringUtils.fillThreeDigits(minCellVoltageId);
} }
public Long getMinCellVoltageId() public String getMinCellVoltageId()
{ {
return minCellVoltageId; return minCellVoltageId;
} }
@ -572,12 +589,12 @@ public class EmsBatteryCluster extends BaseEntity
return maxCellTemp; return maxCellTemp;
} }
public void setMaxCellTempId(Long maxCellTempId) public void setMaxCellTempId(String maxCellTempId)
{ {
this.maxCellTempId = maxCellTempId; this.maxCellTempId = StringUtils.fillThreeDigits(maxCellTempId);
} }
public Long getMaxCellTempId() public String getMaxCellTempId()
{ {
return maxCellTempId; return maxCellTempId;
} }
@ -592,12 +609,12 @@ public class EmsBatteryCluster extends BaseEntity
return minCellTemp; return minCellTemp;
} }
public void setMinCellTempId(Long minCellTempId) public void setMinCellTempId(String minCellTempId)
{ {
this.minCellTempId = minCellTempId; this.minCellTempId = StringUtils.fillThreeDigits(minCellTempId);
} }
public Long getMinCellTempId() public String getMinCellTempId()
{ {
return minCellTempId; return minCellTempId;
} }
@ -612,12 +629,12 @@ public class EmsBatteryCluster extends BaseEntity
return maxCellSoc; return maxCellSoc;
} }
public void setMaxCellSocId(Long maxCellSocId) public void setMaxCellSocId(String maxCellSocId)
{ {
this.maxCellSocId = maxCellSocId; this.maxCellSocId = StringUtils.fillThreeDigits(maxCellSocId);
} }
public Long getMaxCellSocId() public String getMaxCellSocId()
{ {
return maxCellSocId; return maxCellSocId;
} }
@ -632,12 +649,12 @@ public class EmsBatteryCluster extends BaseEntity
return minCellSoc; return minCellSoc;
} }
public void setMinCellSocId(Long minCellSocId) public void setMinCellSocId(String minCellSocId)
{ {
this.minCellSocId = minCellSocId; this.minCellSocId = StringUtils.fillThreeDigits(minCellSocId);
} }
public Long getMinCellSocId() public String getMinCellSocId()
{ {
return minCellSocId; return minCellSocId;
} }
@ -652,12 +669,12 @@ public class EmsBatteryCluster extends BaseEntity
return maxCellSoh; return maxCellSoh;
} }
public void setMaxCellSohId(Long maxCellSohId) public void setMaxCellSohId(String maxCellSohId)
{ {
this.maxCellSohId = maxCellSohId; this.maxCellSohId = StringUtils.fillThreeDigits(maxCellSohId);
} }
public Long getMaxCellSohId() public String getMaxCellSohId()
{ {
return maxCellSohId; return maxCellSohId;
} }
@ -672,12 +689,12 @@ public class EmsBatteryCluster extends BaseEntity
return minCellSoh; return minCellSoh;
} }
public void setMinCellSohId(Long minCellSohId) public void setMinCellSohId(String minCellSohId)
{ {
this.minCellSohId = minCellSohId; this.minCellSohId = StringUtils.fillThreeDigits(minCellSohId);
} }
public Long getMinCellSohId() public String getMinCellSohId()
{ {
return minCellSohId; return minCellSohId;
} }

View File

@ -1,12 +1,14 @@
package com.xzzn.ems.domain; package com.xzzn.ems.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.xzzn.common.annotation.Excel;
import com.xzzn.common.core.domain.BaseEntity;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Date; import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.xzzn.common.core.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import com.xzzn.common.annotation.Excel;
/** /**
* 单体电池实时数据对象 ems_battery_data * 单体电池实时数据对象 ems_battery_data
@ -70,6 +72,10 @@ public class EmsBatteryData extends BaseEntity
@Excel(name = "单体电池内阻") @Excel(name = "单体电池内阻")
private BigDecimal interResistance; private BigDecimal interResistance;
/** 单体电池电流 */
@Excel(name = "单体电池电流")
private BigDecimal current;
public void setId(Long id) public void setId(Long id)
{ {
this.id = id; this.id = id;
@ -200,6 +206,14 @@ public class EmsBatteryData extends BaseEntity
return interResistance; return interResistance;
} }
public BigDecimal getCurrent() {
return current;
}
public void setCurrent(BigDecimal current) {
this.current = current;
}
@Override @Override
public String toString() { public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
@ -221,6 +235,7 @@ public class EmsBatteryData extends BaseEntity
.append("deviceId", getDeviceId()) .append("deviceId", getDeviceId())
.append("clusterDeviceId", getClusterDeviceId()) .append("clusterDeviceId", getClusterDeviceId())
.append("interResistance", getInterResistance()) .append("interResistance", getInterResistance())
.append("current", getCurrent())
.toString(); .toString();
} }
} }

View File

@ -0,0 +1,257 @@
package com.xzzn.ems.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.xzzn.common.annotation.Excel;
import com.xzzn.common.core.domain.BaseEntity;
import java.math.BigDecimal;
import java.util.Date;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
/**
* 单体电池天级数据对象 ems_battery_data_day
*
* @author xzzn
* @date 2025-08-20
*/
public class EmsBatteryDataDay extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** $column.columnComment */
private Long id;
/** 电池堆 */
@Excel(name = "电池堆")
private String batteryPack;
/** 电池簇 */
@Excel(name = "电池簇")
private String batteryCluster;
/** 单体编号 */
@Excel(name = "单体编号")
private String batteryCellId;
/** 电压 (V) */
@Excel(name = "电压 (V)")
private BigDecimal voltage;
/** 温度 (℃) */
@Excel(name = "温度 (℃)")
private BigDecimal temperature;
/** SOC (%) */
@Excel(name = "SOC (%)")
private BigDecimal soc;
/** SOH (%) */
@Excel(name = "SOH (%)")
private BigDecimal soh;
/** 数据采集时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "数据采集时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date dataTimestamp;
/** 站点id */
@Excel(name = "站点id")
private String siteId;
/** 设备唯一标识符 */
@Excel(name = "设备唯一标识符")
private String deviceId;
/** 簇设备id */
@Excel(name = "簇设备id")
private String clusterDeviceId;
/** 单体电池内阻 */
@Excel(name = "单体电池内阻")
private BigDecimal interResistance;
/** 天级时间维度 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "天级时间维度", width = 30, dateFormat = "yyyy-MM-dd")
private Date dayTime;
/** 单体电池电流 */
@Excel(name = "单体电池电流")
private BigDecimal current;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setBatteryPack(String batteryPack)
{
this.batteryPack = batteryPack;
}
public String getBatteryPack()
{
return batteryPack;
}
public void setBatteryCluster(String batteryCluster)
{
this.batteryCluster = batteryCluster;
}
public String getBatteryCluster()
{
return batteryCluster;
}
public void setBatteryCellId(String batteryCellId)
{
this.batteryCellId = batteryCellId;
}
public String getBatteryCellId()
{
return batteryCellId;
}
public void setVoltage(BigDecimal voltage)
{
this.voltage = voltage;
}
public BigDecimal getVoltage()
{
return voltage;
}
public void setTemperature(BigDecimal temperature)
{
this.temperature = temperature;
}
public BigDecimal getTemperature()
{
return temperature;
}
public void setSoc(BigDecimal soc)
{
this.soc = soc;
}
public BigDecimal getSoc()
{
return soc;
}
public void setSoh(BigDecimal soh)
{
this.soh = soh;
}
public BigDecimal getSoh()
{
return soh;
}
public void setDataTimestamp(Date dataTimestamp)
{
this.dataTimestamp = dataTimestamp;
}
public Date getDataTimestamp()
{
return dataTimestamp;
}
public void setSiteId(String siteId)
{
this.siteId = siteId;
}
public String getSiteId()
{
return siteId;
}
public void setDeviceId(String deviceId)
{
this.deviceId = deviceId;
}
public String getDeviceId()
{
return deviceId;
}
public void setClusterDeviceId(String clusterDeviceId)
{
this.clusterDeviceId = clusterDeviceId;
}
public String getClusterDeviceId()
{
return clusterDeviceId;
}
public void setInterResistance(BigDecimal interResistance)
{
this.interResistance = interResistance;
}
public BigDecimal getInterResistance()
{
return interResistance;
}
public void setDayTime(Date dayTime)
{
this.dayTime = dayTime;
}
public Date getDayTime()
{
return dayTime;
}
public BigDecimal getCurrent() {
return current;
}
public void setCurrent(BigDecimal current) {
this.current = current;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("batteryPack", getBatteryPack())
.append("batteryCluster", getBatteryCluster())
.append("batteryCellId", getBatteryCellId())
.append("voltage", getVoltage())
.append("temperature", getTemperature())
.append("soc", getSoc())
.append("soh", getSoh())
.append("dataTimestamp", getDataTimestamp())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
.append("siteId", getSiteId())
.append("deviceId", getDeviceId())
.append("clusterDeviceId", getClusterDeviceId())
.append("interResistance", getInterResistance())
.append("dayTime", getDayTime())
.append("current", getCurrent())
.toString();
}
}

View File

@ -0,0 +1,242 @@
package com.xzzn.ems.domain;
import java.math.BigDecimal;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.xzzn.common.core.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.xzzn.common.annotation.Excel;
/**
* 单体电池小时级数据对象 ems_battery_data_hour
*
* @author xzzn
* @date 2025-08-19
*/
public class EmsBatteryDataHour extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** $column.columnComment */
private Long id;
/** 电池堆 */
@Excel(name = "电池堆")
private String batteryPack;
/** 电池簇 */
@Excel(name = "电池簇")
private String batteryCluster;
/** 单体编号 */
@Excel(name = "单体编号")
private String batteryCellId;
/** 电压 (V) */
@Excel(name = "电压 (V)")
private BigDecimal voltage;
/** 温度 (℃) */
@Excel(name = "温度 (℃)")
private BigDecimal temperature;
/** SOC (%) */
@Excel(name = "SOC (%)")
private BigDecimal soc;
/** SOH (%) */
@Excel(name = "SOH (%)")
private BigDecimal soh;
/** 数据采集时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "数据采集时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date dataTimestamp;
/** 站点id */
@Excel(name = "站点id")
private String siteId;
/** 设备唯一标识符 */
@Excel(name = "设备唯一标识符")
private String deviceId;
/** 簇设备id */
@Excel(name = "簇设备id")
private String clusterDeviceId;
/** 单体电池内阻 */
@Excel(name = "单体电池内阻")
private BigDecimal interResistance;
/** 小时级时间维度 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "小时级时间维度", width = 30, dateFormat = "yyyy-MM-dd")
private Date hourTime;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setBatteryPack(String batteryPack)
{
this.batteryPack = batteryPack;
}
public String getBatteryPack()
{
return batteryPack;
}
public void setBatteryCluster(String batteryCluster)
{
this.batteryCluster = batteryCluster;
}
public String getBatteryCluster()
{
return batteryCluster;
}
public void setBatteryCellId(String batteryCellId)
{
this.batteryCellId = batteryCellId;
}
public String getBatteryCellId()
{
return batteryCellId;
}
public void setVoltage(BigDecimal voltage)
{
this.voltage = voltage;
}
public BigDecimal getVoltage()
{
return voltage;
}
public void setTemperature(BigDecimal temperature)
{
this.temperature = temperature;
}
public BigDecimal getTemperature()
{
return temperature;
}
public void setSoc(BigDecimal soc)
{
this.soc = soc;
}
public BigDecimal getSoc()
{
return soc;
}
public void setSoh(BigDecimal soh)
{
this.soh = soh;
}
public BigDecimal getSoh()
{
return soh;
}
public void setDataTimestamp(Date dataTimestamp)
{
this.dataTimestamp = dataTimestamp;
}
public Date getDataTimestamp()
{
return dataTimestamp;
}
public void setSiteId(String siteId)
{
this.siteId = siteId;
}
public String getSiteId()
{
return siteId;
}
public void setDeviceId(String deviceId)
{
this.deviceId = deviceId;
}
public String getDeviceId()
{
return deviceId;
}
public void setClusterDeviceId(String clusterDeviceId)
{
this.clusterDeviceId = clusterDeviceId;
}
public String getClusterDeviceId()
{
return clusterDeviceId;
}
public void setInterResistance(BigDecimal interResistance)
{
this.interResistance = interResistance;
}
public BigDecimal getInterResistance()
{
return interResistance;
}
public void setHourTime(Date hourTime)
{
this.hourTime = hourTime;
}
public Date getHourTime()
{
return hourTime;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("batteryPack", getBatteryPack())
.append("batteryCluster", getBatteryCluster())
.append("batteryCellId", getBatteryCellId())
.append("voltage", getVoltage())
.append("temperature", getTemperature())
.append("soc", getSoc())
.append("soh", getSoh())
.append("dataTimestamp", getDataTimestamp())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
.append("siteId", getSiteId())
.append("deviceId", getDeviceId())
.append("clusterDeviceId", getClusterDeviceId())
.append("interResistance", getInterResistance())
.append("hourTime", getHourTime())
.toString();
}
}

View File

@ -0,0 +1,226 @@
package com.xzzn.ems.domain;
import java.math.BigDecimal;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.xzzn.common.core.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.xzzn.common.annotation.Excel;
/**
* 单体电池分钟级数据对象 ems_battery_data_minutes
*
* @author xzzn
* @date 2025-08-18
*/
public class EmsBatteryDataMinutes extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** $column.columnComment */
private Long id;
/** 电池堆 */
@Excel(name = "电池堆")
private String batteryPack;
/** 电池簇 */
@Excel(name = "电池簇")
private String batteryCluster;
/** 单体编号 */
@Excel(name = "单体编号")
private String batteryCellId;
/** 电压 (V) */
@Excel(name = "电压 (V)")
private BigDecimal voltage;
/** 温度 (℃) */
@Excel(name = "温度 (℃)")
private BigDecimal temperature;
/** SOC (%) */
@Excel(name = "SOC (%)")
private BigDecimal soc;
/** SOH (%) */
@Excel(name = "SOH (%)")
private BigDecimal soh;
/** 数据采集时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "数据采集时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date dataTimestamp;
/** 站点id */
@Excel(name = "站点id")
private String siteId;
/** 设备唯一标识符 */
@Excel(name = "设备唯一标识符")
private String deviceId;
/** 簇设备id */
@Excel(name = "簇设备id")
private String clusterDeviceId;
/** 单体电池内阻 */
@Excel(name = "单体电池内阻")
private BigDecimal interResistance;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setBatteryPack(String batteryPack)
{
this.batteryPack = batteryPack;
}
public String getBatteryPack()
{
return batteryPack;
}
public void setBatteryCluster(String batteryCluster)
{
this.batteryCluster = batteryCluster;
}
public String getBatteryCluster()
{
return batteryCluster;
}
public void setBatteryCellId(String batteryCellId)
{
this.batteryCellId = batteryCellId;
}
public String getBatteryCellId()
{
return batteryCellId;
}
public void setVoltage(BigDecimal voltage)
{
this.voltage = voltage;
}
public BigDecimal getVoltage()
{
return voltage;
}
public void setTemperature(BigDecimal temperature)
{
this.temperature = temperature;
}
public BigDecimal getTemperature()
{
return temperature;
}
public void setSoc(BigDecimal soc)
{
this.soc = soc;
}
public BigDecimal getSoc()
{
return soc;
}
public void setSoh(BigDecimal soh)
{
this.soh = soh;
}
public BigDecimal getSoh()
{
return soh;
}
public void setDataTimestamp(Date dataTimestamp)
{
this.dataTimestamp = dataTimestamp;
}
public Date getDataTimestamp()
{
return dataTimestamp;
}
public void setSiteId(String siteId)
{
this.siteId = siteId;
}
public String getSiteId()
{
return siteId;
}
public void setDeviceId(String deviceId)
{
this.deviceId = deviceId;
}
public String getDeviceId()
{
return deviceId;
}
public void setClusterDeviceId(String clusterDeviceId)
{
this.clusterDeviceId = clusterDeviceId;
}
public String getClusterDeviceId()
{
return clusterDeviceId;
}
public void setInterResistance(BigDecimal interResistance)
{
this.interResistance = interResistance;
}
public BigDecimal getInterResistance()
{
return interResistance;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("batteryPack", getBatteryPack())
.append("batteryCluster", getBatteryCluster())
.append("batteryCellId", getBatteryCellId())
.append("voltage", getVoltage())
.append("temperature", getTemperature())
.append("soc", getSoc())
.append("soh", getSoh())
.append("dataTimestamp", getDataTimestamp())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
.append("siteId", getSiteId())
.append("deviceId", getDeviceId())
.append("clusterDeviceId", getClusterDeviceId())
.append("interResistance", getInterResistance())
.toString();
}
}

View File

@ -0,0 +1,242 @@
package com.xzzn.ems.domain;
import java.math.BigDecimal;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.xzzn.common.core.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.xzzn.common.annotation.Excel;
/**
* 单体电池月级数据对象 ems_battery_data_month
*
* @author xzzn
* @date 2025-08-22
*/
public class EmsBatteryDataMonth extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** $column.columnComment */
private Long id;
/** 电池堆 */
@Excel(name = "电池堆")
private String batteryPack;
/** 电池簇 */
@Excel(name = "电池簇")
private String batteryCluster;
/** 单体编号 */
@Excel(name = "单体编号")
private String batteryCellId;
/** 电压 (V) */
@Excel(name = "电压 (V)")
private BigDecimal voltage;
/** 温度 (℃) */
@Excel(name = "温度 (℃)")
private BigDecimal temperature;
/** SOC (%) */
@Excel(name = "SOC (%)")
private BigDecimal soc;
/** SOH (%) */
@Excel(name = "SOH (%)")
private BigDecimal soh;
/** 数据采集时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "数据采集时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date dataTimestamp;
/** 站点id */
@Excel(name = "站点id")
private String siteId;
/** 设备唯一标识符 */
@Excel(name = "设备唯一标识符")
private String deviceId;
/** 簇设备id */
@Excel(name = "簇设备id")
private String clusterDeviceId;
/** 单体电池内阻 */
@Excel(name = "单体电池内阻")
private BigDecimal interResistance;
/** 月级时间维度 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "月级时间维度", width = 30, dateFormat = "yyyy-MM-dd")
private Date monthTime;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setBatteryPack(String batteryPack)
{
this.batteryPack = batteryPack;
}
public String getBatteryPack()
{
return batteryPack;
}
public void setBatteryCluster(String batteryCluster)
{
this.batteryCluster = batteryCluster;
}
public String getBatteryCluster()
{
return batteryCluster;
}
public void setBatteryCellId(String batteryCellId)
{
this.batteryCellId = batteryCellId;
}
public String getBatteryCellId()
{
return batteryCellId;
}
public void setVoltage(BigDecimal voltage)
{
this.voltage = voltage;
}
public BigDecimal getVoltage()
{
return voltage;
}
public void setTemperature(BigDecimal temperature)
{
this.temperature = temperature;
}
public BigDecimal getTemperature()
{
return temperature;
}
public void setSoc(BigDecimal soc)
{
this.soc = soc;
}
public BigDecimal getSoc()
{
return soc;
}
public void setSoh(BigDecimal soh)
{
this.soh = soh;
}
public BigDecimal getSoh()
{
return soh;
}
public void setDataTimestamp(Date dataTimestamp)
{
this.dataTimestamp = dataTimestamp;
}
public Date getDataTimestamp()
{
return dataTimestamp;
}
public void setSiteId(String siteId)
{
this.siteId = siteId;
}
public String getSiteId()
{
return siteId;
}
public void setDeviceId(String deviceId)
{
this.deviceId = deviceId;
}
public String getDeviceId()
{
return deviceId;
}
public void setClusterDeviceId(String clusterDeviceId)
{
this.clusterDeviceId = clusterDeviceId;
}
public String getClusterDeviceId()
{
return clusterDeviceId;
}
public void setInterResistance(BigDecimal interResistance)
{
this.interResistance = interResistance;
}
public BigDecimal getInterResistance()
{
return interResistance;
}
public void setMonthTime(Date monthTime)
{
this.monthTime = monthTime;
}
public Date getMonthTime()
{
return monthTime;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("batteryPack", getBatteryPack())
.append("batteryCluster", getBatteryCluster())
.append("batteryCellId", getBatteryCellId())
.append("voltage", getVoltage())
.append("temperature", getTemperature())
.append("soc", getSoc())
.append("soh", getSoh())
.append("dataTimestamp", getDataTimestamp())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
.append("siteId", getSiteId())
.append("deviceId", getDeviceId())
.append("clusterDeviceId", getClusterDeviceId())
.append("interResistance", getInterResistance())
.append("monthTime", getMonthTime())
.toString();
}
}

View File

@ -1,11 +1,14 @@
package com.xzzn.ems.domain; package com.xzzn.ems.domain;
import java.math.BigDecimal; import com.fasterxml.jackson.annotation.JsonFormat;
import com.xzzn.common.annotation.Excel;
import com.xzzn.common.core.domain.BaseEntity; import com.xzzn.common.core.domain.BaseEntity;
import java.math.BigDecimal;
import java.util.Date;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import com.xzzn.common.annotation.Excel;
/** /**
* 电池堆数据对象 ems_battery_stack * 电池堆数据对象 ems_battery_stack
@ -20,8 +23,13 @@ public class EmsBatteryStack extends BaseEntity
/** */ /** */
private Long id; private Long id;
/** 工作状态0-正常 1-异常 2-停止 */ /** 数据更新时间 */
@Excel(name = "工作状态0-正常 1-异常 2-停止") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "数据更新时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date dataUpdateTime;
/** 工作状态0-运行 1-停机 2-故障 */
@Excel(name = "工作状态0-运行 1-停机 2-故障")
private String workStatus; private String workStatus;
/** 与PCS通信状态0-正常 1-通信中断 2-异常 */ /** 与PCS通信状态0-正常 1-通信中断 2-异常 */
@ -254,6 +262,14 @@ public class EmsBatteryStack extends BaseEntity
return id; return id;
} }
public Date getDataUpdateTime() {
return dataUpdateTime;
}
public void setDataUpdateTime(Date dataUpdateTime) {
this.dataUpdateTime = dataUpdateTime;
}
public void setWorkStatus(String workStatus) public void setWorkStatus(String workStatus)
{ {
this.workStatus = workStatus; this.workStatus = workStatus;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,795 @@
package com.xzzn.ems.domain;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.xzzn.common.core.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.xzzn.common.annotation.Excel;
/**
* ZSLQ告警故障数据对象 ems_cooling_alarm_data
*
* @author xzzn
* @date 2025-10-22
*/
public class EmsCoolingAlarmData extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 记录唯一ID */
private Long id;
/** 数据采集时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "数据采集时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date dataTimestamp;
/** 站点ID */
@Excel(name = "站点ID")
private String siteId;
/** 设备唯一标识符 */
@Excel(name = "设备唯一标识符")
private String deviceId;
/** */
@Excel(name = "")
private Integer lsjyx;
/** */
@Excel(name = "")
private Integer pqdcfyx;
/** */
@Excel(name = "")
private Integer djrq2yx;
/** */
@Excel(name = "")
private Integer djrq1yx;
/** */
@Excel(name = "")
private Integer bqbyx;
/** */
@Excel(name = "")
private Integer bsbyx;
/** */
@Excel(name = "")
private Integer zxhbyx;
/** */
@Excel(name = "")
private Integer sltzgz;
/** */
@Excel(name = "")
private Integer slzhyj;
/** */
@Excel(name = "")
private Integer slxtyctzxh;
/** */
@Excel(name = "")
private Integer slxtycqdxh;
/** */
@Excel(name = "")
private Integer slxtyckz;
/** */
@Excel(name = "")
private Integer slxtbdkz;
/** */
@Excel(name = "")
private Integer slxtzdms;
/** */
@Excel(name = "")
private Integer slxtsdms;
/** */
@Excel(name = "")
private Integer hsylcdyj;
/** */
@Excel(name = "")
private Integer hsyldyj;
/** */
@Excel(name = "")
private Integer gsylcgyj;
/** */
@Excel(name = "")
private Integer gsylgyj;
/** */
@Excel(name = "")
private Integer gsylcdyj;
/** */
@Excel(name = "")
private Integer gsyldyj;
/** */
@Excel(name = "")
private Integer hcgywg;
/** */
@Excel(name = "")
private Integer hcgywcdtz;
/** */
@Excel(name = "")
private Integer hcgywd;
/** */
@Excel(name = "")
private Integer hcgylg;
/** */
@Excel(name = "")
private Integer hcgyld;
/** */
@Excel(name = "")
private Integer lysylgyj;
/** $column.columnComment */
@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
private Integer lysyldyj;
/** */
@Excel(name = "")
private Integer hswdcgyj;
/** */
@Excel(name = "")
private Integer hswdgyj;
/** */
@Excel(name = "")
private Integer gswdcgtz;
/** */
@Excel(name = "")
private Integer gswdgyj;
/** */
@Excel(name = "")
private Integer gswddyj;
/** */
@Excel(name = "")
private Integer ddf2gz;
/** */
@Excel(name = "")
private Integer ddf1gz;
/** */
@Excel(name = "")
private Integer lyswdbsqyJ;
/** */
@Excel(name = "")
private Integer hsylbsqyJ;
/** */
@Excel(name = "")
private Integer gsylbsqyJ;
/** */
@Excel(name = "")
private Integer hswdbsqyJ;
/** */
@Excel(name = "")
private Integer gswdbsqgztz;
/** */
@Excel(name = "")
private Integer lsjgz;
/** */
@Excel(name = "")
private Integer djrq2gz;
/** */
@Excel(name = "")
private Integer djrq1gz;
/** */
@Excel(name = "")
private Integer bqbgz;
/** */
@Excel(name = "")
private Integer bsbgz;
/** */
@Excel(name = "")
private Integer xhbgz;
/** */
@Excel(name = "")
private Integer zdygz;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setDataTimestamp(Date dataTimestamp)
{
this.dataTimestamp = dataTimestamp;
}
public Date getDataTimestamp()
{
return dataTimestamp;
}
public void setSiteId(String siteId)
{
this.siteId = siteId;
}
public String getSiteId()
{
return siteId;
}
public void setDeviceId(String deviceId)
{
this.deviceId = deviceId;
}
public String getDeviceId()
{
return deviceId;
}
public void setLsjyx(Integer lsjyx)
{
this.lsjyx = lsjyx;
}
public Integer getLsjyx()
{
return lsjyx;
}
public void setPqdcfyx(Integer pqdcfyx)
{
this.pqdcfyx = pqdcfyx;
}
public Integer getPqdcfyx()
{
return pqdcfyx;
}
public void setDjrq2yx(Integer djrq2yx)
{
this.djrq2yx = djrq2yx;
}
public Integer getDjrq2yx()
{
return djrq2yx;
}
public void setDjrq1yx(Integer djrq1yx)
{
this.djrq1yx = djrq1yx;
}
public Integer getDjrq1yx()
{
return djrq1yx;
}
public void setBqbyx(Integer bqbyx)
{
this.bqbyx = bqbyx;
}
public Integer getBqbyx()
{
return bqbyx;
}
public void setBsbyx(Integer bsbyx)
{
this.bsbyx = bsbyx;
}
public Integer getBsbyx()
{
return bsbyx;
}
public void setZxhbyx(Integer zxhbyx)
{
this.zxhbyx = zxhbyx;
}
public Integer getZxhbyx()
{
return zxhbyx;
}
public void setSltzgz(Integer sltzgz)
{
this.sltzgz = sltzgz;
}
public Integer getSltzgz()
{
return sltzgz;
}
public void setSlzhyj(Integer slzhyj)
{
this.slzhyj = slzhyj;
}
public Integer getSlzhyj()
{
return slzhyj;
}
public void setSlxtyctzxh(Integer slxtyctzxh)
{
this.slxtyctzxh = slxtyctzxh;
}
public Integer getSlxtyctzxh()
{
return slxtyctzxh;
}
public void setSlxtycqdxh(Integer slxtycqdxh)
{
this.slxtycqdxh = slxtycqdxh;
}
public Integer getSlxtycqdxh()
{
return slxtycqdxh;
}
public void setSlxtyckz(Integer slxtyckz)
{
this.slxtyckz = slxtyckz;
}
public Integer getSlxtyckz()
{
return slxtyckz;
}
public void setSlxtbdkz(Integer slxtbdkz)
{
this.slxtbdkz = slxtbdkz;
}
public Integer getSlxtbdkz()
{
return slxtbdkz;
}
public void setSlxtzdms(Integer slxtzdms)
{
this.slxtzdms = slxtzdms;
}
public Integer getSlxtzdms()
{
return slxtzdms;
}
public void setSlxtsdms(Integer slxtsdms)
{
this.slxtsdms = slxtsdms;
}
public Integer getSlxtsdms()
{
return slxtsdms;
}
public void setHsylcdyj(Integer hsylcdyj)
{
this.hsylcdyj = hsylcdyj;
}
public Integer getHsylcdyj()
{
return hsylcdyj;
}
public void setHsyldyj(Integer hsyldyj)
{
this.hsyldyj = hsyldyj;
}
public Integer getHsyldyj()
{
return hsyldyj;
}
public void setGsylcgyj(Integer gsylcgyj)
{
this.gsylcgyj = gsylcgyj;
}
public Integer getGsylcgyj()
{
return gsylcgyj;
}
public void setGsylgyj(Integer gsylgyj)
{
this.gsylgyj = gsylgyj;
}
public Integer getGsylgyj()
{
return gsylgyj;
}
public void setGsylcdyj(Integer gsylcdyj)
{
this.gsylcdyj = gsylcdyj;
}
public Integer getGsylcdyj()
{
return gsylcdyj;
}
public void setGsyldyj(Integer gsyldyj)
{
this.gsyldyj = gsyldyj;
}
public Integer getGsyldyj()
{
return gsyldyj;
}
public void setHcgywg(Integer hcgywg)
{
this.hcgywg = hcgywg;
}
public Integer getHcgywg()
{
return hcgywg;
}
public void setHcgywcdtz(Integer hcgywcdtz)
{
this.hcgywcdtz = hcgywcdtz;
}
public Integer getHcgywcdtz()
{
return hcgywcdtz;
}
public void setHcgywd(Integer hcgywd)
{
this.hcgywd = hcgywd;
}
public Integer getHcgywd()
{
return hcgywd;
}
public void setHcgylg(Integer hcgylg)
{
this.hcgylg = hcgylg;
}
public Integer getHcgylg()
{
return hcgylg;
}
public void setHcgyld(Integer hcgyld)
{
this.hcgyld = hcgyld;
}
public Integer getHcgyld()
{
return hcgyld;
}
public void setLysylgyj(Integer lysylgyj)
{
this.lysylgyj = lysylgyj;
}
public Integer getLysylgyj()
{
return lysylgyj;
}
public void setLysyldyj(Integer lysyldyj)
{
this.lysyldyj = lysyldyj;
}
public Integer getLysyldyj()
{
return lysyldyj;
}
public void setHswdcgyj(Integer hswdcgyj)
{
this.hswdcgyj = hswdcgyj;
}
public Integer getHswdcgyj()
{
return hswdcgyj;
}
public void setHswdgyj(Integer hswdgyj)
{
this.hswdgyj = hswdgyj;
}
public Integer getHswdgyj()
{
return hswdgyj;
}
public void setGswdcgtz(Integer gswdcgtz)
{
this.gswdcgtz = gswdcgtz;
}
public Integer getGswdcgtz()
{
return gswdcgtz;
}
public void setGswdgyj(Integer gswdgyj)
{
this.gswdgyj = gswdgyj;
}
public Integer getGswdgyj()
{
return gswdgyj;
}
public void setGswddyj(Integer gswddyj)
{
this.gswddyj = gswddyj;
}
public Integer getGswddyj()
{
return gswddyj;
}
public void setDdf2gz(Integer ddf2gz)
{
this.ddf2gz = ddf2gz;
}
public Integer getDdf2gz()
{
return ddf2gz;
}
public void setDdf1gz(Integer ddf1gz)
{
this.ddf1gz = ddf1gz;
}
public Integer getDdf1gz()
{
return ddf1gz;
}
public void setLyswdbsqyJ(Integer lyswdbsqyJ)
{
this.lyswdbsqyJ = lyswdbsqyJ;
}
public Integer getLyswdbsqyJ()
{
return lyswdbsqyJ;
}
public void setHsylbsqyJ(Integer hsylbsqyJ)
{
this.hsylbsqyJ = hsylbsqyJ;
}
public Integer getHsylbsqyJ()
{
return hsylbsqyJ;
}
public void setGsylbsqyJ(Integer gsylbsqyJ)
{
this.gsylbsqyJ = gsylbsqyJ;
}
public Integer getGsylbsqyJ()
{
return gsylbsqyJ;
}
public void setHswdbsqyJ(Integer hswdbsqyJ)
{
this.hswdbsqyJ = hswdbsqyJ;
}
public Integer getHswdbsqyJ()
{
return hswdbsqyJ;
}
public void setGswdbsqgztz(Integer gswdbsqgztz)
{
this.gswdbsqgztz = gswdbsqgztz;
}
public Integer getGswdbsqgztz()
{
return gswdbsqgztz;
}
public void setLsjgz(Integer lsjgz)
{
this.lsjgz = lsjgz;
}
public Integer getLsjgz()
{
return lsjgz;
}
public void setDjrq2gz(Integer djrq2gz)
{
this.djrq2gz = djrq2gz;
}
public Integer getDjrq2gz()
{
return djrq2gz;
}
public void setDjrq1gz(Integer djrq1gz)
{
this.djrq1gz = djrq1gz;
}
public Integer getDjrq1gz()
{
return djrq1gz;
}
public void setBqbgz(Integer bqbgz)
{
this.bqbgz = bqbgz;
}
public Integer getBqbgz()
{
return bqbgz;
}
public void setBsbgz(Integer bsbgz)
{
this.bsbgz = bsbgz;
}
public Integer getBsbgz()
{
return bsbgz;
}
public void setXhbgz(Integer xhbgz)
{
this.xhbgz = xhbgz;
}
public Integer getXhbgz()
{
return xhbgz;
}
public void setZdygz(Integer zdygz)
{
this.zdygz = zdygz;
}
public Integer getZdygz()
{
return zdygz;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("dataTimestamp", getDataTimestamp())
.append("siteId", getSiteId())
.append("deviceId", getDeviceId())
.append("lsjyx", getLsjyx())
.append("pqdcfyx", getPqdcfyx())
.append("djrq2yx", getDjrq2yx())
.append("djrq1yx", getDjrq1yx())
.append("bqbyx", getBqbyx())
.append("bsbyx", getBsbyx())
.append("zxhbyx", getZxhbyx())
.append("sltzgz", getSltzgz())
.append("slzhyj", getSlzhyj())
.append("slxtyctzxh", getSlxtyctzxh())
.append("slxtycqdxh", getSlxtycqdxh())
.append("slxtyckz", getSlxtyckz())
.append("slxtbdkz", getSlxtbdkz())
.append("slxtzdms", getSlxtzdms())
.append("slxtsdms", getSlxtsdms())
.append("hsylcdyj", getHsylcdyj())
.append("hsyldyj", getHsyldyj())
.append("gsylcgyj", getGsylcgyj())
.append("gsylgyj", getGsylgyj())
.append("gsylcdyj", getGsylcdyj())
.append("gsyldyj", getGsyldyj())
.append("hcgywg", getHcgywg())
.append("hcgywcdtz", getHcgywcdtz())
.append("hcgywd", getHcgywd())
.append("hcgylg", getHcgylg())
.append("hcgyld", getHcgyld())
.append("lysylgyj", getLysylgyj())
.append("lysyldyj", getLysyldyj())
.append("hswdcgyj", getHswdcgyj())
.append("hswdgyj", getHswdgyj())
.append("gswdcgtz", getGswdcgtz())
.append("gswdgyj", getGswdgyj())
.append("gswddyj", getGswddyj())
.append("ddf2gz", getDdf2gz())
.append("ddf1gz", getDdf1gz())
.append("lyswdbsqyJ", getLyswdbsqyJ())
.append("hsylbsqyJ", getHsylbsqyJ())
.append("gsylbsqyJ", getGsylbsqyJ())
.append("hswdbsqyJ", getHswdbsqyJ())
.append("gswdbsqgztz", getGswdbsqgztz())
.append("lsjgz", getLsjgz())
.append("djrq2gz", getDjrq2gz())
.append("djrq1gz", getDjrq1gz())
.append("bqbgz", getBqbgz())
.append("bsbgz", getBsbgz())
.append("xhbgz", getXhbgz())
.append("zdygz", getZdygz())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
.toString();
}
}

View File

@ -1,7 +1,9 @@
package com.xzzn.ems.domain; package com.xzzn.ems.domain;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.xzzn.common.core.domain.BaseEntity; import com.xzzn.common.core.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
@ -11,7 +13,7 @@ import com.xzzn.common.annotation.Excel;
* 冷却系统参数对象 ems_cooling_data * 冷却系统参数对象 ems_cooling_data
* *
* @author xzzn * @author xzzn
* @date 2025-06-29 * @date 2025-09-26
*/ */
public class EmsCoolingData extends BaseEntity public class EmsCoolingData extends BaseEntity
{ {
@ -20,12 +22,17 @@ public class EmsCoolingData extends BaseEntity
/** */ /** */
private Long id; private Long id;
/** 数据更新时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "数据更新时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date dataUpdateTime;
/** 系统名称如1#液冷 */ /** 系统名称如1#液冷 */
@Excel(name = "系统名称如1#液冷") @Excel(name = "系统名称如1#液冷")
private String systemName; private String systemName;
/** 工作模式 */ /** 工作模式0-正常 1-停止 */
@Excel(name = "工作模式") @Excel(name = "工作模式0-正常 1-停止")
private String workMode; private String workMode;
/** 当前温度 (℃) */ /** 当前温度 (℃) */
@ -56,6 +63,34 @@ public class EmsCoolingData extends BaseEntity
@Excel(name = "低温告警点 (℃)") @Excel(name = "低温告警点 (℃)")
private BigDecimal lowTempAlarmPoint; private BigDecimal lowTempAlarmPoint;
/** 供水温度 */
@Excel(name = "供水温度")
private BigDecimal gsTemp;
/** 回水温度 */
@Excel(name = "回水温度")
private BigDecimal hsTemp;
/** 供水压力 */
@Excel(name = "供水压力")
private BigDecimal gsPressure;
/** 回水压力 */
@Excel(name = "回水压力")
private BigDecimal hsPressure;
/** 冷源水温度 */
@Excel(name = "冷源水温度")
private BigDecimal lysTemp;
/** VB01开度 */
@Excel(name = "VB01开度")
private BigDecimal vb01Kd;
/** VB02开度 */
@Excel(name = "VB02开度")
private BigDecimal vb02Kd;
/** 站点id */ /** 站点id */
@Excel(name = "站点id") @Excel(name = "站点id")
private String siteId; private String siteId;
@ -74,6 +109,14 @@ public class EmsCoolingData extends BaseEntity
return id; return id;
} }
public Date getDataUpdateTime() {
return dataUpdateTime;
}
public void setDataUpdateTime(Date dataUpdateTime) {
this.dataUpdateTime = dataUpdateTime;
}
public void setSystemName(String systemName) public void setSystemName(String systemName)
{ {
this.systemName = systemName; this.systemName = systemName;
@ -164,6 +207,76 @@ public class EmsCoolingData extends BaseEntity
return lowTempAlarmPoint; return lowTempAlarmPoint;
} }
public void setGsTemp(BigDecimal gsTemp)
{
this.gsTemp = gsTemp;
}
public BigDecimal getGsTemp()
{
return gsTemp;
}
public void setHsTemp(BigDecimal hsTemp)
{
this.hsTemp = hsTemp;
}
public BigDecimal getHsTemp()
{
return hsTemp;
}
public void setGsPressure(BigDecimal gsPressure)
{
this.gsPressure = gsPressure;
}
public BigDecimal getGsPressure()
{
return gsPressure;
}
public void setHsPressure(BigDecimal hsPressure)
{
this.hsPressure = hsPressure;
}
public BigDecimal getHsPressure()
{
return hsPressure;
}
public void setLysTemp(BigDecimal lysTemp)
{
this.lysTemp = lysTemp;
}
public BigDecimal getLysTemp()
{
return lysTemp;
}
public void setVb01Kd(BigDecimal vb01Kd)
{
this.vb01Kd = vb01Kd;
}
public BigDecimal getVb01Kd()
{
return vb01Kd;
}
public void setVb02Kd(BigDecimal vb02Kd)
{
this.vb02Kd = vb02Kd;
}
public BigDecimal getVb02Kd()
{
return vb02Kd;
}
public void setSiteId(String siteId) public void setSiteId(String siteId)
{ {
this.siteId = siteId; this.siteId = siteId;
@ -197,6 +310,13 @@ public class EmsCoolingData extends BaseEntity
.append("heatingStopPoint", getHeatingStopPoint()) .append("heatingStopPoint", getHeatingStopPoint())
.append("coolingStopPoint", getCoolingStopPoint()) .append("coolingStopPoint", getCoolingStopPoint())
.append("lowTempAlarmPoint", getLowTempAlarmPoint()) .append("lowTempAlarmPoint", getLowTempAlarmPoint())
.append("gsTemp", getGsTemp())
.append("hsTemp", getHsTemp())
.append("gsPressure", getGsPressure())
.append("hsPressure", getHsPressure())
.append("lysTemp", getLysTemp())
.append("vb01Kd", getVb01Kd())
.append("vb02Kd", getVb02Kd())
.append("createBy", getCreateBy()) .append("createBy", getCreateBy())
.append("createTime", getCreateTime()) .append("createTime", getCreateTime())
.append("updateBy", getUpdateBy()) .append("updateBy", getUpdateBy())

View File

@ -0,0 +1,151 @@
package com.xzzn.ems.domain;
import java.math.BigDecimal;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.xzzn.common.core.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.xzzn.common.annotation.Excel;
/**
* 站点每日充放电数据对象 ems_daily_charge_data
*
* @author xzzn
* @date 2025-08-27
*/
public class EmsDailyChargeData extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** $column.columnComment */
private Long id;
/** 站点id */
@Excel(name = "站点id")
private String siteId;
/** 设备唯一标识符 */
@Excel(name = "设备唯一标识符")
private String deviceId;
/** 数据日期:yyyy-MM-dd */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "数据日期:yyyy-MM-dd", width = 30, dateFormat = "yyyy-MM-dd")
private Date dateTime;
/** 总充电量 */
@Excel(name = "总充电量")
private BigDecimal totalChargeData;
/** 总放电量 */
@Excel(name = "总放电量")
private BigDecimal totalDischargeData;
/** 当日充电量 */
@Excel(name = "当日充电量")
private BigDecimal chargeData;
/** 当日放电量 */
@Excel(name = "当日放电量")
private BigDecimal dischargeData;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setSiteId(String siteId)
{
this.siteId = siteId;
}
public String getSiteId()
{
return siteId;
}
public void setDeviceId(String deviceId)
{
this.deviceId = deviceId;
}
public String getDeviceId()
{
return deviceId;
}
public void setDateTime(Date dateTime)
{
this.dateTime = dateTime;
}
public Date getDateTime()
{
return dateTime;
}
public void setTotalChargeData(BigDecimal totalChargeData)
{
this.totalChargeData = totalChargeData;
}
public BigDecimal getTotalChargeData()
{
return totalChargeData;
}
public void setTotalDischargeData(BigDecimal totalDischargeData)
{
this.totalDischargeData = totalDischargeData;
}
public BigDecimal getTotalDischargeData()
{
return totalDischargeData;
}
public void setChargeData(BigDecimal chargeData)
{
this.chargeData = chargeData;
}
public BigDecimal getChargeData()
{
return chargeData;
}
public void setDischargeData(BigDecimal dischargeData)
{
this.dischargeData = dischargeData;
}
public BigDecimal getDischargeData()
{
return dischargeData;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("siteId", getSiteId())
.append("deviceId", getDeviceId())
.append("dateTime", getDateTime())
.append("totalChargeData", getTotalChargeData())
.append("totalDischargeData", getTotalDischargeData())
.append("chargeData", getChargeData())
.append("dischargeData", getDischargeData())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
.toString();
}
}

View File

@ -0,0 +1,241 @@
package com.xzzn.ems.domain;
import java.math.BigDecimal;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.xzzn.common.core.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.xzzn.common.annotation.Excel;
/**
* 站点电每日数据对象 ems_daily_energy_data
*
* @author xzzn
* @date 2025-10-09
*/
public class EmsDailyEnergyData extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** */
private Long id;
/** 站点id */
@Excel(name = "站点id")
private String siteId;
/** 数据日期:yyyy-MM-dd */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "数据日期:yyyy-MM-dd", width = 30, dateFormat = "yyyy-MM-dd")
private Date dataDate;
/** 总收入 */
@Excel(name = "总收入")
private BigDecimal totalRevenue;
/** 当日实时收入 */
@Excel(name = "当日实时收入")
private BigDecimal dayRevenue;
/** 尖峰时段充电差值 */
@Excel(name = "尖峰时段充电差值")
private BigDecimal peakChargeDiff;
/** 尖峰时段放电差值 */
@Excel(name = "尖峰时段放电差值")
private BigDecimal peakDischargeDiff;
/** 峰时时段充电差值 */
@Excel(name = "峰时时段充电差值")
private BigDecimal highChargeDiff;
/** 峰时时段放电差值 */
@Excel(name = "峰时时段放电差值")
private BigDecimal highDischargeDiff;
/** 平时时段充电差值 */
@Excel(name = "平时时段充电差值")
private BigDecimal flatChargeDiff;
/** 平时时段放电差值 */
@Excel(name = "平时时段放电差值")
private BigDecimal flatDischargeDiff;
/** 谷时时段充电差值 */
@Excel(name = "谷时时段充电差值")
private BigDecimal valleyChargeDiff;
/** 谷时时段放电差值 */
@Excel(name = "谷时时段放电差值")
private BigDecimal valleyDischargeDiff;
/** 差值计算时间如2025-10-10 23:59:00 */
@Excel(name = "差值计算时间", readConverterExp = "如=2025-10-10,2=3:59:00")
private Date calcTime;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setSiteId(String siteId)
{
this.siteId = siteId;
}
public String getSiteId()
{
return siteId;
}
public void setDataDate(Date dataDate)
{
this.dataDate = dataDate;
}
public Date getDataDate()
{
return dataDate;
}
public void setTotalRevenue(BigDecimal totalRevenue)
{
this.totalRevenue = totalRevenue;
}
public BigDecimal getTotalRevenue()
{
return totalRevenue;
}
public void setDayRevenue(BigDecimal dayRevenue)
{
this.dayRevenue = dayRevenue;
}
public BigDecimal getDayRevenue()
{
return dayRevenue;
}
public void setPeakChargeDiff(BigDecimal peakChargeDiff)
{
this.peakChargeDiff = peakChargeDiff;
}
public BigDecimal getPeakChargeDiff()
{
return peakChargeDiff;
}
public void setPeakDischargeDiff(BigDecimal peakDischargeDiff)
{
this.peakDischargeDiff = peakDischargeDiff;
}
public BigDecimal getPeakDischargeDiff()
{
return peakDischargeDiff;
}
public void setHighChargeDiff(BigDecimal highChargeDiff)
{
this.highChargeDiff = highChargeDiff;
}
public BigDecimal getHighChargeDiff()
{
return highChargeDiff;
}
public void setHighDischargeDiff(BigDecimal highDischargeDiff)
{
this.highDischargeDiff = highDischargeDiff;
}
public BigDecimal getHighDischargeDiff()
{
return highDischargeDiff;
}
public void setFlatChargeDiff(BigDecimal flatChargeDiff)
{
this.flatChargeDiff = flatChargeDiff;
}
public BigDecimal getFlatChargeDiff()
{
return flatChargeDiff;
}
public void setFlatDischargeDiff(BigDecimal flatDischargeDiff)
{
this.flatDischargeDiff = flatDischargeDiff;
}
public BigDecimal getFlatDischargeDiff()
{
return flatDischargeDiff;
}
public void setValleyChargeDiff(BigDecimal valleyChargeDiff)
{
this.valleyChargeDiff = valleyChargeDiff;
}
public BigDecimal getValleyChargeDiff()
{
return valleyChargeDiff;
}
public void setValleyDischargeDiff(BigDecimal valleyDischargeDiff)
{
this.valleyDischargeDiff = valleyDischargeDiff;
}
public BigDecimal getValleyDischargeDiff()
{
return valleyDischargeDiff;
}
public void setCalcTime(Date calcTime)
{
this.calcTime = calcTime;
}
public Date getCalcTime()
{
return calcTime;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("siteId", getSiteId())
.append("dataDate", getDataDate())
.append("totalRevenue", getTotalRevenue())
.append("dayRevenue", getDayRevenue())
.append("peakChargeDiff", getPeakChargeDiff())
.append("peakDischargeDiff", getPeakDischargeDiff())
.append("highChargeDiff", getHighChargeDiff())
.append("highDischargeDiff", getHighDischargeDiff())
.append("flatChargeDiff", getFlatChargeDiff())
.append("flatDischargeDiff", getFlatDischargeDiff())
.append("valleyChargeDiff", getValleyChargeDiff())
.append("valleyDischargeDiff", getValleyDischargeDiff())
.append("calcTime", getCalcTime())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
.toString();
}
}

View File

@ -0,0 +1,134 @@
package com.xzzn.ems.domain;
import java.util.Date;
import com.xzzn.common.core.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.xzzn.common.annotation.Excel;
/**
* 设备状态变更记录对象 ems_device_change_log
*
* @author xzzn
* @date 2025-11-15
*/
public class EmsDeviceChangeLog extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** $column.columnComment */
private Long id;
/** 日志IDUUID */
@Excel(name = "日志ID", readConverterExp = "U=UID")
private String logId;
/** 日志时间(精确到秒) */
@Excel(name = "日志时间", readConverterExp = "精=确到秒")
private Date logTime;
/** 站点id */
@Excel(name = "站点id")
private String siteId;
/** 设备id */
@Excel(name = "设备id")
private String deviceId;
/** 变更前状态0-离线、1-待机、2-运行、3-故障、4-停机 */
@Excel(name = "变更前状态0-离线、1-待机、2-运行、3-故障、4-停机")
private String beforeStatus;
/** 变更后状态0-离线、1-待机、2-运行、3-故障、4-停机 */
@Excel(name = "变更后状态0-离线、1-待机、2-运行、3-故障、4-停机")
private String afterStatus;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setLogId(String logId)
{
this.logId = logId;
}
public String getLogId()
{
return logId;
}
public void setLogTime(Date logTime)
{
this.logTime = logTime;
}
public Date getLogTime()
{
return logTime;
}
public void setSiteId(String siteId)
{
this.siteId = siteId;
}
public String getSiteId()
{
return siteId;
}
public void setDeviceId(String deviceId)
{
this.deviceId = deviceId;
}
public String getDeviceId()
{
return deviceId;
}
public void setBeforeStatus(String beforeStatus)
{
this.beforeStatus = beforeStatus;
}
public String getBeforeStatus()
{
return beforeStatus;
}
public void setAfterStatus(String afterStatus)
{
this.afterStatus = afterStatus;
}
public String getAfterStatus()
{
return afterStatus;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("logId", getLogId())
.append("logTime", getLogTime())
.append("siteId", getSiteId())
.append("deviceId", getDeviceId())
.append("beforeStatus", getBeforeStatus())
.append("afterStatus", getAfterStatus())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
.toString();
}
}

View File

@ -1,11 +1,13 @@
package com.xzzn.ems.domain; package com.xzzn.ems.domain;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import com.xzzn.common.annotation.Excel;
import com.xzzn.common.core.domain.BaseEntity; import com.xzzn.common.core.domain.BaseEntity;
import java.util.Date;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import com.xzzn.common.annotation.Excel;
/** /**
* Modbus设备配置对象 ems_devices_setting * Modbus设备配置对象 ems_devices_setting
@ -102,6 +104,14 @@ public class EmsDevicesSetting extends BaseEntity
@Excel(name = "设备类别例如“STACK/CLUSTER/PCS等”") @Excel(name = "设备类别例如“STACK/CLUSTER/PCS等”")
private String deviceCategory; private String deviceCategory;
/** 设备运行状态0-离线、1-在线 */
@Excel(name = "设备运行状态0-离线、1-在线")
private String deviceStatus;
/** 设备运行状态0-离线、1-在线 */
@Excel(name = "工作状态0-运行 1-停机 2-故障")
private String workStatus;
/** 设备图像地址 */ /** 设备图像地址 */
@Excel(name = "设备图像地址") @Excel(name = "设备图像地址")
private String pictureUrl; private String pictureUrl;
@ -316,6 +326,22 @@ public class EmsDevicesSetting extends BaseEntity
return deviceCategory; return deviceCategory;
} }
public String getDeviceStatus() {
return deviceStatus;
}
public void setDeviceStatus(String deviceStatus) {
this.deviceStatus = deviceStatus;
}
public String getWorkStatus() {
return workStatus;
}
public void setWorkStatus(String workStatus) {
this.workStatus = workStatus;
}
public void setPictureUrl(String pictureUrl) public void setPictureUrl(String pictureUrl)
{ {
this.pictureUrl = pictureUrl; this.pictureUrl = pictureUrl;
@ -350,6 +376,7 @@ public class EmsDevicesSetting extends BaseEntity
.append("deviceId", getDeviceId()) .append("deviceId", getDeviceId())
.append("parentId", getParentId()) .append("parentId", getParentId())
.append("deviceCategory", getDeviceCategory()) .append("deviceCategory", getDeviceCategory())
.append("deviceStatus", getDeviceStatus())
.append("pictureUrl", getPictureUrl()) .append("pictureUrl", getPictureUrl())
.toString(); .toString();
} }

View File

@ -1,7 +1,9 @@
package com.xzzn.ems.domain; package com.xzzn.ems.domain;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.xzzn.common.core.domain.BaseEntity; import com.xzzn.common.core.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
@ -20,6 +22,11 @@ public class EmsDhData extends BaseEntity
/** $column.columnComment */ /** $column.columnComment */
private Long id; private Long id;
/** 数据更新时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "数据更新时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date dataUpdateTime;
/** 湿度 */ /** 湿度 */
@Excel(name = "湿度") @Excel(name = "湿度")
private BigDecimal humidity; private BigDecimal humidity;
@ -46,6 +53,14 @@ public class EmsDhData extends BaseEntity
return id; return id;
} }
public Date getDataUpdateTime() {
return dataUpdateTime;
}
public void setDataUpdateTime(Date dataUpdateTime) {
this.dataUpdateTime = dataUpdateTime;
}
public void setHumidity(BigDecimal humidity) public void setHumidity(BigDecimal humidity)
{ {
this.humidity = humidity; this.humidity = humidity;

View File

@ -0,0 +1,241 @@
package com.xzzn.ems.domain;
import java.math.BigDecimal;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.xzzn.common.annotation.Excel;
import com.xzzn.common.core.domain.BaseEntity;
/**
* 数据对象 ems_ems_data
*
* @author xzzn
* @date 2025-12-09
*/
public class EmsEmsData extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** $column.columnComment */
private Long id;
/** 数据采集时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "数据采集时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date dataUpdateTime;
/** EMS控制模式 */
@Excel(name = "EMS控制模式")
private BigDecimal emsStatus;
/** BMS1SOC */
@Excel(name = "BMS1SOC")
private BigDecimal bms1Soc;
/** BMS2SOC */
@Excel(name = "BMS2SOC")
private BigDecimal bms2Soc;
/** BMS3SOC */
@Excel(name = "BMS3SOC")
private BigDecimal bms3Soc;
/** BMS4SOC */
@Excel(name = "BMS4SOC")
private BigDecimal bms4Soc;
/** PCS-1有功功率 */
@Excel(name = "PCS-1有功功率")
private BigDecimal pcs1Yggl;
/** PCS-2有功功率 */
@Excel(name = "PCS-2有功功率")
private BigDecimal pcs2Yggl;
/** PCS-3有功功率 */
@Excel(name = "PCS-3有功功率")
private BigDecimal pcs3Yggl;
/** PCS-4有功功率 */
@Excel(name = "PCS-4有功功率")
private BigDecimal pcs4Yggl;
/** EMS有功功率 */
@Excel(name = "EMS有功功率")
private BigDecimal emsYggl;
/** 站点id */
@Excel(name = "站点id")
private String siteId;
/** 设备唯一标识符 */
@Excel(name = "设备唯一标识符")
private String deviceId;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setDataUpdateTime(Date dataUpdateTime)
{
this.dataUpdateTime = dataUpdateTime;
}
public Date getDataUpdateTime()
{
return dataUpdateTime;
}
public void setEmsStatus(BigDecimal emsStatus)
{
this.emsStatus = emsStatus;
}
public BigDecimal getEmsStatus()
{
return emsStatus;
}
public void setBms1Soc(BigDecimal bms1Soc)
{
this.bms1Soc = bms1Soc;
}
public BigDecimal getBms1Soc()
{
return bms1Soc;
}
public void setBms2Soc(BigDecimal bms2Soc)
{
this.bms2Soc = bms2Soc;
}
public BigDecimal getBms2Soc()
{
return bms2Soc;
}
public void setBms3Soc(BigDecimal bms3Soc)
{
this.bms3Soc = bms3Soc;
}
public BigDecimal getBms3Soc()
{
return bms3Soc;
}
public void setBms4Soc(BigDecimal bms4Soc)
{
this.bms4Soc = bms4Soc;
}
public BigDecimal getBms4Soc()
{
return bms4Soc;
}
public void setPcs1Yggl(BigDecimal pcs1Yggl)
{
this.pcs1Yggl = pcs1Yggl;
}
public BigDecimal getPcs1Yggl()
{
return pcs1Yggl;
}
public void setPcs2Yggl(BigDecimal pcs2Yggl)
{
this.pcs2Yggl = pcs2Yggl;
}
public BigDecimal getPcs2Yggl()
{
return pcs2Yggl;
}
public void setPcs3Yggl(BigDecimal pcs3Yggl)
{
this.pcs3Yggl = pcs3Yggl;
}
public BigDecimal getPcs3Yggl()
{
return pcs3Yggl;
}
public void setPcs4Yggl(BigDecimal pcs4Yggl)
{
this.pcs4Yggl = pcs4Yggl;
}
public BigDecimal getPcs4Yggl()
{
return pcs4Yggl;
}
public void setEmsYggl(BigDecimal emsYggl)
{
this.emsYggl = emsYggl;
}
public BigDecimal getEmsYggl()
{
return emsYggl;
}
public void setSiteId(String siteId)
{
this.siteId = siteId;
}
public String getSiteId()
{
return siteId;
}
public void setDeviceId(String deviceId)
{
this.deviceId = deviceId;
}
public String getDeviceId()
{
return deviceId;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("dataUpdateTime", getDataUpdateTime())
.append("emsStatus", getEmsStatus())
.append("bms1Soc", getBms1Soc())
.append("bms2Soc", getBms2Soc())
.append("bms3Soc", getBms3Soc())
.append("bms4Soc", getBms4Soc())
.append("pcs1Yggl", getPcs1Yggl())
.append("pcs2Yggl", getPcs2Yggl())
.append("pcs3Yggl", getPcs3Yggl())
.append("pcs4Yggl", getPcs4Yggl())
.append("emsYggl", getEmsYggl())
.append("siteId", getSiteId())
.append("deviceId", getDeviceId())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
.toString();
}
}

View File

@ -0,0 +1,149 @@
package com.xzzn.ems.domain;
import java.math.BigDecimal;
import com.xzzn.common.core.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.xzzn.common.annotation.Excel;
/**
* 电价配置对象 ems_energy_price_config
*
* @author xzzn
* @date 2025-10-09
*/
public class EmsEnergyPriceConfig extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** */
private Long id;
/** 站点id */
@Excel(name = "站点id")
private String siteId;
/** 年份如2025 */
@Excel(name = "年份如2025")
private String year;
/** 月份,如"9"表示9月 */
@Excel(name = "月份,如\"9\"表示9月")
private String month;
/** 尖-peak电价(元/kWh) */
@Excel(name = "尖-peak电价(元/kWh)")
private BigDecimal peak;
/** 峰-high电价(元/kWh) */
@Excel(name = "峰-high电价(元/kWh)")
private BigDecimal high;
/** 平-flat电价(元/kWh) */
@Excel(name = "平-flat电价(元/kWh)")
private BigDecimal flat;
/** 谷-valley电价(元/kWh) */
@Excel(name = "谷-valley电价(元/kWh)")
private BigDecimal valley;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setSiteId(String siteId)
{
this.siteId = siteId;
}
public String getSiteId()
{
return siteId;
}
public void setYear(String year)
{
this.year = year;
}
public String getYear()
{
return year;
}
public void setMonth(String month)
{
this.month = month;
}
public String getMonth()
{
return month;
}
public void setPeak(BigDecimal peak)
{
this.peak = peak;
}
public BigDecimal getPeak()
{
return peak;
}
public void setHigh(BigDecimal high)
{
this.high = high;
}
public BigDecimal getHigh()
{
return high;
}
public void setFlat(BigDecimal flat)
{
this.flat = flat;
}
public BigDecimal getFlat()
{
return flat;
}
public void setValley(BigDecimal valley)
{
this.valley = valley;
}
public BigDecimal getValley()
{
return valley;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("siteId", getSiteId())
.append("year", getYear())
.append("month", getMonth())
.append("peak", getPeak())
.append("high", getHigh())
.append("flat", getFlat())
.append("valley", getValley())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
.toString();
}
}

View File

@ -0,0 +1,150 @@
package com.xzzn.ems.domain;
import java.math.BigDecimal;
import java.util.Date;
import com.xzzn.common.core.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.xzzn.common.annotation.Excel;
/**
* 告警保护方案下发日志对象 ems_fault_issue_log
*
* @author xzzn
* @date 2025-11-15
*/
public class EmsFaultIssueLog extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 主键 */
private Long id;
/** 日志IDUUID */
@Excel(name = "日志ID", readConverterExp = "U=UID")
private String logId;
/** 日志时间(精确到秒) */
@Excel(name = "日志时间", readConverterExp = "精=确到秒")
private Date logTime;
/** 日志等级同方案等级1/2/3等 */
@Excel(name = "日志等级", readConverterExp = "同=方案等级1/2/3等")
private Integer logLevel;
/** 站点名称/编号 */
@Excel(name = "站点名称/编号")
private String siteId;
/** 设备ID */
@Excel(name = "设备ID")
private String deviceId;
/** 操作点位 */
@Excel(name = "操作点位")
private String point;
/** 设置数据 */
@Excel(name = "设置数据")
private BigDecimal value;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setLogId(String logId)
{
this.logId = logId;
}
public String getLogId()
{
return logId;
}
public void setLogTime(Date logTime)
{
this.logTime = logTime;
}
public Date getLogTime()
{
return logTime;
}
public void setLogLevel(Integer logLevel)
{
this.logLevel = logLevel;
}
public Integer getLogLevel()
{
return logLevel;
}
public void setSiteId(String siteId)
{
this.siteId = siteId;
}
public String getSiteId()
{
return siteId;
}
public void setDeviceId(String deviceId)
{
this.deviceId = deviceId;
}
public String getDeviceId()
{
return deviceId;
}
public void setPoint(String point)
{
this.point = point;
}
public String getPoint()
{
return point;
}
public void setValue(BigDecimal value)
{
this.value = value;
}
public BigDecimal getValue()
{
return value;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("logId", getLogId())
.append("logTime", getLogTime())
.append("logLevel", getLogLevel())
.append("site", getSiteId())
.append("deviceId", getDeviceId())
.append("point", getPoint())
.append("value", getValue())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
.toString();
}
}

View File

@ -0,0 +1,191 @@
package com.xzzn.ems.domain;
import com.xzzn.common.core.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.xzzn.common.annotation.Excel;
/**
* 故障告警保护方案对象 ems_fault_protection_plan
*
* @author xzzn
* @date 2025-10-24
*/
public class EmsFaultProtectionPlan extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 主键 */
private Long id;
/** 站点id */
@Excel(name = "站点id")
private String siteId;
/** 故障名称(如:总压高、放电总压过低) */
@Excel(name = "故障名称", readConverterExp = "如=:总压高、放电总压过低")
private String faultName;
/** 故障等级1级/2级/3级 */
@Excel(name = "故障等级", readConverterExp = "1=级/2级/3级")
private Integer faultLevel;
/** 保护设置:点位/故障值/比较方式/释放值等 */
@Excel(name = "保护设置:点位/故障值/比较方式/释放值等")
private String protectionSettings;
/** 故障延时3S→3 */
@Excel(name = "故障延时", readConverterExp = "秒=3S→3")
private Long faultDelaySeconds;
/** 保护方案:修改目标点位和值 */
@Excel(name = "保护方案:修改目标点位和值")
private String protectionPlan;
/** 释放延时5S→53级可能无 */
@Excel(name = "释放延时", readConverterExp = "秒=5S→53级可能无")
private Long releaseDelaySeconds;
/** 处理方案描述例如报警降功率50%运行) */
@Excel(name = "处理方案描述", readConverterExp = "例=如报警降功率50%运行")
private String description;
/** 是否触发告警0 - 不告警1 - 告警) */
@Excel(name = "是否触发告警", readConverterExp = "0=不告警1=告警")
private Integer isAlert;
/** 方案是否启用 0-未启用 1-已启用 */
@Excel(name = "方案是否启用 0-未启用 1-已启用")
private Long status;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setSiteId(String siteId)
{
this.siteId = siteId;
}
public String getSiteId()
{
return siteId;
}
public void setFaultName(String faultName)
{
this.faultName = faultName;
}
public String getFaultName()
{
return faultName;
}
public void setFaultLevel(Integer faultLevel)
{
this.faultLevel = faultLevel;
}
public Integer getFaultLevel()
{
return faultLevel;
}
public void setProtectionSettings(String protectionSettings)
{
this.protectionSettings = protectionSettings;
}
public String getProtectionSettings()
{
return protectionSettings;
}
public void setFaultDelaySeconds(Long faultDelaySeconds)
{
this.faultDelaySeconds = faultDelaySeconds;
}
public Long getFaultDelaySeconds()
{
return faultDelaySeconds;
}
public void setProtectionPlan(String protectionPlan)
{
this.protectionPlan = protectionPlan;
}
public String getProtectionPlan()
{
return protectionPlan;
}
public void setReleaseDelaySeconds(Long releaseDelaySeconds)
{
this.releaseDelaySeconds = releaseDelaySeconds;
}
public Long getReleaseDelaySeconds()
{
return releaseDelaySeconds;
}
public void setDescription(String description)
{
this.description = description;
}
public String getDescription()
{
return description;
}
public void setIsAlert(Integer isAlert)
{
this.isAlert = isAlert;
}
public Integer getIsAlert()
{
return isAlert;
}
public void setStatus(Long status)
{
this.status = status;
}
public Long getStatus()
{
return status;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("siteId", getSiteId())
.append("faultName", getFaultName())
.append("faultLevel", getFaultLevel())
.append("protectionSettings", getProtectionSettings())
.append("faultDelaySeconds", getFaultDelaySeconds())
.append("protectionPlan", getProtectionPlan())
.append("releaseDelaySeconds", getReleaseDelaySeconds())
.append("description", getDescription())
.append("isAlert", getIsAlert())
.append("status", getStatus())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.toString();
}
}

View File

@ -0,0 +1,130 @@
package com.xzzn.ems.domain;
import com.xzzn.common.core.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.xzzn.common.annotation.Excel;
/**
* 站点topic配置对象 ems_mqtt_topic_config
*
* @author xzzn
* @date 2025-11-06
*/
public class EmsMqttTopicConfig extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 主键 */
private Long id;
/** 订阅topic */
@Excel(name = "订阅topic")
private String mqttTopic;
/** QoS等级0/1/2 */
@Excel(name = "QoS等级", readConverterExp = "0=/1/2")
private Integer qos;
/** topic描述 */
@Excel(name = "topic描述")
private String topicName;
/** 对应方法 */
@Excel(name = "对应方法")
private String handleMethod;
/** 处理器类型:(DEVICE=设备数据SYSTEM=系统状态ALARM=告警数据等) */
@Excel(name = "处理器类型:(DEVICE=设备数据SYSTEM=系统状态ALARM=告警数据等)")
private String handleType;
/** 站点id */
@Excel(name = "站点id")
private String siteId;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setMqttTopic(String mqttTopic)
{
this.mqttTopic = mqttTopic;
}
public String getMqttTopic()
{
return mqttTopic;
}
public void setQos(Integer qos)
{
this.qos = qos;
}
public Integer getQos()
{
return qos;
}
public void setTopicName(String topicName)
{
this.topicName = topicName;
}
public String getTopicName()
{
return topicName;
}
public void setHandleMethod(String handleMethod)
{
this.handleMethod = handleMethod;
}
public String getHandleMethod()
{
return handleMethod;
}
public void setHandleType(String handleType)
{
this.handleType = handleType;
}
public String getHandleType()
{
return handleType;
}
public void setSiteId(String siteId)
{
this.siteId = siteId;
}
public String getSiteId()
{
return siteId;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("mqttTopic", getMqttTopic())
.append("qos", getQos())
.append("topicName", getTopicName())
.append("handleMethod", getHandleMethod())
.append("handleType", getHandleType())
.append("siteId", getSiteId())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.toString();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,20 @@
package com.xzzn.ems.domain; package com.xzzn.ems.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.xzzn.common.annotation.Excel;
import com.xzzn.common.core.domain.BaseEntity;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Date; import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.xzzn.common.core.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import com.xzzn.common.annotation.Excel;
/** /**
* PCS数据对象 ems_pcs_data * PCS数据对象 ems_pcs_data
* *
* @author xzzn * @author xzzn
* @date 2025-07-07 * @date 2025-09-25
*/ */
public class EmsPcsData extends BaseEntity public class EmsPcsData extends BaseEntity
{ {
@ -26,16 +28,16 @@ public class EmsPcsData extends BaseEntity
@Excel(name = "数据更新时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") @Excel(name = "数据更新时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date dataUpdateTime; private Date dataUpdateTime;
/** 工作状态0-正常 1-异常 2-停止 */ /** 工作状态0-运行 1-停机 2-故障 */
@Excel(name = "工作状态0-正常 1-异常 2-停止") @Excel(name = "工作状态0-运行 1-停机 2-故障")
private String workStatus; private String workStatus;
/** 并网状态0-并网 1-未并网 */ /** 并网状态0-并网 1-未并网 */
@Excel(name = "并网状态0-并网 1-未并网") @Excel(name = "并网状态0-并网 1-未并网")
private String gridStatus; private String gridStatus;
/** 设备状态0-在线 1-离线 2-维修中 */ /** 设备运行状态0-离线、1-在线 */
@Excel(name = "设备状态0-在线 1-离线 2-维修中") @Excel(name = "设备运行状态0-离线、1-在线")
private String deviceStatus; private String deviceStatus;
/** 控制模式0-远程 1-本地 */ /** 控制模式0-远程 1-本地 */
@ -206,6 +208,34 @@ public class EmsPcsData extends BaseEntity
@Excel(name = "电网频率") @Excel(name = "电网频率")
private BigDecimal dwFrequency; private BigDecimal dwFrequency;
/** 单元1U相IGBT温度 */
@Excel(name = "单元1U相IGBT温度")
private BigDecimal uTemperature;
/** 单元1V相IGBT温度 */
@Excel(name = "单元1V相IGBT温度")
private BigDecimal vTemperature;
/** 单元1W相IGBT温度 */
@Excel(name = "单元1W相IGBT温度")
private BigDecimal wTemperature;
/** 1#模块IGBT最高温 */
@Excel(name = "1#模块IGBT最高温")
private BigDecimal module1Temp;
/** 2#模块IGBT最高温 */
@Excel(name = "2#模块IGBT最高温")
private BigDecimal module2Temp;
/** 3#模块IGBT最高温 */
@Excel(name = "3#模块IGBT最高温")
private BigDecimal module3Temp;
/** 4#模块IGBT最高温 */
@Excel(name = "4#模块IGBT最高温")
private BigDecimal module4Temp;
public void setId(Long id) public void setId(Long id)
{ {
this.id = id; this.id = id;
@ -676,6 +706,76 @@ public class EmsPcsData extends BaseEntity
return dwFrequency; return dwFrequency;
} }
public void setuTemperature(BigDecimal uTemperature)
{
this.uTemperature = uTemperature;
}
public BigDecimal getuTemperature()
{
return uTemperature;
}
public void setvTemperature(BigDecimal vTemperature)
{
this.vTemperature = vTemperature;
}
public BigDecimal getvTemperature()
{
return vTemperature;
}
public void setwTemperature(BigDecimal wTemperature)
{
this.wTemperature = wTemperature;
}
public BigDecimal getwTemperature()
{
return wTemperature;
}
public void setModule1Temp(BigDecimal module1Temp)
{
this.module1Temp = module1Temp;
}
public BigDecimal getModule1Temp()
{
return module1Temp;
}
public void setModule2Temp(BigDecimal module2Temp)
{
this.module2Temp = module2Temp;
}
public BigDecimal getModule2Temp()
{
return module2Temp;
}
public void setModule3Temp(BigDecimal module3Temp)
{
this.module3Temp = module3Temp;
}
public BigDecimal getModule3Temp()
{
return module3Temp;
}
public void setModule4Temp(BigDecimal module4Temp)
{
this.module4Temp = module4Temp;
}
public BigDecimal getModule4Temp()
{
return module4Temp;
}
@Override @Override
public String toString() { public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
@ -731,6 +831,13 @@ public class EmsPcsData extends BaseEntity
.append("sysVCurrent", getSysVCurrent()) .append("sysVCurrent", getSysVCurrent())
.append("sysWCurrent", getSysWCurrent()) .append("sysWCurrent", getSysWCurrent())
.append("dwFrequency", getDwFrequency()) .append("dwFrequency", getDwFrequency())
.append("uTemperature", getuTemperature())
.append("vTemperature", getvTemperature())
.append("wTemperature", getwTemperature())
.append("module1Temp", getModule1Temp())
.append("module2Temp", getModule2Temp())
.append("module3Temp", getModule3Temp())
.append("module4Temp", getModule4Temp())
.toString(); .toString();
} }
} }

View File

@ -0,0 +1,177 @@
package com.xzzn.ems.domain;
import com.xzzn.common.annotation.Excel;
import com.xzzn.common.core.domain.BaseEntity;
import java.math.BigDecimal;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
/**
* PCS设备配置对象 ems_pcs_setting
*
* @author xzzn
* @date 2025-12-29
*/
public class EmsPcsSetting extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 设备ID主键自增长 */
private Long id;
/** 设备配置关联ID */
@Excel(name = "设备配置关联ID")
private Long deviceSettingId;
/** 开关机地址 */
@Excel(name = "开关机地址")
private String pointAddress;
/** 功率地址 */
@Excel(name = "功率地址")
private String powerAddress;
/** 开机指令 */
@Excel(name = "开机指令")
private String startCommand;
/** 关机指令 */
@Excel(name = "关机指令")
private String stopCommand;
/** 开机目标功率 */
@Excel(name = "开机目标功率")
private BigDecimal startPower;
/** 关机目标功率 */
@Excel(name = "关机目标功率")
private BigDecimal stopPower;
/** 电池簇数 */
@Excel(name = "电池簇数")
private Integer clusterNum;
/** 电池簇地址 */
@Excel(name = "电池簇地址")
private String clusterPointAddress;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setDeviceSettingId(Long deviceSettingId)
{
this.deviceSettingId = deviceSettingId;
}
public Long getDeviceSettingId()
{
return deviceSettingId;
}
public String getPowerAddress() {
return powerAddress;
}
public void setPowerAddress(String powerAddress) {
this.powerAddress = powerAddress;
}
public void setPointAddress(String pointAddress)
{
this.pointAddress = pointAddress;
}
public String getPointAddress()
{
return pointAddress;
}
public void setStartCommand(String startCommand)
{
this.startCommand = startCommand;
}
public String getStartCommand()
{
return startCommand;
}
public void setStopCommand(String stopCommand)
{
this.stopCommand = stopCommand;
}
public String getStopCommand()
{
return stopCommand;
}
public void setStartPower(BigDecimal startPower)
{
this.startPower = startPower;
}
public BigDecimal getStartPower()
{
return startPower;
}
public void setStopPower(BigDecimal stopPower)
{
this.stopPower = stopPower;
}
public BigDecimal getStopPower()
{
return stopPower;
}
public void setClusterNum(Integer clusterNum)
{
this.clusterNum = clusterNum;
}
public Integer getClusterNum()
{
return clusterNum;
}
public void setClusterPointAddress(String clusterPointAddress)
{
this.clusterPointAddress = clusterPointAddress;
}
public String getClusterPointAddress()
{
return clusterPointAddress;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("deviceSettingId", getDeviceSettingId())
.append("pointAddress", getPointAddress())
.append("startCommand", getStartCommand())
.append("stopCommand", getStopCommand())
.append("startPower", getStartPower())
.append("stopPower", getStopPower())
.append("clusterNum", getClusterNum())
.append("clusterPointAddress", getClusterPointAddress())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
.toString();
}
}

View File

@ -0,0 +1,147 @@
package com.xzzn.ems.domain;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.xzzn.common.annotation.Excel;
import com.xzzn.common.core.domain.BaseEntity;
/**
* 点位枚举匹配对象 ems_point_enum_match
*
* @author xzzn
* @date 2025-12-08
*/
public class EmsPointEnumMatch extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 主键自增长 */
private Long id;
/** 点位匹配字段 */
@Excel(name = "点位匹配字段")
private String matchField;
/** 站点id */
@Excel(name = "站点id")
private String siteId;
/** 设备类别例如“STACK/CLUSTER/PCS等” */
@Excel(name = "设备类别例如“STACK/CLUSTER/PCS等”")
private String deviceCategory;
/** 系统枚举代码 */
@Excel(name = "系统枚举代码")
private String enumCode;
/** 系统枚举名称 */
@Excel(name = "系统枚举名称")
private String enumName;
/** 系统枚举描述 */
@Excel(name = "系统枚举描述")
private String enumDesc;
/** 数据枚举代码 */
@Excel(name = "数据枚举代码")
private String dataEnumCode;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setMatchField(String matchField)
{
this.matchField = matchField;
}
public String getMatchField()
{
return matchField;
}
public void setSiteId(String siteId)
{
this.siteId = siteId;
}
public String getSiteId()
{
return siteId;
}
public void setDeviceCategory(String deviceCategory)
{
this.deviceCategory = deviceCategory;
}
public String getDeviceCategory()
{
return deviceCategory;
}
public void setEnumCode(String enumCode)
{
this.enumCode = enumCode;
}
public String getEnumCode()
{
return enumCode;
}
public void setEnumName(String enumName)
{
this.enumName = enumName;
}
public String getEnumName()
{
return enumName;
}
public void setEnumDesc(String enumDesc)
{
this.enumDesc = enumDesc;
}
public String getEnumDesc()
{
return enumDesc;
}
public void setDataEnumCode(String dataEnumCode)
{
this.dataEnumCode = dataEnumCode;
}
public String getDataEnumCode()
{
return dataEnumCode;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("matchField", getMatchField())
.append("siteId", getSiteId())
.append("deviceCategory", getDeviceCategory())
.append("enumCode", getEnumCode())
.append("enumName", getEnumName())
.append("enumDesc", getEnumDesc())
.append("dataEnumCode", getDataEnumCode())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
.toString();
}
}

Some files were not shown because too many files have changed in this diff Show More