同步获取响应数据

This commit is contained in:
Guoqs
2024-08-01 15:13:14 +08:00
parent 0ad35c9300
commit 49ad9419fe
10 changed files with 492 additions and 104 deletions

View File

@@ -0,0 +1,72 @@
package com.jsowell.pile.rpc;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class SyncPromise {
// 用于接收结果
// private RpcResponse rpcResponse;
private byte[] rpcResult;
private final CountDownLatch countDownLatch = new CountDownLatch(1);
// 用于判断是否超时
private boolean isTimeout = false;
public boolean isTimeout() {
return isTimeout;
}
public byte[] getRpcResult() {
return rpcResult;
}
public void setRpcResult(byte[] rpcResult) {
this.rpcResult = rpcResult;
}
/**
* 同步等待返回结果
*/
// public RpcResponse get(long timeout, TimeUnit unit) throws InterruptedException {
// // 等待阻塞超时时间内countDownLatch减到0将提前唤醒以此作为是否超时判断
// boolean earlyWakeUp = countDownLatch.await(timeout, unit);
//
// if(earlyWakeUp) {
// // 超时时间内countDownLatch减到0提前唤醒说明已有结果
// return rpcResponse;
// } else {
// // 超时时间内countDownLatch没有减到0自动唤醒说明超时时间内没有等到结果
// isTimeout = true;
// return null;
// }
// }
public byte[] get2(long timeout, TimeUnit unit) throws InterruptedException {
// 等待阻塞超时时间内countDownLatch减到0将提前唤醒以此作为是否超时判断
boolean earlyWakeUp = countDownLatch.await(timeout, unit);
if(earlyWakeUp) {
// 超时时间内countDownLatch减到0提前唤醒说明已有结果
return rpcResult;
} else {
// 超时时间内countDownLatch没有减到0自动唤醒说明超时时间内没有等到结果
isTimeout = true;
return null;
}
}
public void wake() {
countDownLatch.countDown();
}
// public RpcResponse getRpcResponse() {
// return rpcResponse;
// }
// public void setRpcResponse(RpcResponse rpcResponse) {
// this.rpcResponse = rpcResponse;
// }
}

View File

