Onvif协议:IPC客户端开发-IPC相机控制(c语言版)

news2025/6/1 6:56:54

前言:

本博文主要是借鉴OceanStar大神的博文,在他的博文的基础之上做了一部分修改与简化。

博文链接:

Onvif协议:IPC客户端开发之鉴权_onvif鉴权方式-CSDN博客
Onvif协议:IPC客户端开发之PTZ控制_onvif ptz-CSDN博客

不知道什么是Onvif协议以及相关的基础知识可以查看大神的相关文章或者看我前面的文章:

ONVIF协议网络摄像机客户端使用gsoap获取RTSP流地址GStreamer拉流播放_onvif取流软件-CSDN博客

onvif代码都是靠工具生成的,我们得根据我们的要求生成头文件,onvif是根据模块来生成头文件,需要的模块多,生成的代码就庞大,相反则很少,使用onvif官方提供的wsdl来生成代码。

根据上面链接博文中介绍的使用gsoap的工具来生成客户端脚本

1:生成头文件

1.1:准备工作

首先先创建一个目录,目录里面放连个文件夹分别名字为:bin、gsoap

然后把gsoap的工具:

拷贝到刚刚创建好的bin文件中。

然后把源码中的

拷贝到gsoap目录下

这样准备工作就基本完成了。

1.2:编写生成头文件脚本

mkdir onvif_head
cd onvif_head
# 添加 -c 选项生成C头文件
../bin/wsdl2h -c -o onvif.h -s -d -x -t ../gsoap/WS/typemap.dat \
http://www.onvif.org/onvif/ver10/network/wsdl/remotediscovery.wsdl \
https://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl \
http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl \
http://www.onvif.org/onvif/ver20/ptz/wsdl/ptz.wsdl \
http://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl

然后记得给脚本赋予权限然后执行

执行成功

1.3:修改头文件内容

在生成的Onvif.h文件中添加

#import "wsse.h"

2:自动生成程序

2.1:编写脚本

这里是使用c语言实现的所以生成的程序就只生成c语言的即可

#!/bin/bash
DIR=soap
mkdir $DIR
cd $DIR
 ../bin/soapcpp2 -2 -x -c -C ../onvif_head/onvif.h  -L -I ../gsoap/import -I ../gsoap/

然后赋予权限运行。

运行会出现问题:

wsa5.h(290): **ERROR**: service operation name clash: struct/class 'SOAP_ENV__Fault' already declared at wsa.h:278

解决方法:

打开 gsoap\import 路径下的wsa5.h, 将277行的SOAP_ENV__Fault结构体注释掉

正确修改完后的结果为:

3:编写核心程序main.c

学习大神的方法,把相关的程序全部放在一起方便管理。

创建一个目录用来存放这些文件

创建一个application目录,存放我们需要的所有代码(比如soap下生成的soapC.c、soapClient.c、soapH.h、soapStub.h、wsdd.nsmap;gsoap源码目录下的stdsoap2.c、stdsoap2.h,gsoap/目录下(dom.c)/gsoap/import目录下的(dom.h)gsoap/plugin目录下的wsseapi.h、wsseapi.c、smdevp.h、smdevp.c、mecevp.c、mecevp.h、threads.c、threads.h、wsaapi.c、wsaapi.h等)/gsoap/custom的(struct_timeval.c、struct_timeval.h)。并创建一个main.c

我这个main.c是简化版本的,里面没有使用到设备发现和获取流地址,适用于设备已知的情况的ptz相机控制,其中的功能有绝对移动和持续移动、停止移动的控制。

#include <assert.h>
#include "soapH.h"
#include "wsdd.nsmap"
#include "soapStub.h"
#include "wsseapi.h"
#include "wsaapi.h"
#include "dom.h"  // 添加 DOM 支持
#include "time.h"
// #include <map>

//定义移动枚举
enum PTZCMD
{
    PTZ_CMD_LEFT,
    PTZ_CMD_RIGHT,
    PTZ_CMD_UP,
    PTZ_CMD_DOWN,
    PTZ_CMD_LEFTUP,
    PTZ_CMD_LEFTDOWN,
    PTZ_CMD_RIGHTUP,
    PTZ_CMD_RIGHTDOWN,
    PTZ_CMD_ZOOM_IN,
    PTZ_CMD_ZOOM_OUT,
};

