[v0.0.1] 移植插件的上线、认证、重连功能;增加uavId,fkId,哈勃sn的映射管理;增加HTTP\JSON工具类处理

master
shiyi 2 months ago
parent f016e02e75
commit e1467baf2b

1
.gitignore vendored

@ -1,2 +1,3 @@
/target/ /target/
/.idea/ /.idea/
/log/

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version> <version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository --> <relativePath/> <!-- lookup parent from repository -->
</parent> </parent>
<groupId>com.platform</groupId> <groupId>com.platform</groupId>
@ -37,33 +37,18 @@
<dependency> <dependency>
<groupId>io.netty</groupId> <groupId>io.netty</groupId>
<artifactId>netty-all</artifactId> <artifactId>netty-all</artifactId>
<version>4.1.32.Final</version> <version>4.1.86.Final</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.alibaba</groupId> <groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId> <artifactId>fastjson</artifactId>
<version>1.2.76</version> <version>1.2.83</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId> <artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- log4j2 日志记录-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!-- 加上这个才能辨认到log4j2.yml文件 -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId> <artifactId>spring-boot-starter-actuator</artifactId>
@ -79,12 +64,25 @@
<version>2.7.0</version> <version>2.7.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.apache.commons</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId> <artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.6.0</version>
<!-- 去掉Android的包 -->
<exclusions>
<exclusion>
<groupId>com.google.android</groupId>
<artifactId>android</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId> <artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency> </dependency>
</dependencies> </dependencies>

@ -0,0 +1,214 @@
package com.platform.cac;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.platform.info.GlobalData;
import com.platform.model.DirectControlUavParam;
import com.platform.model.Result;
import com.platform.util.HttpClientUtils;
import com.platform.util.JSONUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.net.ConnectException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.platform.util.BaseAuthorizationUtils.generateAuthAndDateHeader;
@Slf4j
@Component
public class CacHpApi {
private static final String HTFP_PATH = "/htfp/gcs";
public static String HOST;
@Value("${http-cac.host}")
public void setHost(String host) {
CacHpApi.HOST = host;
log.info(">>>中心指控地址 {}<<<", host);
}
private static String postRequest(String api, String body) {
String path = "http://" + HOST + api;
HashMap<String, String> header = new HashMap<>(3);
generateAuthAndDateHeader(header, "POST", GlobalData.GCS_ID, GlobalData.GCS_TOKEN, api);
try {
return HttpClientUtils.sendPost(path, body, header);
} catch (ConnectException e) {
log.error("http请求超时: {}", e.getMessage());
return null;
} catch (IOException e) {
log.error("http请求错误: {}", e.getMessage());
return null;
}
}
private static Result postRequestAndGetResult(String api, String body) {
String path = "http://" + HOST + api;
HashMap<String, String> header = new HashMap<>(3);
generateAuthAndDateHeader(header, "POST", GlobalData.GCS_ID, GlobalData.GCS_TOKEN, api);
try {
return HttpClientUtils.sendPost(path, body, header, Result.class);
} catch (ConnectException e) {
log.error("http请求超时: {}", e.getMessage());
return null;
} catch (Exception e) {
log.error("http请求错误: {}, body={}", e.getMessage(), body);
return null;
}
}
// /**
// * 响应体解析
// *
// * @param responseStr
// * @return
// */
// public static JSONObject checkResponse(String responseStr) {
// JSONObject responseJson = JSON.parseObject(responseStr);
// if (responseJson == null) {
// return null;
// }
//
// Boolean success = responseJson.getBoolean("success");
// if (!success) {
// log.error("HTTP请求响应失败, code:{}, message: {}", responseJson.getInteger("code"), responseJson.getString("message"));
// } else {
// log.debug("HTTP请求响应成功, code:{}, message: {}", responseJson.getInteger("code"), responseJson.getString("message"));
// }
// return responseJson.getJSONObject("data");
// }
/**
* 线
*/
public static String gcsSignIn(String body) {
final String gcsSignApi = HTFP_PATH + "/signIn";
Result result = postRequestAndGetResult(gcsSignApi, body);
if (result == null || !result.isSuccess()) {
log.error("地面站上线请求失败: {}", result);
return null;
}
return result.getData().toString();
}
/**
* 线 线
*/
// public static String gcsSignOut(String body) {
// final String gcsSignApi = HTFP_PATH + "/signOut";
// return postRequest(gcsSignApi, body);
// }
/**
*
*/
public static String uavPowerOff(String body) {
final String gcsSignApi = HTFP_PATH + "/uavPowerOff";
return postRequest(gcsSignApi, body);
}
/**
*
*/
public static String queryGcsSnapshot(String body) {
final String gcsSignApi = HTFP_PATH + "/queryGcsSnapshot";
return postRequest(gcsSignApi, body);
}
/**
*
*/
public static String queryGcsControlUav(String body) {
final String gcsSignApi = HTFP_PATH + "/queryGcsControlUav";
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);
}
/**
*
*/
public static String commandNotify(String body) {
final String gcsSignApi = HTFP_PATH + "/notifyUavCommand";
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);
}
public static String uavControlReceive(String body) {
final String gcsSignApi = HTFP_PATH + "/receiveUavControlRight";
return postRequest(gcsSignApi, body);
}
public static String queryUavId(String body) {
final String gcsSignApi = HTFP_PATH + "/queryUavIdByFlightControlSnAndType";
return postRequest(gcsSignApi, body);
}
public static String queryUavMapping(String body) {
final String gcsSignApi = HTFP_PATH + "/queryUavIdAndFlightControlSnMapping";
return postRequest(gcsSignApi, body);
}
public static List<DirectControlUavParam> queryAllCacDirectControlUavList(String body) {
final String gcsSignApi = HTFP_PATH + "/queryAllCacDirectControlUavList";
try {
Result result = postRequestAndGetResult(gcsSignApi, body);
if (result == null || !result.isSuccess()){
log.error("查询全量无人机映射关系失败: {}", result);
return null;
}
return JSONUtils.json2list(result.getData().toString(), DirectControlUavParam.class);
} catch (Exception e) {
log.error("查询全量无人机映射关系失败: {}", e.getMessage());
}
return null;
}
public static DirectControlUavParam queryCacDirectControlUav(String body) {
final String gcsSignApi = HTFP_PATH + "/queryCacDirectControlUav";
try {
Result result = postRequestAndGetResult(gcsSignApi, body);
if (result == null || !result.isSuccess()){
log.error("查询单个无人机映射关系失败body={}, result={}", body, result);
return null;
}
return JSONUtils.json2obj(result.getData().toString(), DirectControlUavParam.class);
} catch (Exception e) {
log.error("查询全量无人机映射关系失败: {}", e.getMessage());
}
return null;
}
}

@ -0,0 +1,248 @@
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;
/**
* @Author : shiyi
* @Date : 2024/1/3 15:08
* @Description :
*
* // TODO 2024/11/28: 方法归类有点混乱,与地面站的交互和与中心指控的交互应该分开
*/
@Component
@Slf4j
public class GcsService {
@Resource
CacClient cacClient;
@Resource
RemoteService remoteService;
public static Channel gcsChannel;
// /**
// * 向地面站发送消息
// *
// * @param type 帧类型编码
// * @param data 数据内容(除去帧头、帧类型)
// * @return
// */
// public ChannelFuture sendToGcs(byte type, byte[] data) {
// GcsMessage gcsMessage = new GcsMessage();
// gcsMessage.setHead(ClientTypeEnum.GCS.getWriteHead());
// gcsMessage.setType(type);
// gcsMessage.setReadableDataBytes(data);
// if (gcsChannel != null && gcsChannel.isActive()) {
// return gcsChannel.writeAndFlush(gcsMessage);
// } else {
// log.warn("地面站消息发送失败: 与地面站握手未成功");
// }
// return null;
// }
// /**
// * 向地面站发送消息
// *
// * @param type 帧类型编码
// * @param buf 数据内容(除去帧头、帧类型)
// * @return
// */
// public ChannelFuture sendToGcs(byte type, ByteBuf buf) {
// GcsMessage gcsMessage = new GcsMessage();
// gcsMessage.setHead(ClientTypeEnum.GCS.getWriteHead());
// gcsMessage.setType(type);
// gcsMessage.setReadableDataBytes(ByteBufUtil.getBytes(buf));
// buf.release();
// if (gcsChannel != null && gcsChannel.isActive()) {
// return gcsChannel.writeAndFlush(gcsMessage);
// } else {
// log.warn("地面站消息发送失败: 与地面站握手未成功");
// }
// return null;
// }
/**
*
* @param dataframe
* @param type
*/
public void buildRemoteBaseDataFrame(RemoteTcpBaseDataFrame dataframe, byte type) {
dataframe.setMagicCode(GlobalData.REMOTE_HEAD);
dataframe.setVersion(GlobalData.REMOTE_VERSION2);
dataframe.setSerializationAlgorithm(GlobalData.REMOTE_SER_ALG);
dataframe.setType(type);
dataframe.setGcsIdLength((byte) GlobalData.GCS_ID.length());
dataframe.setGcsId(GlobalData.GCS_ID);
dataframe.setGcsAuthLength((byte) GlobalData.AUTHORIZATION.length());
dataframe.setGcsAuth(GlobalData.AUTHORIZATION);
}
public boolean isSignIn() {
if (GlobalData.GCS_SIGNIN) {
return true;
} else {
log.warn("地面站未上线!");
return false;
}
}
public boolean gcsSignInRequest(){
if (GlobalData.GCS_SIGNIN) {
log.info("已登录中心指控,无需重复登录");
return true;
}
JSONObject body = new JSONObject();
body.put("gcsId", GlobalData.GCS_ID);
try {
GlobalData.AUTHORIZATION = CacHpApi.gcsSignIn(body.toString());
if (GlobalData.AUTHORIZATION != null) {
log.info("地面站上线请求成功,认证中...");
}
return GlobalData.AUTHORIZATION != null;
} catch (Exception e) {
e.printStackTrace();
log.error("地面站上线请求失败: {}", e.getMessage());
return false;
}
}
/**
* 线tcp
*/
public void gcsAuthRequestToCtrl() {
TcpRemoteAuthCacRequest tcpGcsAuthCacRequest = new TcpRemoteAuthCacRequest();
ChannelFuture sendFuture = null;
try {
if (StringUtils.isEmpty(GlobalData.AUTHORIZATION)) {
log.info("[tcpAuth] authorization 为空,重新发起上线请求");
gcsSignInRequest();
}
buildRemoteBaseDataFrame(tcpGcsAuthCacRequest, RemoteFrameEnum.GCS_AUTH_CAC_REQUEST.getCode());
tcpGcsAuthCacRequest.setUavIdLength((byte) 0);
tcpGcsAuthCacRequest.setUavId("");
tcpGcsAuthCacRequest.setReadableDataBytesLength((byte) 0);
tcpGcsAuthCacRequest.setReadableDataBytes(null);
if (cacClient.channel.isActive()) {
sendFuture = cacClient.channel.writeAndFlush(tcpGcsAuthCacRequest).sync();
if (sendFuture.isSuccess()){
log.info("[tcpAuth] 认证请求已成功发送发送,认证码:{}", GlobalData.AUTHORIZATION);
}
} else {
log.error("[tcpAuth] 和中心指控之间tcp连接异常tcp认证发送失败");
}
} catch (Exception e) {
log.info("[tcpAuth] tcp认证数据构建失败 ", e);
}
if (sendFuture == null || !sendFuture.isSuccess()) {
// 如果发送失败,则重复验证
cacClient.channel.eventLoop().schedule(this::gcsAuthRequestToCtrl, 3, TimeUnit.SECONDS);
}
}
/**
*
* @param fkUavId id
* @return
*/
// public boolean singleUavPowerOff(int fkUavId) {
// String uavId = UavIdMap.getUavId(fkUavId);
// JSONObject body = new JSONObject();
// body.put("gcsId", GlobalData.GCS_ID);
// body.put("uavId", uavId);
// JSONObject responseJson = JSON.parseObject(CacHpApi.uavPowerOff(body.toString()));
// if (responseJson != null && responseJson.getBoolean("success")) {
// log.info("在控飞机 {} 下电成功", fkUavId);
// return true;
// } else {
// log.info("在控飞机 {} 下电失败: {}", fkUavId, responseJson);
// return false;
// }
// }
//
// /**
// * 无人机全部下电
// */
// public boolean allUavPowerOff() {
// try {
// List<Integer> fkUavIdList = remoteService.queryGcsControlUav();
// if (fkUavIdList == null) {
// log.warn("查询在控飞机失败, 无法下线");
// return false;
// }
// if (fkUavIdList.isEmpty()) {
// log.info("当前无在控飞机");
// return true;
// }
// // 逐个下电
// for (int fkUavId : fkUavIdList) {
// singleUavPowerOff(fkUavId);
// }
//
// fkUavIdList = remoteService.queryGcsControlUav();
// if (fkUavIdList == null) {
// return false;
// }
// if (fkUavIdList.isEmpty()) {
// log.info("地面站所有在控飞机下电成功");
// return true;
// } else {
// log.warn("以下在控飞机下电失败: {}, 无法下线", fkUavIdList);
// return false;
// }
// } catch (Exception e) {
// e.printStackTrace();
// return false;
// }
// }
/**
*
* @param fkUavId id
* @param rightState
*/
// public void issuedUavControl(int fkUavId, boolean rightState) {
// ByteBuf bufToGcs = Unpooled.buffer();
// bufToGcs.writeByte((byte) fkUavId);
// bufToGcs.writeByte((byte) (rightState ? 0x00 : 0x01));
// ChannelFuture sendFuture = sendToGcs(GcsFrameEnum.CONTROL_ISSUE.getCode(), bufToGcs);
// if (sendFuture!=null && sendFuture.isSuccess() && rightState){
// uavYgStatusChangeNotify(fkUavId, 0);
// }
// }
}

