SpringBoot集成海康网络设备SDK

news2025/3/11 8:25:23

目录

  • SDK介绍
    • 概述
    • 功能
    • 下载
    • 对接指南
  • 集成
    • 初始化项目
    • 初始化SDK
      • 初始化SDK概述
      • 新建AppRunner
      • 新建SdkInitService
      • 新建InitSdkTask
      • 新建 HCNetSDK
    • 调用业务接口
  • 部署
    • 拷贝so库文件到部署目录
    • 追加环境变量
    • 追加so库加载路径
    • 验证SDK初始化是否成功
  • 代码

SDK介绍

概述

设备网络SDK是基于设备私有网络通信协议开发的,为嵌入式网络硬盘录像机、NVR、网络摄像机、网络球机、视频服务器、解码器、报警主机、网络存储等产品服务的配套模块,用于远程访问和控制设备软件的二次开发。

功能

图像预览, 文件回放和下载, 云台控制, 布防/撤防, 语音对讲, 日志管理, 解码卡, 远程升级, 远程重启/关闭, 格式化硬盘, 参数配置(系统配置, 通道配置, 串口配置, 报警配置, 用户配置), 多路解码器, 智能设备功能和获取设备能力集等。

下载

海康开放平台

对接指南

以java为例

由于我司提供的设备网络SDK是封装的动态链接库(Windows的dll或者Linux的so),各种开发语言对接SDK,都是通过加载动态库链接,调用动态库中的接口实现功能模块对接,因此,设备网络SDK的对接不区分开发语言,而且对接的流程和对应的接口都是通用的,各种语言调用动态库的方式有所不同。本文重点介绍java开发语言如何对接设备网络SDK。目前我司提供的java语言开发的demo是通过JNA的方式调用动态链接库中的接口,JNA(Java Native Access)框架是SUN公司主导开发的开源java框架,是建立在JNI的基础上的一个框架,JNA框架提供了一组java工具类用于在运行期间动态访问动态链接库(native library:如Window的dll、Linux的so),实现在java语言中调用C/C++语言封装的接口,java开发人员只需要在一个java接口中描述目标native library的函数与结构,JNA将自动实现Java接口到native function的映射,而不需要编写任何Native/JNI代码,大大降低了Java调用动态链接库的开发难度。相比于JNI的方式,JNA大大简化了调用本地方法的过程,使用很方便,基本上不需要脱离Java环境就可以完成。JNA调用C/C++的过程大致如下:

集成

以 SpringBoot 项目为例,海康SDK版本为6.1.9.47,JNA版本为3.0.9,在windows环境使用Intellij IDEA 2022.2.3开发

初始化项目

  • 新建 SpringBoot 项目,版本 2.5.3
  • 添加pom依赖:jna,fastjson2

<dependency>
        <groupId>com.sun.jna</groupId>
        <artifactId>jna</artifactId>
        <version>3.0.9</version>
</dependency>


<dependency>
        <groupId>com.alibaba.fastjson2</groupId>
        <artifactId>fastjson2</artifactId>
        <version>2.0.20</version>
</dependency>          


  • 将下载下来的海康sdk放到项目目录,并根据操作系统不同分别创建相应的文件夹
  • 若出现 HCNetSDK.NET_DVR_PREVIEWINFO.HWND找不到引用,可做如下处理:1:在ProjectStructure中引入官方示例代码中的 examples.jar ;2:将 HWND 类型修改为 int

 

  • 设置一个喜欢的端口 server.port

初始化SDK

初始化SDK概述