#define USERNAME "admin"     // 替换为实际用户名
#define PASSWORD "admin"     // 替换为实际密码
#define DEVICE_IP "192.168.20.10"  // 替换为设备IP
#define DEVICE_PORT 8000       // 替换为设备端口
#define DEVICE_XADDR "http://%s:%d/onvif/device_service"  // 设备服务地址模板
#define SOAP_ASSERT     assert
#define SOAP_DBGERR     printf

#define SOAP_SOCK_TIMEOUT    (10)               // socket超时时间(单秒秒)

void soap_perror(struct soap *soap, const char *str)
{
    if (NULL == str) {
        SOAP_DBGERR("[soap] error: %d, %s, %s\n", soap->error, *soap_faultcode(soap), *soap_faultstring(soap));
    } else {
        SOAP_DBGERR("[soap] %s error: %d, %s, %s\n", str, soap->error, *soap_faultcode(soap), *soap_faultstring(soap));
    }
}

#define SOAP_CHECK_ERROR(result, soap, str) \
    do { \
        if (SOAP_OK != (result) || SOAP_OK != (soap)->error) { \
            soap_perror((soap), (str)); \
            if (SOAP_OK == (result)) { \
                (result) = (soap)->error; \
            } \
            goto EXIT; \
        } \
} while (0)


struct soap *ONVIF_soap_new(int timeout)
{
    struct soap *soap = NULL;                                                   // soap环境变量

    SOAP_ASSERT(NULL != (soap = soap_new()));

    soap_set_namespaces(soap, namespaces);                                      // 设置soap的namespaces
    soap->recv_timeout    = timeout;                                            // 设置超时(超过指定时间没有数据就退出)
    soap->send_timeout    = timeout;
    soap->connect_timeout = timeout;

#if defined(__linux__) || defined(__linux)                                      // 参考https://www.genivia.com/dev.html#client-c的修改:
    soap->socket_flags = MSG_NOSIGNAL;                                          // To prevent connection reset errors
#endif

    soap_set_mode(soap, SOAP_C_UTFSTRING);                                      // 设置为UTF-8编码,否则叠加中文OSD会乱码

    return soap;
}

void ONVIF_soap_delete(struct soap *soap)
{
    soap_destroy(soap);                                                         // remove deserialized class instances (C++ only)
    soap_end(soap);                                                             // Clean up deserialized data (except class instances) and temporary data
    soap_done(soap);                                                            // Reset, close communications, and remove callbacks
    soap_free(soap);                                                            // Reset and deallocate the context created with soap_new or soap_copy
}


int ONVIF_GetCapabilities(const char *deviceXAddr, char **ptzXAddr)
{
    int result = 0;
    struct soap *soap = NULL;
    struct _tds__GetCapabilities devinfo_req;
    struct _tds__GetCapabilitiesResponse devinfo_resp;
    
    // 初始化结构体
    memset(&devinfo_req, 0, sizeof(devinfo_req));
    memset(&devinfo_resp, 0, sizeof(devinfo_resp));
    
    SOAP_ASSERT(deviceXAddr != NULL);
    SOAP_ASSERT((soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)) != NULL);

    result = soap_call___tds__GetCapabilities(soap, deviceXAddr, NULL, &devinfo_req, &devinfo_resp);
    SOAP_CHECK_ERROR(result, soap, "GetCapabilities");

    if (devinfo_resp.Capabilities != NULL && 
        devinfo_resp.Capabilities->PTZ != NULL && 
        devinfo_resp.Capabilities->PTZ->XAddr != NULL) 
    {
        // 分配内存并复制字符串
        *ptzXAddr = strdup(devinfo_resp.Capabilities->PTZ->XAddr);
        if (*ptzXAddr == NULL) {
            result = -1; // 内存分配失败
            goto EXIT;
        }
    } else {
        *ptzXAddr = NULL; // 设置为NULL表示未找到
    }

EXIT:
    if (soap != NULL) {
        // 注意:这里可能需要清理devinfo_resp结构体
        ONVIF_soap_delete(soap);
    }
    return result;
}

static int ONVIF_SetAuthInfo(struct soap *soap, const char *username, const char *password)
{
    int result = 0;

    SOAP_ASSERT(NULL != username);
    SOAP_ASSERT(NULL != password);

    result = soap_wsse_add_UsernameTokenDigest(soap, NULL, username, password);
    SOAP_CHECK_ERROR(result, soap, "add_UsernameTokenDigest");

    EXIT:

    return result;
}