@ -0,0 +1,184 @@
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.info.GlobalData;
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;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
* @Author : shiyi
* @Date : 2024/1/31 18:54
* @Description :
*/
@Component
@Slf4j
public class RemoteService {
@Resource
CacClient cacClient;
/**
*
*/
public Boolean remoteResponse(RemoteTcpBaseDataFrame data, byte type) {
try {
ByteBuf bufToRemote = Unpooled.buffer();
bufToRemote.writeShort(data.getMagicCode());
bufToRemote.writeByte(data.getVersion());
bufToRemote.writeByte(data.getSerializationAlgorithm());
bufToRemote.writeByte(type);
bufToRemote.writeByte(data.getGcsIdLength());
bufToRemote.writeBytes(data.getGcsId().getBytes(StandardCharsets.UTF_8));
bufToRemote.writeByte(data.getGcsAuthLength());
bufToRemote.writeBytes(data.getGcsAuth().getBytes(StandardCharsets.UTF_8));
bufToRemote.writeByte(data.getUavIdLength());
if (data.getUavId() != null) {
bufToRemote.writeBytes(data.getUavId().getBytes(StandardCharsets.UTF_8));
}
bufToRemote.writeByte(0x01);
bufToRemote.writeInt(0);
bufToRemote.writeByte(0x00);
cacClient.channel.writeAndFlush(bufToRemote);
return true;
} catch (Exception e) {
log.error("运管报文回复失败", e);
return false;
}
}
/**
* fkIduavId
* @param fkUavId
* @return uavId uavId
*/
// 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 queryCacDirectControlMapping() {
// todo 每次申请都查一次映射关系
JSONObject body = new JSONObject();
body.put("gcsId", GlobalData.GCS_ID);
log.debug("查询全量无人机映射关系request body: {}", body);
try {
List<DirectControlUavParam> directControlUavParamList= CacHpApi.queryAllCacDirectControlUavList(body.toString());
if (CollectionUtils.isEmpty(directControlUavParamList)) {
log.error("查询全量无人机映射关系失败");
} else {
UavIdMap.clear();
HaborUavMap.clear();
directControlUavParamList.forEach(mapping -> {
UavTypeEnum uavType = UavTypeEnum.getByRemoteCode(mapping.getUavType());
// DONE 2024/6/26: 记录所有型号的飞机映射
UavIdMap.addMap(uavType, Integer.parseInt(mapping.getFlightControlSn()), mapping.getUavId());
HaborUavMap.addMap(mapping.getUavId(), mapping.getHarborSn());
});
log.info("直控无人机参数映射关系: {}", directControlUavParamList);
}
// if (UAV_TYPE != null) {
// log.info("当前{}型号无人机映射关系fkId→uavId{}", UAV_TYPE, UavIdMap.showMap());
// } else {
// log.info("无法获取当前无人机型号所有型号无人机映射关系fkId→uavId{}", UavIdMap.showAllMap());
// }
} catch (Exception e) {
e.printStackTrace();
log.error("查询全量无人机映射关系失败");
}
}
/**
*
*/
// public List<Integer> queryGcsControlUav() {
// List<String> uavIdList = queryGcsControlUavByUavId();
// if (uavIdList == null) {
// return null;
// }
// UavIdControlMap.clear();
// ArrayList<Integer> fkUavIdList = new ArrayList<>();
// for (String uavId : uavIdList) {
// // 如果uavId不属于当前地面站可控型号则跳过
// if (UavIdMap.uavIdControllable(uavId)) {
// int fkId = UavIdMap.getFkId(uavId);
// fkUavIdList.add(fkId);
// UavIdControlMap.addMap(fkId, uavId);
// }
// }
// return fkUavIdList;
// }
/**
*
*/
// public List<String> queryGcsControlUavByUavId() {
// JSONObject body = new JSONObject();
// body.put("gcsId", GCS_ID);
//
// String responseStr = CacHpApi.queryGcsControlUav(body.toString());
// JSONObject controlUav = CacHpApi.checkResponse(responseStr);
// if (StringUtils.isEmpty(responseStr) || controlUav == null){
// log.error("在控飞机查询失败, 请检查无人机映射关系:{}", UavIdMap.showMap());
// return null;
// }
// return controlUav.getObject("uavIdList", ArrayList.class);
// }
/**
*
*/
// public GcsTypeEnum queryGcsType() {
// JSONObject body = new JSONObject();
// body.put("gcsId", GCS_ID);
// Result result = JSON.parseObject(CacHpApi.queryGcsTypeByGcsId(body.toString()), Result.class);
// if (result == null || !result.isSuccess()) {
// log.error("查询地面站类型失败!");
// return null;
// } else {
// Integer gcsType = Integer.valueOf((String) result.getData());
// return GcsTypeEnum.getTypeByCode(gcsType);
// }
// }
}

@ -0,0 +1,115 @@
package com.platform.cac.tcp;
import com.platform.cac.GcsService;
import com.platform.cac.tcp.codec.RemoteTcpBaseDataDecoder;
import com.platform.cac.tcp.codec.RemoteTcpBaseDataEncoder;
import com.platform.cac.tcp.message.RemoteMessageDispatcher;
import com.platform.config.CacRemoteConfig;
import com.platform.info.GlobalData;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
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;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;
/**
* @Author : shiyi
* @Date : 2023/12/30 16:57
* @Description :
*/
@Slf4j
@Component
public class CacClient {
@Resource
private CacRemoteConfig cacRemoteConfig;
@Resource
CacConnectionHandler cacConnectionHandler;
@Resource
RemoteMessageDispatcher remoteMessageDispatcher;
static EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
private Bootstrap bootstrap = new Bootstrap();
public Channel channel;
@Resource
GcsService gcsService;
public void initClient(EventLoopGroup group) {
ByteBuf delimiter = Unpooled.buffer();
delimiter.writeShort(GlobalData.REMOTE_HEAD);
if (null == group) {
group = eventLoopGroup;
}
bootstrap.group(group)
.channel(NioSocketChannel.class)
.remoteAddress(new InetSocketAddress(cacRemoteConfig.getIp(), cacRemoteConfig.getRemoteTcpPort()))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("ping", new IdleStateHandler(10, 10, 10, TimeUnit.SECONDS));
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, true, true, delimiter));
ch.pipeline().addLast(new RemoteTcpBaseDataDecoder());
ch.pipeline().addLast(new RemoteTcpBaseDataEncoder());
ch.pipeline().addLast(cacConnectionHandler);
ch.pipeline().addLast(remoteMessageDispatcher);
}
});
}
@PostConstruct
public void start() {
initClient(null);
// ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.ADVANCED);
ChannelFuture future = bootstrap.connect().addListener((ChannelFuture futureListener) -> {
if (!futureListener.isSuccess()) {
log.info("与" + cacRemoteConfig.getIp() + ":" + cacRemoteConfig.getRemoteTcpPort() + "连接失败! 重连中...");
connect();
} else {
channel = futureListener.channel();
log.debug("start-channel: {}", channel);
}
});
}
/**
*
*/
public void connect() {
ChannelFuture channelFuture = bootstrap.connect();
// 使用最新的ChannelFuture -> 开启最新的监听器
channelFuture.addListener((ChannelFutureListener) future -> {
if (future.cause() != null) {
log.info("与{}:{}连接失败! 重连中...", cacRemoteConfig.getIp(),cacRemoteConfig.getRemoteTcpPort() );
future.channel().eventLoop().schedule(this::connect, 3, TimeUnit.SECONDS);
} else {
channel = future.channel();
log.debug("reconnect-channel: {}", channel);
if (GlobalData.GCS_SIGNIN) {
log.info("[reconnect] 与中心指控{}:{}重连成功, 认证中", cacRemoteConfig.getIp(),cacRemoteConfig.getRemoteTcpPort() );
gcsService.gcsAuthRequestToCtrl();
}
}
});
}
@PreDestroy
private void end() {
eventLoopGroup.shutdownGracefully();
}
}

