puzhibing
2026-09-11 72ac1dce414d20a973104a0eb046809a2eaa5ed7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
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; }
}