| | |
| | | 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.emind.domain.EmindStation; |
| | | import com.ruoyi.other.emind.dto.EmindIncomeHistoryDto; |
| | | import com.ruoyi.other.emind.mapper.EmindStationMapper; |
| | | import com.ruoyi.other.emind.mapper.EmindStorageDashboardMapper; |
| | | import com.ruoyi.other.emind.service.EmindEnergyChainDetailService; |
| | | import com.ruoyi.other.udan.model.*; |
| | | import com.ruoyi.other.udan.util.UdanEmsKafkaUtil; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.commons.collections4.CollectionUtils; |
| | | import org.apache.commons.lang3.StringUtils; |
| | | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import javax.annotation.PostConstruct; |
| | | import java.io.IOException; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | import java.util.Objects; |
| | | import java.util.Set; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** 将 iems-open-v1.0.5.proto 消息解析为业务对象,并映射为待入库对象。 */ |
| | | /** 将 IEmsAllDeviceState 中模型已有的 EMS 和能耗计字段转换为业务对象。 */ |
| | | @Slf4j |
| | | @Component |
| | | @ConditionalOnProperty(prefix = "udan.ems.kafka", name = "enabled", havingValue = "true") |
| | | public class UdanEmsMessageProcessor { |
| | | private final UdanEmsKafkaUtil kafkaUtil; |
| | | private final EmindStorageDashboardMapper dashboardMapper; |
| | | private final EmindStationMapper stationMapper; |
| | | private final EmindEnergyChainDetailService emindEnergyChainDetailService; |
| | | |
| | | /** 注入 Kafka 消费工具。 */ |
| | | public UdanEmsMessageProcessor(UdanEmsKafkaUtil kafkaUtil) { this.kafkaUtil = kafkaUtil; } |
| | | public UdanEmsMessageProcessor(UdanEmsKafkaUtil kafkaUtil, EmindStorageDashboardMapper dashboardMapper, EmindStationMapper stationMapper, EmindEnergyChainDetailService emindEnergyChainDetailService) { |
| | | this.kafkaUtil = kafkaUtil; |
| | | this.dashboardMapper = dashboardMapper; |
| | | this.stationMapper = stationMapper; |
| | | this.emindEnergyChainDetailService = emindEnergyChainDetailService; |
| | | } |
| | | |
| | | /** 注册消息回调,避免消费者只消费但没有业务处理。 */ |
| | | //@PostConstruct |
| | | public void register() { kafkaUtil.setMessageHandler(this::process); } |
| | | @PostConstruct |
| | | public void register() { |
| | | kafkaUtil.setMessageHandler(this::process); |
| | | } |
| | | |
| | | /** 解析消息、转换待入库对象并输出日志;此处暂不调用数据库。 */ |
| | | public void process(String emsSystemId, byte[] protobufBytes) { |
| | | public void process(String id, byte[] bytes, String emsDeviceId) { |
| | | try { |
| | | UdanEmsDeviceState state = parse(emsSystemId, protobufBytes); |
| | | log.info("2、UDAN EMS实际对象: {}", JSON.toJSONString(state)); |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | UdanEmsDeviceState udanEmsDeviceState = parse(id, emsDeviceId, bytes); |
| | | if(StringUtils.isNotEmpty(udanEmsDeviceState.getEmsDeviceId())) { |
| | | log.info("2、UDAN EMS实际对象: {}", JSON.toJSONString(udanEmsDeviceState)); |
| | | List<EmindStation> stations = stationMapper.selectRemoteActiveStations(); |
| | | EmindStation station = stations.stream().filter(s -> udanEmsDeviceState.getEmsDeviceId().equals(s.getStationCode())).findFirst().orElse(null); |
| | | if(Objects.nonNull(station)) { |
| | | List<EmindIncomeHistoryDto> daily = udanEmsDeviceState.convert(station.getStationId()); |
| | | Set<LocalDate> dateSet = daily.stream().map(EmindIncomeHistoryDto::getDate).collect(Collectors.toSet()); |
| | | // 删除电站下该天已存在的旧数据 |
| | | if(CollectionUtils.isNotEmpty(dateSet)) { |
| | | dashboardMapper.deleteByStationIdDay(station.getStationId(), dateSet); |
| | | } |
| | | if(CollectionUtils.isNotEmpty(daily)) { |
| | | dashboardMapper.upsertDaily(station.getStationId(), daily, now); |
| | | } |
| | | }else{ |
| | | log.error("UDAN EMS数据同步错误-电站信息不存在!"); |
| | | } |
| | | // 保存设备其它信息 |
| | | emindEnergyChainDetailService.saveAll(udanEmsDeviceState); |
| | | } |
| | | |
| | | } catch (IOException e) { |
| | | log.error("UDAN EMS protobuf解析失败: emsSystemId={}, bytes={}", emsSystemId, protobufBytes == null ? 0 : protobufBytes.length, e); |
| | | log.error("UDAN EMS protobuf解析失败: emsSystemId={}, bytes={}", id, |
| | | bytes == null ? 0 : bytes.length, e); |
| | | } |
| | | } |
| | | |
| | | /** 按顶层 IEmsAllDeviceState 字段号读取并构造真实业务对象。 */ |
| | | /** 兼容不带 Header 的直接调用。 */ |
| | | public void process(String id, byte[] bytes) { |
| | | process(id, bytes, null); |
| | | } |
| | | |
| | | public UdanEmsDeviceState parse(String id, String emsDeviceId, byte[] bytes) throws IOException { |
| | | UdanEmsDeviceState state = new UdanEmsDeviceState(); |
| | | state.setEmsSystemId(id); |
| | | state.setEmsDeviceId(emsDeviceId); |
| | | read(bytes, (field, type, in) -> { |
| | | if (field == 1 && type == 0) state.setTime(in.readUInt64()); |
| | | else if (type == 2 && field == 2) state.setEmsState(parseEms(in.readBytes().toByteArray())); |
| | | else if (type == 2 && field == 4 && state.getPcsState() == null) { |
| | | state.setPcsState(parsePcs(in.readBytes().toByteArray())); |
| | | } |
| | | else if (type == 2 && field == 5 && state.getBatteryStackState() == null) { |
| | | state.setBatteryStackState(parseBatteryStack(in.readBytes().toByteArray())); |
| | | } |
| | | else if (type == 2 && field == 7 && state.getElectricMeterState() == null) { |
| | | state.setElectricMeterState(parseElectricMeter(in.readBytes().toByteArray())); |
| | | } |
| | | else if (type == 2 && field == 8) state.setPowerMeterState(parsePowerMeter(in.readBytes().toByteArray())); |
| | | else if (type == 2 && field == 14 && state.getTmsState() == null) { |
| | | state.setTmsState(parseTms(in.readBytes().toByteArray())); |
| | | } |
| | | else if (type == 2 && field == 15 && state.getPeripheralDeviceState() == null) { |
| | | state.setPeripheralDeviceState(parsePeripheral(in.readBytes().toByteArray())); |
| | | } |
| | | else skip(in, type); |
| | | }); |
| | | return state; |
| | | } |
| | | |
| | | /** 兼容不带 Header 的直接解析调用。 */ |
| | | 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; |
| | | return parse(id, null, bytes); |
| | | } |
| | | |
| | | |
| | | /** 解析顶层各子消息,字段号来自 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); }); |
| | | private EmsState parseEms(byte[] bytes) throws IOException { |
| | | EmsState x = new EmsState(); |
| | | read(bytes, (f, t, in) -> { |
| | | switch (f) { |
| | | case 1: x.setDeviceId(str(in,t)); break; case 9: x.setEmsStatus(enumName(in,t,"RunStatus")); break; |
| | | case 18: x.setDemandPower(f32(in,t)); break; case 20: x.setTime(u64(in,t)); break; case 21: x.setProtoProtocolVersion(str(in,t)); break; |
| | | case 22: x.setEmsRunStatus(enumAt(EmsState.EmsRunStatus.values(),in,t)); break; case 23: x.setEmsWorkStatus(enumAt(EmsState.EmsWorkStatus.values(),in,t)); break; |
| | | case 24: x.setFaultStatus(enumAt(EmsState.ErrorStatus.values(),in,t)); break; case 25: x.setOnGridStatus(enumName(in,t,"GridStatus")); break; |
| | | case 26: x.setControlSrc(enumAt(EmsState.ControlSrc.values(),in,t)); break; case 27: x.setEmuModeCode(enumAt(EmsState.EmuModeCode.values(),in,t)); break; |
| | | case 28: x.setEmuAutoMod(enumAt(EmsState.EmuAutoMode.values(),in,t)); break; case 29: x.setOverDischargeProtect(enumAt(EmsState.YesNo.values(),in,t)); break; |
| | | case 30: x.setOverChargeProtect(enumAt(EmsState.YesNo.values(),in,t)); break; case 31: x.setDemandControl(enumAt(EmsState.YesNo.values(),in,t)); break; case 32: x.setCapacityExpansionModeCtrl(enumAt(EmsState.YesNo.values(),in,t)); break; |
| | | case 33: x.setDailyChargeEnergyKwh(f32(in,t)); break; case 34: x.setDailyDischargeEnergyKwh(f32(in,t)); break; case 35: x.setMonthlyChargeEnergyKwh(f32(in,t)); break; case 36: x.setMonthlyDischargeEnergyKwh(f32(in,t)); break; |
| | | case 37: x.setTotalChargeEnergyKwh(f64(in,t)); break; case 38: x.setTotalDischargeEnergyKwh(f64(in,t)); break; case 39: x.setPcsSumsActivePower(f32(in,t)); break; case 40: x.setPcsSumsApparentPower(f32(in,t)); break; |
| | | case 41: x.setDcBusVoltage(f32(in,t)); break; case 42: x.setDcBusCurrent(f32(in,t)); break; case 43: x.setSumsSoc(f32(in,t)); break; case 44: x.setCellSums(u32(in,t)); break; |
| | | case 45: x.setBatteryAverageTempr(f32(in,t)); break; case 46: x.setBatteryMaxTemprDiff(f32(in,t)); break; case 47: x.setMaxCellVoltage(u32(in,t)); break; case 48: x.setMinCellVoltage(u32(in,t)); break; |
| | | case 49: x.setSystemMaxAllowChgPower(f32(in,t)); break; case 50: x.setSystemMaxAllowDisPower(f32(in,t)); break; case 51: x.setRechargeableKwh(f32(in,t)); break; case 52: x.setDischargeableKwh(f32(in,t)); break; |
| | | case 53: x.setCurrentMaxAllowChgPower(f32(in,t)); break; case 54: x.setCurrentMaxAllowDisPower(f32(in,t)); break; case 55: x.setSocUpperLimit(f32(in,t)); break; case 56: x.setSocLowerLimit(f32(in,t)); break; |
| | | case 57: x.setMaxPowerDischargeDuration(u32(in,t)); break; case 58: x.setMaxPowerChargeDuration(u32(in,t)); break; case 60: x.setMasterCommStatus(enumName(in,t,"CommStatus")); break; |
| | | case 61: x.setGridImportActiveEnergyKwh(f64(in,t)); break; case 62: x.setGridExportActiveEnergyKwh(f64(in,t)); break; case 63: x.setDcTotalPower(f32(in,t)); break; case 64: x.setLoadTotalPower(f32(in,t)); break; |
| | | case 65: x.setEnergyStoreTotalPower(f32(in,t)); break; case 66: x.setHighSideGridPower(f32(in,t)); break; case 67: x.setLowSideGridPower(f32(in,t)); break; case 68: x.setPvPower(f32(in,t)); break; case 69: x.setSumsSoh(f32(in,t)); break; |
| | | default: 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 PowerMeterState parsePowerMeter(byte[] bytes) throws IOException { |
| | | PowerMeterState value = new PowerMeterState(); |
| | | read(bytes, (field, type, input) -> { |
| | | switch (field) { |
| | | case 1: value.setCumulativeImportAmountPeak(f64(input, type)); break; |
| | | case 2: value.setCumulativeImportAmountShoulder(f64(input, type)); break; |
| | | case 3: value.setCumulativeImportAmountValley(f64(input, type)); break; |
| | | case 4: value.setCumulativeImportAmount(f64(input, type)); break; |
| | | case 5: value.setDailyImportAmount((float) f64(input, type)); break; |
| | | case 6: value.setMonthlyImportAmount(f64(input, type)); break; |
| | | case 7: value.setDailyProfit(f64(input, type)); break; |
| | | case 8: value.setYesterdayProfit(f64(input, type)); break; |
| | | case 9: value.setMonthlyProfit(f64(input, type)); break; |
| | | case 10: value.setLastMonthlyProfit(f64(input, type)); break; |
| | | case 11: value.setCumulativeProfit(f64(input, type)); break; |
| | | case 12: value.setCumulativeImportAmountSharp(f64(input, type)); break; |
| | | case 15: value.setCumulativeImportKwh(f64(input, type)); break; |
| | | case 16: value.setCumulativeImportKwhSharp(f64(input, type)); break; |
| | | case 17: value.setCumulativeImportKwhPeak(f64(input, type)); break; |
| | | case 18: value.setCumulativeImportKwhShoulder(f64(input, type)); break; |
| | | case 19: value.setCumulativeImportKwhValley(f64(input, type)); break; |
| | | case 20: value.setCumulativeExportKwh(f64(input, type)); break; |
| | | case 21: value.setCumulativeExportKwhSharp(f64(input, type)); break; |
| | | case 22: value.setCumulativeExportKwhPeak(f64(input, type)); break; |
| | | case 23: value.setCumulativeExportKwhShoulder(f64(input, type)); break; |
| | | case 24: value.setCumulativeExportKwhValley(f64(input, type)); break; |
| | | case 25: value.setMonthlyImportKwh(f64(input, type)); break; |
| | | case 26: value.setMonthlyImportKwhSharp(f64(input, type)); break; |
| | | case 27: value.setMonthlyImportKwhPeak(f64(input, type)); break; |
| | | case 28: value.setMonthlyImportKwhShoulder(f64(input, type)); break; |
| | | case 29: value.setMonthlyImportKwhValley(f64(input, type)); break; |
| | | case 30: value.setMonthlyExportKwh(f64(input, type)); break; |
| | | case 31: value.setMonthlyExportKwhSharp(f64(input, type)); break; |
| | | case 32: value.setMonthlyExportKwhPeak(f64(input, type)); break; |
| | | case 33: value.setMonthlyExportKwhShoulder(f64(input, type)); break; |
| | | case 34: value.setMonthlyExportKwhValley(f64(input, type)); break; |
| | | case 35: value.setDailyImportKwh(f32(input, type)); break; |
| | | case 36: value.setDailyImportKwhSharp(f32(input, type)); break; |
| | | case 37: value.setDailyImportKwhPeak(f32(input, type)); break; |
| | | case 38: value.setDailyImportKwhShoulder(f32(input, type)); break; |
| | | case 39: value.setDailyImportKwhValley(f32(input, type)); break; |
| | | case 40: value.setDailyExportKwh(f32(input, type)); break; |
| | | case 41: value.setDailyExportKwhSharp(f32(input, type)); break; |
| | | case 42: value.setDailyExportKwhPeak(f32(input, type)); break; |
| | | case 43: value.setDailyExportKwhShoulder(f32(input, type)); break; |
| | | case 44: value.setDailyExportKwhValley(f32(input, type)); break; |
| | | case 45: value.setCumulativeExportAmount(f64(input, type)); break; |
| | | case 46: value.setCumulativeExportAmountSharp(f64(input, type)); break; |
| | | case 47: value.setCumulativeExportAmountPeak(f64(input, type)); break; |
| | | case 48: value.setCumulativeExportAmountShoulder(f64(input, type)); break; |
| | | case 49: value.setCumulativeExportAmountValley(f64(input, type)); break; |
| | | case 50: value.setMonthlyImportAmountSharp(f64(input, type)); break; |
| | | case 51: value.setMonthlyImportAmountPeak(f64(input, type)); break; |
| | | case 52: value.setMonthlyImportAmountShoulder(f64(input, type)); break; |
| | | case 53: value.setMonthlyImportAmountValley(f64(input, type)); break; |
| | | case 54: value.setMonthlyExportAmount(f64(input, type)); break; |
| | | case 55: value.setMonthlyExportAmountSharp(f64(input, type)); break; |
| | | case 56: value.setMonthlyExportAmountPeak(f64(input, type)); break; |
| | | case 57: value.setMonthlyExportAmountShoulder(f64(input, type)); break; |
| | | case 58: value.setMonthlyExportAmountValley(f64(input, type)); break; |
| | | case 59: value.setDailyImportAmountSharp(f32(input, type)); break; |
| | | case 60: value.setDailyImportAmountPeak(f32(input, type)); break; |
| | | case 61: value.setDailyImportAmountShoulder(f32(input, type)); break; |
| | | case 62: value.setDailyImportAmountValley(f32(input, type)); break; |
| | | case 63: value.setDailyExportAmount(f32(input, type)); break; |
| | | case 64: value.setDailyExportAmountSharp(f32(input, type)); break; |
| | | case 65: value.setDailyExportAmountPeak(f32(input, type)); break; |
| | | case 66: value.setDailyExportAmountShoulder(f32(input, type)); break; |
| | | case 67: value.setDailyExportAmountValley(f32(input, type)); break; |
| | | case 68: value.setTime(u64(input, type)); break; |
| | | default: skip(input, type); |
| | | } |
| | | }); |
| | | return value; |
| | | } |
| | | |
| | | /** 解析电池堆实际字段。 */ |
| | | 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; } |
| | | private PcsState parsePcs(byte[] bytes) throws IOException { |
| | | PcsState value = new PcsState(); |
| | | read(bytes, (field, type, input) -> { |
| | | switch (field) { |
| | | case 2: value.setDeviceId(str(input, type)); break; |
| | | case 3: value.setErrorStatus(enumName(input, type, "ErrorStatus")); break; |
| | | case 4: value.setAlarmStatus(enumName(input, type, "AlarmStatus")); break; |
| | | case 5: value.setRunStatus(enumName(input, type, "RunStatus")); break; |
| | | case 6: value.setOnGridStatus(enumName(input, type, "GridStatus")); break; |
| | | case 13: value.setAcTotalActivePower((double) f32(input, type)); break; |
| | | case 17: value.setDailyChargeEnergyKwh((double) f32(input, type)); break; |
| | | case 18: value.setDailyDischargeEnergyKwh((double) f32(input, type)); break; |
| | | case 19: value.setTotalChargeEnergyKwh(f64(input, type)); break; |
| | | case 20: value.setTotalDischargeEnergyKwh(f64(input, type)); break; |
| | | case 31: value.setAPhaseActivePower((double) f32(input, type)); break; |
| | | case 34: value.setBPhaseActivePower((double) f32(input, type)); break; |
| | | case 37: value.setCPhaseActivePower((double) f32(input, type)); break; |
| | | case 50: value.setControlMode(enumName(input, type, "PcsControlMode")); break; |
| | | case 60: value.setCommStatus(enumName(input, type, "CommStatus")); break; |
| | | |
| | | /** 遍历嵌套 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 { |
| | | default: skip(input, type); |
| | | } |
| | | }); |
| | | return value; |
| | | } |
| | | |
| | | private BatteryStackState parseBatteryStack(byte[] bytes) throws IOException { |
| | | BatteryStackState value = new BatteryStackState(); |
| | | read(bytes, (field, type, input) -> { |
| | | switch (field) { |
| | | case 2: value.setDeviceId(str(input, type)); break; |
| | | case 4: value.setWorkStatus(enumName(input, type, "BatteryWorkStatus")); break; |
| | | case 5: value.setErrorStatus(enumName(input, type, "ErrorStatus")); break; |
| | | case 6: value.setWarningStatus(enumName(input, type, "AlarmStatus")); break; |
| | | case 7: value.setTv((double) f32(input, type)); break; |
| | | case 8: value.setTc((double) f32(input, type)); break; |
| | | case 9: value.setSoh((double) f32(input, type)); break; |
| | | case 10: value.setSoc((double) f32(input, type)); break; |
| | | case 15: value.setMinCellVoltMv((double) u32(input, type)); break; |
| | | case 23: value.setMaxCellVoltMv((double) u32(input, type)); break; |
| | | case 31: value.setRechargeableCapacityKwh((double) f32(input, type)); break; |
| | | case 32: value.setDischargeableCapacityKwh((double) f32(input, type)); break; |
| | | case 33: value.setCumulativeDischargeKwh(f64(input, type)); break; |
| | | case 34: value.setCumulativeChargeKwh(f64(input, type)); break; |
| | | case 36: value.setAvgCellTemp((double) u32(input, type) - 40D); break; |
| | | case 52: value.setCommStatus(enumName(input, type, "CommStatus")); break; |
| | | case 48: value.setBatteryStatus(enumName(input, type, "BatteryStatus")); break; |
| | | |
| | | default: skip(input, type); |
| | | } |
| | | }); |
| | | return value; |
| | | } |
| | | |
| | | private ElectricMeterState parseElectricMeter(byte[] bytes) throws IOException { |
| | | ElectricMeterState value = new ElectricMeterState(); |
| | | read(bytes, (field, type, input) -> { |
| | | switch (field) { |
| | | case 2: value.setDeviceId(str(input, type)); break; |
| | | case 48: value.setAPhaseVolt((double) f32(input, type)); break; |
| | | case 49: value.setBPhaseVolt((double) f32(input, type)); break; |
| | | case 50: value.setCPhaseVolt((double) f32(input, type)); break; |
| | | case 51: value.setAPhaseCurrent((double) f32(input, type)); break; |
| | | case 52: value.setBPhaseCurrent((double) f32(input, type)); break; |
| | | case 53: value.setCPhaseCurrent((double) f32(input, type)); break; |
| | | case 54: value.setAcTotalActivePower((double) f32(input, type)); break; |
| | | case 69: value.setFrequency((double) f32(input, type)); break; |
| | | case 74: value.setAcTotalPowerFactor((double) f32(input, type)); break; |
| | | default: skip(input, type); |
| | | } |
| | | }); |
| | | return value; |
| | | } |
| | | |
| | | private TmsState parseTms(byte[] bytes) throws IOException { |
| | | TmsState value = new TmsState(); |
| | | read(bytes, (field, type, input) -> { |
| | | switch (field) { |
| | | case 1: value.setDeviceId(str(input, type)); break; |
| | | case 2: value.setRun_status(enumName(input, type, "TmsRunStatus")); break; |
| | | case 3: value.setError_status(enumName(input, type, "ErrorStatus")); break; |
| | | case 4: value.setSupply_temp((double) f32(input, type)); break; |
| | | case 5: value.setReturn_temp((double) f32(input, type)); break; |
| | | case 8: value.setEnv_temp((double) f32(input, type)); break; |
| | | case 21: value.setCool_start_temp((double) f32(input, type)); break; |
| | | case 22: value.setCool_stop_temp((double) f32(input, type)); break; |
| | | case 23: value.setHeat_start_temp((double) f32(input, type)); break; |
| | | case 24: value.setHeat_stop_temp((double) f32(input, type)); break; |
| | | case 6: value.setTms_mode(enumName(input, type, "TmsMode")); break; |
| | | case 28: value.setControl_mode(enumName(input, type, "TmsControlMode")); break; |
| | | case 25: value.setComm_status(enumName(input, type, "CommStatus")); break; |
| | | default: skip(input, type); |
| | | } |
| | | }); |
| | | return value; |
| | | } |
| | | |
| | | private PeripheralDeviceState parsePeripheral(byte[] bytes) throws IOException { |
| | | PeripheralDeviceState value = new PeripheralDeviceState(); |
| | | read(bytes, (field, type, input) -> { |
| | | switch (field) { |
| | | case 1: value.setDeviceId(str(input, type)); break; |
| | | case 2: value.setDiState(u32(input, type)); break; |
| | | default: skip(input, type); |
| | | } |
| | | }); |
| | | value.convert(); |
| | | return value; |
| | | } |
| | | |
| | | private void read(byte[] bytes, Reader reader) throws IOException { |
| | | CodedInputStream input = CodedInputStream.newInstance(bytes == null ? new byte[0] : bytes); |
| | | while (!input.isAtEnd()) { |
| | | int tag = input.readTag(); |
| | | if (tag == 0) { |
| | | break; |
| | | } |
| | | reader.read(WireFormat.getTagFieldNumber(tag), WireFormat.getTagWireType(tag), input); |
| | | } |
| | | } |
| | | |
| | | private static String str(CodedInputStream input, int type) throws IOException { |
| | | if (type == WireFormat.WIRETYPE_LENGTH_DELIMITED) { |
| | | return input.readString(); |
| | | } |
| | | skip(input, type); |
| | | return null; |
| | | } |
| | | |
| | | private static long u64(CodedInputStream input, int type) throws IOException { |
| | | if (type == WireFormat.WIRETYPE_VARINT) { |
| | | return input.readUInt64(); |
| | | } |
| | | skip(input, type); |
| | | return 0L; |
| | | } |
| | | |
| | | private static int u32(CodedInputStream input, int type) throws IOException { |
| | | return (int) u64(input, type); |
| | | } |
| | | |
| | | private static float f32(CodedInputStream input, int type) throws IOException { |
| | | if (type == WireFormat.WIRETYPE_FIXED32) { |
| | | return input.readFloat(); |
| | | } |
| | | skip(input, type); |
| | | return 0F; |
| | | } |
| | | |
| | | private static double f64(CodedInputStream input, int type) throws IOException { |
| | | if (type == WireFormat.WIRETYPE_FIXED64) { |
| | | return input.readDouble(); |
| | | } |
| | | skip(input, type); |
| | | return 0D; |
| | | } |
| | | private static String enumName(CodedInputStream input, int type, String prefix) throws IOException { |
| | | if (type != WireFormat.WIRETYPE_VARINT) { |
| | | skip(input, type); |
| | | return null; |
| | | } |
| | | int number = input.readInt32(); |
| | | if ("RunStatus".equals(prefix)) { |
| | | return new String[]{"RUN_STATUS_UNKNOWN", "RUN_STATUS_STOPPED", "RUN_STATUS_RUNNING"}[number < 0 || number > 2 ? 0 : number]; |
| | | } |
| | | if ("GridStatus".equals(prefix)) { |
| | | return new String[]{"GRID_STATUS_UNKNOWN", "GRID_STATUS_ON_GRID", "GRID_STATUS_OFF_GRID"}[number < 0 || number > 2 ? 0 : number]; |
| | | } |
| | | if ("CommStatus".equals(prefix)) { |
| | | return new String[]{"COMM_STATUS_UNKNOWN", "COMM_STATUS_DISCONNECTED", "COMM_STATUS_CONNECTED"}[number < 0 || number > 2 ? 0 : number]; |
| | | } |
| | | if ("ErrorStatus".equals(prefix)) { |
| | | return new String[]{"ERROR_STATUS_UNKNOWN", "ERROR_STATUS_OCCURRING", "ERROR_STATUS_NONE"}[number < 0 || number > 2 ? 0 : number]; |
| | | } |
| | | if ("AlarmStatus".equals(prefix)) { |
| | | return new String[]{"ALARM_STATUS_UNKNOWN", "ALARM_STATUS_OCCURRING", "ALARM_STATUS_NONE"}[number < 0 || number > 2 ? 0 : number]; |
| | | } |
| | | if ("BatteryWorkStatus".equals(prefix)) { |
| | | return new String[]{"BATTERY_WORK_STATUS_UNKNOWN", "BATTERY_WORK_STATUS_CHARGING", "BATTERY_WORK_STATUS_DISCHARGING", "BATTERY_WORK_STATUS_IDLE"}[number < 0 || number > 3 ? 0 : number]; |
| | | } |
| | | if ("TmsRunStatus".equals(prefix)) { |
| | | return new String[]{"TMS_RUN_STATUS_UNKNOWN", "TMS_RUN_STATUS_IDLE", "TMS_RUN_STATUS_READY_TO_START", "TMS_RUN_STATUS_RUNNING", "TMS_RUN_STATUS_DELAYED_SHUTDOWN", "TMS_RUN_STATUS_SELF_TEST", "TMS_RUN_STATUS_ALARMING"}[number < 0 || number > 6 ? 0 : number]; |
| | | } |
| | | if ("PcsControlMode".equals(prefix)) { |
| | | return new String[]{"PCS_CONTROL_MODE_UNKNOWN", "PCS_CONTROL_MODE_LOCAL_CONTROL", "PCS_CONTROL_MODE_REMOTE_CONTROL"}[number < 0 || number > 3 ? 0 : number]; |
| | | } |
| | | if ("BatteryStatus".equals(prefix)) { |
| | | return new String[]{"Battery_Status_BAN_CHARGE_DISCHARGE", "Battery_Status_ONLY_CHARGE", "Battery_Status_ONLY_DISCHARGE","Battery_Status_NORMAL","Battery_Status_BAN_CHARGE_DISCHARGE_NEW","Battery_Status_IDLE"}[number < 0 || number > 6 ? 0 : number]; |
| | | } |
| | | if ("TmsMode".equals(prefix)) { |
| | | return new String[]{"TMS_MODE_UNKNOWN", "TMS_MODE_REFRIGERATION", "TMS_MODE_HEATING","TMS_MODE_SELF_LOOP","TMS_MODE_AUTOMATIC","TMS_MODE_STOP"}[number < 0 || number > 6 ? 0 : number]; |
| | | } |
| | | if ("TmsControlMode".equals(prefix)) { |
| | | return new String[]{"UNKNOWN", "OUTLET_WATER_TEMP", "INLET_WATER_TEMP","CELL_WEIGHT_TEMP","CELL_AVG_TEMP"}[number < 0 || number > 5 ? 0 : number]; |
| | | } |
| | | |
| | | |
| | | return prefix + "_" + number; |
| | | } |
| | | private static <T> T enumAt(T[] values, CodedInputStream input, int type) throws IOException { |
| | | int number = (int) u64(input, type); |
| | | return number >= 0 && number < values.length ? values[number] : null; |
| | | } |
| | | |
| | | private static void skip(CodedInputStream input, 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; |
| | | case WireFormat.WIRETYPE_VARINT: |
| | | input.readInt64(); |
| | | break; |
| | | case WireFormat.WIRETYPE_FIXED64: |
| | | input.readFixed64(); |
| | | break; |
| | | case WireFormat.WIRETYPE_LENGTH_DELIMITED: |
| | | input.readBytes(); |
| | | break; |
| | | case WireFormat.WIRETYPE_FIXED32: |
| | | input.readFixed32(); |
| | | break; |
| | | default: |
| | | throw new IOException("Unsupported protobuf wire type: " + type); |
| | | } |
| | | } |
| | | @FunctionalInterface private interface FieldReader { void read(int field, int type, CodedInputStream input) throws IOException; } |
| | | |
| | | @FunctionalInterface |
| | | private interface Reader { |
| | | void read(int field, int type, CodedInputStream input) throws IOException; |
| | | } |
| | | |
| | | } |