@ -0,0 +1,107 @@
package com.platform.cac.tcp;
import com.platform.cac.GcsService;
import com.platform.cac.tcp.message.dataframe.send.TcpHeartBeatRequest;
import com.platform.info.GlobalData;
import com.platform.info.enums.GcsTypeEnum;
import com.platform.info.enums.RemoteFrameEnum;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.concurrent.TimeUnit;
/**
* @Author : shiyi
* @Date : 2024/1/3 14:14
* @Description :
*/
@Slf4j
@Component
@ChannelHandler.Sharable
public class CacConnectionHandler extends ChannelInboundHandlerAdapter {
@Resource
CacClient cacClient;
@Resource
GcsService gcsService;
@Value("${airport.enable:#{false}}")
boolean isAirport = false;
/**
*
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
InetSocketAddress ipSocket = (InetSocketAddress) ctx.channel().remoteAddress();
int port = ipSocket.getPort();
String host = ipSocket.getHostString();
log.info("与中心指控{}:{}建立tcp连接!", host, port);
cacClient.channel = ctx.channel();
log.info("地面站发送上线请求");
if (gcsService.gcsSignInRequest()) {
gcsService.gcsAuthRequestToCtrl();
}
log.debug("connect-channel: {}", ctx.channel());
ctx.fireChannelActive();
}
/**
*
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
InetSocketAddress ipSocket = (InetSocketAddress) ctx.channel().remoteAddress();
int port = ipSocket.getPort();
String host = ipSocket.getHostString();
log.error("与中心指控{}:{}连接断开!", host, port);
log.info("断线重连......");
cacClient.connect();
}
/**
*
*/
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleState state = ((IdleStateEvent) evt).state();
if (IdleState.WRITER_IDLE.equals(state)) {
//心跳
TcpHeartBeatRequest tcpHeartBeatRequest = buildHeatBeatRequest();
ctx.channel().writeAndFlush(tcpHeartBeatRequest);
}
}
}
private TcpHeartBeatRequest buildHeatBeatRequest(){
TcpHeartBeatRequest tcpHeartBeatRequest = new TcpHeartBeatRequest();
tcpHeartBeatRequest.setMagicCode(GlobalData.REMOTE_HEAD);
tcpHeartBeatRequest.setVersion(GlobalData.REMOTE_VERSION2);
tcpHeartBeatRequest.setSerializationAlgorithm(GlobalData.REMOTE_SER_ALG);
tcpHeartBeatRequest.setType(RemoteFrameEnum.HEART_BEAT_REQUEST.getCode());
tcpHeartBeatRequest.setGcsIdLength((byte) GlobalData.GCS_ID.getBytes().length);
tcpHeartBeatRequest.setGcsId(GlobalData.GCS_ID);
tcpHeartBeatRequest.setGcsAuthLength((byte) GlobalData.GCS_TOKEN.getBytes().length);
tcpHeartBeatRequest.setGcsAuth(GlobalData.GCS_TOKEN);
tcpHeartBeatRequest.setUavIdLength((byte) 0);
tcpHeartBeatRequest.setCurrentTime(System.currentTimeMillis());
byte[] readableDataBytes = ByteBuffer.allocate(Long.SIZE / Byte.SIZE).putLong(tcpHeartBeatRequest.getCurrentTime()).array();
tcpHeartBeatRequest.setReadableDataBytesLength(Long.SIZE / Byte.SIZE);
tcpHeartBeatRequest.setReadableDataBytes(readableDataBytes);
return tcpHeartBeatRequest;
}
}

@ -0,0 +1,101 @@
package com.platform.cac.tcp.codec;
import com.platform.cac.tcp.message.dataframe.RemoteTcpBaseDataFrame;
import com.platform.info.GlobalData;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
@Slf4j
public class RemoteTcpBaseDataDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
// byte[] buffer = new byte[in.readableBytes()];
// in.readBytes(buffer);
// in.release();
// log.info("msg: {}", ByteUtils.bytes2HexString(buffer));
RemoteTcpBaseDataFrame remoteTcpBaseDataFrame = new RemoteTcpBaseDataFrame();
// 标记当前读取位置
in.markReaderIndex();
// 判断是否大于最小长度
if (in.readableBytes() <= 8) {
in.resetReaderIndex();
log.error("[GcsTcpBaseDataDecoder][连接({}) 解析消息失败数据可读长度小于数据帧最小长度in={}]", ctx.channel().id(), in.toString());
in.clear();
return;
}
byte[] totalIn = new byte[in.readableBytes()];
in.readBytes(totalIn);
// if (totalIn[2] != 0x04) {
// log.debug("完整消息: 88 99 {}", ByteUtils.bytes2HexString(totalIn));
// }
in.resetReaderIndex();
remoteTcpBaseDataFrame.setMagicCode(GlobalData.REMOTE_HEAD);
remoteTcpBaseDataFrame.setVersion(in.readByte());
remoteTcpBaseDataFrame.setSerializationAlgorithm(in.readByte());
remoteTcpBaseDataFrame.setType(in.readByte());
// 获取地面站编号长度以及地面站编号
byte gcsIdLengthByte = in.readByte();
int gcsIdLength = Byte.toUnsignedInt(gcsIdLengthByte);
if (in.readableBytes() < gcsIdLength) {
in.resetReaderIndex();
log.error("[GcsTcpBaseDataDecoder][连接({}) 解析消息失败数据剩余可读长度小于gcsId长度in={}]", ctx.channel().id(), in.toString());
return;
}
byte[] gcsIdByteArray = new byte[gcsIdLength];
in.readBytes(gcsIdByteArray);
remoteTcpBaseDataFrame.setGcsIdLength(gcsIdLengthByte);
remoteTcpBaseDataFrame.setGcsId(new String(gcsIdByteArray));
// 获取地面站Token长度以及地面站Token
byte gcsTokenLengthByte = in.readByte();
int gcsTokenLength = Byte.toUnsignedInt(gcsTokenLengthByte);
if (in.readableBytes() < gcsTokenLength) {
in.resetReaderIndex();
log.error("[GcsTcpBaseDataDecoder][连接({}) 解析消息失败数据剩余可读长度小于gcsToken长度in={}]", ctx.channel().id(), in.toString());
return;
}
byte[] gcsTokenByteArray = new byte[gcsTokenLength];
in.readBytes(gcsTokenByteArray);
remoteTcpBaseDataFrame.setGcsAuthLength(gcsTokenLengthByte);
remoteTcpBaseDataFrame.setGcsAuth(new String(gcsTokenByteArray));
// 获取无人机编号长度以及无人机编号
byte uavIdLengthByte = in.readByte();
int uavIdLength = Byte.toUnsignedInt(uavIdLengthByte);
if (in.readableBytes() < uavIdLength) {
in.resetReaderIndex();
log.error("[GcsTcpBaseDataDecoder][连接({}) 解析消息失败数据剩余可读长度小于uavId长度in={}]", ctx.channel().id(), in.toString());
return;
}
if (uavIdLength == 0) {
remoteTcpBaseDataFrame.setUavIdLength(uavIdLengthByte);
remoteTcpBaseDataFrame.setUavId(null);
} else {
byte[] uavIdByteArray = new byte[uavIdLength];
in.readBytes(uavIdByteArray);
remoteTcpBaseDataFrame.setUavIdLength(uavIdLengthByte);
remoteTcpBaseDataFrame.setUavId(new String(uavIdByteArray));
}
// 读取剩余字节
remoteTcpBaseDataFrame.setReadableDataBytesLength(in.readableBytes());
if (in.readableBytes() > 0) {
byte[] readableDataBytes = new byte[in.readableBytes()];
in.readBytes(readableDataBytes);
remoteTcpBaseDataFrame.setReadableDataBytes(readableDataBytes);
}
out.add(remoteTcpBaseDataFrame);
// TODO: 2023/6/14 测试之后记得删除此log
// log.info("[GcsTcpBaseDataDecoder][连接({}) 解析到一条消息({})]", ctx.channel().id(), gcsTcpBaseDataFrame.toString());
}
}

@ -0,0 +1,42 @@
package com.platform.cac.tcp.codec;
import com.platform.cac.tcp.message.dataframe.RemoteTcpBaseDataFrame;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class RemoteTcpBaseDataEncoder extends MessageToByteEncoder<RemoteTcpBaseDataFrame> {
@Override
protected void encode(ChannelHandlerContext ctx, RemoteTcpBaseDataFrame dataFrame, ByteBuf buf) {
if (dataFrame != null) {
buf.writeShort(dataFrame.getMagicCode());
buf.writeByte(dataFrame.getVersion());
buf.writeByte(dataFrame.getSerializationAlgorithm());
buf.writeByte(dataFrame.getType());
buf.writeByte(dataFrame.getGcsIdLength());
if (dataFrame.getGcsIdLength() > 0) {
buf.writeBytes(dataFrame.getGcsId().getBytes(), 0, dataFrame.getGcsIdLength());
}
buf.writeByte(dataFrame.getGcsAuthLength());
if (dataFrame.getGcsAuthLength() > 0) {
buf.writeBytes(dataFrame.getGcsAuth().getBytes(), 0, dataFrame.getGcsAuthLength());
}
buf.writeByte(dataFrame.getUavIdLength());
if (dataFrame.getUavIdLength() > 0) {
buf.writeBytes(dataFrame.getUavId().getBytes(), 0, dataFrame.getUavIdLength());
}
if (dataFrame.getReadableDataBytesLength() > 0) {
buf.writeBytes(dataFrame.getReadableDataBytes(), 0, dataFrame.getReadableDataBytesLength());
}
// TODO: 2023/6/13 测试之后记得删除此log
// log.debug("[encode]连接({}) 编码了一条消息: {}", ctx.channel().id(), ByteUtils.bytes2HexString(ByteBufUtil.getBytes(buf)));
}
}
}

@ -0,0 +1,64 @@
package com.platform.cac.tcp.message;
import com.platform.cac.tcp.message.dataframe.RemoteTcpBaseDataFrame;
import com.platform.cac.tcp.message.handler.IRemoteMessageHandler;
import com.platform.info.enums.RemoteFrameEnum;
import com.platform.util.ByteUtils;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @Author shiyi
* @Date 2023/12/27
* @Description
*/
@ChannelHandler.Sharable
@Component
@Slf4j
public class RemoteMessageDispatcher extends SimpleChannelInboundHandler<RemoteTcpBaseDataFrame> {
@Autowired
private RemoteMessageHandlerContainer remoteMessageHandlerContainer;
@Override
public void channelRead0(ChannelHandlerContext ctx, RemoteTcpBaseDataFrame message) throws InterruptedException {
RemoteFrameEnum messageType = getNettyMessageType(message);
// 获得 type 对应的 MessageHandler 处理器
IRemoteMessageHandler messageHandler = remoteMessageHandlerContainer.getMessageHandler(messageType);
// 执行逻辑
if (messageHandler != null) {
log.debug("执行逻辑 {}", messageHandler.getFrameType());
messageHandler.execute(ctx.channel(), message);
} else {
if (messageType != null) {
ctx.fireChannelRead(message);
}
}
}
private RemoteFrameEnum getNettyMessageType(RemoteTcpBaseDataFrame message) {
RemoteFrameEnum remoteFrameEnum = RemoteFrameEnum.getByCode(message.getType());
if (remoteFrameEnum == null) {
log.warn("未知的远程消息类型: {}, 无对应处理逻辑", ByteUtils.byteToHex(message.getType()));
return null;
}
if (remoteFrameEnum.equals(RemoteFrameEnum.HEART_BEAT_RESPONSE) || remoteFrameEnum.equals(RemoteFrameEnum.HEART_BEAT_REQUEST)) {
// 过滤心跳信息
return null;
}
return remoteFrameEnum;
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
log.error("exceptionCaught: {}", cause.getMessage(), cause);
}
}

@ -0,0 +1,47 @@
package com.platform.cac.tcp.message;
import com.platform.cac.tcp.message.handler.IRemoteMessageHandler;
import com.platform.info.enums.RemoteFrameEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* @Author sunjipeng
* @Date 2023/6/14
* @Description
*/
@Slf4j
@Component
public class RemoteMessageHandlerContainer implements InitializingBean {
/**
* IGcsMessageHandler
*/
private final Map<RemoteFrameEnum, IRemoteMessageHandler> handlers = new HashMap<>();
@Autowired
private ApplicationContext applicationContext;
@Override
public void afterPropertiesSet() throws Exception {
// 通过 ApplicationContext 获得所有 MessageHandler Bean
applicationContext.getBeansOfType(IRemoteMessageHandler.class).values()
.forEach(messageHandler -> handlers.put(messageHandler.getFrameType(), messageHandler));
log.info("[afterPropertiesSet] IRemoteMessageHandler 数量:{}", handlers.size());
}
/**
* IMessageHandler
*/
public IRemoteMessageHandler getMessageHandler(RemoteFrameEnum remoteFrameEnum) {
return handlers.get(remoteFrameEnum);
}
}

