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();

    // 对应的其他接口
}