|
|
@@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import com.hdkj.lt.bf.application.FeederGroupApplication;
|
|
|
import com.hdkj.lt.bf.common.SeMonitorThreshold;
|
|
|
import com.hdkj.lt.bf.common.SeSnapshotConstants;
|
|
|
+import com.hdkj.lt.bf.entity.DwdShbDsTransformerBase;
|
|
|
import com.hdkj.lt.bf.entity.FhzgSeCurrentEvent;
|
|
|
import com.hdkj.lt.bf.entity.FhzgSeMonitorCurrent;
|
|
|
import com.hdkj.lt.bf.entity.FhzgSeMonitorVoltage;
|
|
|
@@ -19,6 +20,7 @@ import com.hdkj.lt.bf.mapper.FhzgSeMonitorCurrentMapper;
|
|
|
import com.hdkj.lt.bf.mapper.FhzgSeMonitorVoltageMapper;
|
|
|
import com.hdkj.lt.bf.mapper.FhzgSeSnapshotDetailMapper;
|
|
|
import com.hdkj.lt.bf.mapper.FhzgSeVoltageEventMapper;
|
|
|
+import com.hdkj.lt.bf.mapper.DwdShbDsTransformerBaseMapper;
|
|
|
import com.hdkj.lt.bf.service.SeSnapshotService;
|
|
|
import com.hdkj.fhzg.optimization.response.FeederGroupTreeNode;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
@@ -44,6 +46,7 @@ public class SeSnapshotServiceImpl implements SeSnapshotService {
|
|
|
private final FhzgSeMonitorVoltageMapper monitorVoltageMapper;
|
|
|
private final FhzgSeCurrentEventMapper currentEventMapper;
|
|
|
private final FhzgSeVoltageEventMapper voltageEventMapper;
|
|
|
+ private final DwdShbDsTransformerBaseMapper transformerBaseMapper;
|
|
|
private final FeederGroupApplication feederGroupApplication;
|
|
|
private final ApplicationEventPublisher eventPublisher;
|
|
|
|
|
|
@@ -87,13 +90,21 @@ public class SeSnapshotServiceImpl implements SeSnapshotService {
|
|
|
|
|
|
// === 2. 配变 ===
|
|
|
JSONArray mvtrans = root.getJSONArray("periodMVTransSeResult");
|
|
|
- if (mvtrans != null) {
|
|
|
+ Map<String, String> transformerNameMap = new HashMap<>();
|
|
|
+ if (mvtrans != null && !mvtrans.isEmpty()) {
|
|
|
+ // 先收集当前断面所有配变 ID,再一次性查名称(避免全表加载)
|
|
|
+ Set<String> mvtransIds = new HashSet<>();
|
|
|
+ for (int i = 0; i < mvtrans.size(); i++) {
|
|
|
+ String id = mvtrans.getJSONObject(i).getString("psrId");
|
|
|
+ if (id != null) mvtransIds.add(id);
|
|
|
+ }
|
|
|
+ transformerNameMap = buildTransformerMapping(mvtransIds);
|
|
|
for (int i = 0; i < mvtrans.size(); i++) {
|
|
|
JSONObject m = mvtrans.getJSONObject(i);
|
|
|
FhzgSeSnapshotDetail d = parseMvtrans(m, snapTime, feederMap);
|
|
|
if (d != null) {
|
|
|
batch.add(d);
|
|
|
- processMvtransMonitor(m, d, snapTime, feederMap);
|
|
|
+ processMvtransMonitor(m, d, snapTime, feederMap, transformerNameMap);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -143,7 +154,7 @@ public class SeSnapshotServiceImpl implements SeSnapshotService {
|
|
|
}
|
|
|
|
|
|
private void processFeederCurrent(JSONObject f, FhzgSeSnapshotDetail detail,
|
|
|
- LocalDateTime snapTime, Map<String, String[]> feederMap) {
|
|
|
+ LocalDateTime snapTime, Map<String, String[]> feederMap) {
|
|
|
if (detail.getCurrentStatus() == null) return;
|
|
|
String feederId = detail.getFeederId();
|
|
|
String[] loc = feederMap.get(feederId);
|
|
|
@@ -168,6 +179,15 @@ public class SeSnapshotServiceImpl implements SeSnapshotService {
|
|
|
} else {
|
|
|
LocalDateTime lastSnap = state.getLastSnapTime();
|
|
|
if (lastSnap != null && Duration.between(lastSnap, snapTime).toMinutes() > SeMonitorThreshold.SNAP_INTERVAL_MINUTES) {
|
|
|
+ // 断档间隙:先关掉还在活跃的事件,再清空状态机引用
|
|
|
+ if (state.getAlarmTriggered() == 1 && state.getAlarmEventId() != null) {
|
|
|
+ FhzgSeCurrentEvent close = new FhzgSeCurrentEvent();
|
|
|
+ close.setId(state.getAlarmEventId());
|
|
|
+ close.setStatus(2);
|
|
|
+ close.setRecoverTime(snapTime);
|
|
|
+ close.setLastLoadRate(loadRate);
|
|
|
+ currentEventMapper.updateById(close);
|
|
|
+ }
|
|
|
state.setFirstOverTime(null);
|
|
|
state.setAlarmTriggered(0);
|
|
|
state.setAlarmEventId(null);
|
|
|
@@ -200,13 +220,33 @@ public class SeSnapshotServiceImpl implements SeSnapshotService {
|
|
|
event.getId(), feederId, feederName, countyId, subsId,
|
|
|
alarmType, maxLrTime, state.getFirstOverTime()));
|
|
|
}
|
|
|
+ // 活跃事件:断面负载率比记录值高则刷新 maxLoadRate
|
|
|
+ if (state.getAlarmTriggered() == 1 && state.getAlarmEventId() != null) {
|
|
|
+ FhzgSeCurrentEvent cur = currentEventMapper.selectById(state.getAlarmEventId());
|
|
|
+ if (cur != null) {
|
|
|
+ BigDecimal curMax = cur.getMaxLoadRate();
|
|
|
+ if (loadRate != null && (curMax == null || loadRate.compareTo(curMax) > 0)) {
|
|
|
+ FhzgSeCurrentEvent upd = new FhzgSeCurrentEvent();
|
|
|
+ upd.setId(state.getAlarmEventId());
|
|
|
+ upd.setMaxLoadRate(loadRate);
|
|
|
+ currentEventMapper.updateById(upd);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
} else {
|
|
|
if (state.getAlarmTriggered() == 1 && state.getAlarmEventId() != null) {
|
|
|
- FhzgSeCurrentEvent update = new FhzgSeCurrentEvent();
|
|
|
- update.setId(state.getAlarmEventId());
|
|
|
- update.setStatus(2); update.setRecoverTime(snapTime);
|
|
|
- update.setLastLoadRate(loadRate); update.setMaxLoadRate(maxLoadRate);
|
|
|
- currentEventMapper.updateById(update);
|
|
|
+ FhzgSeCurrentEvent cur = currentEventMapper.selectById(state.getAlarmEventId());
|
|
|
+ if (cur != null) {
|
|
|
+ BigDecimal trueMax = cur.getMaxLoadRate();
|
|
|
+ if (trueMax == null || (maxLoadRate != null && maxLoadRate.compareTo(trueMax) > 0)) {
|
|
|
+ trueMax = maxLoadRate;
|
|
|
+ }
|
|
|
+ FhzgSeCurrentEvent update = new FhzgSeCurrentEvent();
|
|
|
+ update.setId(state.getAlarmEventId());
|
|
|
+ update.setStatus(2); update.setRecoverTime(snapTime);
|
|
|
+ update.setLastLoadRate(loadRate); update.setMaxLoadRate(trueMax);
|
|
|
+ currentEventMapper.updateById(update);
|
|
|
+ }
|
|
|
}
|
|
|
state.setFirstOverTime(null); state.setAlarmTriggered(0); state.setAlarmEventId(null);
|
|
|
}
|
|
|
@@ -216,11 +256,12 @@ public class SeSnapshotServiceImpl implements SeSnapshotService {
|
|
|
}
|
|
|
|
|
|
private void processFeederVoltage(JSONObject f, FhzgSeSnapshotDetail detail,
|
|
|
- LocalDateTime snapTime, Map<String, String[]> feederMap) {
|
|
|
+ LocalDateTime snapTime, Map<String, String[]> feederMap) {
|
|
|
if (detail.getVoltageStatus() == null) return;
|
|
|
String[] loc = feederMap.get(detail.getFeederId());
|
|
|
- checkVoltageMonitor(detail.getFeederId(), "feeder", detail.getVoltageRatio(), detail.getVoltageStatus(),
|
|
|
- snapTime, detail.getFeederId(), loc != null && loc.length > 2 ? loc[2] : null,
|
|
|
+ String feederName = loc != null && loc.length > 2 ? loc[2] : null;
|
|
|
+ checkVoltageMonitor(detail.getFeederId(), "feeder", feederName, detail.getVoltageRatio(), detail.getVoltageStatus(),
|
|
|
+ snapTime, detail.getFeederId(), feederName,
|
|
|
loc != null ? loc[0] : "", loc != null ? loc[1] : "", null, null);
|
|
|
}
|
|
|
|
|
|
@@ -247,14 +288,17 @@ public class SeSnapshotServiceImpl implements SeSnapshotService {
|
|
|
}
|
|
|
|
|
|
private void processMvtransMonitor(JSONObject m, FhzgSeSnapshotDetail detail,
|
|
|
- LocalDateTime snapTime, Map<String, String[]> feederMap) {
|
|
|
+ LocalDateTime snapTime, Map<String, String[]> feederMap,
|
|
|
+ Map<String, String> transformerNameMap) {
|
|
|
if (detail.getVoltageStatus() == null) return;
|
|
|
String mvtransId = detail.getDeviceId();
|
|
|
String[] loc = feederMap.get(detail.getFeederId());
|
|
|
int status = detail.getVoltageStatus();
|
|
|
if (status == 0) recoverUserAlarms(mvtransId, snapTime);
|
|
|
|
|
|
- checkVoltageMonitor(mvtransId, "mvtrans", detail.getVoltageRatio(), status,
|
|
|
+ String transformerName = transformerNameMap != null
|
|
|
+ ? transformerNameMap.getOrDefault(mvtransId, mvtransId) : mvtransId;
|
|
|
+ checkVoltageMonitor(mvtransId, "mvtrans", transformerName, detail.getVoltageRatio(), status,
|
|
|
snapTime, detail.getFeederId(), loc != null && loc.length > 2 ? loc[2] : null,
|
|
|
loc != null ? loc[0] : "", loc != null ? loc[1] : "", mvtransId, null);
|
|
|
|
|
|
@@ -270,10 +314,10 @@ public class SeSnapshotServiceImpl implements SeSnapshotService {
|
|
|
// 电压状态机
|
|
|
// ============================================================
|
|
|
|
|
|
- private void checkVoltageMonitor(String deviceId, String deviceType,
|
|
|
- BigDecimal voltageRatio, int voltageStatus,
|
|
|
- LocalDateTime snapTime, String feederId, String feederName,
|
|
|
- String subsId, String countyId, String mvtransId, String consumerId) {
|
|
|
+ private void checkVoltageMonitor(String deviceId, String deviceType, String deviceName,
|
|
|
+ BigDecimal voltageRatio, int voltageStatus,
|
|
|
+ LocalDateTime snapTime, String feederId, String feederName,
|
|
|
+ String subsId, String countyId, String mvtransId, String consumerId) {
|
|
|
FhzgSeMonitorVoltage state = monitorVoltageMapper.selectOne(
|
|
|
new LambdaQueryWrapper<FhzgSeMonitorVoltage>()
|
|
|
.eq(FhzgSeMonitorVoltage::getDeviceId, deviceId)
|
|
|
@@ -293,6 +337,13 @@ public class SeSnapshotServiceImpl implements SeSnapshotService {
|
|
|
} else {
|
|
|
LocalDateTime lastSnap = state.getLastSnapTime();
|
|
|
if (lastSnap != null && Duration.between(lastSnap, snapTime).toMinutes() > SeMonitorThreshold.SNAP_INTERVAL_MINUTES) {
|
|
|
+ // 断档间隙:先关掉还在活跃的事件,再清空状态机引用
|
|
|
+ if (state.getAlarmTriggered() == 1 && state.getAlarmEventId() != null) {
|
|
|
+ FhzgSeVoltageEvent close = new FhzgSeVoltageEvent();
|
|
|
+ close.setId(state.getAlarmEventId());
|
|
|
+ close.setStatus(2); close.setRecoverTime(snapTime); close.setLastValue(voltageRatio);
|
|
|
+ voltageEventMapper.updateById(close);
|
|
|
+ }
|
|
|
state.setFirstOverTime(null); state.setAlarmTriggered(0); state.setAlarmEventId(null);
|
|
|
}
|
|
|
if (isOverLimit) {
|
|
|
@@ -300,62 +351,60 @@ public class SeSnapshotServiceImpl implements SeSnapshotService {
|
|
|
if (state.getFirstOverTime() != null
|
|
|
&& Duration.between(state.getFirstOverTime(), snapTime).toMinutes() >= SeMonitorThreshold.TRIGGER_DURATION_MINUTES
|
|
|
&& state.getAlarmTriggered() == 0) {
|
|
|
- String countyName = lookupCountyName(countyId, null);
|
|
|
-
|
|
|
- if ("feeder".equals(deviceType)) {
|
|
|
- // === 馈线自身越限 → 创建 feeder 级独立事件 ===
|
|
|
- String alarmType = resolveVoltageAlarmType(deviceType, voltageStatus);
|
|
|
- BigDecimal threshold = resolveVoltageThreshold(deviceType, voltageStatus);
|
|
|
- BigDecimal[] vr = queryVoltageRangeFromSnap(feederId, state.getFirstOverTime(), snapTime);
|
|
|
- int[] mc = queryMvtransCounts(feederId, state.getFirstOverTime(), snapTime);
|
|
|
- FhzgSeVoltageEvent event = FhzgSeVoltageEvent.builder()
|
|
|
- .deviceId(deviceId).deviceName(feederName)
|
|
|
- .deviceType(deviceType).alarmLevel(deviceType).alarmType(alarmType)
|
|
|
- .feederId(feederId).feederName(feederName)
|
|
|
- .subsId(subsId).countyId(countyId).countyName(countyName)
|
|
|
- .threshold(threshold)
|
|
|
- .firstOverTime(state.getFirstOverTime()).triggerTime(snapTime)
|
|
|
- .triggerValue(voltageRatio)
|
|
|
- .voltageRangeMax(vr[0]).voltageRangeMin(vr[1])
|
|
|
- .overUpperMvtrans(mc[0]).overLowerMvtrans(mc[1])
|
|
|
- .lastValue(voltageRatio).status(1).build();
|
|
|
- voltageEventMapper.insert(event);
|
|
|
- state.setAlarmTriggered(1); state.setAlarmEventId(event.getId());
|
|
|
- // 馈线越下限 → 异步触发重构算法
|
|
|
- if ("feeder".equals(deviceType) && "voltage_under".equals(alarmType)) {
|
|
|
- eventPublisher.publishEvent(new ReconTriggerEvent(
|
|
|
- event.getId(), feederId, feederName, countyId, subsId,
|
|
|
- alarmType, snapTime, state.getFirstOverTime()));
|
|
|
+ String countyName = lookupCountyName(countyId, null);
|
|
|
+
|
|
|
+ if ("feeder".equals(deviceType)) {
|
|
|
+ // === 馈线自身越限 → 创建 feeder 级独立事件 ===
|
|
|
+ String alarmType = resolveVoltageAlarmType(deviceType, voltageStatus);
|
|
|
+ BigDecimal threshold = resolveVoltageThreshold(deviceType, voltageStatus);
|
|
|
+ BigDecimal[] vr = queryVoltageRangeFromSnap(feederId, state.getFirstOverTime(), snapTime);
|
|
|
+ int[] mc = queryMvtransCounts(feederId, state.getFirstOverTime(), snapTime);
|
|
|
+ FhzgSeVoltageEvent event = FhzgSeVoltageEvent.builder()
|
|
|
+ .deviceId(deviceId).deviceName(deviceName)
|
|
|
+ .deviceType(deviceType).alarmLevel(deviceType).alarmType(alarmType)
|
|
|
+ .feederId(feederId).feederName(feederName)
|
|
|
+ .subsId(subsId).countyId(countyId).countyName(countyName)
|
|
|
+ .threshold(threshold)
|
|
|
+ .firstOverTime(state.getFirstOverTime()).triggerTime(snapTime)
|
|
|
+ .triggerValue(voltageRatio)
|
|
|
+ .voltageRangeMax(vr[0]).voltageRangeMin(vr[1])
|
|
|
+ .overUpperMvtrans(mc[0]).overLowerMvtrans(mc[1])
|
|
|
+ .lastValue(voltageRatio).status(1).build();
|
|
|
+ voltageEventMapper.insert(event);
|
|
|
+ state.setAlarmTriggered(1); state.setAlarmEventId(event.getId());
|
|
|
+ // 馈线越下限 → 异步触发重构算法
|
|
|
+ if ("feeder".equals(deviceType) && "voltage_under".equals(alarmType)) {
|
|
|
+ eventPublisher.publishEvent(new ReconTriggerEvent(
|
|
|
+ event.getId(), feederId, feederName, countyId, subsId,
|
|
|
+ alarmType, snapTime, state.getFirstOverTime()));
|
|
|
+ }
|
|
|
+ } else if ("mvtrans".equals(deviceType)) {
|
|
|
+ // === 台区越限 → 创建 mvtrans 级独立事件 ===
|
|
|
+ String alarmType = resolveVoltageAlarmType(deviceType, voltageStatus);
|
|
|
+ BigDecimal threshold = resolveVoltageThreshold(deviceType, voltageStatus);
|
|
|
+ FhzgSeVoltageEvent event = FhzgSeVoltageEvent.builder()
|
|
|
+ .deviceId(deviceId).deviceName(deviceName)
|
|
|
+ .deviceType(deviceType).alarmLevel(deviceType).alarmType(alarmType)
|
|
|
+ .feederId(feederId).feederName(feederName)
|
|
|
+ .subsId(subsId).countyId(countyId).countyName(countyName)
|
|
|
+ .mvtransId(mvtransId)
|
|
|
+ .threshold(threshold)
|
|
|
+ .firstOverTime(state.getFirstOverTime()).triggerTime(snapTime)
|
|
|
+ .triggerValue(voltageRatio).lastValue(voltageRatio).status(1)
|
|
|
+ .build();
|
|
|
+ voltageEventMapper.insert(event);
|
|
|
+ state.setAlarmTriggered(1);
|
|
|
+ state.setAlarmEventId(event.getId());
|
|
|
+ }
|
|
|
+ // consumer 级别暂走旧逻辑(如有可后续优化)
|
|
|
}
|
|
|
- } else if ("mvtrans".equals(deviceType)) {
|
|
|
- // === 台区越限 → 创建 mvtrans 级独立事件 ===
|
|
|
- String alarmType = resolveVoltageAlarmType(deviceType, voltageStatus);
|
|
|
- BigDecimal threshold = resolveVoltageThreshold(deviceType, voltageStatus);
|
|
|
- FhzgSeVoltageEvent event = FhzgSeVoltageEvent.builder()
|
|
|
- .deviceId(deviceId).deviceName(feederName)
|
|
|
- .deviceType(deviceType).alarmLevel(deviceType).alarmType(alarmType)
|
|
|
- .feederId(feederId).feederName(feederName)
|
|
|
- .subsId(subsId).countyId(countyId).countyName(countyName)
|
|
|
- .mvtransId(mvtransId)
|
|
|
- .threshold(threshold)
|
|
|
- .firstOverTime(state.getFirstOverTime()).triggerTime(snapTime)
|
|
|
- .triggerValue(voltageRatio).lastValue(voltageRatio).status(1)
|
|
|
- .build();
|
|
|
- voltageEventMapper.insert(event);
|
|
|
- state.setAlarmTriggered(1);
|
|
|
- state.setAlarmEventId(event.getId());
|
|
|
- }
|
|
|
- // consumer 级别暂走旧逻辑(如有可后续优化)
|
|
|
- }
|
|
|
} else {
|
|
|
+ // 设备恢复正常(任何 deviceType 都走):关自己的事件
|
|
|
if (state.getAlarmTriggered() == 1 && state.getAlarmEventId() != null) {
|
|
|
- // 只有 feeder 自身恢复才标聚合事件 status=2;mvtrans 恢复不动聚合事件
|
|
|
- if ("feeder".equals(deviceType)) {
|
|
|
- FhzgSeVoltageEvent update = new FhzgSeVoltageEvent();
|
|
|
- update.setId(state.getAlarmEventId());
|
|
|
- update.setStatus(2); update.setRecoverTime(snapTime); update.setLastValue(voltageRatio);
|
|
|
- voltageEventMapper.updateById(update);
|
|
|
- }
|
|
|
+ FhzgSeVoltageEvent update = new FhzgSeVoltageEvent();
|
|
|
+ update.setId(state.getAlarmEventId());
|
|
|
+ update.setStatus(2); update.setRecoverTime(snapTime); update.setLastValue(voltageRatio);
|
|
|
+ voltageEventMapper.updateById(update);
|
|
|
}
|
|
|
state.setFirstOverTime(null); state.setAlarmTriggered(0); state.setAlarmEventId(null);
|
|
|
}
|
|
|
@@ -434,14 +483,14 @@ public class SeSnapshotServiceImpl implements SeSnapshotService {
|
|
|
}
|
|
|
|
|
|
private int calcFeederVoltageStatus(BigDecimal ratio) {
|
|
|
- if (ratio == null) return 0;
|
|
|
+ if (ratio == null || ratio.signum() <= 0) return 0;
|
|
|
if (ratio.compareTo(SeMonitorThreshold.FEEDER_V_OVER) > 0) return 1;
|
|
|
if (ratio.compareTo(SeMonitorThreshold.FEEDER_V_UNDER) < 0) return 2;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
private int calcMvtransVoltageStatus(BigDecimal ratio) {
|
|
|
- if (ratio == null) return 0;
|
|
|
+ if (ratio == null || ratio.signum() <= 0) return 0;
|
|
|
if (ratio.compareTo(SeMonitorThreshold.MVTRANS_V_OVER) > 0) return 1;
|
|
|
if (ratio.compareTo(SeMonitorThreshold.MVTRANS_V_UNDER) < 0) return 2;
|
|
|
return 0;
|
|
|
@@ -547,8 +596,31 @@ public class SeSnapshotServiceImpl implements SeSnapshotService {
|
|
|
return map;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 按配变 ID 集合批量查名称(只查当前断面出现的,不加载全表)
|
|
|
+ * 查不到的设备以 ID 代替
|
|
|
+ */
|
|
|
+ private Map<String, String> buildTransformerMapping(Collection<String> ids) {
|
|
|
+ if (ids == null || ids.isEmpty()) return Collections.emptyMap();
|
|
|
+ Map<String, String> map = new HashMap<>();
|
|
|
+ try {
|
|
|
+ List<DwdShbDsTransformerBase> rows = transformerBaseMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<DwdShbDsTransformerBase>()
|
|
|
+ .in(DwdShbDsTransformerBase::getPsrId, ids)
|
|
|
+ .isNotNull(DwdShbDsTransformerBase::getName));
|
|
|
+ for (DwdShbDsTransformerBase row : rows) {
|
|
|
+ map.put(row.getPsrId(), row.getName());
|
|
|
+ }
|
|
|
+ log.info("配变名称映射加载完成: 配变{}台, 命中{}条, 未命中{}条",
|
|
|
+ ids.size(), map.size(), ids.size() - map.size());
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("加载配变名称映射失败", e);
|
|
|
+ }
|
|
|
+ return map;
|
|
|
+ }
|
|
|
+
|
|
|
private void buildFeederMappingRecursive(List<FeederGroupTreeNode> nodes, Map<String, String[]> map,
|
|
|
- String parentSubsId, String parentCountyId) {
|
|
|
+ String parentSubsId, String parentCountyId) {
|
|
|
if (nodes == null) return;
|
|
|
for (FeederGroupTreeNode node : nodes) {
|
|
|
String subsId = parentSubsId, countyId = parentCountyId;
|