package com.ruoyi.other.udan.service; import com.alibaba.fastjson.JSON; import com.google.protobuf.CodedInputStream; import com.google.protobuf.WireFormat; import com.ruoyi.other.udan.model.UdanEmsDeviceState; import com.ruoyi.other.udan.util.UdanEmsKafkaUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; import java.io.IOException; /** 将 iems-open-v1.0.5.proto 消息解析为业务对象,并映射为待入库对象。 */ @Slf4j @Component @ConditionalOnProperty(prefix = "udan.ems.kafka", name = "enabled", havingValue = "true") public class UdanEmsMessageProcessor { private final UdanEmsKafkaUtil kafkaUtil; /** 注入 Kafka 消费工具。 */ public UdanEmsMessageProcessor(UdanEmsKafkaUtil kafkaUtil) { this.kafkaUtil = kafkaUtil; } /** 注册消息回调,避免消费者只消费但没有业务处理。 */ //@PostConstruct public void register() { kafkaUtil.setMessageHandler(this::process); } /** 解析消息、转换待入库对象并输出日志;此处暂不调用数据库。 */ public void process(String emsSystemId, byte[] protobufBytes) { try { UdanEmsDeviceState state = parse(emsSystemId, protobufBytes); log.info("2、UDAN EMS实际对象: {}", JSON.toJSONString(state)); } catch (IOException e) { log.error("UDAN EMS protobuf解析失败: emsSystemId={}, bytes={}", emsSystemId, protobufBytes == null ? 0 : protobufBytes.length, e); } } /** 按顶层 IEmsAllDeviceState 字段号读取并构造真实业务对象。 */ public UdanEmsDeviceState parse(String id, byte[] bytes) throws IOException { UdanEmsDeviceState s = new UdanEmsDeviceState(); s.setEmsSystemId(id); CodedInputStream in = CodedInputStream.newInstance(bytes == null ? new byte[0] : bytes); while (!in.isAtEnd()) { int tag = in.readTag(); if (tag == 0) break; int field = WireFormat.getTagFieldNumber(tag); int type = WireFormat.getTagWireType(tag); if (field == 1 && type == WireFormat.WIRETYPE_VARINT) s.setTime(in.readUInt64()); else if (type == WireFormat.WIRETYPE_LENGTH_DELIMITED) parseChild(s, field, in.readBytes().toByteArray()); else in.skipField(tag); } return s; } /** 解析顶层各子消息,字段号来自 iems-open-v1.0.5.proto。 */ private void parseChild(UdanEmsDeviceState s, int field, byte[] data) throws IOException { switch (field) { case 2: s.setEms(parseEms(data)); break; case 4: s.getPcsStates().add(parsePcs(data)); break; case 5: s.getBatteryStackStates().add(parseStack(data)); break; case 6: s.getBatteryClusterStates().add(parseCluster(data)); break; case 7: s.getElectricMeterStates().add(parseMeter(data)); break; case 8: s.setPowerMeterCount(s.getPowerMeterCount() + 1); break; case 9: s.setAirConditionerCount(s.getAirConditionerCount() + 1); break; case 11: s.setFireCount(s.getFireCount() + 1); break; case 13: s.setJpfCount(s.getJpfCount() + 1); break; case 14: s.setTmsCount(s.getTmsCount() + 1); break; case 18: s.setDehumidifierCount(s.getDehumidifierCount() + 1); break; case 19: s.setUpsCount(s.getUpsCount() + 1); break; case 20: s.getPvStates().add(parsePv(data)); break; case 21: s.getChargeStationStates().add(parseChargeStation(data)); break; case 23: s.setExtPeripheralCount(s.getExtPeripheralCount() + 1); break; default: break; } } /** 解析 EMS 状态对象中的实际字段。 */ private UdanEmsDeviceState.EmsState parseEms(byte[] b) throws IOException { UdanEmsDeviceState.EmsState x = new UdanEmsDeviceState.EmsState(); read(b, (f, t, in) -> { if (t == 2 && f == 1) x.setDeviceId(in.readString()); else if (t == 5 && f == 18) x.setDemandPower(in.readFloat()); else if (t == 2 && f == 21) x.setProtoProtocolVersion(in.readString()); else if (t == 5 && f == 33) x.setDailyChargeEnergyKwh(in.readFloat()); else if (t == 5 && f == 34) x.setDailyDischargeEnergyKwh(in.readFloat()); else if (t == 1 && f == 37) x.setTotalChargeEnergyKwh(in.readDouble()); else if (t == 1 && f == 38) x.setTotalDischargeEnergyKwh(in.readDouble()); else if (t == 5 && f == 43) x.setSumsSoc(in.readFloat()); else if (t == 5 && f == 69) x.setSumsSoh(in.readFloat()); else if (t == 5 && f == 41) x.setDcBusVoltage(in.readFloat()); else if (t == 5 && f == 42) x.setDcBusCurrent(in.readFloat()); else skip(in, t); }); return x; } /** 解析 PCS 实际字段。 */ private UdanEmsDeviceState.PcsState parsePcs(byte[] b) throws IOException { UdanEmsDeviceState.PcsState x = new UdanEmsDeviceState.PcsState(); read(b, (f, t, in) -> { if (t == 2 && f == 2) x.setDeviceId(in.readString()); else if (t == 5 && f == 13) x.setAcTotalActivePower(in.readFloat()); else if (t == 5 && f == 21) x.setAcTotalReactivePower(in.readFloat()); else if (t == 5 && f == 22) x.setAcTotalApparentPower(in.readFloat()); else if (t == 5 && f == 23) x.setAcTotalPowerFactor(in.readFloat()); else if (t == 5 && f == 17) x.setDailyChargeEnergyKwh(in.readFloat()); else if (t == 5 && f == 18) x.setDailyDischargeEnergyKwh(in.readFloat()); else if (t == 1 && f == 19) x.setTotalChargeEnergyKwh(in.readDouble()); else if (t == 1 && f == 20) x.setTotalDischargeEnergyKwh(in.readDouble()); else if (t == 5 && f == 59) x.setAmbientTemp(in.readFloat()); else skip(in, t); }); return x; } /** 解析电池堆实际字段。 */ private UdanEmsDeviceState.BatteryStackState parseStack(byte[] b) throws IOException { UdanEmsDeviceState.BatteryStackState x = new UdanEmsDeviceState.BatteryStackState(); read(b, (f,t,in)-> { if(t==2&&f==2)x.setDeviceId(in.readString()); else if(t==5&&f==7)x.setTv(in.readFloat()); else if(t==5&&f==8)x.setTc(in.readFloat()); else if(t==5&&f==9)x.setSoh(in.readFloat()); else if(t==5&&f==10)x.setSoc(in.readFloat()); else if(t==5&&f==31)x.setRechargeableCapacityKwh(in.readFloat()); else if(t==5&&f==32)x.setDischargeableCapacityKwh(in.readFloat()); else if(t==1&&f==33)x.setCumulativeDischargeKwh(in.readDouble()); else if(t==1&&f==34)x.setCumulativeChargeKwh(in.readDouble()); else skip(in,t); }); return x; } /** 解析电池簇实际字段。 */ private UdanEmsDeviceState.BatteryClusterState parseCluster(byte[] b) throws IOException { UdanEmsDeviceState.BatteryClusterState x = new UdanEmsDeviceState.BatteryClusterState(); read(b,(f,t,in)->{if(t==2&&f==2)x.setDeviceId(in.readString());else if(t==2&&f==3)x.setStackDeviceId(in.readString());else if(t==5&&f==7)x.setTv(in.readFloat());else if(t==5&&f==8)x.setTc(in.readFloat());else if(t==5&&f==9)x.setSoh(in.readFloat());else if(t==5&&f==10)x.setSoc(in.readFloat());else skip(in,t);});return x; } /** 解析电表实际字段。 */ private UdanEmsDeviceState.ElectricMeterState parseMeter(byte[] b) throws IOException { UdanEmsDeviceState.ElectricMeterState x = new UdanEmsDeviceState.ElectricMeterState(); read(b,(f,t,in)->{if(t==2&&f==2)x.setDeviceId(in.readString());else if(t==1&&f==17)x.setCumulativeImportKwh(in.readDouble());else if(t==1&&f==22)x.setCumulativeExportKwh(in.readDouble());else if(t==5&&f==54)x.setAcTotalActivePower(in.readFloat());else skip(in,t);});return x; } /** 解析光伏实际字段。 */ private UdanEmsDeviceState.PvState parsePv(byte[] b) throws IOException { UdanEmsDeviceState.PvState x = new UdanEmsDeviceState.PvState(); read(b,(f,t,in)->{if(t==2&&f==1)x.setDeviceId(in.readString());else if(t==5&&f==18)x.setActivePower(in.readFloat());else if(t==5&&f==19)x.setReactivePower(in.readFloat());else if(t==5&&f==20)x.setPowerFactor(in.readFloat());else if(t==5&&f==21)x.setDailyPowerGeneration(in.readFloat());else if(t==1&&f==22)x.setTotalPowerGeneration(in.readDouble());else skip(in,t);});return x; } /** 解析充电桩实际字段。 */ private UdanEmsDeviceState.ChargeStationState parseChargeStation(byte[] b) throws IOException { UdanEmsDeviceState.ChargeStationState x = new UdanEmsDeviceState.ChargeStationState(); read(b,(f,t,in)->{if(t==2&&f==1)x.setDeviceId(in.readString());else if(t==0&&f==2)x.setTime(in.readUInt64());else skip(in,t);});return x; } /** 遍历嵌套 protobuf 字段,并把字段号、线类型和读取器交给解析器。 */ private void read(byte[] b, FieldReader reader) throws IOException { CodedInputStream in=CodedInputStream.newInstance(b); while(!in.isAtEnd()){int tag=in.readTag();if(tag==0)break;reader.read(WireFormat.getTagFieldNumber(tag),WireFormat.getTagWireType(tag),in);} } /** 跳过当前不需要映射的字段。 */ private void skip(CodedInputStream in, int type) throws IOException { switch (type) { case WireFormat.WIRETYPE_VARINT: in.readInt64(); break; case WireFormat.WIRETYPE_FIXED64: in.readFixed64(); break; case WireFormat.WIRETYPE_LENGTH_DELIMITED: in.readBytes(); break; case WireFormat.WIRETYPE_FIXED32: in.readFixed32(); break; default: break; } } @FunctionalInterface private interface FieldReader { void read(int field, int type, CodedInputStream input) throws IOException; } }