一般的,我们希望在程序启动的时候就初始化sdk。

  • 这里使用了 ApplicationRunner 作为初始化入口,当程序启动成功后,将执行 Runner 做初始化
  • 为避免初始化sdk对主线程造成影响,所以 ApplicationRunner 需要放在线程池中 ThreadPoolExecutor,并添加try-catch处理
  • HCNetSDK是SDK示例代码中提供的一个对象,此对象继承Library,负责和更底层的C/C++库(更底层也许是C写的,这里不确定)交互,即执行 Native 调用。通过实例化此对象完成sdk依赖库的导入,并在后续业务开发中使用此对象向摄像机发布指令。
  • 涉及多操作系统平台的差异性,官方分别提供不同sdk依赖库,具体包含:win32,win64,linux32,linux64等,所以当初始化SDK的时候需要根据当前所处环境不同分别加载不同的依赖库文件
  • 上述提到的依赖库文件,在windows下就是 dll 后缀文件 , 在 linux 下就是 so 后缀文件
  • 真正执行初始化就是调用 hCNetSDK.NET_DVR_Init() 此方法,并可通过返回值为 true 或 false 判断初始化是否成功。

新建AppRunner

  • AppRunner 需要实现 ApplicationRunner 接口,并将
    AppRunner作为组件放到Spring 容器中管理
  • AppRunner 中注入SdkInitService ,并在run 方法中调用 SdkInitService 的initSdk 方法实现SDK的初始化

package com.ramble.hikvisionsdkintegration;
import com.ramble.hikvisionsdkintegration.service.SdkInitService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Component
public class AppRunner  implements ApplicationRunner {
    @Autowired
    private SdkInitService hksdkInitService;
    @Override
    public void run(ApplicationArguments args) throws Exception {
        hksdkInitService.initSdk();
    }
}


新建SdkInitService

  • 定义一个公开的 hCNetSDK 属性,类型为 HCNetSDK ,并在构造函数中初始化 hCNetSDK 属性的值,此值需要全局唯一,这里参照官方代码做了单例处理。HCNetSDK 是官方提供的一个接口,一般的都是直接copy到项目源代码中,你会发现,所有和设备交互的地方都是通过这个接口来完成的
  • 内部定义一个异常回调类,用来处理和设备交互的时候全局异常的处理
  • 注入 ThreadPoolExecutor 执行器,真正的初始化将放到子线程中进行
  • 定义 initSdk 方法用来执行初始化
  • 需要注意的是,构造函数中为 hCNetSDK 属性初始化值,仅仅只是为了将 sdk 所需的依赖库文件 加载到运行时中,并没有真正的做初始化SDK的工作
  • 需要重点关注OSUtils中的代码,加载依赖库文件的前提是找到对应的库文件,的操作是在 getLoadLibrary 方法中管理的,这里编写的代码需要和部署时候选择的部署方式对应,否则可能会出现在windows中开发正常,部署到linux 中就报异常的问题

SdkInitService:


package com.ramble.hikvisionsdkintegration.service;
import com.ramble.hikvisionsdkintegration.sdklib.HCNetSDK;
import com.ramble.hikvisionsdkintegration.task.InitSdkTask;
import com.ramble.hikvisionsdkintegration.util.OSUtils;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.ThreadPoolExecutor;

@Slf4j
@Component
public class SdkInitService {
    public static HCNetSDK hCNetSDK = null;
    static FExceptionCallBack_Imp fExceptionCallBack;
    static class FExceptionCallBack_Imp implements HCNetSDK.FExceptionCallBack {
        public void invoke(int dwType, int lUserID, int lHandle, Pointer pUser) {
            System.out.println("异常事件类型:" + dwType);
            return;
        }
    }
    public SdkInitService() {
        if (hCNetSDK == null) {
            synchronized (HCNetSDK.class) {
                try {
                    hCNetSDK = (HCNetSDK) Native.loadLibrary(OSUtils.getLoadLibrary(), HCNetSDK.class);
                } catch (Exception ex) {
                    log.error("SdkInitService-init-hCNetSDK-error");
                }
            }
        }
    }
    @Autowired
    private ThreadPoolExecutor executor;
    public void initSdk() {
        log.info("HKSDKInitService-init-coming");
        executor.execute(new InitSdkTask());
    }
}

OSUtils:


package com.ramble.hikvisionsdkintegration.util;
import com.sun.jna.Platform;
import lombok.extern.slf4j.Slf4j;
import java.io.File;

