springboot中使用JNA调用c++sdk总结
主要问题是JNA的接口、实体、指针、传参、参数、接收参数、调用方式等等问题。
还有就是sdk支持多少位的,jdk版本也得用多少位的
注意:Springboot项目中引用devtools工具可能会引发一直重启问题
结构体对应问题
一般结构体
c++中:
//用户名登录信息
typedef struct tagHikLoginInfo
{
char szUserName[64]; // 登录名称
char szPassword[64]; // 密码
char szServerUrl[64]; // CMS服务地址
unsigned int uServerPort; // 服务器端口
int nErrCode; // 登入失败错误码
char szErrInfo[512]; // 登入失败错误详情
}HikLoginInfo;
对应java中结构体:
其中 TODO 注释1和 TODO 注释3处必选其一(字段顺序固定),TODO 注释2处是转json忽略此处
@Data
@Structure.FieldOrder({"szUserName", "szPassword", "szServerUrl", "uServerPort", "nErrCode", "szErrInfo"}) // TODO 注释1
public class HikLoginInfo extends Structure {
@JsonIgnoreProperties(value = {"pointer"}) // TODO 注释2
public static class ByReference extends HikLoginInfo implements Structure.ByReference {
}
@JsonIgnoreProperties(value = {"pointer"})
public static class ByValue extends HikLoginInfo implements Structure.ByValue {
}
public byte[] szUserName = new byte[64]; // 登录名称
public byte[] szPassword = new byte[64]; // 密码
public byte[] szServerUrl = new byte[64]; // CMS服务器地址
public int uServerPort; // 服务器端口
public int nErrCode; // 登入失败错误码
public byte[] szErrInfo = new byte[512]; // 登入失败错误详情
// @Override
// protected List getFieldOrder() { // TODO 注释3
// return Arrays.asList(new String[]{"szUserName", "szPassword", "szServerUrl", "uServerPort", "nErrCode", "szErrInfo"});
// }
}
带有指针的结构体
c++中:
typedef struct tagPresetInfo
{
int nPresetNo; //预置点号,对应于设备中的顺序
char szPresetName[130]; //预置点名称
tagPresetInfo* pNext;
}HikPresetInfo;
java中对应实体:
@Data
@Structure.FieldOrder({"nPresetNo", "szPresetName", "pNext"})
public class HikPresetInfo extends Structure {
@JsonIgnoreProperties(value = {"pointer"})
public static class ByReference extends HikPresetInfo implements Structure.ByReference {
}
@JsonIgnoreProperties(value = {"pointer"})
public static class ByValue extends HikPresetInfo implements Structure.ByValue {
}
public int nPresetNo; // 预置点号,对应于设备中的顺序
public byte[] szPresetName = new byte[130]; // 预置点名称
public HikPresetInfo.ByReference pNext;
}
对接sdk函数接口问题
一般函数调用:
c++中:
// 无参函数
int HikPt_Init ();
// 有参函数
int HikPt_Login (HikLoginInfo* pLoginInfo); //in参数是输入参数
int HikPt_GetResCellList (
HikCellInfo* pResCellList, // out参数是输出参数
int& nListSize, // out参数是输出参数
int nResPrivilege, //in参数是输入参数
int nParentCellId); //in参数是输入参数
java中对应:
// 3.3.1 初始化
int HikPt_Init();
// 3.4.1 用户登录
int HikPt_Login(HikLoginInfo hikLoginInfo);
// 3.5.1 获取资源组织树列表
int HikPt_GetResCellList(HikCellInfo.ByReference pResCellList, // 返回资源组织树列表
IntByReference nListSize, // 返回实际组织节点数
int nResPrivilege, // 操作权限码
int nParentCellId); // 组织父节点的ID
含有回调的函数接口调用:
c++中:
int HikPt_StartRecvRealAlarmEvent (
pAlarmEventCallback pfun, // in入参 事件接收回调
void* pUserData); // in入参 用户数据
typedef void (__stdcall* pAlarmEventCallback)(
const ALARMINFO* pAlrInfo, // out出参 返回告警信息
void* pUser); // out出参 传入的用户数据直接返回回来
java中对应:
int HikPt_StartRecvRealAlarmEvent(pAlarmEventCallback pfun,
Pointer pUserData);
public interface pAlarmEventCallback extends Callback {
public void MessageHandle(ALARMINFO pAlrInfo, Pointer pUser);
}
java中调用回调接口:
// lambda表达式方式实现,也可以实现上述定义的接口方式传入接口
Pointer pointer = new Pointer(11);
int startAlarmEventResult = INSTANCE.HikPt_StartRecvRealAlarmEvent((pAlrInfo, pUser) -> {
log.info("【告警回调成功】:接收到一条告警事件");
log.info("alarmStarttime:" + ConvertUtils.byteToStr(pAlrInfo.alarmStarttime));
log.info("---------------------------------------------------");
}, pointer);
上述回调中 pAlrInfo中的byte[]数组接收乱码,各种编码格式都试了总是乱码,仔细查看byte[]数据会发现一个规律,要是遇到0,后边的全部放弃,然后解码出来是正确的。(byte[]转string乱码问题)
// 当然要看sdk返回的数据是多少进制了
public static String byteToStr(byte[] src) {
StringBuilder stringBuilder = new StringBuilder("");
if (src == null || src.length <= 0) {
return null;
}
int flag = 0;
for (int i = 0; i < src.length; i++) {
if (src[i] != 0 && i == flag) {
flag++;
}
}
byte[] res = new byte[flag];
for (int i = 0; i < flag; i++) {
res[i] = src[i];
}
return new String(res);
}
调用sdk接口实体传参问题;
不能用set方式赋值。Int类型可以直接赋值,byte[]不能直接用”suyl”.getByte()赋值
赋值方式如下:
HikLoginInfo hikLoginInfo = new HikLoginInfo();
ConvertUtils.fillByteArray(hikLoginInfo.szUserName, userConfig.getSzUserName());
ConvertUtils.fillByteArray(hikLoginInfo.szPassword, userConfig.getSzPassword());
ConvertUtils.fillByteArray(hikLoginInfo.szServerUrl, userConfig.getSzServerUrl());
hikLoginInfo.uServerPort = userConfig.getUServerPort();
/**
* 将字符串转为byte数据赋值给目标
*
* @param target 目标数组
* @param source 原字符串
*/
public static void fillByteArray(byte[] target, String source) {
byte[] temp = source.getBytes();
for (int i = 0; i < temp.length; i++) {
target[i] = temp[i];
}
target[temp.length] = 0;
}
其他需要注意的:
public interface HikPlatformSDK extends Library {
// sdk放到resources下边
String JNA_Library_temp = Thread.currentThread().getContextClassLoader().getResource("JNA_DLL_T/HikPlatformSDK.dll").getPath();
String JNA_Library_NAME = JnaUtil.getJNAPath(JNA_Library_temp);
public static final HikPlatformSDK INSTANCE = (HikPlatformSDK) Native.loadLibrary(JNA_Library_NAME.replaceAll("/", "\\\\"), HikPlatformSDK.class);
// 3.3.1 初始化
int HikPt_Init();
// 对应的其他接口
}