mirror of
https://gitee.com/san-bing/JChargePointProtocol
synced 2026-05-06 02:49:57 +08:00
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 开源代码,仅供学习和交流研究使用,商用请联系三丙
|
||||
* 微信:mohan_88888
|
||||
* 抖音:程序员三丙
|
||||
* 付费课程知识星球:https://t.zsxq.com/aKtXo
|
||||
*/
|
||||
package sanbing.jcpp.protocol.lvneng.v340;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import sanbing.jcpp.infrastructure.util.annotation.ProtocolComponent;
|
||||
import sanbing.jcpp.protocol.ProtocolBootstrap;
|
||||
import sanbing.jcpp.protocol.ProtocolMessageProcessor;
|
||||
import sanbing.jcpp.protocol.lvneng.LvnengProtocolMessageProcessor;
|
||||
|
||||
import static sanbing.jcpp.protocol.lvneng.v340.LvnengV340ProtocolBootstrap.PROTOCOL_NAME;
|
||||
|
||||
@ProtocolComponent(PROTOCOL_NAME)
|
||||
@Slf4j
|
||||
public class LvnengV340ProtocolBootstrap extends ProtocolBootstrap {
|
||||
|
||||
public static final String PROTOCOL_NAME = "lvnengV340";
|
||||
@Override
|
||||
protected String getProtocolName() {
|
||||
return PROTOCOL_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void _init() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void _destroy() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProtocolMessageProcessor messageProcessor() {
|
||||
return new LvnengProtocolMessageProcessor(forwarder, protocolContext);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* 开源代码,仅供学习和交流研究使用,商用请联系三丙
|
||||
* 微信:mohan_88888
|
||||
* 抖音:程序员三丙
|
||||
* 付费课程知识星球:https://t.zsxq.com/aKtXo
|
||||
*/
|
||||
package sanbing.jcpp.protocol.lvneng.v340.cmd;
|
||||
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import sanbing.jcpp.infrastructure.util.codec.BCDUtil;
|
||||
import sanbing.jcpp.infrastructure.util.codec.CP56Time2aUtil;
|
||||
import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil;
|
||||
import sanbing.jcpp.infrastructure.util.mdc.MDCUtils;
|
||||
import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil;
|
||||
import sanbing.jcpp.proto.gen.ProtocolProto.LoginResponse;
|
||||
import sanbing.jcpp.protocol.ProtocolContext;
|
||||
import sanbing.jcpp.protocol.listener.tcp.TcpSession;
|
||||
import sanbing.jcpp.protocol.listener.tcp.enums.SequenceNumberLength;
|
||||
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.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static sanbing.jcpp.infrastructure.util.config.ThreadPoolConfiguration.PROTOCOL_SESSION_SCHEDULED;
|
||||
import static sanbing.jcpp.protocol.domain.SessionCloseReason.MANUALLY;
|
||||
import static sanbing.jcpp.protocol.listener.tcp.TcpSession.SCHEDULE_KEY_AUTO_SYNC_TIME;
|
||||
import static sanbing.jcpp.protocol.lvneng.enums.LvnengDownlinkCmdEnum.LOGIN_ACK;
|
||||
import static sanbing.jcpp.protocol.lvneng.enums.LvnengDownlinkCmdEnum.SYNC_TIME;
|
||||
|
||||
/**
|
||||
* 绿能3.4 服务器应答充电桩签到命令
|
||||
*/
|
||||
@Slf4j
|
||||
@LvnengCmd(105)
|
||||
public class LvnengV340LoginAckDLCmd extends LvnengDownlinkCmdExe {
|
||||
@Override
|
||||
public void execute(TcpSession tcpSession, LvnengDwonlinkMessage lvnengDwonlinkMessage, ProtocolContext ctx) {
|
||||
log.debug("{} 绿能3.4登录认证应答", tcpSession);
|
||||
|
||||
if (!lvnengDwonlinkMessage.getMsg().hasLoginResponse()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LoginResponse loginResponse = lvnengDwonlinkMessage.getMsg().getLoginResponse();
|
||||
|
||||
LvnengUplinkMessage requestData = JacksonUtil.fromBytes(lvnengDwonlinkMessage.getMsg().getRequestData().toByteArray(), LvnengUplinkMessage.class);
|
||||
|
||||
// 获取上行报文
|
||||
byte[] uplinkRawFrame = requestData.getRawFrame();
|
||||
// 从上行报文中取出桩编号字节数组
|
||||
byte[] pileCodeBytes = Arrays.copyOfRange(uplinkRawFrame, 12, 44);
|
||||
byte[] randomNumBytes = Arrays.copyOfRange(uplinkRawFrame, 98, 102);
|
||||
|
||||
if (loginResponse.getSuccess()) {
|
||||
|
||||
// 构造并下发登录ACK
|
||||
loginAck(tcpSession, requestData, randomNumBytes);
|
||||
|
||||
// 构造定时对时
|
||||
registerSyncTimeTask(tcpSession, pileCodeBytes, requestData);
|
||||
|
||||
} else {
|
||||
|
||||
log.info("绿能3.4登录认证失败,服务端断开连接。 pileCode:{}", loginResponse.getPileCode());
|
||||
|
||||
// 构造并下发登录ACK
|
||||
loginAck(tcpSession, requestData, new byte[]{0x00, 0x00, 0x00, 0x00});
|
||||
|
||||
// 断开连接
|
||||
tcpSession.close(MANUALLY);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void loginAck(TcpSession tcpSession, LvnengUplinkMessage requestData, byte[] randomNumBytes) {
|
||||
// 创建ACK消息体7字节桩编号+1字节登录结果
|
||||
ByteBuf loginAckMsgBody = Unpooled.buffer(18);
|
||||
loginAckMsgBody.writeShortLE(0x00);
|
||||
loginAckMsgBody.writeShortLE(0x00);
|
||||
loginAckMsgBody.writeBytes(randomNumBytes);
|
||||
loginAckMsgBody.writeByte(0x00);
|
||||
|
||||
encodeAndWriteFlush(LOGIN_ACK,
|
||||
requestData.getSequenceNumber(),
|
||||
requestData.getEncryptionFlag(),
|
||||
loginAckMsgBody,
|
||||
tcpSession);
|
||||
}
|
||||
|
||||
private void registerSyncTimeTask(TcpSession tcpSession, byte[] pileCodeBytes, LvnengUplinkMessage requestData) {
|
||||
tcpSession.addSchedule(SCHEDULE_KEY_AUTO_SYNC_TIME, k -> {
|
||||
log.info("{} 云快充3.4开始注册定时对时任务", tcpSession);
|
||||
return PROTOCOL_SESSION_SCHEDULED.scheduleAtFixedRate(() ->
|
||||
syncTime(tcpSession, pileCodeBytes, requestData),
|
||||
0, RandomUtil.randomInt(420, 480), TimeUnit.MINUTES);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private void syncTime(TcpSession tcpSession, byte[] pileCodeBytes, LvnengUplinkMessage requestData) {
|
||||
TracerContextUtil.newTracer();
|
||||
MDCUtils.recordTracer();
|
||||
log.info("{} 绿能3.4开始下发对时报文", tcpSession);
|
||||
ByteBuf syncTimeMsgBody = Unpooled.buffer(14);
|
||||
syncTimeMsgBody.writeBytes(pileCodeBytes);
|
||||
syncTimeMsgBody.writeBytes(CP56Time2aUtil.encode(LocalDateTime.now()));
|
||||
|
||||
ByteBuf msgBodyBuf = Unpooled.buffer();
|
||||
// 预留1
|
||||
msgBodyBuf.writeShortLE(0);
|
||||
// 预留1
|
||||
msgBodyBuf.writeShortLE(0);
|
||||
msgBodyBuf.writeByte(1);
|
||||
// 4 参数起始地址,子命令
|
||||
msgBodyBuf.writeIntLE(2);
|
||||
//6 参数字节长度
|
||||
msgBodyBuf.writeShortLE(8);
|
||||
//7 命令参数数据
|
||||
msgBodyBuf.writeBytes(BCDUtil.dateToBcd8(LocalDateTime.now()));
|
||||
|
||||
encodeAndWriteFlush(SYNC_TIME,
|
||||
tcpSession.nextSeqNo(SequenceNumberLength.SHORT),
|
||||
requestData.getEncryptionFlag(),
|
||||
syncTimeMsgBody,
|
||||
tcpSession);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* 开源代码,仅供学习和交流研究使用,商用请联系三丙
|
||||
* 微信: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.LoginRequest;
|
||||
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 java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
|
||||
/**
|
||||
* 绿能3.4 充电桩签到信息上报
|
||||
*/
|
||||
@Slf4j
|
||||
@LvnengCmd(106)
|
||||
public class LvnengV340LoginULCmd 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();
|
||||
|
||||
// 预留
|
||||
byteBuf.readShortLE();
|
||||
// 预留
|
||||
byteBuf.readShortLE();
|
||||
|
||||
// 充电桩编码
|
||||
byte[] pileCodeBytes = new byte[32];
|
||||
byteBuf.readBytes(pileCodeBytes);
|
||||
String pileCode = StringUtils.trim(new String(pileCodeBytes, StandardCharsets.US_ASCII));
|
||||
|
||||
//预留
|
||||
int flag = byteBuf.readByte();
|
||||
additionalInfo.put("标识", flag);
|
||||
|
||||
// 充电桩软件版本 (4字节)
|
||||
// 格式: 0x00 0x01 0x0100 表示 V0.1.256
|
||||
int major = byteBuf.readUnsignedByte(); // 主版本号
|
||||
int minor = byteBuf.readUnsignedByte(); // 次版本号
|
||||
int patch = byteBuf.readUnsignedShort(); // 修订版本号
|
||||
String version = String.format("V%d.%d.%d", major, minor, patch);
|
||||
additionalInfo.put("版本", version);
|
||||
|
||||
// 预留
|
||||
byteBuf.skipBytes(10);
|
||||
|
||||
// 充电枪个数
|
||||
int gunsNum = byteBuf.readByte();
|
||||
additionalInfo.put("充电枪个数", gunsNum);
|
||||
|
||||
// 预留
|
||||
byteBuf.skipBytes(6);
|
||||
|
||||
// 当前充电桩时间
|
||||
byte[] pileSystemTimeBytes = new byte[8];
|
||||
byteBuf.readBytes(pileSystemTimeBytes);
|
||||
LocalDateTime pileSystemTime = BCDUtil.bcdToDate(pileSystemTimeBytes);
|
||||
additionalInfo.put("当前充电桩时间", pileSystemTime == null ? null : pileSystemTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
|
||||
// 预留
|
||||
byteBuf.readLong();
|
||||
byteBuf.readLong();
|
||||
byteBuf.readLong();
|
||||
|
||||
long randomNum = byteBuf.readUnsignedIntLE();
|
||||
additionalInfo.put("桩生成的随机数", randomNum);
|
||||
|
||||
// 充电桩与服务器通信协议版本 (2字节)
|
||||
// 十进制30表示V3.0
|
||||
int softVersion = byteBuf.readUnsignedShortLE();
|
||||
int softMajor = softVersion / 10; // 主版本号
|
||||
int softMinor = softVersion % 10; // 次版本号
|
||||
additionalInfo.put("桩后台通信协议版本", String.format("V%d.%d", softMajor, softMinor));
|
||||
|
||||
int whiteVersion = byteBuf.readIntLE();
|
||||
additionalInfo.put("白名单版本号", whiteVersion);
|
||||
|
||||
tcpSession.addPileCode(pileCode);
|
||||
|
||||
// 注册前置会话
|
||||
ctx.getProtocolSessionRegistryProvider().register(tcpSession);
|
||||
|
||||
// 转发到后端
|
||||
LoginRequest loginRequest = LoginRequest.newBuilder()
|
||||
.setPileCode(pileCode)
|
||||
.setCredential(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(loginRequest.getPileCode(), tcpSession, lvnengUplinkMessage)
|
||||
.setLoginRequest(loginRequest)
|
||||
.build();
|
||||
tcpSession.getForwarder().sendMessage(uplinkQueueMessage);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user