package com.ruoyi.wrzs.monitor.service;
|
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.ruoyi.common.utils.SecurityUtils;
|
import com.ruoyi.wrzs.monitor.mapper.MonitorAlarmMapper;
|
import com.ruoyi.wrzs.monitor.mapper.MonitorControlMapper;
|
import com.ruoyi.wrzs.monitor.mapper.MonitorDataMapper;
|
import com.ruoyi.wrzs.monitor.mapper.MonitorDeviceMapper;
|
import com.ruoyi.wrzs.monitor.mapper.MonitorMetricMapper;
|
import com.ruoyi.wrzs.monitor.pojo.MonitorAlarm;
|
import com.ruoyi.wrzs.monitor.pojo.MonitorControl;
|
import com.ruoyi.wrzs.monitor.pojo.MonitorData;
|
import com.ruoyi.wrzs.monitor.pojo.MonitorDevice;
|
import com.ruoyi.wrzs.monitor.pojo.MonitorMetric;
|
import lombok.AllArgsConstructor;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.StringUtils;
|
|
import java.math.BigDecimal;
|
import java.math.RoundingMode;
|
import java.time.LocalDateTime;
|
import java.util.HashMap;
|
import java.util.List;
|
import java.util.Map;
|
|
/**
|
* 远程监控设备业务
|
* <p>
|
* 设备(温度/压力/能耗) -> 指标阈值监测 -> 越限自动报警 -> 远程启停控制
|
* 不支持在线监测/远程控制的设备如实标记,页面不做假数据,支持人工补录与人工登记
|
*/
|
@Slf4j
|
@AllArgsConstructor
|
@Service
|
public class MonitorService {
|
|
private final MonitorDeviceMapper deviceMapper;
|
private final MonitorMetricMapper metricMapper;
|
private final MonitorDataMapper dataMapper;
|
private final MonitorAlarmMapper alarmMapper;
|
private final MonitorControlMapper controlMapper;
|
|
// ==================== 设备台账 ====================
|
|
public IPage<MonitorDevice> devicePage(Page<MonitorDevice> page, MonitorDevice query) {
|
return deviceMapper.selectPage(page, Wrappers.<MonitorDevice>lambdaQuery()
|
.like(StringUtils.hasText(query.getDeviceName()), MonitorDevice::getDeviceName, query.getDeviceName())
|
.like(StringUtils.hasText(query.getDeviceCode()), MonitorDevice::getDeviceCode, query.getDeviceCode())
|
.eq(StringUtils.hasText(query.getDeviceType()), MonitorDevice::getDeviceType, query.getDeviceType())
|
.eq(StringUtils.hasText(query.getOnlineStatus()), MonitorDevice::getOnlineStatus, query.getOnlineStatus())
|
.eq(StringUtils.hasText(query.getLocation()), MonitorDevice::getLocation, query.getLocation())
|
.orderByDesc(MonitorDevice::getId));
|
}
|
|
public List<MonitorDevice> deviceList() {
|
return deviceMapper.selectList(Wrappers.<MonitorDevice>lambdaQuery().orderByAsc(MonitorDevice::getDeviceCode));
|
}
|
|
/** 设备详情(附带监测指标) */
|
public Map<String, Object> deviceInfo(Long id) {
|
Map<String, Object> result = new HashMap<>();
|
MonitorDevice device = deviceMapper.selectById(id);
|
result.put("device", device);
|
result.put("metrics", metricList(id));
|
return result;
|
}
|
|
@Transactional(rollbackFor = Exception.class)
|
public boolean deviceSave(MonitorDevice device) {
|
if (!StringUtils.hasText(device.getOnlineStatus())) {
|
device.setOnlineStatus("1");
|
}
|
if (!StringUtils.hasText(device.getRunStatus())) {
|
device.setRunStatus("0");
|
}
|
if (!StringUtils.hasText(device.getMonitorEnabled())) {
|
device.setMonitorEnabled("1");
|
}
|
if (!StringUtils.hasText(device.getControlEnabled())) {
|
device.setControlEnabled("0");
|
}
|
return deviceMapper.insert(device) > 0;
|
}
|
|
public boolean deviceUpdate(MonitorDevice device) {
|
return deviceMapper.updateById(device) > 0;
|
}
|
|
public boolean deviceRemove(List<Long> ids) {
|
if (CollectionUtils.isEmpty(ids)) {
|
return false;
|
}
|
metricMapper.delete(Wrappers.<MonitorMetric>lambdaQuery().in(MonitorMetric::getDeviceId, ids));
|
return deviceMapper.deleteBatchIds(ids) > 0;
|
}
|
|
// ==================== 监测指标 ====================
|
|
public List<MonitorMetric> metricList(Long deviceId) {
|
return metricMapper.selectList(Wrappers.<MonitorMetric>lambdaQuery()
|
.eq(deviceId != null, MonitorMetric::getDeviceId, deviceId)
|
.orderByAsc(MonitorMetric::getId));
|
}
|
|
public boolean metricSave(MonitorMetric metric) {
|
if (!StringUtils.hasText(metric.getAlarmEnabled())) {
|
metric.setAlarmEnabled("1");
|
}
|
if (!StringUtils.hasText(metric.getCollectType())) {
|
metric.setCollectType("0");
|
}
|
return metricMapper.insert(metric) > 0;
|
}
|
|
public boolean metricUpdate(MonitorMetric metric) {
|
return metricMapper.updateById(metric) > 0;
|
}
|
|
public boolean metricRemove(List<Long> ids) {
|
if (CollectionUtils.isEmpty(ids)) {
|
return false;
|
}
|
return metricMapper.deleteBatchIds(ids) > 0;
|
}
|
|
// ==================== 数据采集与报警 ====================
|
|
/** 监测历史数据(用于实时监测趋势曲线) */
|
public List<MonitorData> dataHistory(Long deviceId, String metricCode, String startTime, String endTime) {
|
return dataMapper.selectList(Wrappers.<MonitorData>lambdaQuery()
|
.eq(deviceId != null, MonitorData::getDeviceId, deviceId)
|
.eq(StringUtils.hasText(metricCode), MonitorData::getMetricCode, metricCode)
|
.ge(StringUtils.hasText(startTime), MonitorData::getCollectTime, startTime)
|
.le(StringUtils.hasText(endTime), MonitorData::getCollectTime, endTime)
|
.orderByAsc(MonitorData::getCollectTime)
|
.last("limit 500"));
|
}
|
|
/**
|
* 采集数据上报(自动采集端调用,也可由页面模拟采集)
|
* 写入历史数据、更新指标当前值,并做阈值报警判定
|
*/
|
@Transactional(rollbackFor = Exception.class)
|
public MonitorAlarm dataReport(Long deviceId, String metricCode, BigDecimal value, String dataSource) {
|
MonitorDevice device = deviceMapper.selectById(deviceId);
|
if (device == null) {
|
throw new IllegalArgumentException("设备不存在");
|
}
|
MonitorMetric metric = metricMapper.selectOne(Wrappers.<MonitorMetric>lambdaQuery()
|
.eq(MonitorMetric::getDeviceId, deviceId)
|
.eq(MonitorMetric::getMetricCode, metricCode)
|
.last("limit 1"));
|
if (metric == null) {
|
throw new IllegalArgumentException("未配置该监测指标");
|
}
|
LocalDateTime now = LocalDateTime.now();
|
metric.setCurrentValue(value);
|
metric.setLastCollectTime(now);
|
metric.setCollectType("1".equals(dataSource) ? "1" : "0");
|
metricMapper.updateById(metric);
|
|
MonitorData data = new MonitorData();
|
data.setDeviceId(deviceId);
|
data.setDeviceName(device.getDeviceName());
|
data.setMetricCode(metric.getMetricCode());
|
data.setMetricName(metric.getMetricName());
|
data.setMetricValue(value);
|
data.setUnit(metric.getUnit());
|
data.setCollectTime(now);
|
data.setDataSource("1".equals(dataSource) ? "1" : "0");
|
dataMapper.insert(data);
|
|
device.setLastHeartbeat(now);
|
device.setOnlineStatus("0");
|
deviceMapper.updateById(device);
|
|
return checkAlarm(device, metric, value, now);
|
}
|
|
/** 阈值判断,越限生成报警(同一指标未处理的报警不重复生成,只更新数值) */
|
private MonitorAlarm checkAlarm(MonitorDevice device, MonitorMetric metric, BigDecimal value, LocalDateTime now) {
|
if (!"1".equals(metric.getAlarmEnabled()) || value == null) {
|
return null;
|
}
|
boolean belowMin = metric.getThresholdMin() != null && value.compareTo(metric.getThresholdMin()) < 0;
|
boolean aboveMax = metric.getThresholdMax() != null && value.compareTo(metric.getThresholdMax()) > 0;
|
if (!belowMin && !aboveMax) {
|
return null;
|
}
|
MonitorAlarm exists = alarmMapper.selectOne(Wrappers.<MonitorAlarm>lambdaQuery()
|
.eq(MonitorAlarm::getDeviceId, device.getId())
|
.eq(MonitorAlarm::getMetricCode, metric.getMetricCode())
|
.eq(MonitorAlarm::getAlarmStatus, "0")
|
.orderByDesc(MonitorAlarm::getId)
|
.last("limit 1"));
|
MonitorAlarm alarm = exists == null ? new MonitorAlarm() : exists;
|
alarm.setDeviceId(device.getId());
|
alarm.setDeviceName(device.getDeviceName());
|
alarm.setLocation(device.getLocation());
|
alarm.setMetricCode(metric.getMetricCode());
|
alarm.setMetricName(metric.getMetricName());
|
alarm.setAlarmValue(value);
|
alarm.setUnit(metric.getUnit());
|
alarm.setThresholdMin(metric.getThresholdMin());
|
alarm.setThresholdMax(metric.getThresholdMax());
|
alarm.setAlarmLevel(resolveLevel(metric, value));
|
alarm.setAlarmContent(buildAlarmContent(device, metric, value));
|
alarm.setAlarmTime(now);
|
alarm.setAlarmStatus("0");
|
alarm.setPushStatus("1");
|
if (exists == null) {
|
alarmMapper.insert(alarm);
|
} else {
|
alarmMapper.updateById(alarm);
|
}
|
// 设备状态置为报警
|
device.setOnlineStatus("2");
|
deviceMapper.updateById(device);
|
return alarm;
|
}
|
|
private String resolveLevel(MonitorMetric metric, BigDecimal value) {
|
BigDecimal base = null;
|
if (metric.getThresholdMax() != null && value.compareTo(metric.getThresholdMax()) > 0) {
|
base = metric.getThresholdMax();
|
} else if (metric.getThresholdMin() != null && value.compareTo(metric.getThresholdMin()) < 0) {
|
base = metric.getThresholdMin();
|
}
|
if (base == null || base.compareTo(BigDecimal.ZERO) == 0) {
|
return "1";
|
}
|
BigDecimal rate = value.subtract(base).abs().multiply(BigDecimal.valueOf(100))
|
.divide(base.abs(), 2, RoundingMode.HALF_UP);
|
if (rate.compareTo(BigDecimal.valueOf(15)) >= 0) {
|
return "2";
|
}
|
if (rate.compareTo(BigDecimal.valueOf(5)) >= 0) {
|
return "1";
|
}
|
return "0";
|
}
|
|
private String buildAlarmContent(MonitorDevice device, MonitorMetric metric, BigDecimal value) {
|
String threshold = "下限 " + (metric.getThresholdMin() == null ? "-" : metric.getThresholdMin())
|
+ " / 上限 " + (metric.getThresholdMax() == null ? "-" : metric.getThresholdMax());
|
return device.getDeviceName() + " " + metric.getMetricName() + " 当前值 " + value
|
+ (StringUtils.hasText(metric.getUnit()) ? metric.getUnit() : "")
|
+ " 超出阈值(" + threshold + "),请及时处理";
|
}
|
|
// ==================== 报警管理 ====================
|
|
public IPage<MonitorAlarm> alarmPage(Page<MonitorAlarm> page, MonitorAlarm query) {
|
return alarmMapper.selectPage(page, Wrappers.<MonitorAlarm>lambdaQuery()
|
.like(StringUtils.hasText(query.getDeviceName()), MonitorAlarm::getDeviceName, query.getDeviceName())
|
.eq(StringUtils.hasText(query.getAlarmStatus()), MonitorAlarm::getAlarmStatus, query.getAlarmStatus())
|
.eq(StringUtils.hasText(query.getAlarmLevel()), MonitorAlarm::getAlarmLevel, query.getAlarmLevel())
|
.orderByDesc(MonitorAlarm::getId));
|
}
|
|
public long unhandledAlarmCount() {
|
return alarmMapper.selectCount(Wrappers.<MonitorAlarm>lambdaQuery().eq(MonitorAlarm::getAlarmStatus, "0"));
|
}
|
|
public List<MonitorAlarm> unhandledAlarms() {
|
return alarmMapper.selectList(Wrappers.<MonitorAlarm>lambdaQuery()
|
.eq(MonitorAlarm::getAlarmStatus, "0")
|
.orderByDesc(MonitorAlarm::getAlarmTime)
|
.last("limit 20"));
|
}
|
|
/** 处理报警 */
|
public boolean alarmHandle(Long id, String handleRemark) {
|
MonitorAlarm alarm = alarmMapper.selectById(id);
|
if (alarm == null) {
|
return false;
|
}
|
alarm.setAlarmStatus("1");
|
alarm.setHandleTime(LocalDateTime.now());
|
alarm.setHandleRemark(handleRemark);
|
alarm.setHandleUser(currentUserName());
|
boolean updated = alarmMapper.updateById(alarm) > 0;
|
// 该设备已无未处理报警则恢复在线状态
|
Long left = alarmMapper.selectCount(Wrappers.<MonitorAlarm>lambdaQuery()
|
.eq(MonitorAlarm::getDeviceId, alarm.getDeviceId())
|
.eq(MonitorAlarm::getAlarmStatus, "0"));
|
if (left == null || left == 0) {
|
MonitorDevice device = deviceMapper.selectById(alarm.getDeviceId());
|
if (device != null) {
|
device.setOnlineStatus("0");
|
deviceMapper.updateById(device);
|
}
|
}
|
return updated;
|
}
|
|
public boolean alarmRemove(List<Long> ids) {
|
if (CollectionUtils.isEmpty(ids)) {
|
return false;
|
}
|
return alarmMapper.deleteBatchIds(ids) > 0;
|
}
|
|
// ==================== 远程控制 ====================
|
|
/**
|
* 远程启停控制
|
* 不支持远程控制的设备直接拒绝,避免页面上出现"假按钮"
|
*/
|
@Transactional(rollbackFor = Exception.class)
|
public MonitorControl control(Long deviceId, String command, String remark) {
|
MonitorDevice device = deviceMapper.selectById(deviceId);
|
if (device == null) {
|
throw new IllegalArgumentException("设备不存在");
|
}
|
if (!"1".equals(device.getControlEnabled())) {
|
throw new IllegalArgumentException("该设备未接入远程控制回路,不支持远程启停,请现场操作");
|
}
|
LocalDateTime now = LocalDateTime.now();
|
MonitorControl control = new MonitorControl();
|
control.setDeviceId(deviceId);
|
control.setDeviceName(device.getDeviceName());
|
control.setControlCommand(command);
|
control.setControlType(StringUtils.hasText(device.getControlType()) ? device.getControlType() : "启停");
|
control.setCommandSource("远程控制");
|
control.setOperator(currentUserName());
|
control.setOperateTime(now);
|
control.setRemark(remark);
|
if ("stop".equalsIgnoreCase(command)) {
|
device.setRunStatus("0");
|
control.setResultMsg("停机指令已下发");
|
} else if ("start".equalsIgnoreCase(command)) {
|
device.setRunStatus("1");
|
control.setResultMsg("启动指令已下发");
|
} else if ("restart".equalsIgnoreCase(command)) {
|
device.setRunStatus("1");
|
control.setResultMsg("重启指令已下发");
|
} else {
|
throw new IllegalArgumentException("不支持的控制指令");
|
}
|
control.setExecResult("0");
|
controlMapper.insert(control);
|
deviceMapper.updateById(device);
|
return control;
|
}
|
|
public IPage<MonitorControl> controlPage(Page<MonitorControl> page, MonitorControl query) {
|
return controlMapper.selectPage(page, Wrappers.<MonitorControl>lambdaQuery()
|
.like(StringUtils.hasText(query.getDeviceName()), MonitorControl::getDeviceName, query.getDeviceName())
|
.eq(StringUtils.hasText(query.getControlCommand()), MonitorControl::getControlCommand, query.getControlCommand())
|
.orderByDesc(MonitorControl::getId));
|
}
|
|
// ==================== 总览 ====================
|
|
public Map<String, Object> overview() {
|
Map<String, Object> result = new HashMap<>();
|
long total = deviceMapper.selectCount(Wrappers.<MonitorDevice>lambdaQuery());
|
long online = deviceMapper.selectCount(Wrappers.<MonitorDevice>lambdaQuery().eq(MonitorDevice::getOnlineStatus, "0"));
|
long offline = deviceMapper.selectCount(Wrappers.<MonitorDevice>lambdaQuery().eq(MonitorDevice::getOnlineStatus, "1"));
|
long alarming = deviceMapper.selectCount(Wrappers.<MonitorDevice>lambdaQuery().eq(MonitorDevice::getOnlineStatus, "2"));
|
long monitorEnabled = deviceMapper.selectCount(Wrappers.<MonitorDevice>lambdaQuery().eq(MonitorDevice::getMonitorEnabled, "1"));
|
long controlEnabled = deviceMapper.selectCount(Wrappers.<MonitorDevice>lambdaQuery().eq(MonitorDevice::getControlEnabled, "1"));
|
long unhandled = unhandledAlarmCount();
|
result.put("total", total);
|
result.put("online", online);
|
result.put("offline", offline);
|
result.put("alarming", alarming);
|
result.put("monitorEnabled", monitorEnabled);
|
result.put("controlEnabled", controlEnabled);
|
result.put("unhandledAlarm", unhandled);
|
return result;
|
}
|
|
private String currentUserName() {
|
try {
|
return SecurityUtils.getLoginUser().getUser().getNickName();
|
} catch (Exception e) {
|
return "系统";
|
}
|
}
|
}
|