@Slf4j
public class OSUtils {
    // 获取操作平台信息
    public static String getOsPrefix() {
        String arch = System.getProperty("os.arch").toLowerCase();
        final String name = System.getProperty("os.name");
        String osPrefix;
        if (Platform.isWindows()) {
            if ("i386".equals(arch)) {
                arch = "x86";
            }
            osPrefix = "win32-" + arch;
        } else if (Platform.isLinux()) {
            if ("x86".equals(arch)) {
                arch = "i386";
            } else if ("x86_64".equals(arch)) {
                arch = "amd64";
            }
            osPrefix = "linux-" + arch;
        } else {
            osPrefix = name.toLowerCase();
            if ("x86".equals(arch)) {
                arch = "i386";
            }
            if ("x86_64".equals(arch)) {
                arch = "amd64";
            }
            int space = osPrefix.indexOf(" ");
            if (space != -1) {
                osPrefix = osPrefix.substring(0, space);
            }
            osPrefix += "-" + arch;
        }
        return osPrefix;
    }
    public static String getOsName() {
        String osName = "";
        String osPrefix = getOsPrefix();
        if (osPrefix.toLowerCase().startsWith("win32-x86")
                || osPrefix.toLowerCase().startsWith("win32-amd64")) {
            osName = "win";
        } else if (osPrefix.toLowerCase().startsWith("linux-i386")
                || osPrefix.toLowerCase().startsWith("linux-amd64")) {
            osName = "linux";
        }
        return osName;
    }
    /**
     * 获取库文件
     * 区分win、linux
     *
     * @return
     */
    public static String getLoadLibrary() {
        if (isChecking()) {
            return null;
        }
        String userDir = System.getProperty("user.dir");
        log.info("getLoadLibrary-userDir={}", userDir);
        String loadLibrary = "";
        String library = "";
        String osPrefix = getOsPrefix();
        if (osPrefix.toLowerCase().startsWith("win32-x86")) {
            loadLibrary = System.getProperty("user.dir") + File.separator + "sdk" + File.separator + "hklibwin32" + File.separator;
            library = "HCNetSDK.dll";
        } else if (osPrefix.toLowerCase().startsWith("win32-amd64")) {
            loadLibrary = System.getProperty("user.dir") + File.separator + "sdk" + File.separator + "hklibwin64" + File.separator;
            library = "HCNetSDK.dll";
        } else if (osPrefix.toLowerCase().startsWith("linux-i386")) {
            //同 linux-amd64
            loadLibrary = "";
            library = "libhcnetsdk.so";
        } else if (osPrefix.toLowerCase().startsWith("linux-amd64")) {
            //方式一:使用系统默认的加载库路径,在系统的/usr/lib文件中加入你Java工程所需要使用的so文件,然后将HCNetSDKCom文件夹下的组件库也复制到/usr/lib目录,HCNetSDKCom文件夹中的组件库不要随意更换路径。CentOS 64位需拷贝到/usr/lib64下。
            //针对方式一,前缀就是绝对路径
            //loadLibrary = "/usr/lib64/lib/hkliblinux64/";
            //方式二:配置LD_LIBRARY_PATH环境变量加载库文件;配置/etc/ld.so.conf,加上你自己的Java工程所需要的so文件的路径
            //针对方式二,无需添加前缀,程序会从linux系统的so共享库中查找libhcnetsdk.so
            loadLibrary = "";
            library = "libhcnetsdk.so";
        }
        log.info("================= Load library Path :{} ==================", loadLibrary + library);
        return loadLibrary + library;
    }
    private static boolean checking = false;
    public static void setChecking() {
        checking = true;
    }
    public static void clearChecking() {
        checking = false;
    }
    public static boolean isChecking() {
        return checking;
    }
}