int ONVIF_GetProfiles(const char *ptzXAddr, char **profilesToken)
{
    int result = 0;
    struct soap *soap = NULL;
    struct _trt__GetProfiles devinfo_req;
    struct _trt__GetProfilesResponse devinfo_resp;
    
    // 初始化结构体
    memset(&devinfo_req, 0, sizeof(devinfo_req));
    memset(&devinfo_resp, 0, sizeof(devinfo_resp));
    
    SOAP_ASSERT(ptzXAddr != NULL);
    SOAP_ASSERT((soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)) != NULL);

    ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);
    result = soap_call___trt__GetProfiles(soap, ptzXAddr, NULL, &devinfo_req, &devinfo_resp);
    SOAP_CHECK_ERROR(result, soap, "ONVIF_GetProfiles");

    SOAP_ASSERT(devinfo_resp.__sizeProfiles > 0 && devinfo_resp.Profiles != NULL);
    
    // 分配内存并复制token
    *profilesToken = strdup(devinfo_resp.Profiles[0].token);
    if (*profilesToken == NULL) {
        result = -1; // 内存分配失败
        goto EXIT;
    }

EXIT:
    if (soap != NULL) {
        // 注意:这里可能需要清理devinfo_resp结构体
        ONVIF_soap_delete(soap);
    }
    return result;
}


/************************************************************************
**函数:ONVIF_PTZAbsoluteMove
**功能:绝对移动
**参数:
        [in] ptzXAddr     - ptzXAddr
        [in] ProfileToken - 配置令牌
**返回:
        0表明成功,非0表明失败
**备注:
************************************************************************/
int ONVIF_PTZAbsoluteMove(const char *ptzXAddr, const char *ProfileToken)
{
    int result = 0;
    struct soap *soap = NULL;
    struct _tptz__AbsoluteMove           absoluteMove;
    struct _tptz__AbsoluteMoveResponse   absoluteMoveResponse;

    // 参数检查
    if (ptzXAddr == NULL || ProfileToken == NULL) {
        printf("Invalid input parameters.\n");
        return -1;
    }

    // 创建 SOAP 上下文
    soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT);
    if (!soap) {
        printf("Failed to create soap context.\n");
        return -1;
    }

    // 设置认证信息
    ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);

    // 初始化 AbsoluteMove 请求结构体
    memset(&absoluteMove, 0, sizeof(absoluteMove));

    // 设置 ProfileToken(从参数拷贝)
    absoluteMove.ProfileToken = (char *)soap_strdup(soap, ProfileToken);

    absoluteMove.Position = soap_new_tt__PTZVector(soap,sizeof(struct tt__PTZVector));
    absoluteMove.Position->PanTilt = soap_new_tt__Vector2D(soap, sizeof(struct tt__Vector2D));
    absoluteMove.Position->Zoom = soap_new_tt__Vector1D(soap, sizeof(struct tt__Vector1D));

    absoluteMove.Speed = soap_new_tt__PTZSpeed(soap, sizeof(struct tt__PTZSpeed));
    absoluteMove.Speed->PanTilt = soap_new_tt__Vector2D(soap, sizeof(struct tt__Vector2D));
    absoluteMove.Speed->Zoom = soap_new_tt__Vector1D(soap, sizeof(struct tt__Vector1D));

    // 设置目标位置
    absoluteMove.Position->PanTilt->x = 0;  // Pan
    absoluteMove.Position->PanTilt->y = 0;    // Tilt
    absoluteMove.Position->Zoom->x = 0;       // Zoom

    // 设置速度
    absoluteMove.Speed->PanTilt->x = 0.5;     // Pan speed
    absoluteMove.Speed->PanTilt->y = 0.5;     // Tilt speed
    absoluteMove.Speed->Zoom->x = 0.5;        // Zoom speed
    //绝对移动接口函数调用
    result = soap_call___tptz__AbsoluteMove(soap,ptzXAddr, NULL,&absoluteMove,&absoluteMoveResponse);
    if (result != SOAP_OK) {
        printf("ONVIF_PTZAbsoluteMove failed: %d\n", result);
    } else {
        printf("AbsoluteMove succeeded.\n");
    }
    return 0;

}

