mirror of
https://gitee.com/san-bing/JChargePointProtocol
synced 2026-05-06 10:59:57 +08:00
!21 绿能109,103,102,101,203,201指令提交
* 合并后代码修改 * 合并后代码修改 * Merge remote-tracking branch 'refs/remotes/upstream/master' into maste… * PR后代码修改 * 109)充电桩状态信息包上报 * 109)充电桩状态信息包上报
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* 开源代码,仅供学习和交流研究使用,商用请联系三丙
|
||||
* 微信:mohan_88888
|
||||
* 抖音:程序员三丙
|
||||
* 付费课程知识星球:https://t.zsxq.com/aKtXo
|
||||
*/
|
||||
package sanbing.jcpp.protocol.lvneng.v340.cmd;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil;
|
||||
import sanbing.jcpp.proto.gen.ProtocolProto.HeartBeatRequest;
|
||||
import sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage;
|
||||
import sanbing.jcpp.protocol.ProtocolContext;
|
||||
import sanbing.jcpp.protocol.listener.tcp.TcpSession;
|
||||
import sanbing.jcpp.protocol.lvneng.LvnengUplinkCmdExe;
|
||||
import sanbing.jcpp.protocol.lvneng.LvnengUplinkMessage;
|
||||
import sanbing.jcpp.protocol.lvneng.annotation.LvnengCmd;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static sanbing.jcpp.protocol.lvneng.enums.LvnengDownlinkCmdEnum.HEARTBEAT_ACK;
|
||||
|
||||
/**
|
||||
* 绿能3.4 充电桩上传心跳包
|
||||
*/
|
||||
@Slf4j
|
||||
@LvnengCmd(102)
|
||||
public class LvnengV340HeartbeatULCmd extends LvnengUplinkCmdExe {
|
||||
@Override
|
||||
public void execute(TcpSession tcpSession, LvnengUplinkMessage lvnengUplinkMessage, ProtocolContext ctx) {
|
||||
log.debug("{} 绿能3.4充电桩上报心跳包请求", tcpSession);
|
||||
ByteBuf byteBuf = Unpooled.wrappedBuffer(lvnengUplinkMessage.getMsgBody());
|
||||
|
||||
ObjectNode additionalInfo = JacksonUtil.newObjectNode();
|
||||
|
||||
//1预留
|
||||
byteBuf.skipBytes(2);
|
||||
//2预留
|
||||
byteBuf.skipBytes(2);
|
||||
|
||||
//3充电桩编码
|
||||
byte[] pileCodeBytes = new byte[32];
|
||||
byteBuf.readBytes(pileCodeBytes);
|
||||
String pileCode = StringUtils.trim(new String(pileCodeBytes, StandardCharsets.US_ASCII));
|
||||
|
||||
//4心跳序号
|
||||
int flag = byteBuf.readShortLE();
|
||||
additionalInfo.put("心跳序号", flag);
|
||||
//5预留
|
||||
byteBuf.skipBytes(16);
|
||||
|
||||
|
||||
tcpSession.addPileCode(pileCode);
|
||||
|
||||
// 注册前置会话
|
||||
ctx.getProtocolSessionRegistryProvider().register(tcpSession);
|
||||
|
||||
// 转发到后端
|
||||
HeartBeatRequest heartBeatRequest = HeartBeatRequest.newBuilder()
|
||||
.setPileCode(pileCode)
|
||||
.setRemoteAddress(tcpSession.getAddress().toString())
|
||||
.setNodeId(ctx.getServiceInfoProvider().getServiceId())
|
||||
.setNodeHostAddress(ctx.getServiceInfoProvider().getHostAddress())
|
||||
.setNodeRestPort(ctx.getServiceInfoProvider().getRestPort())
|
||||
.setNodeGrpcPort(ctx.getServiceInfoProvider().getGrpcPort())
|
||||
.setAdditionalInfo(additionalInfo.toString())
|
||||
.build();
|
||||
UplinkQueueMessage uplinkQueueMessage = uplinkMessageBuilder(heartBeatRequest.getPileCode(), tcpSession, lvnengUplinkMessage)
|
||||
.setHeartBeatRequest(heartBeatRequest)
|
||||
.build();
|
||||
|
||||
tcpSession.getForwarder().sendMessage(uplinkQueueMessage);
|
||||
//服务器应答心跳包信息
|
||||
pingAck(tcpSession, flag);
|
||||
}
|
||||
|
||||
private void pingAck(TcpSession tcpSession, int flag) {
|
||||
ByteBuf pingAckMsgBody = Unpooled.buffer(6);
|
||||
pingAckMsgBody.writeShortLE(0);
|
||||
pingAckMsgBody.writeShortLE(0);
|
||||
pingAckMsgBody.writeShortLE(flag);
|
||||
|
||||
encodeAndWriteFlush(HEARTBEAT_ACK,
|
||||
pingAckMsgBody,
|
||||
tcpSession);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
/**
|
||||
* 开源代码,仅供学习和交流研究使用,商用请联系三丙
|
||||
* 微信:mohan_88888
|
||||
* 抖音:程序员三丙
|
||||
* 付费课程知识星球:https://t.zsxq.com/aKtXo
|
||||
*/
|
||||
package sanbing.jcpp.protocol.lvneng.v340.cmd;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import sanbing.jcpp.infrastructure.util.codec.BCDUtil;
|
||||
import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil;
|
||||
import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil;
|
||||
import sanbing.jcpp.proto.gen.ProtocolProto;
|
||||
import sanbing.jcpp.proto.gen.ProtocolProto.GunRunStatus;
|
||||
import sanbing.jcpp.proto.gen.ProtocolProto.GunRunStatusProto;
|
||||
import sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage;
|
||||
import sanbing.jcpp.protocol.ProtocolContext;
|
||||
import sanbing.jcpp.protocol.listener.tcp.TcpSession;
|
||||
import sanbing.jcpp.protocol.lvneng.LvnengUplinkCmdExe;
|
||||
import sanbing.jcpp.protocol.lvneng.LvnengUplinkMessage;
|
||||
import sanbing.jcpp.protocol.lvneng.annotation.LvnengCmd;
|
||||
import sanbing.jcpp.protocol.lvneng.enums.LvnengAlarmCodeEnum;
|
||||
import sanbing.jcpp.protocol.lvneng.enums.LvnengPileStartTypeEnum;
|
||||
import sanbing.jcpp.protocol.lvneng.enums.LvnengPileStatusEnum;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static sanbing.jcpp.protocol.lvneng.enums.LvnengDownlinkCmdEnum.REAL_TIME_DATA_ACK;
|
||||
|
||||
/**
|
||||
* 绿能3.4 充电桩状态信息包上报
|
||||
*/
|
||||
@Slf4j
|
||||
@LvnengCmd(109)
|
||||
public class LvnengV340RealTimeDataULCmd extends LvnengUplinkCmdExe {
|
||||
@Override
|
||||
public void execute(TcpSession tcpSession, LvnengUplinkMessage lvnengUplinkMessage, ProtocolContext ctx) {
|
||||
log.debug("{} 绿能3.4充电桩状态信息包上报请求", tcpSession);
|
||||
ByteBuf byteBuf = Unpooled.wrappedBuffer(lvnengUplinkMessage.getMsgBody());
|
||||
|
||||
ObjectNode additionalInfo = JacksonUtil.newObjectNode();
|
||||
// 从Tracer总获取当前时间
|
||||
long ts = TracerContextUtil.getCurrentTracer().getTracerTs();
|
||||
|
||||
//1预留
|
||||
byteBuf.skipBytes(2);
|
||||
//2预留
|
||||
byteBuf.skipBytes(2);
|
||||
|
||||
//3充电桩编码
|
||||
byte[] pileCodeBytes = new byte[32];
|
||||
byteBuf.readBytes(pileCodeBytes);
|
||||
String pileCode = StringUtils.trim(new String(pileCodeBytes, StandardCharsets.US_ASCII));
|
||||
|
||||
//4 充电枪数量
|
||||
byte gunsNum = byteBuf.readByte();
|
||||
additionalInfo.put("充电枪数量", gunsNum);
|
||||
|
||||
//5 充电枪口
|
||||
byte gunCode = byteBuf.readByte();
|
||||
additionalInfo.put("充电枪口", gunCode);
|
||||
|
||||
//6 充电枪类型
|
||||
byte gunType = byteBuf.readByte();
|
||||
additionalInfo.put("充电枪类型", gunType);
|
||||
|
||||
//7 工作状态
|
||||
byte pileStatus = byteBuf.readByte();
|
||||
String pileStatusCode = LvnengPileStatusEnum.getByCode(pileStatus);
|
||||
additionalInfo.put("工作状态", pileStatusCode);
|
||||
|
||||
//8 当前 SOC %
|
||||
byte soc = byteBuf.readByte();
|
||||
additionalInfo.put("soc", soc);
|
||||
|
||||
/** 9 告警码,0-无告警 非0参靠枚举类
|
||||
* @see LvnengAlarmCodeEnum
|
||||
*/
|
||||
long alarmCode = byteBuf.readUnsignedInt();
|
||||
String alarmCodeDesc = alarmCode == 0L ? "" : LvnengAlarmCodeEnum.getByCode(alarmCode);
|
||||
additionalInfo.put("告警码", alarmCodeDesc);
|
||||
|
||||
//10 车连接状态
|
||||
byte carLinkStatus = byteBuf.readByte();
|
||||
additionalInfo.put("车连接状态", getCarLinkDesc(carLinkStatus));
|
||||
|
||||
//11 本次充电累计充电费用 精度0.01元
|
||||
BigDecimal totalAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 100, 2);
|
||||
additionalInfo.put("本次充电累计充电费用", totalAmount);
|
||||
//12 当前时间
|
||||
byte[] bytes = new byte[8];
|
||||
byteBuf.readBytes(bytes);
|
||||
LocalDateTime localDateTime = BCDUtil.bcdToDate(bytes);
|
||||
long instant = localDateTime!=null?localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli():0L;
|
||||
additionalInfo.put("当前时间", instant);
|
||||
|
||||
//13 直流充电电压
|
||||
short DCV = byteBuf.readShortLE();
|
||||
additionalInfo.put("直流充电电压", DCV);
|
||||
|
||||
//14 直流充电电流
|
||||
short DCC = byteBuf.readShortLE();
|
||||
additionalInfo.put("直流充电电压", DCC);
|
||||
|
||||
//15 BMS 需求电压
|
||||
short bmsDCV = byteBuf.readShortLE();
|
||||
additionalInfo.put(" BMS 需求电压", bmsDCV);
|
||||
|
||||
//16 BMS 需求电流
|
||||
short bmsDCC = byteBuf.readShortLE();
|
||||
additionalInfo.put(" BMS 需求电压", bmsDCC);
|
||||
|
||||
//17 BMS 充电模式 1- 恒压 2- 恒流
|
||||
byte bmsChargingType = byteBuf.readByte();
|
||||
additionalInfo.put(" BMS 需求电压", getBmsChargingTypeDesc(bmsChargingType));
|
||||
|
||||
//18 交流 A 相充电电压
|
||||
short aACV = byteBuf.readShortLE();
|
||||
additionalInfo.put("交流 A 相充电电压", aACV);
|
||||
|
||||
//19 交流B相充电电压
|
||||
short bACV = byteBuf.readShortLE();
|
||||
additionalInfo.put("交流B相充电电压", bACV);
|
||||
|
||||
//20 交流 C相充电电压
|
||||
short cACV = byteBuf.readShortLE();
|
||||
additionalInfo.put("交流 C相充电电压", cACV);
|
||||
|
||||
//21 预留
|
||||
byteBuf.skipBytes(2);
|
||||
//22 预留
|
||||
byteBuf.skipBytes(2);
|
||||
//23 预留
|
||||
byteBuf.skipBytes(2);
|
||||
|
||||
//24 剩余充电时间 min
|
||||
short remainingTimeMin = byteBuf.readShortLE();
|
||||
additionalInfo.put("剩余充电时间 min", remainingTimeMin);
|
||||
|
||||
//25 充电时长 秒
|
||||
long totalChargingTime = byteBuf.readUnsignedIntLE();
|
||||
additionalInfo.put("充电时长 秒", totalChargingTime);
|
||||
|
||||
//26 本次充电累计充电电量 精度:0.001lkWh
|
||||
BigDecimal totalChargingEnergyKWh = reduceMagnification(byteBuf.readUnsignedIntLE(), 1000, 3);
|
||||
additionalInfo.put("本次充电累计充电电量", totalChargingEnergyKWh.toString());
|
||||
|
||||
//27 充电前电表读数 精度:0.001lkWh
|
||||
BigDecimal beforeNum = reduceMagnification(byteBuf.readLongLE(), 1000, 3);
|
||||
additionalInfo.put("充电前电表读数", beforeNum.toString());
|
||||
|
||||
//28 当前电表读数 精度:0.001lkWh
|
||||
BigDecimal currentNum = reduceMagnification(byteBuf.readLongLE(), 1000, 3);
|
||||
additionalInfo.put("当前电表读数", currentNum.toString());
|
||||
|
||||
//29 充电启动方式
|
||||
byte chargingType = byteBuf.readByte();
|
||||
String chargingTypeDesc = LvnengPileStartTypeEnum.getByCode(chargingType);
|
||||
additionalInfo.put("充电启动方式", chargingTypeDesc);
|
||||
|
||||
//30 预留
|
||||
byteBuf.skipBytes(1);
|
||||
//31 预留
|
||||
byteBuf.skipBytes(4);
|
||||
//32 预留
|
||||
byteBuf.skipBytes(1);
|
||||
|
||||
//33 云平台充电流水号/充电卡号
|
||||
byte[] tradeNoBytes = new byte[32];
|
||||
byteBuf.readBytes(tradeNoBytes);
|
||||
String tradeNo = BCDUtil.toString(tradeNoBytes);
|
||||
additionalInfo.put("云平台充电流水号/充电卡号", tradeNo);
|
||||
|
||||
//34 预留
|
||||
byteBuf.skipBytes(1);
|
||||
//35 预留
|
||||
byteBuf.skipBytes(8);
|
||||
|
||||
//36 当前电费,这里是整型,要乘以 0.01 才能得到真实的金额,单位元
|
||||
BigDecimal currentFee = reduceMagnification(byteBuf.readUnsignedIntLE(), 100, 2);
|
||||
additionalInfo.put("当前电费", currentFee.toString());
|
||||
|
||||
//37 当前服务费 这里是整型,要乘以 0.01 才能得到真实的金额,单位元
|
||||
BigDecimal currentServiceFee = reduceMagnification(byteBuf.readUnsignedIntLE(), 100, 2);
|
||||
additionalInfo.put("当前服务费", currentServiceFee.toString());
|
||||
|
||||
//38 充电功率 分辨率:0.1kW
|
||||
BigDecimal chargingPower = reduceMagnification(byteBuf.readUnsignedIntLE(), 10, 1);
|
||||
additionalInfo.put("充电功率", chargingPower.toString());
|
||||
|
||||
//39 预留
|
||||
byteBuf.skipBytes(4);
|
||||
//40 预留
|
||||
byteBuf.skipBytes(4);
|
||||
//41 预留
|
||||
byteBuf.skipBytes(4);
|
||||
|
||||
//42 出风口温度 预留 偏移量-50, -50-200
|
||||
byteBuf.skipBytes(1);
|
||||
//43 环境温度 预留 偏移量-50, -50-200
|
||||
byteBuf.skipBytes(1);
|
||||
//44 充电枪温度 预留 偏移量-50, -50-200
|
||||
byteBuf.skipBytes(1);
|
||||
|
||||
//45 车辆 VIN 码 直流桩有效,正常有效长度是字节17,18 位为‘\0’
|
||||
byte[] vinCodeByte = new byte[18];
|
||||
byteBuf.readBytes(vinCodeByte);
|
||||
String vinCode = new String(vinCodeByte, StandardCharsets.US_ASCII);
|
||||
additionalInfo.put("车辆 VIN 码", vinCode);
|
||||
|
||||
//46 预留 1
|
||||
byteBuf.skipBytes(1);
|
||||
|
||||
|
||||
tcpSession.addPileCode(pileCode);
|
||||
|
||||
// 注册前置会话
|
||||
ctx.getProtocolSessionRegistryProvider().register(tcpSession);
|
||||
|
||||
// 抢状态
|
||||
GunRunStatus gunRunStatus = parseGunRunStatus(pileStatus);
|
||||
GunRunStatusProto gunRunStatusProto = GunRunStatusProto.newBuilder()
|
||||
.setTs(ts)
|
||||
.setPileCode(pileCode)
|
||||
.setGunCode(gunCode + "")
|
||||
.setGunRunStatus(gunRunStatus)
|
||||
.addAllFaultMessages(Stream.of(alarmCodeDesc).collect(Collectors.toList()))
|
||||
.setAdditionalInfo(additionalInfo.toString())
|
||||
.build();
|
||||
|
||||
|
||||
// 转发到后端
|
||||
UplinkQueueMessage uplinkQueueMessage = uplinkMessageBuilder(gunRunStatusProto.getPileCode(), tcpSession, lvnengUplinkMessage)
|
||||
.setGunRunStatusProto(gunRunStatusProto)
|
||||
.build();
|
||||
tcpSession.getForwarder().sendMessage(uplinkQueueMessage);
|
||||
|
||||
if (StringUtils.isNotBlank(tradeNo)) {
|
||||
|
||||
// 充电进度
|
||||
ProtocolProto.ChargingProgressProto.Builder chargingProgressProtoBuilder = ProtocolProto.ChargingProgressProto.newBuilder()
|
||||
.setTs(ts)
|
||||
.setPileCode(pileCode)
|
||||
.setGunCode(gunCode + "")
|
||||
.setTradeNo(tradeNo)
|
||||
.setOutputVoltage(String.valueOf(DCV))
|
||||
.setOutputCurrent(String.valueOf(DCC))
|
||||
.setSoc(soc)
|
||||
.setTotalChargingDurationMin((int)totalChargingTime)
|
||||
.setTotalChargingEnergyKWh(totalChargingEnergyKWh.toPlainString())
|
||||
.setTotalChargingCostYuan(totalAmount.toPlainString())
|
||||
.setAdditionalInfo(additionalInfo.toString());
|
||||
|
||||
UplinkQueueMessage chargingProgressMessage = uplinkMessageBuilder(pileCode, tcpSession, lvnengUplinkMessage)
|
||||
.setChargingProgressProto(chargingProgressProtoBuilder)
|
||||
.build();
|
||||
|
||||
tcpSession.getForwarder().sendMessage(chargingProgressMessage);
|
||||
}
|
||||
|
||||
//服务器应答充电桩状态信息包
|
||||
realTimeDataAck(tcpSession, gunCode);
|
||||
}
|
||||
|
||||
private void realTimeDataAck(TcpSession tcpSession, byte gunCode) {
|
||||
ByteBuf pingAckMsgBody = Unpooled.buffer(5);
|
||||
pingAckMsgBody.writeShortLE(0);
|
||||
pingAckMsgBody.writeShortLE(0);
|
||||
pingAckMsgBody.writeByte(gunCode);
|
||||
|
||||
encodeAndWriteFlush(REAL_TIME_DATA_ACK,
|
||||
pingAckMsgBody,
|
||||
tcpSession);
|
||||
}
|
||||
|
||||
protected static BigDecimal reduceMagnification(long value, int magnification, int scale) {
|
||||
return new BigDecimal(value).divide(new BigDecimal(magnification), scale, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
|
||||
protected static String getBmsChargingTypeDesc(int value) {
|
||||
return switch (value) {
|
||||
case 1 -> "恒压";
|
||||
case 2 -> "恒流";
|
||||
default -> "未知";
|
||||
};
|
||||
}
|
||||
|
||||
protected static String getCarLinkDesc(int value) {
|
||||
// 目前只有 0 和 2 状态。
|
||||
return switch (value) {
|
||||
case 0 -> "断开";
|
||||
case 1 -> "半连接";
|
||||
case 2 -> "连接";
|
||||
default -> "未知";
|
||||
};
|
||||
}
|
||||
|
||||
private static GunRunStatus parseGunRunStatus(int gunStatus) {
|
||||
GunRunStatus gunRunStatus = GunRunStatus.UNKNOWN;
|
||||
if (gunStatus == 0) {
|
||||
gunRunStatus = GunRunStatus.IDLE;
|
||||
} else if (gunStatus == 2) {
|
||||
gunRunStatus = GunRunStatus.CHARGING;
|
||||
} else if (gunStatus == 6) {
|
||||
gunRunStatus = GunRunStatus.FAULT;
|
||||
}
|
||||
return gunRunStatus;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* 开源代码,仅供学习和交流研究使用,商用请联系三丙
|
||||
* 微信:mohan_88888
|
||||
* 抖音:程序员三丙
|
||||
* 付费课程知识星球:https://t.zsxq.com/aKtXo
|
||||
*/
|
||||
package sanbing.jcpp.protocol.lvneng.v340.cmd;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil;
|
||||
import sanbing.jcpp.proto.gen.ProtocolProto.TransactionRecordResponse;
|
||||
import sanbing.jcpp.protocol.ProtocolContext;
|
||||
import sanbing.jcpp.protocol.listener.tcp.TcpSession;
|
||||
import sanbing.jcpp.protocol.lvneng.LvnengDownlinkCmdExe;
|
||||
import sanbing.jcpp.protocol.lvneng.LvnengDwonlinkMessage;
|
||||
import sanbing.jcpp.protocol.lvneng.LvnengUplinkMessage;
|
||||
import sanbing.jcpp.protocol.lvneng.annotation.LvnengCmd;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static sanbing.jcpp.protocol.lvneng.enums.LvnengDownlinkCmdEnum.TRANSACTION_RECORD_ACK;
|
||||
|
||||
/**
|
||||
* 绿能3.4 服务器应答订单信息
|
||||
*/
|
||||
@Slf4j
|
||||
@LvnengCmd(201)
|
||||
public class LvnengV340TransactionRecordAckDLCmd extends LvnengDownlinkCmdExe {
|
||||
@Override
|
||||
public void execute(TcpSession tcpSession, LvnengDwonlinkMessage lvnengDwonlinkMessage, ProtocolContext ctx) {
|
||||
log.debug("{} 绿能3.4服务器订单应答", tcpSession);
|
||||
|
||||
if (!lvnengDwonlinkMessage.getMsg().hasTransactionRecordResponse()) {
|
||||
return;
|
||||
}
|
||||
|
||||
TransactionRecordResponse transactionRecordResponse = lvnengDwonlinkMessage.getMsg().getTransactionRecordResponse();
|
||||
|
||||
LvnengUplinkMessage requestData = JacksonUtil.fromBytes(lvnengDwonlinkMessage.getMsg().getRequestData().toByteArray(), LvnengUplinkMessage.class);
|
||||
|
||||
// 获取上行报文
|
||||
byte[] uplinkRawFrame = requestData.getRawFrame();
|
||||
|
||||
// 从上行报文中取出桩编号字节数组
|
||||
byte[] gunCodeBytes = Arrays.copyOfRange(uplinkRawFrame, 45, 46);
|
||||
byte[] indexBytes = Arrays.copyOfRange(uplinkRawFrame, 128, 132);
|
||||
|
||||
ByteBuf pingAckMsgBody = Unpooled.buffer(41);
|
||||
//1预留
|
||||
pingAckMsgBody.writeShortLE(0x00);
|
||||
//2预留
|
||||
pingAckMsgBody.writeShortLE(0x00);
|
||||
//3充电枪口
|
||||
pingAckMsgBody.writeBytes(gunCodeBytes);
|
||||
//4流水号
|
||||
pingAckMsgBody.writeBytes(encodeTradeNo(transactionRecordResponse.getTradeNo()));
|
||||
//5内部索引号
|
||||
pingAckMsgBody.writeBytes(indexBytes);
|
||||
encodeAndWriteFlush(TRANSACTION_RECORD_ACK,
|
||||
pingAckMsgBody,
|
||||
tcpSession);
|
||||
}
|
||||
|
||||
protected static byte[] encodeTradeNo(String tradeNo) {
|
||||
// 将tradeNo 读取到32字节数组内
|
||||
byte[] bytes = new byte[32];
|
||||
byte[] tradeNoBytes = tradeNo.getBytes(StandardCharsets.US_ASCII);
|
||||
int copyLength = Math.min(tradeNoBytes.length, bytes.length);
|
||||
System.arraycopy(tradeNoBytes, 0, bytes, 0, copyLength);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
/**
|
||||
* 开源代码,仅供学习和交流研究使用,商用请联系三丙
|
||||
* 微信:mohan_88888
|
||||
* 抖音:程序员三丙
|
||||
* 付费课程知识星球:https://t.zsxq.com/aKtXo
|
||||
*/
|
||||
package sanbing.jcpp.protocol.lvneng.v340.cmd;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import sanbing.jcpp.infrastructure.util.codec.BCDUtil;
|
||||
import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil;
|
||||
import sanbing.jcpp.proto.gen.ProtocolProto;
|
||||
import sanbing.jcpp.proto.gen.ProtocolProto.TimePeriodDetail;
|
||||
import sanbing.jcpp.proto.gen.ProtocolProto.TimePeriodDetail.PeriodItem;
|
||||
import sanbing.jcpp.proto.gen.ProtocolProto.TransactionDetail;
|
||||
import sanbing.jcpp.proto.gen.ProtocolProto.TransactionRecordRequest;
|
||||
import sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage;
|
||||
import sanbing.jcpp.protocol.ProtocolContext;
|
||||
import sanbing.jcpp.protocol.listener.tcp.TcpSession;
|
||||
import sanbing.jcpp.protocol.lvneng.LvnengUplinkCmdExe;
|
||||
import sanbing.jcpp.protocol.lvneng.LvnengUplinkMessage;
|
||||
import sanbing.jcpp.protocol.lvneng.annotation.LvnengCmd;
|
||||
import sanbing.jcpp.protocol.lvneng.enums.LvnengPileFinishReasonEnum;
|
||||
import sanbing.jcpp.protocol.lvneng.enums.LvnengPileStartTypeEnum;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 绿能3.4 充电桩上报充电订单
|
||||
*/
|
||||
@Slf4j
|
||||
@LvnengCmd(203)
|
||||
public class LvnengV340TransactionRecordULCmd extends LvnengUplinkCmdExe {
|
||||
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");
|
||||
|
||||
@Override
|
||||
public void execute(TcpSession tcpSession, LvnengUplinkMessage lvnengUplinkMessage, ProtocolContext ctx) {
|
||||
log.debug("{} 绿能3.4充电桩上报充电订单请求", tcpSession);
|
||||
ByteBuf byteBuf = Unpooled.wrappedBuffer(lvnengUplinkMessage.getMsgBody());
|
||||
|
||||
ObjectNode additionalInfo = JacksonUtil.newObjectNode();
|
||||
|
||||
//1预留
|
||||
byteBuf.skipBytes(2);
|
||||
//2预留
|
||||
byteBuf.skipBytes(2);
|
||||
|
||||
//3充电桩编码
|
||||
byte[] pileCodeBytes = new byte[32];
|
||||
byteBuf.readBytes(pileCodeBytes);
|
||||
String pileCode = StringUtils.trim(new String(pileCodeBytes, StandardCharsets.US_ASCII));
|
||||
|
||||
//4 充电枪位置类型
|
||||
byte gunType = byteBuf.readByte();
|
||||
additionalInfo.put("充电枪位置类型", gunType);
|
||||
|
||||
//5 充电枪口
|
||||
byte gunCode = byteBuf.readByte();
|
||||
additionalInfo.put("充电枪口", gunCode);
|
||||
|
||||
//6 卡号
|
||||
byte[] cardCodeBytes = new byte[32];
|
||||
byteBuf.readBytes(cardCodeBytes);
|
||||
String cardCode = StringUtils.trim(new String(cardCodeBytes, StandardCharsets.US_ASCII));
|
||||
additionalInfo.put("卡号", cardCode);
|
||||
|
||||
//7 充电开始时间
|
||||
byte[] chargingStartTimeBytes = new byte[8];
|
||||
byteBuf.readBytes(chargingStartTimeBytes);
|
||||
LocalDateTime start = BCDUtil.bcdToDate(chargingStartTimeBytes);
|
||||
long startTs = start != null ? start.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() : 0L;
|
||||
|
||||
//8 充电结束时间
|
||||
byte[] chargingEndTimeBytes = new byte[8];
|
||||
byteBuf.readBytes(chargingEndTimeBytes);
|
||||
LocalDateTime endTime = BCDUtil.bcdToDate(chargingEndTimeBytes);
|
||||
long endTs = endTime != null ? endTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() : 0L;
|
||||
|
||||
//9 充电时间长度 单位秒
|
||||
long chargingTotalTime = byteBuf.readUnsignedIntLE();
|
||||
additionalInfo.put("充电时间长度 单位秒", chargingTotalTime);
|
||||
|
||||
//10 开始 SOC
|
||||
byte startSoc = byteBuf.readByte();
|
||||
additionalInfo.put("开始 SOC", startSoc);
|
||||
|
||||
//11 结束 SOC
|
||||
byte endSoc = byteBuf.readByte();
|
||||
additionalInfo.put("结束 SOC", endSoc);
|
||||
|
||||
//12 充电结束原因
|
||||
long finishReasonCode = byteBuf.readUnsignedIntLE();
|
||||
String finishReason = LvnengPileFinishReasonEnum.getByCode(finishReasonCode);
|
||||
|
||||
//13 本次充电电量 精度:0.001kWh
|
||||
BigDecimal totalEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 1000, 3);
|
||||
|
||||
//14 充电前电表读数 精度:0.001kWh
|
||||
BigDecimal chargingBeforeNum = reduceMagnification(byteBuf.readLongLE(), 1000, 3);
|
||||
additionalInfo.put("充电前电表读数", chargingBeforeNum);
|
||||
|
||||
//15 充电后电表读数 精度:0.001kWh
|
||||
BigDecimal chargingAfterNum = reduceMagnification(byteBuf.readLongLE(), 1000, 3);
|
||||
additionalInfo.put("充电后电表读数", chargingAfterNum);
|
||||
|
||||
//16 本次充电金额 0.01 元 ,电费和服务费之和。
|
||||
BigDecimal totalAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 100, 2);
|
||||
|
||||
//17 内部索引号 4 字节有符号整形,每一条充电记录 都唯一编号,用于充电机内部做唯一标志
|
||||
int index = byteBuf.readIntLE();
|
||||
additionalInfo.put("内部索引号", index);
|
||||
additionalInfo.put("index", index);
|
||||
|
||||
//18 预留
|
||||
byteBuf.skipBytes(4);
|
||||
//19 预留
|
||||
byteBuf.skipBytes(4);
|
||||
//20 预留
|
||||
byteBuf.skipBytes(4);
|
||||
//21 预留
|
||||
byteBuf.skipBytes(1);
|
||||
//22 预留
|
||||
byteBuf.skipBytes(1);
|
||||
//23 预留
|
||||
byteBuf.skipBytes(4);
|
||||
|
||||
//24 车辆 VIN
|
||||
byte[] cardVinBytes = new byte[17];
|
||||
byteBuf.readBytes(cardVinBytes);
|
||||
String cardVin = StringUtils.trim(new String(cardVinBytes, StandardCharsets.US_ASCII));
|
||||
additionalInfo.put("车辆 VIN", cardVin);
|
||||
|
||||
//25 车牌号
|
||||
byte[] cardNumBytes = new byte[8];
|
||||
byteBuf.readBytes(cardNumBytes);
|
||||
String cardNum = StringUtils.trim(new String(cardNumBytes, StandardCharsets.US_ASCII));
|
||||
additionalInfo.put("车牌号", cardNum);
|
||||
|
||||
// 24小时,48个时间段,开始时间段[00:00:00〜00:30:00],最后时间段[23:30:00-00:00:00] 0.001kwh
|
||||
LocalTime currentTime = LocalTime.MIDNIGHT;
|
||||
List<PeriodItem> list = new ArrayList<>();
|
||||
for (int i = 1; i < 49; i++) {
|
||||
BigDecimal chargingCapacity = reduceMagnification(byteBuf.readUnsignedIntLE(), 1000, 3);
|
||||
PeriodItem timePeriodDetail = PeriodItem.newBuilder()
|
||||
.setPeriodNo(i)
|
||||
.setStartTime(dateFormat(currentTime))
|
||||
.setEndTime(dateFormat(currentTime.plusMinutes(30)))
|
||||
.setEnergyKWh(chargingCapacity.toString())
|
||||
.build();
|
||||
list.add(timePeriodDetail);
|
||||
}
|
||||
|
||||
//74 启动方式
|
||||
byte startType = byteBuf.readByte();
|
||||
String startTypeCode = LvnengPileStartTypeEnum.getByCode(startType);
|
||||
additionalInfo.put("启动方式", startTypeCode);
|
||||
|
||||
//75 充电流水号
|
||||
byte[] tradeNoBytes = new byte[32];
|
||||
byteBuf.readBytes(tradeNoBytes);
|
||||
String tradeNo = StringUtils.trim(new String(tradeNoBytes, StandardCharsets.US_ASCII));
|
||||
|
||||
//76 充电服务费
|
||||
BigDecimal serviceFee = reduceMagnification(byteBuf.readUnsignedIntLE(), 100, 2);
|
||||
additionalInfo.put("充电服务费", serviceFee.toString());
|
||||
|
||||
tcpSession.addPileCode(pileCode);
|
||||
|
||||
|
||||
//充电明细
|
||||
TimePeriodDetail timePeriodDetail = TimePeriodDetail.newBuilder()
|
||||
.addAllPeriods(list)
|
||||
.build();
|
||||
//订单明细
|
||||
TransactionDetail transactionDetail = TransactionDetail.newBuilder()
|
||||
.setType(ProtocolProto.DetailType.TIME_PERIOD)
|
||||
.setTimePeriod(timePeriodDetail)
|
||||
.build();
|
||||
|
||||
// 注册前置会话
|
||||
ctx.getProtocolSessionRegistryProvider().register(tcpSession);
|
||||
|
||||
// 转发到后端
|
||||
TransactionRecordRequest transactionRecord = TransactionRecordRequest.newBuilder()
|
||||
.setPileCode(pileCode)
|
||||
.setGunCode(gunCode + "")
|
||||
.setTradeNo(tradeNo)
|
||||
.setStartTs(startTs)
|
||||
.setEndTs(endTs)
|
||||
.setTotalEnergyKWh(totalEnergy.toString())
|
||||
.setTotalAmountYuan(totalAmount.toString())
|
||||
.setStopReason(finishReason)
|
||||
.setDetail(transactionDetail)
|
||||
.setAdditionalInfo(additionalInfo.toString())
|
||||
.build();
|
||||
|
||||
UplinkQueueMessage uplinkQueueMessage = uplinkMessageBuilder(transactionRecord.getPileCode(), tcpSession, lvnengUplinkMessage)
|
||||
.setTransactionRecordRequest(transactionRecord)
|
||||
.build();
|
||||
tcpSession.getForwarder().sendMessage(uplinkQueueMessage);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected static BigDecimal reduceMagnification(long value, int magnification, int scale) {
|
||||
return new BigDecimal(value).divide(new BigDecimal(magnification), scale, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
protected static String dateFormat(LocalTime time) {
|
||||
return time.format(DATE_TIME_FORMATTER);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user