新建InitSdkTask

  • 此类实现 Runnable 接口,并重写run方法。
  • 新建一个私有属性 hCNetSDK 并赋值为 SdkInitService.hCNetSDK ,因为初始化需要用到 HCNetSDK 这个对象和设备交互,所以初始化前必须确保此对象已经创建,本例中,程序在执行 SdkInitService 构造函数的时候初始化了 HCNetSDK 对象,并放到一个全局静态变量中
  • 其实也可以不新建私有属性 hCNetSDK ,在需要用到此对象的地方 使用 SdkInitService.hCNetSDK 的方式获取也可以
  • 通过调用  hCNetSDK.NET_DVR_Init 方法执行初始化,并可以通过返回值确定是否初始化成功,初始化成功后,将可以调用业务接口向设备发送指令。
  • NET_DVR_SetConnectTime,NET_DVR_SetReconnect 是可选的,并不会对初始化SDK本身造成影响。
  • 为了避免对主程序造成影响,初始化代码将需要做 try - catch 处理

InitSdkTask:


package com.ramble.hikvisionsdkintegration.task;
import com.ramble.hikvisionsdkintegration.sdklib.HCNetSDK;
import com.ramble.hikvisionsdkintegration.service.SdkInitService;
import com.ramble.hikvisionsdkintegration.util.OSUtils;
import lombok.extern.slf4j.Slf4j;
import java.util.Objects;

@Slf4j
public class InitSdkTask implements Runnable {
    /**
     * 装配 sdk 所需依赖
     */
    private static HCNetSDK hCNetSDK = SdkInitService.hCNetSDK;
    @Override
    public void run() {
        try {
            if (Objects.equals(OSUtils.getOsName(), "linux")) {
                log.info("InitSdk-is-linux");
                String userDir = System.getProperty("user.dir");
                log.info("InitSdk-userDir={}", userDir);
                String osPrefix = OSUtils.getOsPrefix();
                if (osPrefix.toLowerCase().startsWith("linux-i386")) {
                    HCNetSDK.BYTE_ARRAY ptrByteArray1 = new HCNetSDK.BYTE_ARRAY(256);
                    HCNetSDK.BYTE_ARRAY ptrByteArray2 = new HCNetSDK.BYTE_ARRAY(256);
                    //这里是库的绝对路径,请根据实际情况修改,注意改路径必须有访问权限
                    //linux 下, 库加载参考:OSUtils.getLoadLibrary()
                    String strPath1 = System.getProperty("user.dir") + "/hkliblinux32/libcrypto.so.1.1";
                    String strPath2 = System.getProperty("user.dir") + "/hkliblinux32/libssl.so.1.1";
                    System.arraycopy(strPath1.getBytes(), 0, ptrByteArray1.byValue, 0, strPath1.length());
                    ptrByteArray1.write();
                    hCNetSDK.NET_DVR_SetSDKInitCfg(3, ptrByteArray1.getPointer());
                    System.arraycopy(strPath2.getBytes(), 0, ptrByteArray2.byValue, 0, strPath2.length());
                    ptrByteArray2.write();
                    hCNetSDK.NET_DVR_SetSDKInitCfg(4, ptrByteArray2.getPointer());
                    //linux 下, 库加载参考:OSUtils.getLoadLibrary()
                    String strPathCom = System.getProperty("user.dir") + "/hkliblinux32/HCNetSDKCom/";
                    HCNetSDK.NET_DVR_LOCAL_SDK_PATH struComPath = new HCNetSDK.NET_DVR_LOCAL_SDK_PATH();
                    System.arraycopy(strPathCom.getBytes(), 0, struComPath.sPath, 0, strPathCom.length());
                    struComPath.write();
                    hCNetSDK.NET_DVR_SetSDKInitCfg(2, struComPath.getPointer());
                } else if (osPrefix.toLowerCase().startsWith("linux-amd64")) {
                    HCNetSDK.BYTE_ARRAY ptrByteArray1 = new HCNetSDK.BYTE_ARRAY(256);
                    HCNetSDK.BYTE_ARRAY ptrByteArray2 = new HCNetSDK.BYTE_ARRAY(256);
                    //这里是库的绝对路径,请根据实际情况修改,注意改路径必须有访问权限
                    //linux 下, 库加载参考:OSUtils.getLoadLibrary()
                    String strPath1 = System.getProperty("user.dir") + "/hkliblinux64/libcrypto.so.1.1";
                    String strPath2 = System.getProperty("user.dir") + "/hkliblinux64/libssl.so.1.1";
                    System.arraycopy(strPath1.getBytes(), 0, ptrByteArray1.byValue, 0, strPath1.length());
                    ptrByteArray1.write();
                    hCNetSDK.NET_DVR_SetSDKInitCfg(3, ptrByteArray1.getPointer());
                    System.arraycopy(strPath2.getBytes(), 0, ptrByteArray2.byValue, 0, strPath2.length());
                    ptrByteArray2.write();
                    hCNetSDK.NET_DVR_SetSDKInitCfg(4, ptrByteArray2.getPointer());
                    String strPathCom = System.getProperty("user.dir") + "/hkliblinux64/HCNetSDKCom/";
                    //linux 下, 库加载参考:OSUtils.getLoadLibrary()
                    HCNetSDK.NET_DVR_LOCAL_SDK_PATH struComPath = new HCNetSDK.NET_DVR_LOCAL_SDK_PATH();
                    System.arraycopy(strPathCom.getBytes(), 0, struComPath.sPath, 0, strPathCom.length());
                    struComPath.write();
                    hCNetSDK.NET_DVR_SetSDKInitCfg(2, struComPath.getPointer());
                } else {
                    log.info("osPrefix={}", osPrefix);
                }
            }
            //初始化sdk
            boolean isOk = hCNetSDK.NET_DVR_Init();
            hCNetSDK.NET_DVR_SetConnectTime(10, 1);
            hCNetSDK.NET_DVR_SetReconnect(100, true);
            if (!isOk) {
                log.error("=================== InitSDK init fail ===================");
            } else {
                log.info("============== InitSDK init success ====================");
            }
        } catch (Exception e) {
            log.error("InitSDK-error,e={}", e.getMessage());
            e.printStackTrace();
        }
    }
}