/************************************************************************
**函数:ONVIF_PTZStopMove
**功能:停止运动
**参数:
**返回:
        0表明成功,非0表明失败
**备注:
************************************************************************/
int ONVIF_PTZStopMove(const char* ptzXAddr, const char* ProfileToken){
    int result = 0;
    struct soap *soap = NULL;
    struct _tptz__Stop tptzStop;
    struct _tptz__StopResponse tptzStopResponse;
    // 参数检查
    if (ptzXAddr == NULL || ProfileToken == NULL) {
        printf("Invalid input parameters.\n");
        return -1;
    }

    // 创建 SOAP 上下文
    soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT);
    if (!soap) {
        printf("Failed to create soap context.\n");
        return -1;
    }

    ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);
    tptzStop.ProfileToken = (char *)ProfileToken;
    result = soap_call___tptz__Stop(soap, ptzXAddr, NULL, &tptzStop, &tptzStopResponse);
    SOAP_CHECK_ERROR(result, soap, "ONVIF_PTZStopMove");
    printf("%s result\n", __func__);
    EXIT:
    if (NULL != soap) {
        ONVIF_soap_delete(soap);
    }
    return result;
}
/************************************************************************
**函数:ONVIF_PTZContinuousMove
**功能:连续移动
**参数:
        [in] ptzXAddr     - ptzXAddr
        [in] ProfileToken - 配置令牌
        [in] cmd          - 移动命令
        [in] speed        - 速度
**返回:
        0表明成功,非0表明失败
**备注:
************************************************************************/
int ONVIF_PTZContinuousMove(const char *ptzXAddr, char *ProfileToken, enum PTZCMD cmd, float speed)
{      
    int result = 0;
    struct soap *soap = NULL;
    struct _tptz__ContinuousMove continuousMove;
    struct _tptz__ContinuousMoveResponse continuousMoveResponse;

    // 参数检查
    if (ptzXAddr == NULL || ProfileToken == NULL) {
        printf("Invalid input parameters.\n");
        return -1;
    }

    // 创建 SOAP 上下文
    soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT);
    if (!soap) {
        printf("Failed to create soap context.\n");
        return -1;
    }
    // 设置认证信息
    ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);
    // 初始化请求结构体
    memset(&continuousMove, 0, sizeof(continuousMove));
    continuousMove.ProfileToken = ProfileToken;
    printf("continuousMove.ProfileToken:%s\n", continuousMove.ProfileToken);

    continuousMove.Velocity = soap_new_tt__PTZSpeed(soap,-1);
    continuousMove.Velocity->PanTilt = soap_new_tt__Vector2D(soap,-1);
    continuousMove.Velocity->Zoom = soap_new_tt__Vector1D(soap,-1);
    
    // 设置空间URI(必需字段)
    continuousMove.Velocity->PanTilt->space = "http://www.onvif.org/ver10/tptz/PanTiltSpaces/VelocityGenericSpace";
    continuousMove.Velocity->Zoom->space = "http://www.onvif.org/ver10/tptz/ZoomSpaces/VelocityGenericSpace";
    
    switch (cmd)
    {
        case  PTZ_CMD_LEFT:
            continuousMove.Velocity->PanTilt->x = -speed;
            continuousMove.Velocity->PanTilt->y = 0;
            break;
        case  PTZ_CMD_RIGHT:
            continuousMove.Velocity->PanTilt->x = speed;
            continuousMove.Velocity->PanTilt->y = 0;
            break;
        case  PTZ_CMD_UP:
            continuousMove.Velocity->PanTilt->x = 0;
            continuousMove.Velocity->PanTilt->y = speed;
            break;
        case  PTZ_CMD_DOWN:
            continuousMove.Velocity->PanTilt->x = 0;
            continuousMove.Velocity->PanTilt->y = -speed;
            break;
        case  PTZ_CMD_LEFTUP:
            continuousMove.Velocity->PanTilt->x = -speed;
            continuousMove.Velocity->PanTilt->y = speed;
            break;
        case PTZ_CMD_LEFTDOWN:
            continuousMove.Velocity->PanTilt->x = -speed;
            continuousMove.Velocity->PanTilt->y = -speed;
            break;
        case  PTZ_CMD_RIGHTUP:
            continuousMove.Velocity->PanTilt->x = speed;
            continuousMove.Velocity->PanTilt->y = speed;
            break;
        case PTZ_CMD_RIGHTDOWN:
            continuousMove.Velocity->PanTilt->x = speed;
            continuousMove.Velocity->PanTilt->y = -speed;
            break;
        case  PTZ_CMD_ZOOM_IN:
            continuousMove.Velocity->PanTilt->x = 0;
            continuousMove.Velocity->PanTilt->y = 0;
            continuousMove.Velocity->Zoom->x = speed;
            break;
        case  PTZ_CMD_ZOOM_OUT:
            continuousMove.Velocity->PanTilt->x = 0;
            continuousMove.Velocity->PanTilt->y = 0;
            continuousMove.Velocity->Zoom->x = -speed;
            break;
        default:
            break;
    }
    
    result = soap_call___tptz__ContinuousMove(soap,ptzXAddr, NULL,&continuousMove,&continuousMoveResponse);
    SOAP_CHECK_ERROR(result, soap, "soap_call___tptz__ContinuousMove");
    printf("soap_call___tptz__ContinuousMove :result = %d\n", result);
    // sleep(3); //如果当前soap被删除(或者发送stop指令),就会停止移动
    // printf("延时3S停止运行\n");
    // ONVIF_PTZStopMove(ptzXAddr, ProfileToken);