@ -0,0 +1,41 @@
package com.platform.cac.tcp.message.dataframe;
import lombok.Data;
@Data
public class RemoteTcpBaseDataFrame {
private short magicCode;
private byte version;
private byte serializationAlgorithm;
private byte type;
private byte gcsIdLength;
private String gcsId;
private byte gcsAuthLength;
private String gcsAuth;
private byte uavIdLength;
private String uavId;
private int readableDataBytesLength;
private byte[] readableDataBytes;
public RemoteTcpBaseDataFrame() {
}
/**
*
*/
public RemoteTcpBaseDataFrame(RemoteTcpBaseDataFrame data) {
// 将data的基础属性复制过来
this.magicCode = data.getMagicCode();
this.version = data.getVersion();
this.serializationAlgorithm = data.getSerializationAlgorithm();
this.type = data.getType();
this.gcsIdLength = data.getGcsIdLength();
this.gcsId = data.getGcsId();
this.gcsAuthLength = data.getGcsAuthLength();
this.gcsAuth = data.getGcsAuth();
this.uavIdLength = data.getUavIdLength();
this.uavId = data.getUavId();
}
}

@ -0,0 +1,42 @@
package com.platform.cac.tcp.message.dataframe.receive;
import com.platform.cac.tcp.message.dataframe.RemoteTcpBaseDataFrame;
import com.platform.util.ByteUtils;
import lombok.Getter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import java.nio.ByteBuffer;
@Getter
@ToString
@Slf4j
public class CacReceiveUavControlMessage extends RemoteTcpBaseDataFrame {
private byte receiveGcsIdLength;
private String receiveGcsId;
private byte uavControlAccept;
private byte uavControlAppplyIdLength;
private String uavControlApplyId;
public CacReceiveUavControlMessage(RemoteTcpBaseDataFrame data) {
super(data);
ByteBuffer buffer = ByteBuffer.allocate(data.getReadableDataBytesLength()).put(data.getReadableDataBytes());
buffer.flip();
try {
receiveGcsIdLength = buffer.get();
receiveGcsId = ByteUtils.getString(buffer, receiveGcsIdLength);
uavControlAccept = buffer.get();
uavControlAppplyIdLength = buffer.get();
uavControlApplyId = ByteUtils.getString(buffer, uavControlAppplyIdLength);
} catch (Exception e){
log.error("中心指控-控制权接收数据解析异常");
e.printStackTrace();
}
}
}

@ -0,0 +1,46 @@
package com.platform.cac.tcp.message.dataframe.receive;
import com.platform.cac.tcp.message.dataframe.RemoteTcpBaseDataFrame;
import com.platform.util.ByteUtils;
import lombok.Getter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import java.nio.ByteBuffer;
@Getter
@ToString
@Slf4j
public class CacReplyUavControlMessage extends RemoteTcpBaseDataFrame {
private byte replyGcsIdLength;
private String replyGcsId;
private byte uavControlApprove;
private byte uavControlApplyIdLength;
private String uavControlApplyId;
public CacReplyUavControlMessage(RemoteTcpBaseDataFrame data) {
super(data);
ByteBuffer buffer = ByteBuffer.allocate(data.getReadableDataBytesLength()).put(data.getReadableDataBytes());
buffer.flip();
try {
replyGcsIdLength = buffer.get();
replyGcsId = ByteUtils.getString(buffer, replyGcsIdLength);
uavControlApprove = buffer.get();
uavControlApplyIdLength = buffer.get();
uavControlApplyId = ByteUtils.getString(buffer, uavControlApplyIdLength);
} catch (Exception e) {
log.error("中心指控-控制权回复数据解析异常");
e.printStackTrace();
}
}
}

@ -0,0 +1,41 @@
package com.platform.cac.tcp.message.dataframe.receive;
import com.platform.cac.tcp.message.dataframe.RemoteTcpBaseDataFrame;
import com.platform.util.ByteUtils;
import lombok.Getter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import java.nio.ByteBuffer;
@Slf4j
@Getter
@ToString
public class CacUavCommandMessage extends RemoteTcpBaseDataFrame {
private byte uavCommandCodeIdLength;
private byte[] uavCommandCodeId;
private byte uavControlUniIdLength;
private String uavControlUniId;
public CacUavCommandMessage(RemoteTcpBaseDataFrame data){
super(data);
ByteBuffer buffer = ByteBuffer.allocate(data.getReadableDataBytesLength()).put(data.getReadableDataBytes());
buffer.flip();
try {
uavCommandCodeIdLength = buffer.get();
uavCommandCodeId = new byte[uavCommandCodeIdLength];
buffer.get(uavCommandCodeId);
uavControlUniIdLength=buffer.get();
uavControlUniId = ByteUtils.getString(buffer, uavControlUniIdLength);
} catch (Exception e) {
log.error("中心指控-指令数据解析异常");
e.printStackTrace();
}
}
}

@ -0,0 +1,42 @@
package com.platform.cac.tcp.message.dataframe.receive;
import com.platform.cac.tcp.message.dataframe.RemoteTcpBaseDataFrame;
import com.platform.util.ByteUtils;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import java.nio.ByteBuffer;
@Getter
@Slf4j
public class CacUavControlApplyMessage extends RemoteTcpBaseDataFrame {
private byte applyGcsIdLength;
private String applyGcsId;
private byte uavControlApplyReasonLength;
private String uavControlApplyReason;
private byte uavControlApplyIdLength;
private String uavControlApplyId;
public CacUavControlApplyMessage(RemoteTcpBaseDataFrame data){
super(data);
ByteBuffer buffer = ByteBuffer.allocate(data.getReadableDataBytesLength()).put(data.getReadableDataBytes());
buffer.flip();
try {
applyGcsIdLength = buffer.get();
applyGcsId = ByteUtils.getString(buffer, applyGcsIdLength);
uavControlApplyReasonLength = buffer.get();
uavControlApplyReason = ByteUtils.getString(buffer, uavControlApplyReasonLength);
uavControlApplyIdLength = buffer.get();
uavControlApplyId = ByteUtils.getString(buffer, uavControlApplyIdLength);
} catch (Exception e){
log.error("中心指控-控制权申请数据解析异常");
e.printStackTrace();
}
}
}

@ -0,0 +1,37 @@
package com.platform.cac.tcp.message.dataframe.receive;
import com.platform.cac.tcp.message.dataframe.RemoteTcpBaseDataFrame;
import com.platform.util.ByteUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.nio.ByteBuffer;
@Data
@Slf4j
public class TcpRemoteAuthorization extends RemoteTcpBaseDataFrame {
private byte success;
private int code;
private byte messageLength;
private String message;
public TcpRemoteAuthorization(RemoteTcpBaseDataFrame data) {
super(data);
ByteBuffer buffer = ByteBuffer.allocate(data.getReadableDataBytesLength()).put(data.getReadableDataBytes());
buffer.flip();
try {
success = buffer.get();
code = buffer.getInt();
messageLength = buffer.get();
message = ByteUtils.getString(buffer, messageLength);
} catch (Exception e) {
log.error("中心指控-地面站认证数据解析异常");
e.printStackTrace();
}
}
}

@ -0,0 +1,21 @@
package com.platform.cac.tcp.message.dataframe.send;
import com.platform.cac.tcp.message.dataframe.RemoteTcpBaseDataFrame;
import lombok.Data;
/**
* @Author sunjipeng
* @Date 2023/6/14
* @Description
*/
@Data
public class TcpFlightPlanReplyRequest extends RemoteTcpBaseDataFrame {
private byte applyFlightPlanIdLength;
private String applyFlightPlanId;
private byte replyFlightPlanIdLength;
private String replyFlightPlanId;
private byte flightPlanPass;
}

@ -0,0 +1,20 @@
package com.platform.cac.tcp.message.dataframe.send;
import com.platform.cac.tcp.message.dataframe.RemoteTcpBaseDataFrame;
import lombok.Data;
/**
* @Author sunjipeng
* @Date 2023/6/28
* @Description
*/
@Data
public class TcpFlightPlanRevokeRequest extends RemoteTcpBaseDataFrame {
private byte applyFlightPlanIdLength;
private String applyFlightPlanId;
private byte revokeReasonLength;
private String revokeReason;
}

@ -0,0 +1,21 @@
package com.platform.cac.tcp.message.dataframe.send;
import com.platform.cac.tcp.message.dataframe.RemoteTcpBaseDataFrame;
import lombok.Data;
/**
* @Author sunjipeng
* @Date 2023/6/14
* @Description
*/
@Data
public class TcpFlyReplyRequest extends RemoteTcpBaseDataFrame {
private byte applyFlyIdLength;
private String applyFlyId;
private byte replyFlyIdLength;
private String replyFlyId;
private byte flyPass;
}

@ -0,0 +1,17 @@
package com.platform.cac.tcp.message.dataframe.send;
import com.platform.cac.tcp.message.dataframe.RemoteTcpBaseDataFrame;
import lombok.Data;
/**
* @Author sunjipeng
* @Date 2023/6/14
* @Description
*/
@Data
public class TcpHeartBeatRequest extends RemoteTcpBaseDataFrame {
long currentTime;
}

@ -0,0 +1,19 @@
package com.platform.cac.tcp.message.dataframe.send;
import com.platform.cac.tcp.message.dataframe.RemoteTcpBaseDataFrame;
import lombok.Data;
/**
* @Author sunjipeng
* @Date 2023/6/14
* @Description
*/
@Data
public class TcpReceiveAlarmRequest extends RemoteTcpBaseDataFrame {
private int alarmLevel;
private byte contentLength;
private String content;
private long effectTime;
}

@ -0,0 +1,18 @@
package com.platform.cac.tcp.message.dataframe.send;
import com.platform.cac.tcp.message.dataframe.RemoteTcpBaseDataFrame;
import lombok.Data;
/**
* @Author sunjipeng
* @Date 2023/6/14
* @Description
*/
@Data
public class TcpReceiveAtcRequest extends RemoteTcpBaseDataFrame {
private int atcType;
private long effectTime;
}

@ -0,0 +1,14 @@
package com.platform.cac.tcp.message.dataframe.send;
import com.platform.cac.tcp.message.dataframe.RemoteTcpBaseDataFrame;
import lombok.Data;
/**
* @Author sunjipeng
* @Date 2023/6/14
* @Description
*/
@Data
public class TcpRemoteAuthCacRequest extends RemoteTcpBaseDataFrame {
}

@ -0,0 +1,58 @@
package com.platform.cac.tcp.message.handler;
import com.platform.cac.GcsService;
import com.platform.cac.tcp.message.dataframe.RemoteTcpBaseDataFrame;
import com.platform.cac.tcp.message.dataframe.receive.CacUavCommandMessage;
import com.platform.info.enums.GcsFrameEnum;
import com.platform.info.enums.RemoteFrameEnum;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @Author : shiyi
* @Date : 2024/1/9 11:28
* @Description :
*/
@Component @Slf4j
public class CacUavCommandIssuedHandler implements IRemoteMessageHandler {
@Resource
GcsService gcsService;
@Override
public void execute(Channel channel, RemoteTcpBaseDataFrame remoteTcpBaseDataFrame) {
if (gcsService.isSignIn()){
CacUavCommandMessage uavCommand = new CacUavCommandMessage(remoteTcpBaseDataFrame);
log.info("对地面站下发指令...{}", uavCommand);
// GcsService.commandUniId = uavCommand.getUavControlUniId();
// log.debug("commandUniId Info: {}", GcsService.commandUniId);
// issuedUavCommandToGcs(uavCommand);
} else {
log.warn("无法下发指令");
}
}
/**
*
* @param uavCommand
*/
// private void issuedUavCommandToGcs(CacUavCommandMessage uavCommand) {
// String uavId = uavCommand.getUavId();
// int fkUavId = UavIdMap.getFkId(uavId);
//
// ByteBuf bufToGcs = Unpooled.buffer();
// bufToGcs.writeByte((byte) fkUavId);
// bufToGcs.writeByte(uavCommand.getUavCommandCodeIdLength());
// bufToGcs.writeBytes(uavCommand.getUavCommandCodeId());
// gcsService.sendToGcs(GcsFrameEnum.ISSUED_COMMAND.getCode(), bufToGcs);
// }
@Override
public RemoteFrameEnum getFrameType() {
return RemoteFrameEnum.UAV_COMMAND_ISSUED;
}
}