新建 HCNetSDK

直接从官方示例代码中copy过来即可

调用业务接口

  • 新建一个controller ,尝试调用 获取SDK状态 的接口。
  • 调用所有的业务接口之前都需要先登录

package com.ramble.hikvisionsdkintegration.controller;
import com.alibaba.fastjson2.JSON;
import com.ramble.hikvisionsdkintegration.dto.GlobalResponseEntity;
import com.ramble.hikvisionsdkintegration.sdklib.HCNetSDK;
import com.ramble.hikvisionsdkintegration.service.SdkInitService;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@Slf4j
@AllArgsConstructor
@RestController
@RequestMapping("/test")
public class TestController {
    private static String m_sDeviceIP = "192.168.1.142";
    private static String m_sUsername = "xxx";
    private static String m_sPassword = "xxx";
    
    /**
     * 获取sdk状态
     *
     * @return {@link GlobalResponseEntity}<{@link String}>
     * 返回值举例:{"success":true,"code":"000000","message":"request successfully",
     * "data":"{\"dwRes\":[0,0,0,0,0,0,0,0,0,0],\"dwTotalAlarmChanNum\":0,\"dwTotalBroadCastNum\":0,\"dwTotalFileSearchNum\":0,\"dwTotalFormatNum\":0,
     * \"dwTotalLogSearchNum\":0,\"dwTotalLoginNum\":1,\"dwTotalPlayBackNum\":0,\"dwTotalRealPlayNum\":0,\"dwTotalSerialNum\":0,\"dwTotalUpgradeNum\":0,
     * \"dwTotalVoiceComNum\":0,\"autoRead\":true,\"autoWrite\":true,\"pointer\":{\"size\":84,\"valid\":true}}"}
     */
     
