[v0.0.1] 将哈勃原有逻辑和线上版本对齐(不处理地面站的在控无人机更新信息,直接转发数据);哈勃客户端类管理;CacClient channel隐藏并暴露静态channel引用以供哈勃客户端转发数据

master
shiyi 2 months ago
parent e1467baf2b
commit 46bf0a6363

@ -37,7 +37,7 @@
<dependency> <dependency>
<groupId>io.netty</groupId> <groupId>io.netty</groupId>
<artifactId>netty-all</artifactId> <artifactId>netty-all</artifactId>
<version>4.1.86.Final</version> <version>4.1.34.Final</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.alibaba</groupId> <groupId>com.alibaba</groupId>

@ -32,7 +32,7 @@ public class CacHpApi {
@Value("${http-cac.host}") @Value("${http-cac.host}")
public void setHost(String host) { public void setHost(String host) {
CacHpApi.HOST = host; CacHpApi.HOST = host;
log.info(">>>中心指控地址 {}<<<", host); log.info("[cac] >>>中心指控地址 {}<<<", host);
} }
private static String postRequest(String api, String body) { private static String postRequest(String api, String body) {
@ -42,10 +42,10 @@ public class CacHpApi {
try { try {
return HttpClientUtils.sendPost(path, body, header); return HttpClientUtils.sendPost(path, body, header);
} catch (ConnectException e) { } catch (ConnectException e) {
log.error("http请求超时: {}", e.getMessage()); log.error("[cac] http请求超时: {}", e.getMessage());
return null; return null;
} catch (IOException e) { } catch (IOException e) {
log.error("http请求错误: {}", e.getMessage()); log.error("[cac] http请求错误: {}", e.getMessage());
return null; return null;
} }
} }
@ -56,10 +56,10 @@ public class CacHpApi {
try { try {
return HttpClientUtils.sendPost(path, body, header, Result.class); return HttpClientUtils.sendPost(path, body, header, Result.class);
} catch (ConnectException e) { } catch (ConnectException e) {
log.error("http请求超时: {}", e.getMessage()); log.error("[cac] http请求超时: {}", e.getMessage());
return null; return null;
} catch (Exception e) { } catch (Exception e) {
log.error("http请求错误: {}, body={}", e.getMessage(), body); log.error("[cac] http请求错误: {}, body={}", e.getMessage(), body);
return null; return null;
} }
} }
@ -91,7 +91,7 @@ public class CacHpApi {
final String gcsSignApi = HTFP_PATH + "/signIn"; final String gcsSignApi = HTFP_PATH + "/signIn";
Result result = postRequestAndGetResult(gcsSignApi, body); Result result = postRequestAndGetResult(gcsSignApi, body);
if (result == null || !result.isSuccess()) { if (result == null || !result.isSuccess()) {
log.error("地面站上线请求失败: {}", result); log.error("[cac] 地面站上线失败: {}", result);
return null; return null;
} }
return result.getData().toString(); return result.getData().toString();
@ -105,12 +105,43 @@ public class CacHpApi {
// return postRequest(gcsSignApi, body); // return postRequest(gcsSignApi, body);
// } // }
/**无人机上线*/
public static boolean uavMasterControlNotify(String uavId) {
final String gcsSignApi = HTFP_PATH + "/notifyGetUavControlRight";
Map<String, String> body = new HashMap<>();
body.put("uavId", uavId);
body.put("gcsId", GlobalData.GCS_ID);
try {
Result result = postRequestAndGetResult(gcsSignApi, JSONUtils.obj2json(body));
if (result == null || !result.isSuccess()) {
log.error("[cac] 无人机上线失败: {}", result);
return false;
}
return true;
} catch (IOException e) {
log.error("[cac] 无人机上线失败: {}", e.getMessage());
}
return false;
}
/** /**
* *
*/ */
public static String uavPowerOff(String body) { public static boolean uavPowerOff(String uavId) {
final String gcsSignApi = HTFP_PATH + "/uavPowerOff"; final String gcsSignApi = HTFP_PATH + "/uavPowerOff";
return postRequest(gcsSignApi, body); Map<String, String> body = new HashMap<>();
body.put("uavId", uavId);
body.put("gcsId", GlobalData.GCS_ID);
try {
Result result = postRequestAndGetResult(gcsSignApi, JSONUtils.obj2json(body));
if (result == null || !result.isSuccess()) {
log.error("[cac] 无人机下线失败: {}", result);
return false;
}
return true;
} catch (IOException e) {
log.error("[cac] 无人机下线失败: {}", e.getMessage());
}
return false;
} }
/** /**
@ -129,22 +160,6 @@ public class CacHpApi {
return postRequest(gcsSignApi, body); return postRequest(gcsSignApi, body);
} }
/**
* 退
*/
public static String gcsExceptionOut(String body) {
final String gcsSignApi = HTFP_PATH + "/exceptionOut";
return postRequest(gcsSignApi, body);
}
/**
* 线
*/
public static String gcsReconnect(String body) {
final String gcsSignApi = HTFP_PATH + "/reconnect";
return postRequest(gcsSignApi, body);
}
/** /**
* *
*/ */
@ -153,15 +168,6 @@ public class CacHpApi {
return postRequest(gcsSignApi, body); return postRequest(gcsSignApi, body);
} }
public static String uavControlApply(String body) {
final String gcsSignApi = HTFP_PATH + "/applyUavControlRight";
return postRequest(gcsSignApi, body);
}
public static String uavMasterControlNotify(String body) {
final String gcsSignApi = HTFP_PATH + "/notifyGetUavControlRight";
return postRequest(gcsSignApi, body);
}
public static String uavControlReply(String body) { public static String uavControlReply(String body) {
final String gcsSignApi = HTFP_PATH + "/replyUavControlRight"; final String gcsSignApi = HTFP_PATH + "/replyUavControlRight";
return postRequest(gcsSignApi, body); return postRequest(gcsSignApi, body);

@ -1,32 +1,19 @@
package com.platform.cac; package com.platform.cac;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.platform.cac.tcp.CacClient; import com.platform.cac.tcp.CacClient;
import com.platform.cac.tcp.message.dataframe.RemoteTcpBaseDataFrame; import com.platform.cac.tcp.message.dataframe.RemoteTcpBaseDataFrame;
import com.platform.cac.tcp.message.dataframe.send.TcpRemoteAuthCacRequest; import com.platform.cac.tcp.message.dataframe.send.TcpRemoteAuthCacRequest;
import com.platform.info.GlobalData; import com.platform.info.GlobalData;
import com.platform.info.enums.ClientTypeEnum;
import com.platform.info.enums.GcsFrameEnum;
import com.platform.info.enums.GcsTypeEnum;
import com.platform.info.enums.RemoteFrameEnum; import com.platform.info.enums.RemoteFrameEnum;
import com.platform.model.Result;
import com.platform.util.JSONUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
@ -141,7 +128,7 @@ public class GcsService {
/** /**
* 线tcp * 线tcp
*/ */
public void gcsAuthRequestToCtrl() { public boolean gcsAuthRequestToCtrl() {
TcpRemoteAuthCacRequest tcpGcsAuthCacRequest = new TcpRemoteAuthCacRequest(); TcpRemoteAuthCacRequest tcpGcsAuthCacRequest = new TcpRemoteAuthCacRequest();
ChannelFuture sendFuture = null; ChannelFuture sendFuture = null;
try { try {
@ -155,21 +142,24 @@ public class GcsService {
tcpGcsAuthCacRequest.setReadableDataBytesLength((byte) 0); tcpGcsAuthCacRequest.setReadableDataBytesLength((byte) 0);
tcpGcsAuthCacRequest.setReadableDataBytes(null); tcpGcsAuthCacRequest.setReadableDataBytes(null);
if (cacClient.channel.isActive()) { if (CacClient.channelIsActive()) {
sendFuture = cacClient.channel.writeAndFlush(tcpGcsAuthCacRequest).sync(); sendFuture = CacClient.sendMsgToCac(tcpGcsAuthCacRequest).sync();
if (sendFuture.isSuccess()){ if (sendFuture.isSuccess()){
log.info("[tcpAuth] 认证请求已成功发送发送,认证码:{}", GlobalData.AUTHORIZATION); log.info("[tcpAuth] 认证请求已成功发送发送,认证码:{}", GlobalData.AUTHORIZATION);
return true;
} }
} else { } else {
log.error("[tcpAuth] 和中心指控之间tcp连接异常tcp认证发送失败"); log.error("[tcpAuth] 和中心指控之间tcp连接异常tcp认证发送失败");
return false;
} }
} catch (Exception e) { } catch (Exception e) {
log.info("[tcpAuth] tcp认证数据构建失败 ", e); log.info("[tcpAuth] tcp认证数据构建失败 ", e);
} }
if (sendFuture == null || !sendFuture.isSuccess()) { // if (sendFuture == null || !sendFuture.isSuccess()) {
// 如果发送失败,则重复验证 // // 如果发送失败,则重复验证
cacClient.channel.eventLoop().schedule(this::gcsAuthRequestToCtrl, 3, TimeUnit.SECONDS); // cacClient.channel.eventLoop().schedule(this::gcsAuthRequestToCtrl, 3, TimeUnit.SECONDS);
} // }
return false;
} }

@ -1,6 +1,5 @@
package com.platform.cac; package com.platform.cac;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.platform.cac.tcp.CacClient; import com.platform.cac.tcp.CacClient;
@ -10,8 +9,6 @@ import com.platform.info.enums.UavTypeEnum;
import com.platform.info.mapping.HaborUavMap; import com.platform.info.mapping.HaborUavMap;
import com.platform.info.mapping.UavIdMap; import com.platform.info.mapping.UavIdMap;
import com.platform.model.DirectControlUavParam; import com.platform.model.DirectControlUavParam;
import com.platform.model.Result;
import com.platform.util.JSONUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -55,7 +52,8 @@ public class RemoteService {
bufToRemote.writeByte(0x01); bufToRemote.writeByte(0x01);
bufToRemote.writeInt(0); bufToRemote.writeInt(0);
bufToRemote.writeByte(0x00); bufToRemote.writeByte(0x00);
cacClient.channel.writeAndFlush(bufToRemote); // cacClient.channel.writeAndFlush(bufToRemote);
CacClient.sendMsgToCac(bufToRemote);
return true; return true;
} catch (Exception e) { } catch (Exception e) {
log.error("运管报文回复失败", e); log.error("运管报文回复失败", e);
@ -64,42 +62,21 @@ public class RemoteService {
} }
/** /**
* fkIduavId *
* @param fkUavId * @return haborSn
* @return uavId uavId
*/ */
// public String queryUavId(int fkUavId) { public void queryCacDirectControlUavInfo(String haborSn) {
// JSONObject body = new JSONObject(); DirectControlUavParam info = CacHpApi.queryCacDirectControlUav(haborSn);
// body.put("flightControlSn", String.valueOf(fkUavId)); if (info == null) {
// if (GlobalData.UAV_TYPE == null ) { return;
// log.error("uav_type未知, 查询无人机ID失败"); }
// return null; UavIdMap.addMap(UavTypeEnum.getByRemoteCode(info.getUavType()), Integer.parseInt(info.getFlightControlSn()), info.getUavId());
// } }
// body.put("uavType", GlobalData.UAV_TYPE.getRemoteCode());
// body.put("gcsId", GCS_ID);
// log.debug("查询无人机id: request body: {}", body);
//
// String uavId = null;
// JSONObject responseJson = JSON.parseObject(CacHpApi.queryUavId(body.toString()));
// if (responseJson!= null && responseJson.containsKey("data")) {
// uavId = responseJson.getString("data");
// if (uavId == null){
// log.error("查询无人机ID失败fkId={}找不到对应的uavId", fkUavId);
// } else {
// UavIdMap.addMap(fkUavId, uavId);
// }
// } else {
// log.error("查询无人机ID请求失败{}", responseJson);
// }
//
// return uavId;
// }
/** /**
* * ,
*/ */
public void queryCacDirectControlMapping() { public void queryCacDirectControlMapping() {
// todo 每次申请都查一次映射关系
JSONObject body = new JSONObject(); JSONObject body = new JSONObject();
body.put("gcsId", GlobalData.GCS_ID); body.put("gcsId", GlobalData.GCS_ID);
log.debug("查询全量无人机映射关系request body: {}", body); log.debug("查询全量无人机映射关系request body: {}", body);

@ -17,7 +17,6 @@ import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder; import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.timeout.IdleStateHandler; import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.ResourceLeakDetector;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -45,7 +44,7 @@ public class CacClient {
static EventLoopGroup eventLoopGroup = new NioEventLoopGroup(); static EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
private Bootstrap bootstrap = new Bootstrap(); private Bootstrap bootstrap = new Bootstrap();
public Channel channel; private static Channel channel;
@Resource @Resource
GcsService gcsService; GcsService gcsService;
@ -108,6 +107,17 @@ public class CacClient {
}); });
} }
public static ChannelFuture sendMsgToCac(Object msg) {
if (channelIsActive()){
return CacClient.channel.writeAndFlush(msg);
}
return null;
}
public static boolean channelIsActive() {
return CacClient.channel.isActive();
}
@PreDestroy @PreDestroy
private void end() { private void end() {
eventLoopGroup.shutdownGracefully(); eventLoopGroup.shutdownGracefully();

@ -36,8 +36,7 @@ public class CacConnectionHandler extends ChannelInboundHandlerAdapter {
@Resource @Resource
GcsService gcsService; GcsService gcsService;
@Value("${airport.enable:#{false}}")
boolean isAirport = false;
/** /**
* *
*/ */
@ -47,8 +46,6 @@ public class CacConnectionHandler extends ChannelInboundHandlerAdapter {
int port = ipSocket.getPort(); int port = ipSocket.getPort();
String host = ipSocket.getHostString(); String host = ipSocket.getHostString();
log.info("与中心指控{}:{}建立tcp连接!", host, port); log.info("与中心指控{}:{}建立tcp连接!", host, port);
cacClient.channel = ctx.channel();
log.info("地面站发送上线请求"); log.info("地面站发送上线请求");
if (gcsService.gcsSignInRequest()) { if (gcsService.gcsSignInRequest()) {
gcsService.gcsAuthRequestToCtrl(); gcsService.gcsAuthRequestToCtrl();

@ -182,7 +182,7 @@ public class ClientController {
* *
* @return * @return
*/ */
@GetMapping("/mapping/get") @GetMapping("/mapping/many")
public Map<String, List<String>> getTargetListMap() { public Map<String, List<String>> getTargetListMap() {
return sessionCache.mappingListMap; return sessionCache.mappingListMap;
} }

@ -9,25 +9,31 @@ import lombok.Getter;
*/ */
@Getter @Getter
public enum ClientTypeEnum { public enum ClientTypeEnum {
// /**
GCS((short) 0xCC06, (short) 0xCC06,"地面站"), *
AUTO_AIRPORT((short) 0xCC07, (short) 0xCC07,"自动化机场"), */
UNKNOWN(-1, (short) 0, (short) 0,"未知类型"),
// BOX(0, (short) 0x7479, (short) 0x6A77,"云盒"),
// GCS(1, (short) 0xCC06, (short) 0xCC06,"地面站(云盒通信)"),
// FK(2, (short) 0x55AA, (short) 0x55AA,"飞控"),
// GCS_WT(3, (short) 0xAA55, (short) 0xAA55,"地面站(卫通通信)"),
HABOR(4, null, null,"哈勃终端"),
; ;
private final short readHead; private final Integer code;
private final short writeHead; private final Short readHead;
private final Short writeHead;
private final String info; private final String info;
ClientTypeEnum(short readHead, short writeHead, String info) { ClientTypeEnum(int code, Short readHead, Short writeHead, String info) {
this.code = code;
this.readHead = readHead; this.readHead = readHead;
this.writeHead = writeHead; this.writeHead = writeHead;
this.info = info; this.info = info;
} }
public static ClientTypeEnum getByCode(int code) {
public static ClientTypeEnum getByHead(int head) {
for (ClientTypeEnum clientTypeEnum : ClientTypeEnum.values()) { for (ClientTypeEnum clientTypeEnum : ClientTypeEnum.values()) {
if (clientTypeEnum.getReadHead() == head) { if (clientTypeEnum.getCode() == code) {
return clientTypeEnum; return clientTypeEnum;
} }
} }

@ -1,65 +0,0 @@
package com.platform.info.enums;
/**
* @Author : shiyi
* @Date : 2024/1/2 13:46
* @Description :
*/
public enum GcsFrameEnum implements IClientFrameEnum {
// 与地面站之间TCP传输的报文类型枚举
LOGIN((byte) 0x00, "接入校验"),
TELEMETRY_DATA_TRANSFER((byte) 0x02, "遥测数据透传"),
HEART_BEAT((byte) 0x03, "心跳"),
CONTROL_APPLY((byte) 0x04, "控制权申请,运管在控无人机更新"),
CONTROL_FREE((byte) 0x0A, "控制权释放"),
CONTROL_ISSUE((byte) 0x0B, "控制权下发"),
CONTROL_RECEIVE((byte) 0x0C, "控制权接收"),
ISSUED_COMMAND((byte) 0x05, "通知指令下发"),
RECONNECT((byte) 0x06, "地面站重连"),
SNAPSHOT((byte) 0x07, "发送地面站快照"),
SIGN_IN((byte) 0x08, "连接中心指控"),
SIGN_OUT((byte) 0x09, "断开中心指控"),
COMMAND_NOTIFY((byte) 0x0D, "遥控指令回报"),
UPDATE_UAV_CONTROL((byte) 0x0E, "中心指控在控无人机更新"),
QUERY_COMMAND((byte) 0x20, "查询指令是否完成"),
UAV_MASTER_CONTROL((byte) 0x21, "地面站主控通知"),
UPDATE_REMOTE_CONTROL_STATE((byte) 0x22, "远程控制状态"),
QUERY_REMOTE_CONTROL_STATE_REPLY((byte) 0x23, "查询远程控制状态"),
STREAM_PUSH((byte) 0x32, "视频流推送"),
;
private final byte code;
private final String info;
GcsFrameEnum(byte code, String info) {
this.code = code;
this.info = info;
}
public byte getCode() {
return code;
}
public String getInfo() {
return info;
}
public static GcsFrameEnum getByCode(byte frameCode) {
for (GcsFrameEnum enums : GcsFrameEnum.values()) {
if (enums.code == frameCode) {
return enums;
}
}
return null;
}
@Override
public ClientTypeEnum getClientEnum() {
return ClientTypeEnum.GCS;
}
}

@ -0,0 +1,34 @@
package com.platform.service;
import com.platform.info.enums.ClientTypeEnum;
import com.platform.service.clientmanage.BaseClient;
import com.platform.service.clientmanage.ClientManager;
import com.platform.service.clientmanage.HaborClient;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @Author : shiyi
* @Date : 2025/1/15 16:19
* @Description :
*/
@ChannelHandler.Sharable @Component @Slf4j
public class CacDataRouterHandler extends SimpleChannelInboundHandler<ByteBuf> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
BaseClient client = ClientManager.getClient(ctx.channel());
if (client == null || client.getClientType() != ClientTypeEnum.HABOR) {
return;
}
HaborClient haborClient = (HaborClient) client;
haborClient.sendToCac(msg);
log.info("数据({}bytes): {}", msg.readableBytes(), ByteBufUtil.hexDump(msg));
// ctx.writeAndFlush(msg);
}
}

@ -1,45 +1,45 @@
package com.platform.service; package com.platform.service;
import com.platform.cac.RemoteService;
import com.platform.config.ServiceConfig; import com.platform.config.ServiceConfig;
import com.platform.util.CRCUtil; import com.platform.info.mapping.HaborUavMap;
import com.platform.util.ControlDevice; import com.platform.service.clientmanage.BaseClient;
import com.platform.util.SessionCache; import com.platform.service.clientmanage.ClientManager;
import com.platform.util.StringUtil; import com.platform.service.clientmanage.HaborClient;
import com.platform.util.*;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.Channel; import io.netty.channel.*;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState; import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent; import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import java.nio.ByteBuffer;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
@Slf4j @Slf4j @Component @ChannelHandler.Sharable
public class InMessageHandler extends ChannelInboundHandlerAdapter { public class InMessageHandler extends ChannelInboundHandlerAdapter {
private boolean debug; private boolean debug;
private StringUtil stringUtil; private StringUtil stringUtil;
private SessionCache sessionCache; private SessionCache sessionCache;
@Autowired
RemoteService remoteService;
private static final int head1 = 0xAA; private static final int head1 = 0xAA;
private static final int head2 = 0x44; private static final int head2 = 0x44;
private static final int head3 = 0x61; private static final int head3 = 0x61;
private static final int lengthLow = 0x03; private static final int lengthLow = 0x03;
private static final int lengthHigh = 0x00; private static final int lengthHigh = 0x00;
public InMessageHandler() { @Autowired
}
public InMessageHandler(SessionCache sessionCache, StringUtil stringUtil, ServiceConfig config) { public InMessageHandler(SessionCache sessionCache, StringUtil stringUtil, ServiceConfig config) {
this.stringUtil = stringUtil; this.stringUtil = stringUtil;
this.sessionCache = sessionCache; this.sessionCache = sessionCache;
@ -77,15 +77,16 @@ public class InMessageHandler extends ChannelInboundHandlerAdapter {
public void channelRead(ChannelHandlerContext ctx, Object msg0) throws Exception { public void channelRead(ChannelHandlerContext ctx, Object msg0) throws Exception {
String address = getAddress(ctx); String address = getAddress(ctx);
byte[] msg = (byte[])msg0; // TODO 2025/1/16: // byte[] msg = (byte[])msg0; // TODO 2025/1/16:
ByteBuf msgBuf = (ByteBuf) msg0;
Byte fkID = msg[4];
//上线握手阶段 //上线握手阶段
if (!sessionCache.addToSnMap.containsKey(address)) { // 判断连接是否已经建立,如未建立则先握手 if (!sessionCache.addToSnMap.containsKey(address)) { // 判断连接是否已经建立,如未建立则先握手
byte[] msg = new byte[msgBuf.readableBytes()];
msgBuf.readBytes(msg);
log.info("【接收握手原数据】 " + ByteUtils.bytes2HexString(msg));
// 握手数据不满足协议长度,立即返回 // 握手数据不满足协议长度,立即返回
String d = stringUtil.bytesToHexString(msg); // String d = stringUtil.bytesToHexString(msg);
log.info("【接收握手原数据】 " + d);
// byte[] crc = CRC.crc16(msg, 2, 16); // byte[] crc = CRC.crc16(msg, 2, 16);
// if (crc[0] != msg[18] || crc[1] != msg[19]){ // if (crc[0] != msg[18] || crc[1] != msg[19]){
// log.warn("握手数据crc校验错误期望crc " + stringTool.bytesToHexString(crc) + ", 实际收到 " + d); // log.warn("握手数据crc校验错误期望crc " + stringTool.bytesToHexString(crc) + ", 实际收到 " + d);
@ -97,17 +98,23 @@ public class InMessageHandler extends ChannelInboundHandlerAdapter {
String type = new String(msg, 5, 1, Charset.defaultCharset()); String type = new String(msg, 5, 1, Charset.defaultCharset());
// deviceCode 终端设备取后六位 // deviceCode 终端设备取后六位
String deviceCode; String deviceCode;
String typeName;
if(type.equals("1")) { if(type.equals("1")) {
// 首先读取deviceCode 哈勃deviceCode是前面6个byte // 首先读取deviceCode 哈勃deviceCode是前面6个byte
typeName = "地面站";
deviceCode = new String(msg, 6, 12, Charset.defaultCharset()).toUpperCase(); // 地面站 deviceCode = new String(msg, 6, 12, Charset.defaultCharset()).toUpperCase(); // 地面站
protocolAck(ctx, (byte)1);
} else if (type.equals("2")){ } else if (type.equals("2")){
deviceCode = new String(msg, 12, 6 , Charset.defaultCharset()).toUpperCase(); // 哈勃 typeName = "哈勃终端";
deviceCode = new String(msg, 12, 6 , Charset.defaultCharset()).toUpperCase(); // 哈勃上线
protocolAck(ctx, (byte)1);
haborSignIntoCac(deviceCode, ctx.channel());
} else { } else {
protocolAck(ctx,(byte) 0); protocolAck(ctx,(byte) 0); // 握手失败
return; return;
} }
log.info("【读取握手数据】======> deviceCode = " + deviceCode +" type = " + type); log.info("【读取握手数据】======> deviceCode = " + deviceCode +" type = " + typeName);
//删除旧的连接。如果存在异常断开的时候,旧连接并未清除。这里在上线的时候检查并清除 //删除旧的连接。如果存在异常断开的时候,旧连接并未清除。这里在上线的时候检查并清除
String addr0 = sessionCache.snToAddMap.get(deviceCode); String addr0 = sessionCache.snToAddMap.get(deviceCode);
@ -122,13 +129,17 @@ public class InMessageHandler extends ChannelInboundHandlerAdapter {
sessionCache.snToAddMap.put(deviceCode, address); // mac- ip sessionCache.snToAddMap.put(deviceCode, address); // mac- ip
sessionCache.tcpSession.put(address, ctx.channel()); // ip - channel sessionCache.tcpSession.put(address, ctx.channel()); // ip - channel
protocolAck(ctx,(byte) 1);
return; //握手结束立即返回 return; //握手结束立即返回
}else{ // 如果已经建立连接,判断是否是地面站的控制信息 }
if(msg[0]==(byte)0xCC&msg[1]==(byte)0x06&msg[2]==(byte)0x04){
/*
// NOTE 2025/1/17: 线上版本的逻辑没有在控无人机更新
else {
// 如果已经建立连接,判断是否是地面站的控制信息
if(msgBuf.getByte(0)==(byte)0xCC&msgBuf.getByte(1)==(byte)0x06&msgBuf.getByte(2)==(byte)0x04){
byte[] content = new byte[10]; byte[] content = new byte[10];
content[0] = (byte)0x01; content[0] = (byte)0x01;
int uavNum = StringUtil.byteToInt(msg[3]); int uavNum = StringUtil.byteToInt(msgBuf.getByte(3));
String source = sessionCache.addToSnMap.get(address); //mac String source = sessionCache.addToSnMap.get(address); //mac
ControlDevice.clearCurrenCtrDeviceByMac(source); ControlDevice.clearCurrenCtrDeviceByMac(source);
int errNum = 0; int errNum = 0;
@ -136,67 +147,77 @@ public class InMessageHandler extends ChannelInboundHandlerAdapter {
// List<String> deviceSns = new ArrayList<>(); // List<String> deviceSns = new ArrayList<>();
for(int i=0;i<uavNum;i++){ for(int i=0;i<uavNum;i++){
try{ try{
String deviceSn= ControlDevice.getSn(msg[4+i]); // 根据fkId获取哈勃序列号 String deviceSn= ControlDevice.getSn(msgBuf.getByte(4+i)); // 根据fkId获取哈勃序列号
log.warn("当前"+address+"在控无人机更新为:"+ deviceSn); log.warn("当前"+address+"在控无人机更新为:"+ deviceSn);
ControlDevice.currenCtrDevice.put(deviceSn,source); //mac - 当前mac控制的设备编号 ControlDevice.currenCtrDevice.put(deviceSn,source); //mac - 当前mac控制的设备编号
}catch (Exception e){ }catch (Exception e){
errNum=errNum+1; errNum=errNum+1;
content[0] = (byte)0x00; content[0] = (byte)0x00;
content[1] = (byte)errNum; content[1] = (byte)errNum;
content[errNum+1] = msg[4+i]; content[errNum+1] = msgBuf.getByte(4+i);
} }
// deviceSns.add(deviceSn); // deviceSns.add(deviceSn);
} }
response(ctx,content); response(ctx,content);
return; return;
} }
} }
*/
// 已经建立连接且非地面站控制信息,则进行数据转发
// 已经建立连接且非地面站控制信息,则进行数据转发
else {
if (debug) { if (debug) {
log.info("【转发数据】======> " + stringUtil.bytesToHexString(msg)); log.info("【转发数据】======> " + ByteBufUtil.hexDump(msgBuf));
} }
String sn = sessionCache.addToSnMap.get(address); //sn =mac String sn = sessionCache.addToSnMap.get(address); //sn =mac
//监控相关 //监控相关
if(!sessionCache.snToMonitorMap.isEmpty()){ if (!sessionCache.snToMonitorMap.isEmpty()) {
Optional<Channel> sendChannelOpt = sessionCache.findMonitorChannel (address); Optional<Channel> sendChannelOpt = sessionCache.findMonitorChannel(address);
if (sendChannelOpt.isPresent()) { if (sendChannelOpt.isPresent()) {
Channel sendChannel = sendChannelOpt.get(); Channel sendChannel = sendChannelOpt.get();
log.info("(监控)out channel: " + sendChannel.remoteAddress()+"(" + sessionCache.addToSnMap.get(sendChannel.remoteAddress().toString().replace("/","")) log.info("(监控)out channel: " + sendChannel.remoteAddress() + "(" + sessionCache.addToSnMap.get(sendChannel.remoteAddress().toString().replace("/", ""))
+ ") 即将发送 --- " + "in channel: " + ctx.channel().remoteAddress()+"(" + sn + ")发来的TCP消息"); + ") 即将发送 --- " + "in channel: " + ctx.channel().remoteAddress() + "(" + sn + ")发来的TCP消息");
sendChannel.writeAndFlush(msg0); sendChannel.writeAndFlush(msgBuf.retain());
} }
} }
if (!sessionCache.mappingListMap.isEmpty()){ if (!sessionCache.mappingListMap.isEmpty() && sessionCache.mappingListMap.get(sn) != null) {
if (sessionCache.mappingListMap.get(sn)!=null){
List<Channel> sendChannelOpt = sessionCache.findMappingTargetChannel(sn); // 设备编号对应的通道 List<Channel> sendChannelOpt = sessionCache.findMappingTargetChannel(sn); // 设备编号对应的通道
log.info("send channel count = " + sendChannelOpt.size()); log.info("send channel count of {} = {}", sn, sendChannelOpt.size());
sendChannelOpt.forEach(sendChannel -> { sendChannelOpt.forEach(sendChannel -> {
log.info("out channel: " + sendChannel.remoteAddress()+"(" + sessionCache.addToSnMap.get(sendChannel.remoteAddress().toString().replace("/","")) // log.info("out channel: " + sendChannel.remoteAddress() + "(" + sessionCache.addToSnMap.get(sendChannel.remoteAddress().toString().replace("/", "")) + ")即将发送 --- " + "in channel: " + ctx.channel().remoteAddress() + "(" + sn + ")发来的TCP消息");
+ ")即将发送 --- " + "in channel: " + ctx.channel().remoteAddress()+"(" + sn + ")发来的TCP消息");
// TODO 2025/1/17: 大量打印日志消耗性能,需要优化,用更合理的方式显示通信对象状态
log.debug("out channel: {} ({})即将发送 --- from channel: {} ({})", sendChannel.remoteAddress(), sessionCache.addToSnMap.get(sendChannel.remoteAddress().toString().replace("/", "")), ctx.channel().remoteAddress(), sn);
// 如果需要发送则增加一次计数引用便于中心指控继续使用buffer
sendChannel.writeAndFlush(msgBuf.retain());
/*
// NOTE 2025/1/17: 线上版本的逻辑没有在控无人机更新, 这里也不再判断飞控的控制关系,直接转发
String sendIp = sessionCache.findSnByChannel(sendChannel); // 通道找到IP String sendIp = sessionCache.findSnByChannel(sendChannel); // 通道找到IP
String sendSn = sessionCache.addToSnMap.get(sendIp); // IP找到设备编号 String sendSn = sessionCache.addToSnMap.get(sendIp); // IP找到设备编号
if(sendSn.length()==6){ if (sendSn.length() == 6) {
// 发送给哈勃
List<String> devices = ControlDevice.getCurrenCtrDevicesByMac(sn); List<String> devices = ControlDevice.getCurrenCtrDevicesByMac(sn);
if(devices.contains(sendSn)||devices.contains("all")){ if (devices.contains(sendSn) || devices.contains("all")) {
sendChannel.writeAndFlush(msg0); msgBuf.retain();
sendChannel.writeAndFlush(msgBuf.retain());
} }
} }
if(sendSn.length()==12) { if (sendSn.length() == 12) {
sendChannel.writeAndFlush(msg0); // 发送给地面站
sendChannel.writeAndFlush(msgBuf.retain());
return;
} }
*/
}); });
return;
} }
// else {
// super.channelRead(ctx, msgBuf);
// }
} }
/* String sn = global.addToSnMap.get(address); /* String sn = global.addToSnMap.get(address);
if (!global.snList.isEmpty()){ if (!global.snList.isEmpty()){
if (global.snList.contains(sn)){ if (global.snList.contains(sn)){
@ -222,17 +243,45 @@ public class InMessageHandler extends ChannelInboundHandlerAdapter {
sendChannel.writeAndFlush(msg0); sendChannel.writeAndFlush(msg0);
}*/ }*/
super.channelRead(ctx, msg0);
// 以上是哈勃原来的处理逻辑,完成之后向下游传递,进入中心指控的处理逻辑
ctx.channel().writeAndFlush(msgBuf.retain()); // 模拟转发过程
super.channelRead(ctx, msgBuf);
}
/**接入中心指控系统, 上线*/
private void haborSignIntoCac(String haborSn, Channel channel) {
if (!HaborUavMap.haborIsControllable(haborSn)) {
remoteService.queryCacDirectControlUavInfo(haborSn); // 缓存没查到就向中心指控查一遍,查不到就直接退出
if (!HaborUavMap.haborIsControllable(haborSn)) {
log.info("[cac] 中心指控没有哈勃终端-{}的信息,跳过上线", haborSn);
return;
}
}
if (ClientManager.getClient(channel) != null) {
return;
}
// 新建哈勃客户端并加入管理
ClientManager.addAndOnline(new HaborClient(haborSn, channel));
// channel.pipeline().addAfter(); // 上线后添加相关处理器
}
/**哈勃下线*/
private void haborSignOutOfCac(Channel channel) {
BaseClient client = ClientManager.getClient(channel);
if (client != null) {
ClientManager.removeAndOffline(client);
}
} }
private void protocolAck(ChannelHandlerContext ctx, byte data){ private void protocolAck(ChannelHandlerContext ctx, byte success){
byte[] ack = new byte[8]; byte[] ack = new byte[8];
ack[0] = (byte) head1; ack[0] = (byte) head1;
ack[1] = (byte) head2; ack[1] = (byte) head2;
ack[2] = (byte) head3; ack[2] = (byte) head3;
ack[3] = (byte) lengthLow; ack[3] = (byte) lengthLow;
ack[4] = (byte) lengthHigh; ack[4] = (byte) lengthHigh;
ack[5] = data; ack[5] = success;
byte[] crc = CRCUtil.crc16(ack, 2, 4); byte[] crc = CRCUtil.crc16(ack, 2, 4);
ack[6] = crc[0]; ack[6] = crc[0];
ack[7] = crc[1]; ack[7] = crc[1];
@ -282,6 +331,7 @@ public class InMessageHandler extends ChannelInboundHandlerAdapter {
sessionCache.snToAddMap.remove(sn); sessionCache.snToAddMap.remove(sn);
} }
} }
haborSignOutOfCac(ctx.channel());
super.handlerRemoved(ctx); super.handlerRemoved(ctx);
} }

@ -7,8 +7,6 @@ import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*; import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.bytes.ByteArrayDecoder;
import io.netty.handler.codec.bytes.ByteArrayEncoder;
import io.netty.handler.timeout.IdleStateHandler; import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -30,6 +28,11 @@ public class ServerService {
@Autowired @Autowired
private SessionCache sessionCache; private SessionCache sessionCache;
@Autowired
InMessageHandler inMessageHandler;
@Autowired
CacDataRouterHandler cacDataRouterHandler;
private void startServer() { private void startServer() {
//服务端需要2个线程组 boss处理客户端连接 work进行客服端连接之后的处理 //服务端需要2个线程组 boss处理客户端连接 work进行客服端连接之后的处理
log.warn("【读取配置端口】 端口 = " + config.getPort()); log.warn("【读取配置端口】 端口 = " + config.getPort());
@ -43,11 +46,16 @@ public class ServerService {
bootstrap.childHandler(new ChannelInitializer<Channel>() { bootstrap.childHandler(new ChannelInitializer<Channel>() {
@Override @Override
protected void initChannel(Channel channel) throws Exception { protected void initChannel(Channel channel) throws Exception {
channel.pipeline().addLast(new ByteArrayDecoder()); // revise by shiyi: 解码移动到InMessageHandler中 // channel.pipeline().addLast(new ByteArrayDecoder()); // revise by shiyi: 解码移动到InMessageHandler中
channel.pipeline().addLast(new ByteArrayEncoder()); // channel.pipeline().addLast(new ByteArrayEncoder());
if (!config.isDebug()) {
channel.pipeline().addLast(new IdleStateHandler(0, 0, channel.pipeline().addLast(new IdleStateHandler(0, 0,
60 * 24, TimeUnit.MINUTES)); 60 * 24, TimeUnit.MINUTES));
channel.pipeline().addLast(new InMessageHandler(sessionCache, stringUtil, config)); }
channel.pipeline()
.addLast("OriginalHandler", inMessageHandler) // 原始哈勃消息解码器
.addLast("TelemetryDecoder", new TelemetryDecoder()) // eb90数据分离
.addLast("CacDataRouter", cacDataRouterHandler); // cac数据转发
} }
}); });

@ -0,0 +1,36 @@
package com.platform.service;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import org.springframework.stereotype.Component;
/**
* @Author : shiyi
* @Date : 2025/1/15 16:00
* @Description : EB 90EB 90,
*/
public class TelemetryDecoder extends DelimiterBasedFrameDecoder {
public static final int MAX_FRAME_LENGTH = 1024;
public static ByteBuf DELIMITER = Unpooled.wrappedBuffer(new byte[]{(byte) 0xeb, (byte) 0x90});
public static ByteBuf HEAD = Unpooled.wrappedBuffer(new byte[]{(byte) 0xeb, (byte) 0x90});
public TelemetryDecoder() {
super(MAX_FRAME_LENGTH, DELIMITER);
}
// public TelemetryDecoder(int maxFrameLength, ByteBuf delimiter) {
// super(MAX_FRAME_LENGTH, DELIMITER);
// }
@Override
protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
ByteBuf decodeBuf = (ByteBuf) super.decode(ctx, buffer);
if (decodeBuf == null || decodeBuf.readableBytes()==0) {
return null; // 如果 decodeBuf 为 null直接返回 null
}
HEAD.retain();
return Unpooled.wrappedBuffer(HEAD, decodeBuf); // 只有在 decodeBuf 不为 null 时,才补上帧头
// return decodeBuf;
}
}

@ -0,0 +1,80 @@
package com.platform.service.clientmanage;
import com.platform.info.enums.ClientTypeEnum;
import io.netty.channel.Channel;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import java.net.InetSocketAddress;
import java.util.Objects;
/**
* @Author : shiyi
* @Date : 2023/12/18 14:51
* @Description :
*/
@Slf4j @Getter
public abstract class BaseClient {
protected Channel channel;
protected String sn; // 客户端唯一编号
protected String ip;
protected String port;
protected String serverPort;
// protected boolean online = false;
public BaseClient(String sn, Channel channel) {
this.sn = sn;
this.channel = channel;
InetSocketAddress remoteAddress = (InetSocketAddress) channel.remoteAddress();
InetSocketAddress localAddress = (InetSocketAddress) channel.localAddress();
ip = remoteAddress.getHostString();
port = String.valueOf(remoteAddress.getPort());
serverPort = String.valueOf(localAddress.getPort());
}
/**
* 线
*/
public abstract boolean online();
/**
* 线
*/
public abstract boolean offline();
// public ChannelFuture sendMessage(byte frameType, byte[] data) {
// return ClientManager.send(getClientType(), channel, frameType, data);
// }
public abstract ClientTypeEnum getClientType();
@Override
public String toString() {
return "BaseClient{" +
"channel=" + channel.id() +
", sn='" + sn + '\'' +
", ip='" + ip + '\'' +
", port=" + port +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
BaseClient that = (BaseClient) o;
return Objects.equals(sn, that.sn);
}
@Override
public int hashCode() {
return Objects.hash(sn);
}
}

@ -0,0 +1,142 @@
package com.platform.service.clientmanage;
import com.platform.info.enums.ClientTypeEnum;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
/**
* @Author : shiyi
* @Date : 2023/12/21 17:23
* @Description :
*/
public class ClientManager{
public static final AttributeKey<BaseClient> CLIENT_ATTRIBUTE_KEY = AttributeKey.valueOf("client");
/**
*
*/
private static final Map<String, BaseClient> CLIENT_MAP = new ConcurrentHashMap<>();
public static boolean isLogin(String clientSn){
return null != getClient(clientSn);
}
/** 线
*/
public static void addAndOnline(BaseClient client) {
client.getChannel().attr(CLIENT_ATTRIBUTE_KEY).set(client);
boolean online = client.online();
log.info("[cac] {} {} 上线{}", client.getClientType().getInfo(), client.getSn(), online?"成功":"失败");
// 即使上线失败仍然添加到map中虽然上线失败但是通道并不关闭
CLIENT_MAP.put(client.getSn(), client);
}
// /**
// * 更新客户端编号
// * @param client 指定客户端对象
// * @param newSn 需要更新的新编号
// */
// public static void updateClientSn(BaseClient client, String newSn){
// // 参数检查
// if (client == null || newSn == null || newSn.isEmpty()) {
// return;
// }
// // 获取旧的sn
// String oldSn = client.getSn();
// // 如果新旧sn相同无需更新
// if (oldSn.equals(newSn)) {
// return;
// }
// client.setSn(newSn);
// CLIENT_MAP.remove(oldSn);
// CLIENT_MAP.put(newSn, client);
// }
/** 线
* @param client
*/
public static void removeAndOffline(BaseClient client) {
remove(client);
boolean offline = client.offline();
log.info("[cac] {} {} 下线{}", client.getClientType().getInfo(), client.getSn(), offline ? "成功" : "失败");
client = null;
}
/** channel
*/
public static void remove(BaseClient client) {
CLIENT_MAP.remove(client.getSn());
Channel channel = client.getChannel();
channel.attr(ClientManager.CLIENT_ATTRIBUTE_KEY).set(null);
// channel.close(); // // TODO 2024/3/14: 是否要马上断开连接?
}
/**
* , null
*/
public static BaseClient getClient(String clientSn) throws NullPointerException{
return CLIENT_MAP.get(clientSn);
}
public static ArrayList<BaseClient> getClientGroup(ClientTypeEnum clientType) {
ArrayList<BaseClient> clientList = new ArrayList<>();
CLIENT_MAP.forEach((k, v) -> {
if (v.getClientType() == clientType) {
clientList.add(v);
}
});
return clientList;
}
/** Channel
*/
public static BaseClient getClient(Channel channel) {
return channel.attr(CLIENT_ATTRIBUTE_KEY).get();
}
/**
*
* @param clientType
* @param channel channel
* @param frameType
* @param data
* @return ChannelFuture
*/
// public static ChannelFuture send(ClientTypeEnum clientType, Channel channel, byte frameType, byte[] data) {
// NettyMessage message = NettyMessage.build(clientType);
// if (message == null) {
// log.error("[send] 错误的客户端类型,无法发送消息");
// return null;
// }
// message.setHead(clientType.getWriteHead());
// message.setType(frameType);
// message.setLength((short) data.length);
// message.setContent(data);
//
// return channel.writeAndFlush(message);
// }
//
//
// /** 发送消息给地面站
// */
// public static ChannelFuture sendToGcs(Channel channel, byte frameType, byte[] data) {
// BoxMessage message = new BoxMessage();
//
// message.setHead(ClientTypeEnum.GCS.getWriteHead());
// message.setType(frameType);
// message.setLength((short) data.length);
// message.setContent(data);
//
// return channel.writeAndFlush(message);
// }
}

@ -0,0 +1,69 @@
package com.platform.service.clientmanage;
import com.platform.cac.CacHpApi;
import com.platform.info.enums.ClientTypeEnum;
import com.platform.info.enums.UavTypeEnum;
import com.platform.model.DirectControlUavParam;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.PostConstruct;
/**
* @Author : shiyi
* @Date : 2024/9/18 10:38
* @Description :
*/
@Slf4j @Getter
public class HaborClient extends BaseClient{
// static HashSet<String> fkSnSet = new HashSet<>();
private UavTypeEnum uavType;
private byte fkId;
private String uavId;
public HaborClient(String sn, Channel channel) {
super(sn, channel);
// channel绑定客户端
channel.attr(ClientManager.CLIENT_ATTRIBUTE_KEY).setIfAbsent(this);
}
@Override
public boolean online() {
return CacHpApi.uavMasterControlNotify(uavId);
}
@Override
public boolean offline() {
return CacHpApi.uavPowerOff(uavId);
}
// @Override
// public ChannelFuture sendMessage(byte frameType, byte[] data) {
// return channel.writeAndFlush(Unpooled.wrappedBuffer(data));
// }
@Override
public ClientTypeEnum getClientType() {
return ClientTypeEnum.HABOR;
}
/**
*
* @param srcBuf ByteBuf
*/
public void sendToCac(ByteBuf srcBuf) {
}
private void buildFrameToCac(ByteBuf srcBuf, ByteBuf dstBuf) {
// TODO: 2024/9/18: 构建飞控数据帧到中心指控
}
private void queryCacDirectControlUavInfo() {
DirectControlUavParam directControlUavParam = CacHpApi.queryCacDirectControlUav(sn);
}
}

@ -29,15 +29,14 @@ public class JSONUtils {
/** /**
* javaBean,list,array convert to json string * javaBean,list,array convert to json string
*/ */
public static String obj2json(Object obj) throws Exception { public static String obj2json(Object obj) throws IOException {
return objectMapper.writeValueAsString(obj); return objectMapper.writeValueAsString(obj);
} }
/** /**
* json string convert to javaBean * json string convert to javaBean
*/ */
public static <T> T json2obj(String jsonStr, Class<T> clazz) public static <T> T json2obj(String jsonStr, Class<T> clazz) throws IOException {
throws Exception {
return objectMapper.readValue(jsonStr, clazz); return objectMapper.readValue(jsonStr, clazz);
} }
/** /**
@ -53,7 +52,7 @@ public class JSONUtils {
* json string convert to map * json string convert to map
*/ */
public static <T> Map<String, Object> json2map(String jsonStr) public static <T> Map<String, Object> json2map(String jsonStr)
throws Exception { throws IOException {
return objectMapper.readValue(jsonStr, Map.class); return objectMapper.readValue(jsonStr, Map.class);
} }
@ -61,7 +60,7 @@ public class JSONUtils {
* json string convert to map with javaBean * json string convert to map with javaBean
*/ */
public static <T> Map<String, T> json2map(String jsonStr, Class<T> clazz) public static <T> Map<String, T> json2map(String jsonStr, Class<T> clazz)
throws Exception { throws IOException {
Map<String, Map<String, Object>> map = (Map<String, Map<String, Object>>) objectMapper.readValue(jsonStr, Map<String, Map<String, Object>> map = (Map<String, Map<String, Object>>) objectMapper.readValue(jsonStr,
new TypeReference<Map<String, T>>() { new TypeReference<Map<String, T>>() {
}); });
@ -76,7 +75,7 @@ public class JSONUtils {
* json array string convert to list with javaBean * json array string convert to list with javaBean
*/ */
public static <T> List<T> json2list(String jsonArrayStr, Class<T> clazz) public static <T> List<T> json2list(String jsonArrayStr, Class<T> clazz)
throws Exception { throws IOException {
List<Map<String, Object>> list = (List<Map<String, Object>>) objectMapper.readValue(jsonArrayStr, List<Map<String, Object>> list = (List<Map<String, Object>>) objectMapper.readValue(jsonArrayStr,
new TypeReference<List<T>>() { new TypeReference<List<T>>() {
}); });

@ -101,6 +101,7 @@ public class SessionCache {
return channel; return channel;
} }
/**根据sn获取对应需要转发的channel*/
public List<Channel> findMappingTargetChannel(String sSn) { public List<Channel> findMappingTargetChannel(String sSn) {
List<Channel> channel = new ArrayList<>(); List<Channel> channel = new ArrayList<>();
List<String> tSn = mappingListMap.get(sSn); // 设备的映射关系 List<String> tSn = mappingListMap.get(sSn); // 设备的映射关系

Loading…
Cancel
Save