@ -0,0 +1,57 @@
package com.platform.cac.tcp.message.handler;
import com.platform.cac.GcsService;
import com.platform.cac.tcp.message.dataframe.RemoteTcpBaseDataFrame;
import com.platform.cac.tcp.message.dataframe.receive.CacUavCommandMessage;
import com.platform.info.enums.GcsFrameEnum;
import com.platform.info.enums.RemoteFrameEnum;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @Author : shiyi
* @Date : 2024/1/24 17:38
* @Description :
*/
@Component
@Slf4j
public class CacUavCommandResultHandler implements IRemoteMessageHandler {
@Resource
GcsService gcsService;
@Override
public void execute(Channel channel, RemoteTcpBaseDataFrame remoteTcpBaseDataFrame) {
if (gcsService.isSignIn()){
CacUavCommandMessage uavCommand = new CacUavCommandMessage(remoteTcpBaseDataFrame);
log.debug("向地面站查询指令...{}", uavCommand);
// GcsService.queryCommandUniId= uavCommand.getUavControlUniId();
// log.debug("queryCommandUniId Info: {}", GcsService.queryCommandUniId);
queryUavCommandResult(uavCommand);
} else {
log.warn("无法查询指令");
}
}
/**
*
* @param uavCommand
*/
private void queryUavCommandResult(CacUavCommandMessage uavCommand) {
// String uavId = uavCommand.getUavId();
// int fkUavId = UavIdMap.getFkId(uavId);
// ByteBuf bufToGcs = Unpooled.buffer();
// bufToGcs.writeByte((byte) fkUavId);
// bufToGcs.writeByte(uavCommand.getUavCommandCodeIdLength());
// bufToGcs.writeBytes(uavCommand.getUavCommandCodeId());
// gcsService.sendToGcs(GcsFrameEnum.QUERY_COMMAND.getCode(), bufToGcs);
}
@Override
public RemoteFrameEnum getFrameType() {
return RemoteFrameEnum.UAV_COMMAND_QUERY;
}
}

@ -0,0 +1,60 @@
package com.platform.cac.tcp.message.handler;
import com.platform.cac.tcp.message.dataframe.RemoteTcpBaseDataFrame;
import com.platform.cac.tcp.message.dataframe.receive.CacUavControlApplyMessage;
import com.platform.info.enums.GcsFrameEnum;
import com.platform.info.enums.RemoteFrameEnum;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @Author : shiyi
* @Date : 2024/1/9 10:28
* @Description :
*/
@Component @Slf4j
public class CacUavControlApplyHandler implements IRemoteMessageHandler {
// @Resource
// GcsService gcsService;
@Override
public void execute(Channel channel, RemoteTcpBaseDataFrame remoteTcpBaseDataFrame) {
CacUavControlApplyMessage cacUavControlApplyMessage = new CacUavControlApplyMessage(remoteTcpBaseDataFrame);
applyUavControlToGcs(cacUavControlApplyMessage);
}
/**
*
* : {@link com.htfp.gcsplugin.netty.server.messagehandler.handler.GcsUavControlReply}
*
* @param cacUavControlApplyMessage
*/
private void applyUavControlToGcs(CacUavControlApplyMessage cacUavControlApplyMessage) {
// byte reason = (byte) Integer.parseInt(cacUavControlApplyMessage.getUavControlApplyReason());
// String uavId = cacUavControlApplyMessage.getUavId();
// int fkUavId = UavIdMap.getFkId(uavId);
// byte applyGcsId = (byte) Integer.parseInt(cacUavControlApplyMessage.getApplyGcsId());
// String applyId = cacUavControlApplyMessage.getUavControlApplyId();
// GcsService.applyIdToProcess.push(applyGcsId +"-" + uavId, applyId);
// log.info("收到来自地面站 {} 的对无人机 {} 控制请求, 请求原因: {}, applyId: {}", applyGcsId, fkUavId, reason, applyId);
// log.debug("applyId info: {}", GcsService.applyIdToProcess.toString());
//
// // 回复地面站
// ByteBuf bufToGcs = Unpooled.buffer();
// bufToGcs.writeByte(applyGcsId);
// bufToGcs.writeByte((byte) fkUavId);
// bufToGcs.writeByte(reason);
// gcsService.sendToGcs(GcsFrameEnum.CONTROL_FREE.getCode(), bufToGcs);
}
@Override
public RemoteFrameEnum getFrameType() {
return RemoteFrameEnum.UAV_CONTROL_APPLY;
}
}

@ -0,0 +1,46 @@
package com.platform.cac.tcp.message.handler;
import com.platform.cac.GcsService;
import com.platform.cac.RemoteService;
import com.platform.cac.tcp.message.dataframe.RemoteTcpBaseDataFrame;
import com.platform.cac.tcp.message.dataframe.receive.TcpRemoteAuthorization;
import com.platform.info.GlobalData;
import com.platform.info.enums.RemoteFrameEnum;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @Author : shiyi
* @Date : 2024/1/3 14:11
* @Description :
*/
@Component @Slf4j
public class GcsAuthResponseHandler implements IRemoteMessageHandler {
@Resource
RemoteService remoteService;
@Resource
GcsService gcsService;
@Override
public void execute(Channel channel, RemoteTcpBaseDataFrame remoteTcpBaseDataFrame) {
TcpRemoteAuthorization authResponse = new TcpRemoteAuthorization(remoteTcpBaseDataFrame);
GlobalData.GCS_SIGNIN = (0x01==authResponse.getSuccess());
if (GlobalData.GCS_SIGNIN){
remoteService.queryCacDirectControlMapping(); // 同步操作
// List<Integer> controlFkUavList = remoteService.queryGcsControlUav();
// log.info("当前在控无人机: {}", remoteService.queryGcsControlUav());
}
log.info("[tcp auth]认证上线{}, code={}, message: {}", GlobalData.GCS_SIGNIN ? "成功":"失败",authResponse.getCode(), authResponse.getMessage());
}
@Override
public RemoteFrameEnum getFrameType() {
return RemoteFrameEnum.GCS_AUTH_CAC_RESPONSE;
}
}

@ -0,0 +1,25 @@
package com.platform.cac.tcp.message.handler;
import com.platform.cac.tcp.message.dataframe.RemoteTcpBaseDataFrame;
import com.platform.info.enums.RemoteFrameEnum;
import io.netty.channel.Channel;
public interface IRemoteMessageHandler {
/**
*
*
* @param channel
* @param gcsTcpBaseDataFrame
*/
void execute(Channel channel, RemoteTcpBaseDataFrame remoteTcpBaseDataFrame);
/**
* @return
*/
RemoteFrameEnum getFrameType();
}

@ -0,0 +1,15 @@
package com.platform.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Data
@Configuration
@ConfigurationProperties(prefix = "remote-cac")
public class CacRemoteConfig {
public String ip;
public int remoteTcpPort;
public String udpIp;
public int remoteUdpPort;
}