EXIT:
    if (NULL != soap) {
        ONVIF_soap_delete(soap);
    }
    return result;    
}

// 在main函数前添加内存释放函数
void ONVIF_FreeString(char **str)
{
    if (str && *str) {
        free(*str);
        *str = NULL;
    }
}

int main(int argc, char **argv)
{

    char * ptzXAddr = NULL;
    char *profilesToken = NULL;
    char buffer[1024];
    sprintf(buffer, DEVICE_XADDR, DEVICE_IP , DEVICE_PORT );//拼接服务器的地址


    // 获取能力
    if (ONVIF_GetCapabilities(buffer, &ptzXAddr) != 0) {
        printf("GetCapabilities failed\n");
        return -1;
    }

    if (!ptzXAddr) {
        printf("PTZ service not found\n");
        return -1;
    }
    
    // 获取配置
    if (ONVIF_GetProfiles(ptzXAddr, &profilesToken) != 0) {
        printf("GetProfiles failed\n");
        ONVIF_FreeString(&ptzXAddr);
        return -1;
    }

    if (!profilesToken) {
        printf("No profiles found\n");
        ONVIF_FreeString(&ptzXAddr);
        return -1;
    }

    printf("Using PTZ service at: %s\n", ptzXAddr);
    printf("Using profile token: %s\n", profilesToken);

    ONVIF_PTZAbsoluteMove(ptzXAddr, profilesToken);
    
    // ONVIF_PTZContinuousMove(ptzXAddr, profilesToken, PTZ_CMD_RIGHTDOWN, 0.5);//持续移动

    // sleep(3); //如果当前soap被删除(或者发送stop指令),就会停止移动
    // printf("延时3S停止运行\n");
    // ONVIF_PTZStopMove(ptzXAddr, profilesToken);
    
    // 释放内存
    ONVIF_FreeString(&ptzXAddr);
    ONVIF_FreeString(&profilesToken);
    return 0;
}

4:编写Makefile

# ONVIF PTZ 控制程序 Makefile (C语言版本)
TARGET = onvif-ptz
CC = gcc
CFLAGS = -DWITH_NONAMESPACES -DWITH_DOM -DWITH_OPENSSL -DWITH_PTHREAD -I. -g -Wall -Werror
LDFLAGS = -lssl -lcrypto -lpthread

# 所有源文件
SRCS = dom.c \
       main.c \
       mecevp.c \
       smdevp.c \
       soapC.c \
       soapClient.c \
       stdsoap2.c \
       struct_timeval.c \
       threads.c \
       wsaapi.c \
       wsseapi.c

OBJS = $(SRCS:.c=.o)

all: $(TARGET)

$(TARGET): $(OBJS)
	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)

# 显式依赖规则
dom.o: dom.c dom.h
	$(CC) $(CFLAGS) -c -o $@ $<

main.o: main.c soapH.h soapStub.h stdsoap2.h wsseapi.h dom.h
	$(CC) $(CFLAGS) -c -o $@ $<

mecevp.o: mecevp.c mecevp.h
smdevp.o: smdevp.c smdevp.h
soapC.o: soapC.c soapH.h
soapClient.o: soapClient.c soapH.h
stdsoap2.o: stdsoap2.c stdsoap2.h
struct_timeval.o: struct_timeval.c struct_timeval.h
threads.o: threads.c threads.h
wsaapi.o: wsaapi.c wsaapi.h
wsseapi.o: wsseapi.c wsseapi.h