@@ -151,6 +151,16 @@ public class PileRemoteService {
ykcPushCommandService.pushProofreadTimeCommand(command);
}
public void proofreadTimeTest(String pileSn) {
ProofreadTimeCommand command = ProofreadTimeCommand.builder().pileSn(pileSn).build();
try {
ykcPushCommandService.pushProofreadTimeCommandTest(command);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 下发充电桩计费模型
*/
@@ -291,4 +301,5 @@ public class PileRemoteService {
public void reservationCharging(ReservationChargingCommand command) {
ykcPushCommandService.pushReservationChargingCommand(command);
}
}

View File

@@ -83,4 +83,6 @@ public interface YKCPushCommandService {
* @param command
*/
void pushReservationChargingCommand(ReservationChargingCommand command);
void pushProofreadTimeCommandTest(ProofreadTimeCommand command) throws Exception;
}

View File

@@ -10,6 +10,7 @@ import com.jsowell.common.exception.BusinessException;
import com.jsowell.common.util.*;
import com.jsowell.common.util.Cp56Time2a.Cp56Time2aUtil;
import com.jsowell.pile.domain.ykcCommond.*;
import com.jsowell.pile.rpc.SyncPromise;
import com.jsowell.pile.service.*;
import com.jsowell.pile.vo.web.BillingTemplateVO;
import com.jsowell.pile.vo.web.PileModelInfoVO;
@@ -28,7 +29,11 @@ import java.math.BigDecimal;
import java.time.LocalTime;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* 向充电桩发送指令service
@@ -36,6 +41,8 @@ import java.util.Objects;
@Slf4j
@Service
public class YKCPushCommandServiceImpl implements YKCPushCommandService {
private final static Map<String, SyncPromise> syncPromiseMap = new ConcurrentHashMap<>();
@Autowired
private PileBillingTemplateService pileBillingTemplateService;
@@ -131,6 +138,96 @@ public class YKCPushCommandServiceImpl implements YKCPushCommandService {
return true;
}
public byte[] pushTest(byte[] msg, String pileSn, Enum<YKCFrameTypeCode> frameTypeCode) throws Exception {
return this.pushTest(msg, pileSn, frameTypeCode, 5, TimeUnit.SECONDS);
}
public byte[] pushTest(byte[] msg, String pileSn, Enum<YKCFrameTypeCode> frameTypeCode, long timeout, TimeUnit unit) throws Exception {
// 通过桩编号获取channel
ChannelHandlerContext ctx = PileChannelEntity.getChannelByPileSn(pileSn);
String value = ((YKCFrameTypeCode) frameTypeCode).getValue(); // 帧类型名称
if (Objects.isNull(ctx)) {
log.error("push命令[{}]失败, 桩号:{}无法获取到长连接, 请检查充电桩连接状态!", value, pileSn);
throw new NullPointerException("channel");
}
if(msg == null) {
throw new NullPointerException("msg");
}
if(timeout <= 0) {
throw new IllegalArgumentException("timeout must greater than 0");
}
// 创造一个容器用于存放当前线程与rpcClient中的线程交互
SyncPromise syncPromise = new SyncPromise();
// 消息id = channelId + 帧类型(例如: "0x34")
String msgId = ctx.channel().id().toString() + "_" + YKCUtils.frameType2Str(((YKCFrameTypeCode) frameTypeCode).getBytes());
syncPromiseMap.put(msgId, syncPromise);
// 发送消息此处如果发送玩消息并且在get之前返回了结果下一行的get将不会进入阻塞也可以顺利拿到结果
/*
拼接报文
*/
// 起始标志
byte[] head = new byte[]{0x68};
// 序列号域
byte[] serialNumber = new byte[]{0x00, 0x00};
// 加密标志
byte[] encryptFlag = new byte[]{0x00};
// 帧类型标志
byte[] frameType = new byte[]{(byte) ((YKCFrameTypeCode) frameTypeCode).getCode()};
// 序列号域+加密标志+帧类型标志+消息体
byte[] temp = Bytes.concat(serialNumber, encryptFlag, frameType, msg);
// 数据长度
byte[] length = BytesUtil.intToBytes(temp.length, 1);
// 帧校验域
byte[] crc = BytesUtil.intToBytes(CRC16Util.calcCrc16(temp));
// 返回报文
byte[] writeMsg = Bytes.concat(head, length, temp, crc);
// 返回完整的报文 string类型
String wholeMsg = BytesUtil.binary(writeMsg, 16);
ByteBuf byteBuf = ctx.channel().alloc().buffer().writeBytes(writeMsg);
ChannelFuture channelFuture = ctx.channel().writeAndFlush(byteBuf);
channelFuture.addListener((ChannelFutureListener) channelFutureListener -> {
// 检查操作的状态
if (channelFutureListener.isSuccess()) {
log.info("【push结果===>成功】, pileSn:{}, remoteAddress:{}, channelId:{}, 帧类型:{}, 报文:{}",
pileSn, ctx.channel().remoteAddress(), ctx.channel().id(), value, wholeMsg);
} else {
// 如果发生错误则访问描述原因的Throwable
Throwable cause = channelFutureListener.cause();
log.info("【push结果===>失败】, pileSn:{}, remoteAddress:{}, channelId:{}, 帧类型:{}, 报文:{}",
pileSn, ctx.channel().remoteAddress(), ctx.channel().id(), value, wholeMsg);
log.error("push发送命令失败, pileSn:{}", pileSn, cause);
}
});
// 等待获取结果
byte[] rpcResponse = syncPromise.get2(timeout, unit);
if(rpcResponse == null) {
if(syncPromise.isTimeout()) {
throw new TimeoutException("等待响应结果超时");
} else{
throw new Exception("其他异常");
}
}
// 移除容器
syncPromiseMap.remove(msgId);
return rpcResponse;
}
/**
* 发送启动充电指令
*/
@@ -280,6 +377,24 @@ public class YKCPushCommandServiceImpl implements YKCPushCommandService {
log.info("[充电桩:{}对时, 时间:{}, CP56Time2a:{}]", pileSn, DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, date), BytesUtil.binary(dateBytes, 16));
}
@Override
public void pushProofreadTimeCommandTest(ProofreadTimeCommand command) throws Exception {
String pileSn = command.getPileSn();
byte[] pileSnByteArr = BytesUtil.str2Bcd(pileSn);
// 时间
Date date = DateUtils.parseDate(DateUtils.getDateTime());
byte[] dateBytes = Cp56Time2aUtil.date2Hbyte(date);
// 拼装msg
byte[] msg = Bytes.concat(pileSnByteArr, dateBytes);
byte[] bytes = this.pushTest(msg, pileSn, YKCFrameTypeCode.TIME_CHECK_SETTING_CODE);
log.info("[充电桩:{}对时, 时间:{}, CP56Time2a:{}], 响应:{}",
pileSn, DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, date), BytesUtil.binary(dateBytes, 16), BytesUtil.binary(bytes, 16));
}
/**
* 向充电桩发送计费模板
*
@@ -546,4 +661,5 @@ public class YKCPushCommandServiceImpl implements YKCPushCommandService {
log.info("【=====平台下发指令=====】: 预约充电指令, 交易流水号:{}, 桩编号:{}, 枪口号:{}, 操作:{}, 身份验证:{}, 开始时间:{}, 结束时间:{}, 启动金额:{}",
transactionCode, pileSn, connectorCode, operation, verifyIdentity, DateUtils.formatDateTime(reservedStartTime), DateUtils.formatDateTime(reservedEndTime), amount);
}
}