@ -7,7 +7,7 @@ import org.springframework.context.annotation.Configuration;
@Configuration @Configuration
@Data @Data
public class ServiceConfig { public class ServiceConfig {
@Value("${log.debug}") @Value("${app.debug}")
private boolean debug; private boolean debug;
@Value("${service.port}") @Value("${service.port}")
private Integer port; private Integer port;

@ -15,7 +15,9 @@ import org.springframework.web.bind.annotation.*;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/**
* -
*/
@RestController @RestController
@Slf4j @Slf4j
public class ClientController { public class ClientController {

@ -0,0 +1,40 @@
package com.platform.info;
import com.platform.info.enums.UavTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
/**
* @Author : shiyi
* @Date : 2024/1/2 13:57
* @Description :
*/
@Configuration @Slf4j
public class GlobalData {
@Autowired
public void init(@Value("${cac_gcs.id}") String id, @Value("${cac_gcs.token}") String token) {
GlobalData.GCS_ID = id;
GlobalData.GCS_TOKEN = token;
log.info("中心指控系统-地面站ID: {}", GlobalData.GCS_ID);
}
public static String GCS_ID;
// 地面站上线请求发送之前使用token
public static String GCS_TOKEN;
public static final short REMOTE_HEAD = (short) 0x8899;
public static final byte REMOTE_VERSION1 = 0x01; // 遥测数据中的版本号, 发给运管对应type=0x0F
public static final byte REMOTE_VERSION2 = 0x02; // 遥测数据中的版本号,发给中心指控, 对应type=0x04
public static final byte REMOTE_SER_ALG = 0x00;// 序列化算法
public static UavTypeEnum UAV_TYPE;
public static boolean GCS_LOGIN = false;
public static boolean GCS_SIGNIN = false;
public static String AUTHORIZATION = "";
public static String commandUniId;
public static String queryCommandUniId;
}

@ -0,0 +1,37 @@
package com.platform.info.enums;
import lombok.Getter;
/**
* @Author : shiyi
* @Date : 2024/1/3 9:28
* @Description :
*/
@Getter
public enum ClientTypeEnum {
//
GCS((short) 0xCC06, (short) 0xCC06,"地面站"),
AUTO_AIRPORT((short) 0xCC07, (short) 0xCC07,"自动化机场"),
;
private final short readHead;
private final short writeHead;
private final String info;
ClientTypeEnum(short readHead, short writeHead, String info) {
this.readHead = readHead;
this.writeHead = writeHead;
this.info = info;
}
public static ClientTypeEnum getByHead(int head) {
for (ClientTypeEnum clientTypeEnum : ClientTypeEnum.values()) {
if (clientTypeEnum.getReadHead() == head) {
return clientTypeEnum;
}
}
return null;
}
}

@ -0,0 +1,65 @@
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,41 @@
package com.platform.info.enums;
/**
* @Author : shiyi
* @Date : 2024/2/17 16:36
* @Description :
*/
public enum GcsTypeEnum {
// 地面站类型枚举
REMOTE_GCS(0, "远程地面站"),
BOX_GCS(1, "箱式地面站"),
HANDHELD_GCS(2, "手持地面站"),
CARBIN_GCS(3, "方舱地面站"),
AIRPORT_GCS(4,"机场型地面站"),
;
private final int code;
private final String info;
GcsTypeEnum(int code, String info) {
this.code = code;
this.info = info;
}
public int getCode() {
return code;
}
public String getInfo() {
return info;
}
public static GcsTypeEnum getTypeByCode(Integer code) {
for (GcsTypeEnum enums : GcsTypeEnum.values()) {
if (enums.getCode() == code) {
return enums;
}
}
return null;
}
}

@ -0,0 +1,10 @@
package com.platform.info.enums;
/**
* @Author : shiyi
* @Date : 2023/12/28 12:02
* @Description :
*/
public interface IClientFrameEnum {
public ClientTypeEnum getClientEnum();
}

@ -0,0 +1,71 @@
package com.platform.info.enums;
/**
* @Author : shiyi
* @Date : 2024/1/2 13:46
* @Description :
*/
public enum RemoteFrameEnum {
// 运管
GCS_AUTH_CAC_REQUEST((byte) 0x01, "地面站接入数据路由认证请求"),
GCS_AUTH_CAC_RESPONSE((byte)0x02, "地面站接入数据路由认证响应"),
HEART_BEAT_REQUEST((byte) 0x03, "心跳请求"),
HEART_BEAT_RESPONSE((byte) 0x04, "心跳响应"),
FLIGHT_PLAN_REPLY_REQUEST((byte) 0x05, "飞行计划审批结果通知"),
FLIGHT_PLAN_REPLY_RESPONSE((byte) 0x06, "飞行计划审批结果响应"),
FLY_REPLY_REQUEST((byte) 0x07, "放飞申请结果通知"),
FLY_REPLY_RESPONSE((byte) 0x08, "放飞申请结果响应"),
RECEIVE_ALARM_REQUEST((byte) 0x09, "告警信息"),
RECEIVE_ALARM_RESPONSE((byte) 0x0A, "告警信息接收响应"),
RECEIVE_ATC_REQUEST( (byte) 0x0B, "管制指令通知"),
RECEIVE_ATC_RESPONSE((byte) 0x0C, "管制指令接收响应"),
FLIGHT_PLAN_REVOKE_REQUEST((byte) 0x0D, "飞行计划撤销通知"),
FLIGHT_PLAN_REVOKE_RESPONSE((byte) 0x0E, "飞行计划撤销响应"),
UAV_TELEMETRY_DATA_TRANSFER((byte) 0x0F, "无人机遥测数据透传"),
AIRSPACE_REPLY_REQUEST((byte) 0x1A, "空域申请结果通知"),
AIRSPACE_REPLY_RESPONSE((byte) 0x1B, "空域申请结果收到响应"),
// 中心指控
UAV_CONTROL_APPLY((byte) 0x10, "申请无人机控制权"),
UAV_CONTROL_REPLY((byte) 0x11, "回复无人机控制权"),
UAV_CONTROL_RECEIVE((byte) 0x12, "接收无人机控制权"),
UAV_COMMAND_ISSUED((byte) 0x13, "无人机控制指令下发"),
UAV_COMMAND_QUERY((byte) 0x14, "无人机控制指令查询"),
// 自动化机场
AIRPORT_COMMAND_ISSUED((byte) 0x15, "机场指令下发"),
AIRPORT_COMMAND_QUERY((byte) 0x16, "机场指令查询"),
AIRPORT_STATUS_QUERY((byte) 0x17, "机场状态查询"),
AIRPORT_STATE_OF_CHARGE_QUERY((byte)0x18, "电格电量查询"),
AIRPORT_CONTAINER_STATUS_QUERY((byte)0x19, "货格状态查询"),
// 推流地址
STREAM_PUSH((byte) 0x21, "推流地址");
;
private final byte code;
private final String info;
public byte getCode() {
return code;
}
public String getInfo() {
return info;
}
RemoteFrameEnum(byte code, String info) {
this.code = code;
this.info = info;
}
public static RemoteFrameEnum getByCode(byte frameCode) {
for (RemoteFrameEnum enums : RemoteFrameEnum.values()) {
if (enums.code == frameCode) {
return enums;
}
}
return null;
}
}

@ -0,0 +1,67 @@
package com.platform.info.enums;
import lombok.Getter;
/**
* @Author : shiyi
* @Date : 2024/1/9 9:27
* @Description :
*/
@Getter
public enum UavTypeEnum {
//
FP981A (0x01, 1,"981A", false),
FP981C (0x02, 2,"981C", false),
FP98 (0x04, 4,"98", true),
FP985 (0x08, 8,"985", true),
FP981CS (0x10, 16,"981CS", true),
FH981CH (0x20, 32,"981CH", true),
// 此后的编码和中心指控编码区分开
FH981CH_HYBRID (0x21, 64,"981CH-二代机混动", false),
FH981CH_ELECTRIC (0x22, 128,"981CH-二代机电动", false),
;
private final int gcsCode;
private final int remoteCode;
private final String info;
private final boolean onlyMonitor; // 仅具有监视功能,但不控制
UavTypeEnum(int code, int remoteCode, String info, boolean onlyMonitor) {
this.gcsCode = code;
this.remoteCode = remoteCode;
this.info = info;
this.onlyMonitor = onlyMonitor;
}
public int getGcsCode() {
return gcsCode;
}
public int getRemoteCode() {
return remoteCode;
}
public String getInfo() {
return info;
}
public boolean isOnlyMonitor() {
return onlyMonitor;
}
public String toString() {
return this.info;
}
public static UavTypeEnum getByGcsCode(int code) {
for (UavTypeEnum typeEnum : UavTypeEnum.values()) {
if (typeEnum.getGcsCode() == code) {
return typeEnum;
}
}
return null;
}
public static UavTypeEnum getByRemoteCode(int code) {
for (UavTypeEnum typeEnum : UavTypeEnum.values()) {
if (typeEnum.getRemoteCode() == code) {
return typeEnum;
}
}
return null;
}
}

@ -0,0 +1,55 @@
package com.platform.info.mapping;
import javax.validation.constraints.NotNull;
import java.util.concurrent.ConcurrentHashMap;
/**
* SnuavId
*/
public class HaborUavMap {
// 所有型号uavId到fkId的映射
private static final ConcurrentHashMap<String, String> habor2uavId = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<String, String> uavId2habor = new ConcurrentHashMap<>();
// /** 添加当前型号的映射关系*/
// public static void addMap(Integer fkId, String uavId){
// addMap(UAV_TYPE, fkId, uavId);
// }
/**根据飞机型号增加映射关系*/
public static void addMap(String uavId, String harboSn){
habor2uavId.put(harboSn, uavId);
uavId2habor.put(uavId, harboSn);
}
/** 获取指定型号的映射关系*/
public static String showMap(){
return "haboroSn→uavId: " + habor2uavId;
}
public static String getHaborSnByUavId(@NotNull String uavId){
return uavId2habor.get(uavId);
}
public static String getUavIdByHaborSn(@NotNull String haborSn){
return habor2uavId.get(haborSn);
}
/** 当前地面站是否可控哈勃 */
public static boolean haborIsControllable(@NotNull String haborSn){
return habor2uavId.contains(haborSn) && uavId2habor.containsValue(haborSn);
}
/** 当前地面站是否可控uavId*/
public static boolean uavIsControllable(@NotNull String uavId){
return uavId2habor.contains(uavId) && habor2uavId.containsValue(uavId);
}
public static void clear() {
habor2uavId.clear();
uavId2habor.clear();
}
}

@ -0,0 +1,100 @@
package com.platform.info.mapping;
import com.platform.info.enums.UavTypeEnum;
import java.util.concurrent.ConcurrentHashMap;
/**
* Note:
*/
public class UavIdControlMap {
// 所有型号uavId到fkId的映射
private static final ConcurrentHashMap<String, Integer> globalMapId = new ConcurrentHashMap<>();
// 各个型号fkId到uavId的映射
private static final ConcurrentHashMap<Integer, String> uavMapId981A = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<Integer, String> uavMapId981C = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<Integer, String> uavMapId98 = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<Integer, String> uavMapId985 = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<Integer, String> uavMapId981CS = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<UavTypeEnum, ConcurrentHashMap<Integer, String>> uavTypeMapId = new ConcurrentHashMap<>();
static {
uavTypeMapId.put(UavTypeEnum.FP981A, uavMapId981A);
uavTypeMapId.put(UavTypeEnum.FP981C, uavMapId981C);
uavTypeMapId.put(UavTypeEnum.FP98, uavMapId98);
uavTypeMapId.put(UavTypeEnum.FP985, uavMapId985);
uavTypeMapId.put(UavTypeEnum.FP981CS, uavMapId981CS);
}
// private static BidiMap<Integer, String> uavMapId = new DualHashBidiMap<>();
/**根据飞机型号增加映射关系*/
public static void addMap(UavTypeEnum uavType, Integer fkId, String uavId){
globalMapId.put(uavId, fkId);
if (uavType != null && uavTypeMapId.containsKey(uavType)) {
uavTypeMapId.get(uavType).put(fkId, uavId);
}
}
/** 获取指定型号的映射关系*/
public static String showMap(UavTypeEnum uavType){
if (uavType != null && uavTypeMapId.containsKey(uavType)) {
return uavTypeMapId.get(uavType).toString();
}
return null;
}
/**
*
*/
public static String showAllMap(){
StringBuilder sb = new StringBuilder();
sb.append("{\n");
uavTypeMapId.forEach((uavType, uavIdMap) -> {
sb.append(uavType.getInfo()).append(":").append(uavTypeMapId.get(uavType)).append("\n");
});
sb.append("}");
return sb.toString();
}
/**根据指定型号的fkId获取uavId*/
public static String getUavId(UavTypeEnum uavType, Integer fkId){
ConcurrentHashMap<Integer, String> uavMapId = uavTypeMapId.get(uavType);
if (fkId ==null || uavMapId == null|| !uavMapId.containsKey(fkId)){
throw new IllegalArgumentException(uavType.getInfo() + "类型fkId=" + fkId + " 无法转换为uavId 该类型映射关系: " + uavMapId);
}
return uavMapId.get(fkId);
}
/**
* uavIdid
*/
public static int getFkId(String uavId){
if (uavId ==null || !globalMapId.containsKey(uavId)){
throw new IllegalArgumentException("uavId=" + uavId + " not found in uavMapId: " + globalMapId);
}
return globalMapId.get(uavId);
}
/** uavId是否是当前地面站可控 */
public static boolean uavIdControlling(UavTypeEnum uavType, String uavId){
return uavTypeMapId.get(uavType).containsValue(uavId);
}
/** fkId是否是当前地面站可控 */
public static boolean fkIdControlling(UavTypeEnum uavType, Integer fkId){
return uavTypeMapId.get(uavType).containsKey(fkId);
}
public static void clear() {
uavTypeMapId.forEach((uavType, uavIdMap) -> uavIdMap.clear());
}
}

@ -0,0 +1,113 @@
package com.platform.info.mapping;
import com.platform.info.enums.UavTypeEnum;
import java.util.concurrent.ConcurrentHashMap;
/**
* fkIduavId
* fkIduavId
* fkIduavIduav_type
*/
public class UavIdMap {
// 所有型号uavId到fkId的映射
private static final ConcurrentHashMap<String, Integer> globalMapId = new ConcurrentHashMap<>();
// 各个型号fkId到uavId的映射
private static final ConcurrentHashMap<Integer, String> uavMapId981A = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<Integer, String> uavMapId981C = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<Integer, String> uavMapId98 = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<Integer, String> uavMapId985 = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<Integer, String> uavMapId981CS = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<Integer, String> uavMapId981CH = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<UavTypeEnum, ConcurrentHashMap<Integer, String>> uavTypeMapId = new ConcurrentHashMap<>();
static {
uavTypeMapId.put(UavTypeEnum.FP981A, uavMapId981A);
uavTypeMapId.put(UavTypeEnum.FP981C, uavMapId981C);
uavTypeMapId.put(UavTypeEnum.FP98, uavMapId98);
uavTypeMapId.put(UavTypeEnum.FP985, uavMapId985);
uavTypeMapId.put(UavTypeEnum.FP981CS, uavMapId981CS);
uavTypeMapId.put(UavTypeEnum.FH981CH, uavMapId981CH);
}
// private static BidiMap<Integer, String> uavMapId = new DualHashBidiMap<>();
// /** 添加当前型号的映射关系*/
// public static void addMap(Integer fkId, String uavId){
// addMap(UAV_TYPE, fkId, uavId);
// }
/**根据飞机型号增加映射关系*/
public static void addMap(UavTypeEnum uavType, Integer fkId, String uavId){
globalMapId.put(uavId, fkId);
if (uavType != null && uavTypeMapId.containsKey(uavType)) {
uavTypeMapId.get(uavType).put(fkId, uavId);
}
}
// /** 获取当前型号的映射关系*/
// public static String showMap(){
// return showMap(UAV_TYPE);
// }
/** 获取指定型号的映射关系*/
public static String showMap(UavTypeEnum uavType){
if (uavType != null && uavTypeMapId.containsKey(uavType)) {
return uavTypeMapId.get(uavType).toString();
}
return null;
}
/**
*
*/
public static String showAllMap(){
StringBuilder sb = new StringBuilder();
sb.append("{\n");
uavTypeMapId.forEach((uavType, uavIdMap) -> {
sb.append(uavType.getInfo()).append(":").append(uavTypeMapId.get(uavType)).append("\n");
});
sb.append("}");
return sb.toString();
}
/**根据指定型号的fkId获取uavId*/
public static String getUavId(UavTypeEnum uavType, Integer fkId){
ConcurrentHashMap<Integer, String> uavMapId = uavTypeMapId.get(uavType);
if (fkId ==null || uavMapId == null|| !uavMapId.containsKey(fkId)){
throw new IllegalArgumentException(uavType.getInfo() + "类型fkId=" + fkId + " 无法转换为uavId 该类型映射关系: " + uavMapId);
}
return uavMapId.get(fkId);
}
/**
* uavIdid
*/
public static int getFkId(String uavId){
if (uavId ==null || !globalMapId.containsKey(uavId)){
throw new IllegalArgumentException("uavId=" + uavId + " not found in uavMapId: " + globalMapId);
}
return globalMapId.get(uavId);
}
/** uavId是否是当前地面站可控 */
public static boolean uavIdControllable(UavTypeEnum uavType, String uavId){
return uavTypeMapId.get(uavType).containsValue(uavId);
}
/** fkId是否是当前地面站可控 */
public static boolean fkIdControllable(UavTypeEnum uavType, Integer fkId){
return uavTypeMapId.get(uavType).containsKey(fkId);
}
public static void clear() {
uavTypeMapId.forEach((uavType, uavIdMap) -> uavIdMap.clear());
}
}

@ -0,0 +1,11 @@
package com.platform.model;
import lombok.Data;
@Data
public class DirectControlUavParam {
String harborSn;
String flightControlSn;
String uavId;
Integer uavType;
}

@ -0,0 +1,17 @@
package com.platform.model;
import lombok.Data;
import lombok.ToString;
/**
* @Author : shiyi
* @Date : 2024/2/18 9:16
* @Description : HTTP
*/
@Data
public class Result<T> {
boolean success;
String message;
Integer code;
T data;
}

@ -77,7 +77,7 @@ 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; byte[] msg = (byte[])msg0; // TODO 2025/1/16:
Byte fkID = msg[4]; Byte fkID = msg[4];

@ -43,7 +43,7 @@ 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()); channel.pipeline().addLast(new ByteArrayDecoder()); // revise by shiyi: 解码移动到InMessageHandler中
channel.pipeline().addLast(new ByteArrayEncoder()); channel.pipeline().addLast(new ByteArrayEncoder());
channel.pipeline().addLast(new IdleStateHandler(0, 0, channel.pipeline().addLast(new IdleStateHandler(0, 0,
60 * 24, TimeUnit.MINUTES)); 60 * 24, TimeUnit.MINUTES));

@ -0,0 +1,50 @@
package com.platform.util;
import org.apache.tomcat.util.codec.binary.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
public class BaseAuthorizationUtils {
public static void generateAuthAndDateHeader(Map<String, String> header, String method, String gcsId, String token, String uri) {
Date sysdate = new Date();
SimpleDateFormat df = new SimpleDateFormat("EEE\', \'dd\' \'MMM\' \'yyyy\' \'HH:mm:ss\' \'z", Locale.US);
df.setTimeZone(TimeZone.getTimeZone("GMT"));
String date = df.format(sysdate);
header.put("authorization", generateAuth(method, date, gcsId, token, uri));
header.put("gcsId", gcsId);
header.put("date", date);
}
public static String generateAuth(String method, String date, String gcsId, String token, String uri){
String string_to_sign = method.toUpperCase() + " " + uri + "\n" + date;
String sig = token;
String encoding = "";
try {
byte[] signature = getSignature(string_to_sign.getBytes(), sig.getBytes());
encoding = new String(Base64.encodeBase64(signature));
} catch (Exception var10) {
return null;
}
return gcsId + ":" + encoding;
}
public static byte[] getSignature(byte[] data, byte[] key) throws NoSuchAlgorithmException, InvalidKeyException {
SecretKeySpec signingKey = new SecretKeySpec(key, "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(data);
return rawHmac;
}
}

@ -0,0 +1,543 @@
package com.platform.util;
import java.io.*;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
public class ByteUtils {
/**
* maclong
*
* @param strMac
* @return
*/
public static long macToLong(String strMac) {
byte[] mb = new BigInteger(strMac, 16).toByteArray();
ByteBuffer mD = ByteBuffer.allocate(mb.length);
mD.put(mb);
long mac = 0;
// 如果长度等于8代表没有补0;
if (mD.array().length == 8) {
mac = mD.getLong(0);
} else if (mD.array().length == 9) {
mac = mD.getLong(1);
}
return mac;
}
public static byte[] getBytes(Object obj) throws IOException {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bout);
out.writeObject(obj);
out.flush();
byte[] bytes = bout.toByteArray();
bout.close();
out.close();
return bytes;
}
/**
* hexStr2Byte</br> String
*
* @param hex
*/
public static byte[] hexStr2Byte(String hex) {
ByteBuffer bf = ByteBuffer.allocate(hex.length() / 2);
for (int i = 0; i < hex.length(); i++) {
String hexStr = hex.charAt(i) + "";
i++;
hexStr += hex.charAt(i);
byte b = (byte) Integer.parseInt(hexStr, 16);
bf.put(b);
}
return bf.array();
}
/**
* byteToHex</br> byte16
*
* @param b
*/
public static String byteToHex(byte b) {
String hex = Integer.toHexString(b & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
return hex.toUpperCase(Locale.getDefault());
}
public static Object getObject(byte[] bytes) throws IOException,
ClassNotFoundException {
ByteArrayInputStream bi = new ByteArrayInputStream(bytes);
ObjectInputStream oi = new ObjectInputStream(bi);
Object obj = oi.readObject();
bi.close();
oi.close();
return obj;
}
public static ByteBuffer getByteBuffer(Object obj) throws IOException {
byte[] bytes = ByteUtils.getBytes(obj);
ByteBuffer buff = ByteBuffer.wrap(bytes);
return buff;
}
/**
* byte[] short 2
*
* @param bytes
* @return
*/
public static short bytesToshort(byte[] bytes) {
return (short) ((bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00));
}
/**
* byte Int
*
* @param b
* @return
*/
public static int byteToInt(byte b) {
return (b) & 0xff;
}
/**
* @
* @param
* @return
*/
public static int bytesToInt(byte[] bytes,int offset) {
int num = bytes[offset] & 0xFF;
num |= ((bytes[offset+1] << 8) & 0xFF00);
num |= ((bytes[offset+2] << 16) & 0xFF0000);
num |= ((bytes[offset+3] << 24) & 0xFF000000);
return num;
}
public static byte[] intToByte(int i) {
byte[] abyte0 = new byte[4];
abyte0[0] = (byte) (0xff & i);
abyte0[1] = (byte) ((0xff00 & i) >> 8);
abyte0[2] = (byte) ((0xff0000 & i) >> 16);
abyte0[3] = (byte) ((0xff000000 & i) >> 24);
return abyte0;
}
public static byte[] LongToByte(Long i) {
byte[] abyte0 = new byte[8];
abyte0[0] = (byte) (0xff & i);
abyte0[1] = (byte) ((0xff00 & i) >> 8);
abyte0[2] = (byte) ((0xff0000 & i) >> 16);
abyte0[3] = (byte) ((0xff000000 & i) >> 24);
abyte0[4] = (byte) ((0xff00000000l & i) >> 32);
abyte0[5] = (byte) ((0xff0000000000l & i) >> 40);
abyte0[6] = (byte) ((0xff000000000000l & i) >> 48);
abyte0[7] = (byte) ((0xff00000000000000l & i) >> 56);
return abyte0;
}
/**
* shortChange</br> short
*
* @param mshort
*/
public static short shortChange(Short mshort) {
mshort = (short) ((mshort >> 8 & 0xFF) | (mshort << 8 & 0xFF00));
return mshort;
}
/**
* intChange</br> int
*
* @param mint
*/
public static int intChange(int mint) {
mint = (int) (((mint) >> 24 & 0xFF) | ((mint) >> 8 & 0xFF00)
| ((mint) << 8 & 0xFF0000) | ((mint) << 24 & 0xFF000000));
return mint;
}
/**
* intChange</br> LONG
*
* @param mint
*/
public static long longChange(long mint) {
mint = (long) (((mint) >> 56 & 0xFF) | ((mint) >> 48 & 0xFF00)
| ((mint) >> 24 & 0xFF0000) | ((mint) >> 8 & 0xFF000000)
| ((mint) << 8 & 0xFF00000000l)
| ((mint) << 24 & 0xFF0000000000l)
| ((mint) << 40 & 0xFF000000000000l) | ((mint) << 56 & 0xFF00000000000000l));
return mint;
}
/**
* byteshort
*
* @param b
*
* @return short
*/
public static short byteToUshort(byte b) {
return (short) (b & 0x00ff);
}
/**
* byteint
*
* @param b
*
* @return int
*/
public static int byteToUint(byte b) {
return b & 0x00ff;
}
/**
* bytelong
*
* @param b
*
* @return long
*/
public static long byteToUlong(byte b) {
return b & 0x00ff;
}
/**
* shortint
*
* @param s
* short
* @return int
*/
public static int shortToUint(short s) {
return s & 0x00ffff;
}
/**
* shortlong
*
* @param s
*
* @return long
*/
public static long shortToUlong(short s) {
return s & 0x00ffff;
}
/**
* intlong
*
* @param i
*
* @return long
*/
public static long intToUlong(int i) {
return i & 0x00ffffffff;
}
/**
* shortbyte
*
* @param s
* short
* @return byte
*/
public static byte[] shortToLittleEndianByteArray(short s) {
return ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN)
.putShort(s).array();
}
/**
* intbyte
*
* @param i
* int
* @return byte
*/
public static byte[] intToLittleEndianByteArray(int i) {
return ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(i)
.array();
}
/**
* longbyte
*
* @param l
* long
* @return byte
*/
public static byte[] longToLittleEndianByteArray(long l) {
return ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(l)
.array();
}
/**
* shortbyte
*
* @param s
* short
* @return byte
*/
public static byte[] shortToBigEndianByteArray(short s) {
return ByteBuffer.allocate(2).order(ByteOrder.BIG_ENDIAN).putShort(s)
.array();
}
/**
* intbyte
*
* @param i
* int
* @return byte
*/
public static byte[] intToBigEndianByteArray(int i) {
return ByteBuffer.allocate(2).order(ByteOrder.BIG_ENDIAN).putInt(i)
.array();
}
/**
* longbyte
*
* @param l
* long
* @return byte
*/
public static byte[] longToBigEndianByteArray(long l) {
return ByteBuffer.allocate(2).order(ByteOrder.BIG_ENDIAN).putLong(l)
.array();
}
/**
* short16
*
* @param s
* short
* @param isLittleEndian
* truefalse
* @return
*/
public static String shortToHexString(short s, boolean isLittleEndian) {
byte byteArray[] = null;
if (isLittleEndian) {
byteArray = shortToLittleEndianByteArray(s);
} else {
byteArray = shortToBigEndianByteArray(s);
}
return byteArrayToHexString(byteArray);
}
/**
* int16
*
* @param i
* int
* @param isLittleEndian
* truefalse
* @return
*/
public static String intToHexString(int i, boolean isLittleEndian) {
byte byteArray[] = null;
if (isLittleEndian) {
byteArray = intToLittleEndianByteArray(i);
} else {
byteArray = intToBigEndianByteArray(i);
}
return byteArrayToHexString(byteArray);
}
/**
* long16
*
* @param l
* long
* @param isLittleEndian
* truefalse
* @return
*/
public static String longToHexString(long l, boolean isLittleEndian) {
byte byteArray[] = null;
if (isLittleEndian) {
byteArray = longToLittleEndianByteArray(l);
} else {
byteArray = longToBigEndianByteArray(l);
}
return byteArrayToHexString(byteArray);
}
/**
* 16
*
* @param array
*
* @param toPrint
* true4
* @return
*/
public static String byteArrayToHexString(byte[] array, boolean toPrint) {
if (array == null) {
return "null";
}
StringBuffer sb = new StringBuffer();
for (int i = 0; i < array.length; i++) {
sb.append(byteToHex(array[i]));
if (toPrint && (i + 1) % 4 == 0) {
sb.append(" ");
}
}
return sb.toString();
}
/**
* 16
*
* @param array
* ()
* @return
*/
public static String byteArrayToHexString(byte[] array) {
return byteArrayToHexString(array, false);
}
public static String bytes2HexString(byte[] b) {
String ret = "";
for (int i = 0; i < b.length; i++) {
String hex = Integer.toHexString(b[ i ] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
ret += hex.toUpperCase()+" ";
}
return ret;
}
/**
* long
*
* @param bytes
*
* @return long
*/
public static long byteArrayToLong(byte[] bytes) {
return ((((long) bytes[0] & 0xff) << 24)
| (((long) bytes[1] & 0xff) << 16)
| (((long) bytes[2] & 0xff) << 8) | (((long) bytes[3] & 0xff) << 0));
}
public static byte[] getCRC(int[] ptr){
int[] crc_16_table = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
};
int crc = 0x0000;
int da = 0x00;
for (int j : ptr) {
da = (crc & 0xff00) >> 8;
crc = (crc & 0x00ff) << 8;
int a = da ^ j;
int b = crc_16_table[a];
crc ^=b;
}
return intToLittleEndianByteArray(crc);
}
/**
* CRC16
*
* @param bytes
* @return
*/
public static byte[] getRtuCRC(int[] bytes) {
int CRC = 0x0000ffff;
int POLYNOMIAL = 0x0000a001;
int i, j;
for (i = 0; i < bytes.length; i++) {
CRC ^= (bytes[i] & 0x000000ff);
for (j = 0; j < 8; j++) {
if ((CRC & 0x00000001) != 0) {
CRC >>= 1;
CRC ^= POLYNOMIAL;
} else {
CRC >>= 1;
}
}
}
// CRC = ( (CRC & 0x0000FF00) >> 8) | ( (CRC & 0x000000FF ) << 8);
return intToLittleEndianByteArray(CRC);
}
/**
* ByteBuffer
* @param buffer
* @param length
* @return
*/
public static String getString(ByteBuffer buffer, int length){
byte[] bytes = new byte[length];
buffer.get(bytes);
return new String(bytes, StandardCharsets.UTF_8);
}
}

@ -0,0 +1,147 @@
package com.platform.util;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import okhttp3.Request.Builder;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @Author : shiyi
* @Date : 2024/1/23 16:19
* @Description : HttpClient
*/
@Slf4j
public class HttpClientUtils {
public static final String MEDIA_TYPE_JSON = "application/json; charset=utf-8";
private static final OkHttpClient OKHTTP_CLIENT = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build();
private static final OkHttpClient DONLOAD_OKHTTP_CLIENT = new OkHttpClient.Builder()
.build();
// public static <T> T sendGet(String url, Map<String, String> params, Class<T> responseClass) {
// MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
// if (params != null) {
// params.forEach(queryParams::add);
// }
// Mono<T> mono = WebClient.create(url).method(HttpMethod.GET)
// .uri(uriBuilder -> uriBuilder
// .queryParams(queryParams)
// .build()).retrieve().bodyToMono(responseClass);
// T responseBody;
// try {
// responseBody = mono.block();
// } catch (WebClientResponseException e) {
// log.error("Get 请求错误, {}", e.getMessage());
// return null;
// }
// return responseBody;
// }
public static <T> T sendGet(String url, Map<String, String> params, Class<T> responseClass) throws Exception {
String jsonStr = sendGet(url, params);
return JSONUtils.json2obj(jsonStr, responseClass);
}
public static String sendGet(String url, Map<String, String> params) throws IOException {
StringBuilder stringBuilder = new StringBuilder();
if (!CollectionUtils.isEmpty(params)) {
for (String res : params.keySet()) {
if (res == null || params.get(res) == null) {
continue;
}
if (StringUtils.isNotBlank(stringBuilder)) {
stringBuilder.append("&");
} else {
stringBuilder.append("?");
}
try {
stringBuilder.append(String.format("%s=%s", res, URLEncoder.encode(params.get(res), "UTF-8")));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
}
Request request = new Builder()
.url(url + stringBuilder)
.build();
try (Response response = OKHTTP_CLIENT.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("okhttp Unexpected code " + response);
}
ResponseBody body = response.body();
if (body != null) {
return body.string();
} else {
return null;
}
}
}
public static <T> T sendPost(String url, String json, Map<String, String> headers, Class<T> responseClass) throws Exception {
String jsonStr = sendPost(url, json, headers);
return JSONUtils.json2obj(jsonStr, responseClass);
}
public static <T> T sendPost(String url, String json, Class<T> responseClass) throws Exception {
String jsonStr = sendPost(url, json, (Map) null);
return JSONUtils.json2obj(jsonStr, responseClass);
}
public static String sendPost(String url, String json, Map<String, String> headers) throws IOException {
RequestBody requestBody = RequestBody.create(MediaType.parse(MEDIA_TYPE_JSON), json);
Builder requestBuilder = new Builder()
.url(url)
.post(requestBody);
// 添加请求头
if (!CollectionUtils.isEmpty(headers)) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
requestBuilder.addHeader(entry.getKey(), entry.getValue());
}
}
Request request = requestBuilder.build();
try (Response response = OKHTTP_CLIENT.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("okhttp Unexpected code " + response);
}
ResponseBody body = response.body();
if (body != null) {
return body.string();
} else {
return null;
}
}
}
public static void downloadFileByUrl(String sourceUrl, String localPath) throws IOException {
File file = new File(localPath);
Request request = new Request.Builder()
.url(sourceUrl)
.build();
try (Response response = DONLOAD_OKHTTP_CLIENT.newCall(request).execute()) {
if (response.code() == 200) {
assert response.body() != null;
InputStream stream = response.body().byteStream();
long dataBytes = Files.copy(stream, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
log.debug("文件{}下载完成,文件大小: {} bytes", localPath, dataBytes);
} else {
throw new RuntimeException("文件下载失败Response" + response);
}
}
}
}

@ -0,0 +1,100 @@
package com.platform.util;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.platform.model.Result;
import io.swagger.models.auth.In;
import lombok.Data;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class JSONUtils {
private final static ObjectMapper objectMapper = new ObjectMapper();
private JSONUtils() {
}
public static ObjectMapper getInstance() {
return objectMapper;
}
/**
* javaBean,list,array convert to json string
*/
public static String obj2json(Object obj) throws Exception {
return objectMapper.writeValueAsString(obj);
}
/**
* json string convert to javaBean
*/
public static <T> T json2obj(String jsonStr, Class<T> clazz)
throws Exception {
return objectMapper.readValue(jsonStr, clazz);
}
/**
* JSON T<C>
*/
public static <T, C> T json2obj(String json, Class<T> baseClass, Class<C> genericClass) throws IOException {
// 构造目标类型的 JavaType
JavaType javaType = objectMapper.getTypeFactory().constructParametricType(baseClass, genericClass);
// 使用 ObjectMapper 转换 JSON 字符串为目标类型
return objectMapper.readValue(json, javaType);
}
/**
* json string convert to map
*/
public static <T> Map<String, Object> json2map(String jsonStr)
throws Exception {
return objectMapper.readValue(jsonStr, Map.class);
}
/**
* json string convert to map with javaBean
*/
public static <T> Map<String, T> json2map(String jsonStr, Class<T> clazz)
throws Exception {
Map<String, Map<String, Object>> map = (Map<String, Map<String, Object>>) objectMapper.readValue(jsonStr,
new TypeReference<Map<String, T>>() {
});
Map<String, T> result = new HashMap<String, T>();
for (Map.Entry<String, Map<String, Object>> entry : map.entrySet()) {
result.put(entry.getKey(), map2obj(entry.getValue(), clazz));
}
return result;
}
/**
* json array string convert to list with javaBean
*/
public static <T> List<T> json2list(String jsonArrayStr, Class<T> clazz)
throws Exception {
List<Map<String, Object>> list = (List<Map<String, Object>>) objectMapper.readValue(jsonArrayStr,
new TypeReference<List<T>>() {
});
List<T> result = new ArrayList<T>();
for (Map<String, Object> map : list) {
result.add(map2obj(map, clazz));
}
return result;
}
/**
* map convert to javaBean
*/
public static <T> T map2obj(Map map, Class<T> clazz) {
return objectMapper.convertValue(map, clazz);
}
public static void main(String[] args) throws Exception {
}
}

@ -2,5 +2,33 @@ server:
port: 11727 port: 11727
service: service:
port: 11728 port: 11728
log: app:
debug: false debug: false
# 中心指控系统 tcp udp 配置
remote-cac:
ip: 123.57.54.1
remoteTcpPort: 4567
# 中心指控系统 http 配置
http-cac:
host: 123.57.54.1:8048
# 中心指控系统-地面站配置
cac_gcs:
id: 18
token: SFRGUEJYR0NTMjAyNTAxMTQ=
logging:
level:
com.com.platform: debug
management:
endpoints:
web:
exposure:
include: 'loggers'

@ -1,49 +0,0 @@
Configuration:
status: warn
Properties: # 定义全局变量
Property: # 缺省配置用于开发环境。其他环境需要在VM参数中指定如下
#测试:-Dlog.level.console=warn -Dlog.level.xjj=trace
#生产:-Dlog.level.console=warn -Dlog.level.xjj=info
- name: log.level.console
value: trace
- name: log.path
value: ../logs #
- name: project.name
value: netty-all
Appenders:
Console: #输出到控制台
name: CONSOLE
target: SYSTEM_OUT
PatternLayout:
#pattern: "%d{yyyy-MM-dd HH:mm:ss,SSS}:%4p %t (%F:%L) - %m%n"
pattern: "%d [%X{X-B3-TraceId},%X{X-B3-SpanId},%X{X-B3-ParentSpanId},%X{X-Span-Export}] %-5p %c:%L [%t] - %m%n"
RollingFile:
- name: ROLLING_FILE
ignoreExceptions: false
fileName: ${log.path}/${project.name}.log
filePattern: "${log.path}/$${date:yyyy-MM}/${project.name}-%d{yyyy-MM-dd}-%i.log"
PatternLayout:
pattern: "%d [%X{X-B3-TraceId},%X{X-B3-SpanId},%X{X-B3-ParentSpanId},%X{X-Span-Export}] %-5p %c:%L [%t] - %m%n"
Policies:
TimeBasedTriggeringPolicy:
modulate: true
interval: 1
SizeBasedTriggeringPolicy:
size: "50M"
DefaultRolloverStrategy:
max: 100
Loggers:
Root:
level: info
AppenderRef:
- ref: CONSOLE
- ref: ROLLING_FILE
Logger:
- name: com.platform
additivity: false
level: debug
AppenderRef:
- ref: CONSOLE
- ref: ROLLING_FILE
Loading…
Cancel
Save