    @GetMapping("/state")
    public GlobalResponseEntity<String> getSdkState() {
        //登录
        Integer userId = login();
        log.info("userId={}", userId);
        HCNetSDK.NET_DVR_SDKSTATE sdkState = new HCNetSDK.NET_DVR_SDKSTATE();
        //获取当前SDK状态信息
        boolean result = SdkInitService.hCNetSDK.NET_DVR_GetSDKState(sdkState);
        if (result) {
            sdkState.read();
            String s = JSON.toJSONString(sdkState);
            return GlobalResponseEntity.success(s);
        } else {
            int error = SdkInitService.hCNetSDK.NET_DVR_GetLastError();
            return GlobalResponseEntity.error("获取失败,错误码为:" + error);
        }
    }
    
    
    private Integer login() {
        HCNetSDK.NET_DVR_USER_LOGIN_INFO m_strLoginInfo = new HCNetSDK.NET_DVR_USER_LOGIN_INFO();//设备登录信息
        m_strLoginInfo.sDeviceAddress = new byte[HCNetSDK.NET_DVR_DEV_ADDRESS_MAX_LEN];
        System.arraycopy(m_sDeviceIP.getBytes(), 0, m_strLoginInfo.sDeviceAddress, 0, m_sDeviceIP.length());
        m_strLoginInfo.sUserName = new byte[HCNetSDK.NET_DVR_LOGIN_USERNAME_MAX_LEN];
        System.arraycopy(m_sUsername.getBytes(), 0, m_strLoginInfo.sUserName, 0, m_sUsername.length());
        m_strLoginInfo.sPassword = new byte[HCNetSDK.NET_DVR_LOGIN_PASSWD_MAX_LEN];
        System.arraycopy(m_sPassword.getBytes(), 0, m_strLoginInfo.sPassword, 0, m_sPassword.length());
        m_strLoginInfo.wPort = Short.valueOf("8000");
        m_strLoginInfo.bUseAsynLogin = false; //是否异步登录:0- 否,1- 是
        m_strLoginInfo.write();
        HCNetSDK.NET_DVR_DEVICEINFO_V40 m_strDeviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V40();//设备信息
        int loginHandler = SdkInitService.hCNetSDK.NET_DVR_Login_V40(m_strLoginInfo, m_strDeviceInfo);
        if (loginHandler == -1) {
            int errorCode = SdkInitService.hCNetSDK.NET_DVR_GetLastError();
            IntByReference errorInt = new IntByReference(errorCode);
            log.error("[HK] login fail errorCode:{}, errMsg:{}", errorCode, SdkInitService.hCNetSDK.NET_DVR_GetErrorMsg(errorInt));
            return null;
        } else {
            return loginHandler;
        }
    }
}


部署

拷贝so库文件到部署目录

所有厂家的所有版本sdk库文件均维护在项目源代码中,需要将linux库文件so文件拷贝到部署根目录,和jar文件同级

追加环境变量

通过配置 LD_LIBRARY_PATH 环境变量加载库文件,打开系统的 /etc/profile 配置文件,在最后追加so库文件所在目录:


export  LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/app/jars/hkliblinux64:/home/app/jars/hkliblinux64/HCNetSDKCom

如上所示:
● 32位就追加  hkliblinux32 目录,64位就追加 hkliblinux64 目录
● 不要忘记 HCNetSDKCom 目录也需要配置,因为里面也有so库文件。
执行source 命令,让配置生效:


source   /etc/profile

追加so库加载路径

打开 /etc/ld.so.conf配置文件,追加so库文件所在目录


/home/app/jars/hkliblinux64
/home/app/jars/hkliblinux64/HCNetSDKCom

如上所示:
● 32位就追加  hkliblinux32 目录,64位就追加 hkliblinux64 目录。
● 不要忘记 HCNetSDKCom 目录也需要配置,因为里面也有so库文件。

执行 ldconfig 命令,让配置生效:


ldconfig

验证SDK初始化是否成功

一般来说,可以在程序初始化SDK的时候添加日志,通过日志输出判断是否初始化成功。

代码