# 通用规则
%.o: %.c
	$(CC) $(CFLAGS) -c -o $@ $<

clean:
	rm -f $(TARGET) $(OBJS)

.PHONY: all clean

我编译出现的问题是:

这个问题出现的原因是在soapStub.h中定义过了

在dom.h中修改这个两句话就可以了

然后再编译:Make

编译成功,测试:

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

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

相关文章

如何最简单、通俗地理解Pytorch?神经网络中的“梯度”是怎么自动求出来的?PyTorch的动态计算图是如何实现即时执行的?

PyTorch是一门科学——现代深度学习工程中的一把锋利利器。它的简洁、优雅、强大,正在让越来越多的AI研究者、开发者深度应用。 1. PyTorch到底是什么?为什么它重要? PyTorch是一个开源的深度学习框架,由Facebook AI Research(FAIR)于2016年发布,它的名字由两个部分组成…

QT+opecv如何更改图片的拍摄路径

如何更改相机拍摄图片的路径 前言&#xff1a;基础夯实&#xff1a;效果展示&#xff1a;实现功能&#xff1a;遇到问题&#xff1a;未解决&#xff1a; 核心代码&#xff1a; 前言&#xff1a; 最近在项目开发中遇到需要让用户更改相机拍摄路径的问题&#xff0c;用户可自己选…

秋招Day11 - JVM - 类加载机制

了解类的加载机制吗&#xff1f; JVM是运行Java字节码&#xff0c;也就是运行.class文件的虚拟机&#xff0c;JVM把.class文件中描述类的数据结构加载到内存中&#xff0c;并对数据进行校验&#xff0c;解析和初始化&#xff0c;最终转化为JVM可以使用的类型&#xff08;Klass…

Webug4.0靶场通关笔记03- 第3关SQL注入之时间盲注(手注法+脚本法 两种方法)

目录 一、源码分析 1.分析闭合 2.分析输出 &#xff08;1&#xff09;查询成功 &#xff08;2&#xff09;查询失败 &#xff08;3&#xff09;SQL语句执行报错 二、第03关 延时注入 1.打开靶场 2.SQL手注 &#xff08;1&#xff09;盲注分析 &#xff08;2&#xf…

Vert.x学习笔记-什么是Handler

Vert.x学习笔记 在Vert.x中&#xff0c;Handler是一个核心概念&#xff0c;用于处理异步事件和回调。它是Vert.x响应式编程模型的核心组件之一&#xff0c;通过函数式接口的方式简化了异步编程的复杂性。 1. Handler的定义 Handler是一个函数式接口&#xff0c;定义如下&#…

【Echarts】象形图

目录 效果代码 效果 代码 <!-- 业务类型 --> <template><div class"ywlx" :style"{ --height: height }"><div class"header_count count_linear_bg"><div>当月业务总量<span class"common_count text_s…

集星云推短视频矩阵系统的定制化与私有化部署方案

在当今数字化营销时代&#xff0c;短视频矩阵系统成为众多企业和机构拓展影响力、实现精准营销的关键工具。集星云推短视频矩阵系统凭借其强大的功能和灵活的定制性&#xff0c;为企业提供了全方位的解决方案。 一、API接口定制&#xff1a;无缝对接自有系统 集星云推短视频矩…

XCTF-web-file_include

解析 <?php highlight_file(__FILE__); // 高亮显示当前PHP文件源代码 include("./check.php"); // 包含检查文件&#xff08;可能包含安全过滤逻辑&#xff09;if(isset($_GET[filename])) { // 检查是否传入filename参数$filename $_GET[f…

5.28 后端面经

为什么golang在并发环境下更有优势 Go语言&#xff08;Golang&#xff09;在并发环境下的优势主要源自其设计哲学和内置的并发机制&#xff0c;这些机制在语言层面提供了高效、简洁且安全的并发编程工具。以下是其核心优势的详细分析&#xff1a; 1. Goroutine&#xff1a;轻量…

CPP中CAS std::chrono 信号量与Any类的手动实现

前言 CAS&#xff08;Compare and Swap&#xff09; 是一种用于多线程同步的原子指令。它通过比较和交换操作来确保数据的一致性和线程安全性。CAS操作涉及三个操作数&#xff1a;内存位置V、预期值E和新值U。当且仅当内存位置V的值与预期值E相等时&#xff0c;CAS才会将内存位…

PHP生成pdf方法

