From 46bf0a6363435927b9d65550c18a2d6509fbbdb0 Mon Sep 17 00:00:00 2001 From: shiyi Date: Fri, 17 Jan 2025 15:52:17 +0800 Subject: [PATCH] =?UTF-8?q?[v0.0.1]=20=E5=B0=86=E5=93=88=E5=8B=83=E5=8E=9F?= =?UTF-8?q?=E6=9C=89=E9=80=BB=E8=BE=91=E5=92=8C=E7=BA=BF=E4=B8=8A=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E5=AF=B9=E9=BD=90=EF=BC=88=E4=B8=8D=E5=A4=84=E7=90=86?= =?UTF-8?q?=E5=9C=B0=E9=9D=A2=E7=AB=99=E7=9A=84=E5=9C=A8=E6=8E=A7=E6=97=A0?= =?UTF-8?q?=E4=BA=BA=E6=9C=BA=E6=9B=B4=E6=96=B0=E4=BF=A1=E6=81=AF=EF=BC=8C?= =?UTF-8?q?=E7=9B=B4=E6=8E=A5=E8=BD=AC=E5=8F=91=E6=95=B0=E6=8D=AE=EF=BC=89?= =?UTF-8?q?=EF=BC=9B=E5=93=88=E5=8B=83=E5=AE=A2=E6=88=B7=E7=AB=AF=E7=B1=BB?= =?UTF-8?q?=E7=AE=A1=E7=90=86=EF=BC=9BCacClient=20channel=E9=9A=90?= =?UTF-8?q?=E8=97=8F=E5=B9=B6=E6=9A=B4=E9=9C=B2=E9=9D=99=E6=80=81channel?= =?UTF-8?q?=E5=BC=95=E7=94=A8=E4=BB=A5=E4=BE=9B=E5=93=88=E5=8B=83=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E7=AB=AF=E8=BD=AC=E5=8F=91=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- src/main/java/com/platform/cac/CacHpApi.java | 72 ++++---- .../java/com/platform/cac/GcsService.java | 30 ++-- .../java/com/platform/cac/RemoteService.java | 47 ++--- .../java/com/platform/cac/tcp/CacClient.java | 14 +- .../cac/tcp/CacConnectionHandler.java | 5 +- .../platform/controller/ClientController.java | 2 +- .../platform/info/enums/ClientTypeEnum.java | 26 +-- .../com/platform/info/enums/GcsFrameEnum.java | 65 ------- .../service/CacDataRouterHandler.java | 34 ++++ .../platform/service/InMessageHandler.java | 170 +++++++++++------- .../com/platform/service/ServerService.java | 22 ++- .../platform/service/TelemetryDecoder.java | 36 ++++ .../service/clientmanage/BaseClient.java | 80 +++++++++ .../service/clientmanage/ClientManager.java | 142 +++++++++++++++ .../service/clientmanage/HaborClient.java | 69 +++++++ .../java/com/platform/util/JSONUtils.java | 11 +- .../java/com/platform/util/SessionCache.java | 1 + 18 files changed, 584 insertions(+), 244 deletions(-) delete mode 100644 src/main/java/com/platform/info/enums/GcsFrameEnum.java create mode 100644 src/main/java/com/platform/service/CacDataRouterHandler.java create mode 100644 src/main/java/com/platform/service/TelemetryDecoder.java create mode 100644 src/main/java/com/platform/service/clientmanage/BaseClient.java create mode 100644 src/main/java/com/platform/service/clientmanage/ClientManager.java create mode 100644 src/main/java/com/platform/service/clientmanage/HaborClient.java diff --git a/pom.xml b/pom.xml index e77d43d..644eaef 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ io.netty netty-all - 4.1.86.Final + 4.1.34.Final com.alibaba diff --git a/src/main/java/com/platform/cac/CacHpApi.java b/src/main/java/com/platform/cac/CacHpApi.java index d7b52e8..78078e2 100644 --- a/src/main/java/com/platform/cac/CacHpApi.java +++ b/src/main/java/com/platform/cac/CacHpApi.java @@ -32,7 +32,7 @@ public class CacHpApi { @Value("${http-cac.host}") public void setHost(String host) { CacHpApi.HOST = host; - log.info(">>>中心指控地址 {}<<<", host); + log.info("[cac] >>>中心指控地址 {}<<<", host); } private static String postRequest(String api, String body) { @@ -42,10 +42,10 @@ public class CacHpApi { try { return HttpClientUtils.sendPost(path, body, header); } catch (ConnectException e) { - log.error("http请求超时: {}", e.getMessage()); + log.error("[cac] http请求超时: {}", e.getMessage()); return null; } catch (IOException e) { - log.error("http请求错误: {}", e.getMessage()); + log.error("[cac] http请求错误: {}", e.getMessage()); return null; } } @@ -56,10 +56,10 @@ public class CacHpApi { try { return HttpClientUtils.sendPost(path, body, header, Result.class); } catch (ConnectException e) { - log.error("http请求超时: {}", e.getMessage()); + log.error("[cac] http请求超时: {}", e.getMessage()); return null; } catch (Exception e) { - log.error("http请求错误: {}, body={}", e.getMessage(), body); + log.error("[cac] http请求错误: {}, body={}", e.getMessage(), body); return null; } } @@ -91,7 +91,7 @@ public class CacHpApi { final String gcsSignApi = HTFP_PATH + "/signIn"; Result result = postRequestAndGetResult(gcsSignApi, body); if (result == null || !result.isSuccess()) { - log.error("地面站上线请求失败: {}", result); + log.error("[cac] 地面站上线失败: {}", result); return null; } return result.getData().toString(); @@ -105,12 +105,43 @@ public class CacHpApi { // return postRequest(gcsSignApi, body); // } + /**无人机上线*/ + public static boolean uavMasterControlNotify(String uavId) { + final String gcsSignApi = HTFP_PATH + "/notifyGetUavControlRight"; + Map 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"; - return postRequest(gcsSignApi, body); + Map 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); } - /** - * 异常退出 - */ - 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); } - 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) { final String gcsSignApi = HTFP_PATH + "/replyUavControlRight"; return postRequest(gcsSignApi, body); diff --git a/src/main/java/com/platform/cac/GcsService.java b/src/main/java/com/platform/cac/GcsService.java index 9d4904d..3304651 100644 --- a/src/main/java/com/platform/cac/GcsService.java +++ b/src/main/java/com/platform/cac/GcsService.java @@ -1,32 +1,19 @@ package com.platform.cac; -import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.platform.cac.tcp.CacClient; import com.platform.cac.tcp.message.dataframe.RemoteTcpBaseDataFrame; import com.platform.cac.tcp.message.dataframe.send.TcpRemoteAuthCacRequest; 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.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.ChannelFuture; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.annotation.Resource; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; import java.util.concurrent.TimeUnit; /** @@ -141,7 +128,7 @@ public class GcsService { /** * 地面站上线tcp认证 */ - public void gcsAuthRequestToCtrl() { + public boolean gcsAuthRequestToCtrl() { TcpRemoteAuthCacRequest tcpGcsAuthCacRequest = new TcpRemoteAuthCacRequest(); ChannelFuture sendFuture = null; try { @@ -155,21 +142,24 @@ public class GcsService { tcpGcsAuthCacRequest.setReadableDataBytesLength((byte) 0); tcpGcsAuthCacRequest.setReadableDataBytes(null); - if (cacClient.channel.isActive()) { - sendFuture = cacClient.channel.writeAndFlush(tcpGcsAuthCacRequest).sync(); + if (CacClient.channelIsActive()) { + sendFuture = CacClient.sendMsgToCac(tcpGcsAuthCacRequest).sync(); if (sendFuture.isSuccess()){ log.info("[tcpAuth] 认证请求已成功发送发送,认证码:{}", GlobalData.AUTHORIZATION); + return true; } } else { log.error("[tcpAuth] 和中心指控之间tcp连接异常,tcp认证发送失败!"); + return false; } } catch (Exception e) { log.info("[tcpAuth] tcp认证数据构建失败 {}", e); } - if (sendFuture == null || !sendFuture.isSuccess()) { - // 如果发送失败,则重复验证 - cacClient.channel.eventLoop().schedule(this::gcsAuthRequestToCtrl, 3, TimeUnit.SECONDS); - } + // if (sendFuture == null || !sendFuture.isSuccess()) { + // // 如果发送失败,则重复验证 + // cacClient.channel.eventLoop().schedule(this::gcsAuthRequestToCtrl, 3, TimeUnit.SECONDS); + // } + return false; } diff --git a/src/main/java/com/platform/cac/RemoteService.java b/src/main/java/com/platform/cac/RemoteService.java index 9c3036e..d803ddb 100644 --- a/src/main/java/com/platform/cac/RemoteService.java +++ b/src/main/java/com/platform/cac/RemoteService.java @@ -1,6 +1,5 @@ package com.platform.cac; -import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; 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.UavIdMap; import com.platform.model.DirectControlUavParam; -import com.platform.model.Result; -import com.platform.util.JSONUtils; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import lombok.extern.slf4j.Slf4j; @@ -55,7 +52,8 @@ public class RemoteService { bufToRemote.writeByte(0x01); bufToRemote.writeInt(0); bufToRemote.writeByte(0x00); - cacClient.channel.writeAndFlush(bufToRemote); + // cacClient.channel.writeAndFlush(bufToRemote); + CacClient.sendMsgToCac(bufToRemote); return true; } catch (Exception e) { log.error("运管报文回复失败", e); @@ -64,42 +62,21 @@ public class RemoteService { } /** - * 根据fkId和无人机类型查询后台uavId,并更新缓存映射关系 - * @param fkUavId 飞控序列号 - * @return uavId 后端的uavId + * 根据哈勃序列号,查询映射关系,并更新缓存映射关系 + * @return haborSn */ - // public String queryUavId(int fkUavId) { - // JSONObject body = new JSONObject(); - // body.put("flightControlSn", String.valueOf(fkUavId)); - // if (GlobalData.UAV_TYPE == null ) { - // log.error("uav_type未知, 查询无人机ID失败"); - // return null; - // } - // 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 queryCacDirectControlUavInfo(String haborSn) { + DirectControlUavParam info = CacHpApi.queryCacDirectControlUav(haborSn); + if (info == null) { + return; + } + UavIdMap.addMap(UavTypeEnum.getByRemoteCode(info.getUavType()), Integer.parseInt(info.getFlightControlSn()), info.getUavId()); + } /** - * 查询并更新当前地面站在控飞机的无人机映射关系 + * 查询并更新当前地面站在控飞机的无人机映射关系, 需要不定期更新 */ public void queryCacDirectControlMapping() { - // todo 每次申请都查一次映射关系 JSONObject body = new JSONObject(); body.put("gcsId", GlobalData.GCS_ID); log.debug("查询全量无人机映射关系,request body: {}", body); diff --git a/src/main/java/com/platform/cac/tcp/CacClient.java b/src/main/java/com/platform/cac/tcp/CacClient.java index b3da921..8a289e9 100644 --- a/src/main/java/com/platform/cac/tcp/CacClient.java +++ b/src/main/java/com/platform/cac/tcp/CacClient.java @@ -17,7 +17,6 @@ import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.DelimiterBasedFrameDecoder; import io.netty.handler.timeout.IdleStateHandler; -import io.netty.util.ResourceLeakDetector; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -45,7 +44,7 @@ public class CacClient { static EventLoopGroup eventLoopGroup = new NioEventLoopGroup(); private Bootstrap bootstrap = new Bootstrap(); - public Channel channel; + private static Channel channel; @Resource 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 private void end() { eventLoopGroup.shutdownGracefully(); diff --git a/src/main/java/com/platform/cac/tcp/CacConnectionHandler.java b/src/main/java/com/platform/cac/tcp/CacConnectionHandler.java index 43f87a7..83d7d9c 100644 --- a/src/main/java/com/platform/cac/tcp/CacConnectionHandler.java +++ b/src/main/java/com/platform/cac/tcp/CacConnectionHandler.java @@ -36,8 +36,7 @@ public class CacConnectionHandler extends ChannelInboundHandlerAdapter { @Resource GcsService gcsService; - @Value("${airport.enable:#{false}}") - boolean isAirport = false; + /** * 建立连接时 */ @@ -47,8 +46,6 @@ public class CacConnectionHandler extends ChannelInboundHandlerAdapter { int port = ipSocket.getPort(); String host = ipSocket.getHostString(); log.info("与中心指控{}:{}建立tcp连接!", host, port); - cacClient.channel = ctx.channel(); - log.info("地面站发送上线请求"); if (gcsService.gcsSignInRequest()) { gcsService.gcsAuthRequestToCtrl(); diff --git a/src/main/java/com/platform/controller/ClientController.java b/src/main/java/com/platform/controller/ClientController.java index 439038a..bba6429 100644 --- a/src/main/java/com/platform/controller/ClientController.java +++ b/src/main/java/com/platform/controller/ClientController.java @@ -182,7 +182,7 @@ public class ClientController { * 获取所有映射关系 * @return */ - @GetMapping("/mapping/get") + @GetMapping("/mapping/many") public Map> getTargetListMap() { return sessionCache.mappingListMap; } diff --git a/src/main/java/com/platform/info/enums/ClientTypeEnum.java b/src/main/java/com/platform/info/enums/ClientTypeEnum.java index 55c4d21..32dcc45 100644 --- a/src/main/java/com/platform/info/enums/ClientTypeEnum.java +++ b/src/main/java/com/platform/info/enums/ClientTypeEnum.java @@ -9,25 +9,31 @@ import lombok.Getter; */ @Getter 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 short writeHead; + private final Integer code; + private final Short readHead; + private final Short writeHead; 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.writeHead = writeHead; this.info = info; } - - public static ClientTypeEnum getByHead(int head) { + public static ClientTypeEnum getByCode(int code) { for (ClientTypeEnum clientTypeEnum : ClientTypeEnum.values()) { - if (clientTypeEnum.getReadHead() == head) { + if (clientTypeEnum.getCode() == code) { return clientTypeEnum; } } diff --git a/src/main/java/com/platform/info/enums/GcsFrameEnum.java b/src/main/java/com/platform/info/enums/GcsFrameEnum.java deleted file mode 100644 index e2b3038..0000000 --- a/src/main/java/com/platform/info/enums/GcsFrameEnum.java +++ /dev/null @@ -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; - } -} - - diff --git a/src/main/java/com/platform/service/CacDataRouterHandler.java b/src/main/java/com/platform/service/CacDataRouterHandler.java new file mode 100644 index 0000000..17847a6 --- /dev/null +++ b/src/main/java/com/platform/service/CacDataRouterHandler.java @@ -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 { + @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); + } +} diff --git a/src/main/java/com/platform/service/InMessageHandler.java b/src/main/java/com/platform/service/InMessageHandler.java index 5093f90..d38ec47 100644 --- a/src/main/java/com/platform/service/InMessageHandler.java +++ b/src/main/java/com/platform/service/InMessageHandler.java @@ -1,45 +1,45 @@ package com.platform.service; +import com.platform.cac.RemoteService; import com.platform.config.ServiceConfig; -import com.platform.util.CRCUtil; -import com.platform.util.ControlDevice; -import com.platform.util.SessionCache; -import com.platform.util.StringUtil; +import com.platform.info.mapping.HaborUavMap; +import com.platform.service.clientmanage.BaseClient; +import com.platform.service.clientmanage.ClientManager; +import com.platform.service.clientmanage.HaborClient; +import com.platform.util.*; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.*; import io.netty.handler.timeout.IdleState; import io.netty.handler.timeout.IdleStateEvent; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; -import java.nio.ByteBuffer; import java.nio.charset.Charset; -import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Optional; -@Slf4j +@Slf4j @Component @ChannelHandler.Sharable public class InMessageHandler extends ChannelInboundHandlerAdapter { private boolean debug; private StringUtil stringUtil; private SessionCache sessionCache; + @Autowired + RemoteService remoteService; private static final int head1 = 0xAA; private static final int head2 = 0x44; private static final int head3 = 0x61; private static final int lengthLow = 0x03; private static final int lengthHigh = 0x00; - public InMessageHandler() { - } - + @Autowired public InMessageHandler(SessionCache sessionCache, StringUtil stringUtil, ServiceConfig config) { this.stringUtil = stringUtil; this.sessionCache = sessionCache; @@ -77,15 +77,16 @@ public class InMessageHandler extends ChannelInboundHandlerAdapter { public void channelRead(ChannelHandlerContext ctx, Object msg0) throws Exception { String address = getAddress(ctx); - byte[] msg = (byte[])msg0; // TODO 2025/1/16: - - Byte fkID = msg[4]; + // byte[] msg = (byte[])msg0; // TODO 2025/1/16: + ByteBuf msgBuf = (ByteBuf) msg0; //上线握手阶段 if (!sessionCache.addToSnMap.containsKey(address)) { // 判断连接是否已经建立,如未建立则先握手 + byte[] msg = new byte[msgBuf.readableBytes()]; + msgBuf.readBytes(msg); + log.info("【接收握手原数据】 " + ByteUtils.bytes2HexString(msg)); // 握手数据不满足协议长度,立即返回 - String d = stringUtil.bytesToHexString(msg); - log.info("【接收握手原数据】 " + d); + // String d = stringUtil.bytesToHexString(msg); // byte[] crc = CRC.crc16(msg, 2, 16); // if (crc[0] != msg[18] || crc[1] != msg[19]){ // 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()); // deviceCode 终端设备取后六位 String deviceCode; + String typeName; if(type.equals("1")) { // 首先读取deviceCode, 哈勃deviceCode是前面6个byte + typeName = "地面站"; deviceCode = new String(msg, 6, 12, Charset.defaultCharset()).toUpperCase(); // 地面站 + protocolAck(ctx, (byte)1); } 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 { - protocolAck(ctx,(byte) 0); + protocolAck(ctx,(byte) 0); // 握手失败 return; } - log.info("【读取握手数据】======> deviceCode = " + deviceCode +" type = " + type); + log.info("【读取握手数据】======> deviceCode = " + deviceCode +" type = " + typeName); //删除旧的连接。如果存在异常断开的时候,旧连接并未清除。这里在上线的时候检查并清除 String addr0 = sessionCache.snToAddMap.get(deviceCode); @@ -122,13 +129,17 @@ public class InMessageHandler extends ChannelInboundHandlerAdapter { sessionCache.snToAddMap.put(deviceCode, address); // mac- ip sessionCache.tcpSession.put(address, ctx.channel()); // ip - channel - protocolAck(ctx,(byte) 1); 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]; content[0] = (byte)0x01; - int uavNum = StringUtil.byteToInt(msg[3]); + int uavNum = StringUtil.byteToInt(msgBuf.getByte(3)); String source = sessionCache.addToSnMap.get(address); //mac ControlDevice.clearCurrenCtrDeviceByMac(source); int errNum = 0; @@ -136,67 +147,77 @@ public class InMessageHandler extends ChannelInboundHandlerAdapter { // List deviceSns = new ArrayList<>(); for(int i=0;i " + stringUtil.bytesToHexString(msg)); - } - String sn = sessionCache.addToSnMap.get(address); //sn =mac - //监控相关 - if(!sessionCache.snToMonitorMap.isEmpty()){ - Optional sendChannelOpt = sessionCache.findMonitorChannel (address); - if (sendChannelOpt.isPresent()) { - Channel sendChannel = sendChannelOpt.get(); - log.info("(监控)out channel: " + sendChannel.remoteAddress()+"(" + sessionCache.addToSnMap.get(sendChannel.remoteAddress().toString().replace("/","")) - + ") 即将发送 --- " + "in channel: " + ctx.channel().remoteAddress()+"(" + sn + ")发来的TCP消息"); - sendChannel.writeAndFlush(msg0); + // 已经建立连接且非地面站控制信息,则进行数据转发 + else { + if (debug) { + log.info("【转发数据】======> " + ByteBufUtil.hexDump(msgBuf)); } + String sn = sessionCache.addToSnMap.get(address); //sn =mac + //监控相关 + if (!sessionCache.snToMonitorMap.isEmpty()) { + Optional sendChannelOpt = sessionCache.findMonitorChannel(address); + if (sendChannelOpt.isPresent()) { + Channel sendChannel = sendChannelOpt.get(); + log.info("(监控)out channel: " + sendChannel.remoteAddress() + "(" + sessionCache.addToSnMap.get(sendChannel.remoteAddress().toString().replace("/", "")) + + ") 即将发送 --- " + "in channel: " + ctx.channel().remoteAddress() + "(" + sn + ")发来的TCP消息"); + sendChannel.writeAndFlush(msgBuf.retain()); + } - } + } - if (!sessionCache.mappingListMap.isEmpty()){ - if (sessionCache.mappingListMap.get(sn)!=null){ + if (!sessionCache.mappingListMap.isEmpty() && sessionCache.mappingListMap.get(sn) != null) { List sendChannelOpt = sessionCache.findMappingTargetChannel(sn); // 设备编号对应的通道 - log.info("send channel count = " + sendChannelOpt.size()); + log.info("send channel count of {} = {}", sn, sendChannelOpt.size()); sendChannelOpt.forEach(sendChannel -> { - log.info("out channel: " + sendChannel.remoteAddress()+"(" + sessionCache.addToSnMap.get(sendChannel.remoteAddress().toString().replace("/","")) - + ")即将发送 --- " + "in channel: " + ctx.channel().remoteAddress()+"(" + sn + ")发来的TCP消息"); + // log.info("out channel: " + sendChannel.remoteAddress() + "(" + sessionCache.addToSnMap.get(sendChannel.remoteAddress().toString().replace("/", "")) + ")即将发送 --- " + "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 sendSn = sessionCache.addToSnMap.get(sendIp); // IP找到设备编号 - if(sendSn.length()==6){ + if (sendSn.length() == 6) { + // 发送给哈勃 List devices = ControlDevice.getCurrenCtrDevicesByMac(sn); - if(devices.contains(sendSn)||devices.contains("all")){ - sendChannel.writeAndFlush(msg0); + if (devices.contains(sendSn) || devices.contains("all")) { + msgBuf.retain(); + sendChannel.writeAndFlush(msgBuf.retain()); } } - if(sendSn.length()==12) { - sendChannel.writeAndFlush(msg0); + if (sendSn.length() == 12) { + // 发送给地面站 + sendChannel.writeAndFlush(msgBuf.retain()); + return; } + */ }); - - return; } + // else { + // super.channelRead(ctx, msgBuf); + // } } - - /* String sn = global.addToSnMap.get(address); if (!global.snList.isEmpty()){ if (global.snList.contains(sn)){ @@ -222,17 +243,45 @@ public class InMessageHandler extends ChannelInboundHandlerAdapter { 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]; ack[0] = (byte) head1; ack[1] = (byte) head2; ack[2] = (byte) head3; ack[3] = (byte) lengthLow; ack[4] = (byte) lengthHigh; - ack[5] = data; + ack[5] = success; byte[] crc = CRCUtil.crc16(ack, 2, 4); ack[6] = crc[0]; ack[7] = crc[1]; @@ -282,6 +331,7 @@ public class InMessageHandler extends ChannelInboundHandlerAdapter { sessionCache.snToAddMap.remove(sn); } } + haborSignOutOfCac(ctx.channel()); super.handlerRemoved(ctx); } diff --git a/src/main/java/com/platform/service/ServerService.java b/src/main/java/com/platform/service/ServerService.java index e17e20a..b128e5c 100644 --- a/src/main/java/com/platform/service/ServerService.java +++ b/src/main/java/com/platform/service/ServerService.java @@ -7,8 +7,6 @@ import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; 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 lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -30,6 +28,11 @@ public class ServerService { @Autowired private SessionCache sessionCache; + @Autowired + InMessageHandler inMessageHandler; + @Autowired + CacDataRouterHandler cacDataRouterHandler; + private void startServer() { //服务端需要2个线程组 boss处理客户端连接 work进行客服端连接之后的处理 log.warn("【读取配置端口】 端口 = " + config.getPort()); @@ -43,11 +46,16 @@ public class ServerService { bootstrap.childHandler(new ChannelInitializer() { @Override protected void initChannel(Channel channel) throws Exception { - channel.pipeline().addLast(new ByteArrayDecoder()); // revise by shiyi: 解码移动到InMessageHandler中 - channel.pipeline().addLast(new ByteArrayEncoder()); - channel.pipeline().addLast(new IdleStateHandler(0, 0, - 60 * 24, TimeUnit.MINUTES)); - channel.pipeline().addLast(new InMessageHandler(sessionCache, stringUtil, config)); + // channel.pipeline().addLast(new ByteArrayDecoder()); // revise by shiyi: 解码移动到InMessageHandler中 + // channel.pipeline().addLast(new ByteArrayEncoder()); + if (!config.isDebug()) { + channel.pipeline().addLast(new IdleStateHandler(0, 0, + 60 * 24, TimeUnit.MINUTES)); + } + channel.pipeline() + .addLast("OriginalHandler", inMessageHandler) // 原始哈勃消息解码器 + .addLast("TelemetryDecoder", new TelemetryDecoder()) // eb90数据分离 + .addLast("CacDataRouter", cacDataRouterHandler); // cac数据转发 } }); diff --git a/src/main/java/com/platform/service/TelemetryDecoder.java b/src/main/java/com/platform/service/TelemetryDecoder.java new file mode 100644 index 0000000..ebdf986 --- /dev/null +++ b/src/main/java/com/platform/service/TelemetryDecoder.java @@ -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 90之前的一帧,并且不含EB 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; + } +} diff --git a/src/main/java/com/platform/service/clientmanage/BaseClient.java b/src/main/java/com/platform/service/clientmanage/BaseClient.java new file mode 100644 index 0000000..7a438fe --- /dev/null +++ b/src/main/java/com/platform/service/clientmanage/BaseClient.java @@ -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); + } +} diff --git a/src/main/java/com/platform/service/clientmanage/ClientManager.java b/src/main/java/com/platform/service/clientmanage/ClientManager.java new file mode 100644 index 0000000..fd15926 --- /dev/null +++ b/src/main/java/com/platform/service/clientmanage/ClientManager.java @@ -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 CLIENT_ATTRIBUTE_KEY = AttributeKey.valueOf("client"); + /** + * 客户端编号映射 编号→客户端对象 + */ + private static final Map 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 getClientGroup(ClientTypeEnum clientType) { + ArrayList 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); + // } + +} diff --git a/src/main/java/com/platform/service/clientmanage/HaborClient.java b/src/main/java/com/platform/service/clientmanage/HaborClient.java new file mode 100644 index 0000000..f90a26a --- /dev/null +++ b/src/main/java/com/platform/service/clientmanage/HaborClient.java @@ -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 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); + + } +} diff --git a/src/main/java/com/platform/util/JSONUtils.java b/src/main/java/com/platform/util/JSONUtils.java index ea844d7..e14f4fc 100644 --- a/src/main/java/com/platform/util/JSONUtils.java +++ b/src/main/java/com/platform/util/JSONUtils.java @@ -29,15 +29,14 @@ public class JSONUtils { /** * 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); } /** * json string convert to javaBean */ - public static T json2obj(String jsonStr, Class clazz) - throws Exception { + public static T json2obj(String jsonStr, Class clazz) throws IOException { return objectMapper.readValue(jsonStr, clazz); } /** @@ -53,7 +52,7 @@ public class JSONUtils { * json string convert to map */ public static Map json2map(String jsonStr) - throws Exception { + throws IOException { return objectMapper.readValue(jsonStr, Map.class); } @@ -61,7 +60,7 @@ public class JSONUtils { * json string convert to map with javaBean */ public static Map json2map(String jsonStr, Class clazz) - throws Exception { + throws IOException { Map> map = (Map>) objectMapper.readValue(jsonStr, new TypeReference>() { }); @@ -76,7 +75,7 @@ public class JSONUtils { * json array string convert to list with javaBean */ public static List json2list(String jsonArrayStr, Class clazz) - throws Exception { + throws IOException { List> list = (List>) objectMapper.readValue(jsonArrayStr, new TypeReference>() { }); diff --git a/src/main/java/com/platform/util/SessionCache.java b/src/main/java/com/platform/util/SessionCache.java index 765612a..1e6a55d 100644 --- a/src/main/java/com/platform/util/SessionCache.java +++ b/src/main/java/com/platform/util/SessionCache.java @@ -101,6 +101,7 @@ public class SessionCache { return channel; } + /**根据sn获取对应需要转发的channel*/ public List findMappingTargetChannel(String sSn) { List channel = new ArrayList<>(); List tSn = mappingListMap.get(sSn); // 设备的映射关系