Ramble-SpringBoot: SpringBoot漫谈 - Gitee.com

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/761985.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

企业数据怎样管理才能蜕变为可变现的资产?

企业竞争的本质&#xff0c;是在不确定市场环境下资源配置效率的竞争。 如何快速感知市场变化、识别客户需求、增强决策准确性&#xff0c;是企业资源配置的核心关注点和竞争点。 数据作为企业资源的具体表现形式和载体&#xff0c;已经渗透到企业设计、生产、管理、服务和运…

Qt开发5--Qt5中菜单和工具栏

在这部分Qt5 C编程教程中&#xff0c;讨论Qt5应用程序中菜单和工具栏。 一个菜单栏是一个GUI程序的常见部分。它是位于称为菜单的各种位置中的命令组。工具栏提供了对最常用命令的快速访问。 1) Qt5简单菜单 第一个例子展示显示一个简单菜单。以下是源代码&#xff0c;我们有…

【算法 -- LeetCode】(021) 合并两个有序链表

1、题目 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4] 示例 2&#xff1a; 输入&#xff1a;l1 [], l2 [] 输出&#…

【并发编程】深入理解并发工具类CountDownLatch

文章目录 前言一、初识 CountDownLatch二、CountDownLatch 的核心方法三、CountDownLatch 的应用场景应用场景一&#xff1a;等待多个线程任务执行完成应用场景二&#xff1a;等待外部资源初始化应用场景三&#xff1a;控制线程执行顺序 四、CountDownLatch 的源码分析核心方法…

mysql表主键自增过大问题

问题及项目环境 问题 最近在做项目时&#xff0c;发现我创建的每一个表的主键设置自增&#xff0c;在插入数据数据时会出现自增值过大的问题。 问题展示: 在后端执行Basemapper中的insert()方法时&#xff0c;数据库中的主键id字段为下: 且我在对应的实体类的设置为下: 我们…

mysql四种隔离级别以及原理

事务4大特性&#xff1a; 原子性&#xff1a; undolog日志来保证&#xff0c; 记录了要回滚的日志信息&#xff0c; 事务回滚时撤销已经执行的sql. 一致性&#xff1a;由其它3大特性来保证。 隔离性: MVCC来保证&#xff0c; 多版本并发控制。 持久性&#xff1a;由redolog来保…

flask路由、模板、请求与响应、闪现以及请求扩展

一、函数加装饰器的执行顺序 flask的路由基于装饰器---->在视图函数上再加装饰器---->加多个装饰器的执行顺序---->登录认证装饰器---->加载router下,先做路由匹配,匹配成功执行被auth包裹的视图函数 二、路由系统 flask的路由是基于装饰器的---->但是它的…

APB/AHB/AXI总线介绍和理解

APB/AHB/AXI总线介绍&#xff1a; APB/AHB/AXI均属于AMBA (Advanced Microcontroller Bus Architecture)&#xff0c;常用于SoC设计中&#xff0c;全称叫作高级微控制器总线架构&#xff0c;它是由ARM公司研发推出的高性能片上总线协议。 AMBA 1.0还包含ASB和APB&#xff08;A…

MP4格式视频怎么转mov格式?好用的视频格式转换方法分享

MOV格式是苹果公司的专有格式&#xff0c;因此在苹果设备上播放MOV格式的视频时&#xff0c;兼容性更好&#xff0c;因此可以实现更高质量的视频。如果我们需要高质量的视频输出&#xff0c;将MP4转换为MOV格式可能是个好选择。那么怎么进行转换呢&#xff1f;给大家分享几种简…

Linux环境下,Nginx+Naocs远程访问碰到的若干问题

一、配置背景 该项目来源于尚硅谷SpringCloud进阶课程&#xff0c;在linux环境下配置一个注册中心组。 二、碰到的问题 问题主要是远程访问Nginx显示无法连接的问题&#xff0c;接下来是排查方案&#xff1a; 1. 防火墙问题 这里需要确保双方电脑能ping通之后确保端口也能…

k210获取机器码