1&#xff1a;第一种方法&#xff1a; 主要使用PHP的扩展 【 “spatie/browsershot”: “3.57”】 使用这个扩展生成PDF需要环境安装以下依赖 1.1&#xff1a;NPM【版本&#xff1a;9.2.0】 1.2&#xff1a;NODE【版本&#xff1a;v18.19.1】 1.3&#xff1a;puppeteer【npm in…

【Android笔记】记一次 CMake 构建 Filament Android 库的完整排错过程(安卓交叉编译、CMake、Ninja)

写在前面的话&#xff0c;为了保持Sceneform-EQR始终是采用最新的filament&#xff0c;每隔一段时间我都会编译filament&#xff0c;并根据新增内容完善Sceneform-EQR。 现由于更换电脑&#xff0c;环境需重新配置。简单记录下编译出错和解决方式。 Sceneform-EQR 是EQ对谷歌“…

C#中的BeginInvoke和EndInvoke:异步编程的双剑客

文章目录 引言1. BeginInvoke和EndInvoke的基本概念1.1 什么是BeginInvoke和EndInvoke1.2 重要概念解释 2. 委托中的BeginInvoke和EndInvoke2.1 BeginInvoke方法2.2 EndInvoke方法2.3 两者的关系 3. 使用方式与模式3.1 等待模式3.2 轮询模式3.3 等待句柄模式3.4 回调模式 4. 底…

告别延迟!modbus tcp转profine网关助力改造电厂改造升级

发电需求从未如此旺盛。无论您是为客户发电还是为自身运营发电&#xff0c;您都需要提高运营效率&#xff0c;并在资产老化、资源萎缩的情况下&#xff0c;紧跟不断变化的法规。如今&#xff0c;智能系统和技术能够帮助您实现运营转型&#xff0c;提高可视性并实现关键流程自动…

《软件工程》第 5 章 - 需求分析模型的表示

目录 5.1需求分析与验证 5.1.1 顺序图 5.1.2 通信图 5.1.3 状态图 5.1.4 扩充机制 5.2 需求分析的过程模型 5.3 需求优先级分析 5.3.1 确定需求项优先级 5.3.2 排定用例分析的优先顺序 5.4 用例分析 5.4.1 精化领域概念模型 5.4.2 设置分析类 5.4.3 构思分析类之间…

阿里云国际版香港轻量云服务器:CN2 GIA加持,征服海外网络的“速度与激情”!

阿里云国际版香港轻量云服务器&#xff1a;CN2 GIA加持&#xff0c;征服海外网络的“速度与激情”&#xff01; 面对全球化业务拓展对网络连接的严苛要求&#xff0c;阿里云国际版香港轻量云服务器正成为出海企业和开发者的新宠。其核心优势在于搭载了CN2 GIA&#xff08;Glob…

Qt6无法识别OpenCV(Windows端开发)

这段时间在Windows 10上进行Qt6的开发。结果在build过程中&#xff0c;出现了如下错误: 但实际上&#xff0c;我明明安装了OpenCV4.10.0, 并且也在CMakeLists.txt中加入了相关内容。 但是&#xff0c;注意自己的编译输出: [1/5 1.4/sec] Automatic MOC and UIC for target R…

二、网络安全常见编码及算法-(2)

该文章主要介绍古典密码和隐写常用的密码和编码&#xff0c;日常中很少见&#xff0c;主要用于ctf比赛和考试学习一、古典密码 1、古典密码概念概述 古典密码是密码学发展早期所使用的一系列加密技术&#xff0c;这些密码主要依靠手工操作或简单的机械装置来实现信息的加密和…

Windows系统安装MySQL Connector 使用C++ VS2022连接MySQL

1. 官网及版本 1.1. 网址 官方文档 - 安装编译构建&#xff1a; https://dev.mysql.com/doc/connector-cpp/9.3/en/ 官方文档 - 使用案例&#xff1a; https://dev.mysql.com/doc/dev/connector-cpp/latest/ 下载地址&#xff1a; https://dev.mysql.com/downloads/connector/…

D2000平台上Centos使用mmap函数遇到的陷阱

----------原创不易&#xff0c;欢迎点赞收藏。广交嵌入式开发的朋友&#xff0c;讨论技术和产品------------- 在飞腾D2000平台上&#xff0c;安装了麒麟linux系统&#xff0c;我写了个GPIO点灯的程序&#xff0c;在应用层利用mmap函数将内核空间映射到用户态&#xff0c;然后…