准备工作&#xff1a; kflash_gui&#xff08;下载固件到开发板&#xff09; key_gen_v1.2&#xff08;需要将其下载至开发板&#xff09; 1.kflash_gui 下载地址&#xff1a;Releases sipeed/kflash_gui GitHub 2. key_gen_v1.2下载地址&#xff1a;下载站 - Sipeed 3.…

Java经典面试解析:服务器卡顿、CPU飙升、接口负载剧增

01 线上服务器CPU飙升&#xff0c;如何定位到Java代码 解决这个问题的关键是要找到Java代码的位置。下面分享一下排查思路&#xff0c;以CentOS为例&#xff0c;总结为4步。 第1步&#xff0c;使用top命令找到占用CPU高的进程。 第2步&#xff0c;使用ps –mp命令找到进程下…

Three.js——十三、自定义大小画布、UI交互按钮以及3D场景交互、渲染画布为文件(图片)

画布全屏以及自定义大小画布 <!-- canvas元素默认是行内块元素 --> <divclass"model"style"background-color: #ff0000;"width"300"height"180" ></div>画布随窗口变化 // 画布跟随窗口变化 window.onresize fun…

cmake的一个测试demo

目录 一、ubuntu中安装cmake二、单个源文件main.cCMakeLists.txt的编写 三、多个源文件main.ctest1.ctest1.hCMakeLists.txt的编写 一、ubuntu中安装cmake sudo apt-get install cmake查看cmake的版本号 cmake --version二、单个源文件 main.c #include<stdio.h>int …

【微信小程序-uniapp】CustomButton 自定义常用吸底按钮组件

1. 效果图 2. 组件完整代码 <template><view:class="[custom-btn flex-center, size == big ? big : mid, type == primary ? primary : info, plain ? plain : , disabled ? disabled : , round ? round : ]"

苹果笔买原装的还是随便买?ipad触控笔推荐

当像iPad这样的平板电脑功能变得越来越强&#xff0c;能够承载的功能也会越来越多&#xff0c;并且会越来越多地渗透到我们的日常生活和工作中。随着电子设备的更新和软件的不断完善&#xff0c;电容笔的性能也在不断的提升&#xff0c;因此如何挑选一款相对好用的电容笔&#…

【Kafka】Ubuntu 部署kafka中间件,实现Django生产和消费

原文作者&#xff1a;我辈李想 版权声明&#xff1a;文章原创&#xff0c;转载时请务必加上原文超链接、作者信息和本声明。 文章目录 前言一、Kafka安装1.下载并安装Java2.下载和解压 Kafka3.配置 Kafka4.启动 Kafka5.创建主题和生产者/消费者6.发布和订阅消息 二、KafkaDjang…

AIS报文解析

!AIVDM,1,1,A,13u?etPv2;0n:dDPwUM1U1Cb069D,023* 我们知道消息内容就是13u?etPv2;0n:dDPwUM1U1Cb069D&#xff0c;这是一串ASCII码字符串&#xff0c;我们可以获取其对应的ASCII码数值。 但是在AIS的编码表不需要这么多符号&#xff0c;所以截取了其中一部分&#xff0c;如…

良心推荐!5款支持Linux系统的国产软件,兼容国产操作系统

虽然市面上大多数用户使用的是Windows操作系统&#xff0c;但也有不少使用Linux系统的用户&#xff0c;特别是国产操作系统的崛起&#xff0c;让Linux系统阵营的用户越来越多。Linux不像Windows那样&#xff0c;有着完整的生态环境丰富的软件应用&#xff0c;但也逐渐在完善中&…

探秘Session跨页面传递数据的神奇力量

探秘Session跨页面传递数据的神奇力量 前言一、什么是 Session 会话?二、如何创建 Session 和获取(id 号,是否为新)三、Session 域数据的存取四、Session 生命周期控制五、Session的销毁五、浏览器和 Session 之间关联的技术内幕 前言 本博主将用CSDN记录软件开发求学之路上亲…