CC2642的GGS使用笔记

news2024/11/20 0:44:01

一、前言
我们了解BLE的GATT之前需要了解一些基本的概念:
(1)Profile,字面意思简介、概述、形象印象、轮廓、配置文件,在BLE中,我们可能把它理解成配置文件较好,Profile有一些是BLE SIG规定的,有些可以是我们自定义的。
(2)Service,字面意思服务,在GATT中可以有多个服务,同样地,服务有些是BLE SIG定义的,有些是我们自定义的,习惯性叫这些服务为GATT服务。
(3)Characteristic, 字面意思特征,它存在于Service里面,就好比房间是一个服务,里面的装饰配置之类的是特征,而这些特征决定了房间的用处,也就是服务的用处。
(4)Attribute,字面意思是属性,可以这么理解,它描述的是特征的细节,比如描述特征的读写权限,特征的内容(值),特征的简介描述,还有特征的配置。
二、GGS相关的API
  下面列举几个我们GGS常用的协议栈API:

  • bStatus_t GGS_SetParameter( uint8 param, uint8 len, void *value )
    描述:设置GAP GATT服务参数
    参数
      param: Profile参数ID, 取的值可以参考gapgattserver.h的宏定义,其实就是下面表格列出的值:
GGS_DEVICE_NAME_ATT
GGS_APPEARANCE_ATT
GGS_DEVICE_NAME_ATT
GGS_APPEARANCE_ATT
GGS_PERI_CONN_PARAM_ATT
GGS_CENT_ADDR_RES_ATT
GGS_RESOLVABLE_PRIVATE_ADDRESS_ONLY_ATT

  len: 要写入的字节个数
  value: 指向要写的数据
  比如我们在APP的初始化处初始蓝牙设备的名称,可以这么写:

    uint8_t Ble_deviceName[] = "BoBo.cn";
    GGS_SetParameter(GGS_DEVICE_NAME_ATT, strlen(Ble_deviceName), Ble_deviceName);
  • bStatus_t GAP_SetParamValue(uint16_t paramID, uint16_t paramValue)
    描述:设置GAP参数值
    参数
      paramID, 这个参数可以参考Gap_ParamIDs_t枚举
      paramValue, 值
    bStatus_t GGS_AddService( uint32 services )
    描述:添加一个功能到GGS上
    参数:services,该参数是32位,每个位代表一个功能服务,较常用的是bit1,也就是GAP_SERVICE。
  • bStatus_t GATTServApp_AddService( uint32 services )
    描述:添加一个功能到GATT服务上,我们将一个服务注册到GATT服务上之后,在蓝牙建立连接过程中客户端就能发现该服务,如果注册的回调函数功能正常的话,就能够正常的使用该服务。
    参数
      services,该参数是32位,每个位代表一个功能服务,大多数情况下推荐0xFFFFFFFF,即GATT_ALL_SERVICES
  • bStatus_t GATTServApp_RegisterService( gattAttribute_t *pAttrs, uint16 numAttrs, uint8 encKeySize, CONST gattServiceCBs_t *pServiceCBs )
    描述:向GATT服务应用注册服务的属性列表和回调函数
    参数
      pAttrs, 指向属性列表的指针
      numAttrs, 属性单元个数
      encKeySize, 服务所需的最小加密钥匙字节大小(7~16个字节)
      pServiceCBs, 指向服务回调函数的指针

这个API很重要,我们有必要看看结构体gattAttribute_t和gattServiceCBs_t的原型:

typedef struct attAttribute_t
{
	/// @brief GATT Attribute Type format.
	struct
	{
	    uint8 len;         //!< UUID的长度
	    const uint8 *uuid; //!< 指向UUID
	} type; //!< 属性类型 (2或者16字节UUIDs)
  	uint8 permissions;    //!< 属性的权限
  	uint16 handle;       //!< 属性的句柄 - 由属性服务器内部分配
  	uint8* const pValue; //!< 属性值 - 字节个数不超过512
} gattAttribute_t;

typedef struct
{
  pfnGATTReadAttrCB_t pfnReadAttrCB;           //!< 指向读ATT函数
  pfnGATTWriteAttrCB_t pfnWriteAttrCB;         //!< 指向写ATT函数
  pfnGATTAuthorizeAttrCB_t pfnAuthorizeAttrCB; //!< 指向认证函数
} gattServiceCBs_t;

上面三个函数类型如下:

typedef bStatus_t (*pfnGATTReadAttrCB_t)( uint16 connHandle, gattAttribute_t *pAttr,
                                          uint8 *pValue, uint16 *pLen, uint16 offset,
                                          uint16 maxLen, uint8 method );
typedef bStatus_t (*pfnGATTWriteAttrCB_t)( uint16 connHandle, gattAttribute_t *pAttr,
                                           uint8 *pValue, uint16 len, uint16 offset,
                                           uint8 method );
typedef bStatus_t (*pfnGATTAuthorizeAttrCB_t)( uint16 connHandle, gattAttribute_t *pAttr,
                                               uint8 opcode );
  • bStatus_t GATTServApp_ProcessCharCfg( gattCharCfg_t *charCfgTbl, uint8 *pValue, uint8 authenticated, gattAttribute_t *attrTbl, uint16 numAttrs, uint8 taskId, pfnGATTReadAttrCB_t pfnReadAttrCB )
    描述:处理客户端特征配置的改变
    参数
      charCfgTbl,特征配置表
      pValue, 指向属性内容
       authenticated, 是否需要认证
      attrTbl, 服务属性列表
      numAttrs, 服务属性列表单元个数
      taskId, 确认通知的任务ID
      pfnReadAttrCB, Att读回调函数,GATTServApp_ProcessCharCfg最后会通过该函数给客户端发送数据
  • bStatus_t GATTServApp_ProcessCCCWriteReq( uint16 connHandle, gattAttribute_t *pAttr, uint8 *pValue, uint16 len, uint16 offset, uint16 validCfg )
    描述:处理客户端特征配置写请求,这个函数配合UUID 0x2902使用
    参数
      connHandle, 客户端的连接句柄
      pAttr, 指向属性表
      pValue, 指向写入的数据
      Len, 待写的字节数
      offset,偏移字节写入
      validCfg, 配置选项,值可以是GATT_CLIENT_CFG_NOTIFY或者GATT_CLIENT_CFG_INDICATE,或者两者都选(或逻辑)。

三、实现一个数据透传服务
  下面写一个服务,两个特征,一个支持写,一个支持读和notify
在这里插入图片描述
直接上代码:

  • icce_service.c
/*
 * icce_service.c
 *
 *  Created on: 2023年4月15日
 *      Author: 28596
 */
/*********************************************************************
 * INCLUDES
 */
#include <string.h>

//#include <xdc/runtime/Log.h> // Comment this in to use xdc.runtime.Log
#include <ti/common/cc26xx/uartlog/UartLog.h>  // Comment out if using xdc Log

#include <icall.h>

/* This Header file contains all BLE API and icall structure definition */
#include "icall_ble_api.h"

#include "icce_service.h"
#define DataServiceUUID 0xFFF5
#define DataWrite_UUID 0x0001
#define DataRead_UUID  0x0002
#define ATT_BT_UUID_SIZE 2
#define u8DATALEN     247
#define DATA_SERVICE_SERV_UUID_BASE16(uuid) LO_UINT16(uuid), HI_UINT16(uuid)

#define DS_UUID_BASE16(uuid)  LO_UINT16(uuid), HI_UINT16(uuid)

static uint8_t wCharact_Props = GATT_PROP_WRITE;
static uint8_t rnCharact_Props = GATT_PROP_NOTIFY;

static uint8_t ds_icall_rsp_task_id = INVALID_TASK_ID;
// Write Characteristic Value
static uint8_t LOC_InputVal[u8DATALEN] = {0};
static uint8_t LOC_InputValLen = 0;
static uint8_t LOC_OutputVal[u8DATALEN] = {0};
static uint8_t LOC_OutputValLen = 0;
static ICCEDataServiceCBs_t *pAppCBs = NULL;
// Notify Characteristic 客户端特征配置描述符
static gattCharCfg_t *NotifyCharactConfig;//占用字节大小由可连接设备个数决定
// 数据服务的服务UUID Service UUID
CONST uint8_t icce_DataServiceUUID[ATT_BT_UUID_SIZE] =
{
    DATA_SERVICE_SERV_UUID_BASE16(DataServiceUUID)
};
// 数据服务之下的写UUID
CONST uint8_t wUserCharact_UUID[ATT_BT_UUID_SIZE] =
{
    DS_UUID_BASE16(DataWrite_UUID)
};
// 数据服务之下的读&Notify UUID
CONST uint8_t rnUserCharact_UUID[ATT_BT_UUID_SIZE] =
{
    DS_UUID_BASE16(DataRead_UUID)
};

/* 属性类型的定义(UUID的字节长度,UUID)*/
static CONST gattAttrType_t LOC_DataServiceDecl = { ATT_BT_UUID_SIZE, icce_DataServiceUUID };
/* 服务列表 */
static gattAttribute_t ICCE_Data_ServiceAttrTbl[] =
{
    // 数据服务服务什么
    {
        { ATT_BT_UUID_SIZE, primaryServiceUUID },//2800
        GATT_PERMIT_READ,
        0,
        (uint8_t *)&LOC_DataServiceDecl
    },
    // 写特征的申明
    {
        { ATT_BT_UUID_SIZE, characterUUID },//2803
        GATT_PERMIT_READ,
        0,
        &wCharact_Props
    },
    // 写特征的内容
    {
        { ATT_BT_UUID_SIZE, wUserCharact_UUID },
        GATT_PERMIT_WRITE,
        0,
        LOC_InputVal
    },
    // 读/通知特征的声明
    {
        { ATT_BT_UUID_SIZE, characterUUID },
        GATT_PERMIT_READ,
        0,
        &rnCharact_Props
    },
    // 读/通知特征的内容
    {
        { ATT_BT_UUID_SIZE, rnUserCharact_UUID },
        GATT_PERMIT_WRITE,//写权限允许,数组可以更改
        0,
        LOC_OutputVal
    },
    // 通知特征的 CCCD
    {
        { ATT_BT_UUID_SIZE, clientCharCfgUUID },//0x2902
        GATT_PERMIT_READ | GATT_PERMIT_WRITE,
        0,
        (uint8_t *)&NotifyCharactConfig
    },
};


/*********************************************************************
 * 静态函数声明
 */
static bStatus_t ICCE_Data_Service_ReadAttrCB(uint16_t connHandle,
                                         gattAttribute_t *pAttr,
                                         uint8_t *pValue,
                                         uint16_t *pLen,
                                         uint16_t offset,
                                         uint16_t maxLen,
                                         uint8_t method);
static bStatus_t ICCE_Data_Service_WriteAttrCB(uint16_t connHandle,
                                          gattAttribute_t *pAttr,
                                          uint8_t *pValue,
                                          uint16_t len,
                                          uint16_t offset,
                                          uint8_t method);

/*********************************************************************
 * 配置文件的回调
 */
CONST gattServiceCBs_t LOC_ICCE_Data_ServiceCBs =
{
    ICCE_Data_Service_ReadAttrCB,
    ICCE_Data_Service_WriteAttrCB,
    NULL
};

/*
 * ICCE数据服务添加函数 - 向Gatt服务器里注册Gatt属性
 * 传参:rspTaskId - ICALL任务ID
 */
extern bStatus_t ICCE_DataService_AddService(uint8_t rspTaskId)
{
    uint8_t status;

    // 初始化,为客户端特征配置表动态分配一块内存
    NotifyCharactConfig = (gattCharCfg_t *)ICall_malloc( sizeof(gattCharCfg_t) * linkDBNumConns);
    if(NotifyCharactConfig == NULL)
    {
        return(bleMemAllocError);
    }

    // 初始化客户端特征配置属性,默认设置为无效
    GATTServApp_InitCharCfg(LINKDB_CONNHANDLE_INVALID, NotifyCharactConfig);
    // 向GATT服务器添加我们写好的服务
    status = GATTServApp_RegisterService(ICCE_Data_ServiceAttrTbl,
                                         GATT_NUM_ATTRS(ICCE_Data_ServiceAttrTbl),
                                         GATT_MAX_ENCRYPT_KEY_SIZE,
                                         &LOC_ICCE_Data_ServiceCBs);
    Log_info1("Registered service, %d attributes",
              GATT_NUM_ATTRS(ICCE_Data_ServiceAttrTbl));
    ds_icall_rsp_task_id = rspTaskId;

    return(status);
}
/*
 * 数据服务设置参数 - 设置服务里面的属性内容.
 * 传参:param -配置文件参数ID,目前我们ICCE数据服务里面有两个参数,我们把ID的范围取0:1
 *      len   - 带写入的数据长度
 *      value - 指向带写入的数据.
 */
bStatus_t ICCEDataService_SetParameter(uint8_t param, uint16_t len, void *value)
{
    bStatus_t ret = SUCCESS;
    uint8_t  *pAttrVal;
    uint8_t  *pValLen;
    uint16_t valMinLen;
    uint16_t valMaxLen;
    uint8_t sendNotiInd = FALSE;
    gattCharCfg_t *attrConfig;
    uint8_t needAuth;

    switch(param)
    {
    case wCharact_PARAM_ID:
        pAttrVal = LOC_InputVal;
        pValLen = &LOC_InputValLen;
        valMinLen = 0;
        valMaxLen = sizeof(LOC_InputVal);
        break;

    case rnCharact_PARAM_ID:
        pAttrVal = LOC_OutputVal;
        pValLen = &LOC_OutputValLen;
        valMinLen = 0;
        valMaxLen = sizeof(LOC_OutputVal);
        sendNotiInd = TRUE;
        attrConfig = NotifyCharactConfig;
        needAuth = FALSE;  // 这里发送设置成不需要认证
        break;

    default:
        Log_error1("SetParameter: Parameter #%d not valid.", param);
        return(INVALIDPARAMETER);
    }
    // 更新数据和发送notification或者indication
    if(len <= valMaxLen && len >= valMinLen)
    {
        memcpy(pAttrVal, value, len);
        *pValLen = len;

        if(sendNotiInd)
        {
            Log_info2("Trying to send noti/ind: connHandle %x, %s",
                      attrConfig[0].connHandle,
                      (uintptr_t)((attrConfig[0].value ==
                                   0) ? "\x1b[33mNoti/ind disabled\x1b[0m" :
                                  (attrConfig[0].value ==
                                   1) ? "Notification enabled" :
                                  "Indication enabled"));
            // 尝试发送Notification
            GATTServApp_ProcessCharCfg(attrConfig, pAttrVal, needAuth,
                                       ICCE_Data_ServiceAttrTbl,
                                       GATT_NUM_ATTRS(ICCE_Data_ServiceAttrTbl),
                                       ds_icall_rsp_task_id,
                                       ICCE_Data_Service_ReadAttrCB);
        }
    }
    else
    {
        Log_error3("Length outside bounds: Len: %d MinLen: %d MaxLen: %d.", len,
                   valMinLen,
                   valMaxLen);
        ret = bleInvalidRange;
    }

    return(ret);
}


static uint8_t ICCEData_Service_findCharParamId(gattAttribute_t *pAttr)
{
    // Is this a Client Characteristic Configuration Descriptor?
    if(ATT_BT_UUID_SIZE == pAttr->type.len && GATT_CLIENT_CHAR_CFG_UUID ==
       *(uint16_t *)pAttr->type.uuid)
    {
        return(ICCEData_Service_findCharParamId(pAttr - 1));
    }
    // 判断是否是wCharact的UUID
    else if(ATT_BT_UUID_SIZE == pAttr->type.len && !memcmp(pAttr->type.uuid, wUserCharact_UUID, pAttr->type.len))
    {
        return(wCharact_PARAM_ID);
    }
    // 判断是否是rnCharact的UUID
    else if(ATT_BT_UUID_SIZE == pAttr->type.len && !memcmp(pAttr->type.uuid, rnUserCharact_UUID, pAttr->type.len))
    {
        return(rnCharact_PARAM_ID);
    }
    else
    {
        return(0xFF);
    }
}

/*********************************************************************
 * @fn          ICCE_Data_Service_ReadAttrCB
 * @brief       读取一个属性
 * @param       connHandle - 连接句柄,客户端和服务器建立连接之后的句柄
 * @param       pAttr - 指向属性的指针
 * @param       pValue - 指向待读的数据
 * @param       pLen - 指向读取的字节个数
 * @param       offset - 偏移字节读取
 * @param       maxLen - 读取的最大长度
 * @param       method - 读取的方式
 * @return      SUCCESS, blePending or Failure
 */
static bStatus_t ICCE_Data_Service_ReadAttrCB(uint16_t connHandle,
                                         gattAttribute_t *pAttr,
                                         uint8_t *pValue, uint16_t *pLen,
                                         uint16_t offset,
                                         uint16_t maxLen,
                                         uint8_t method)
{
    bStatus_t status = SUCCESS;
    uint16_t valueLen;
    uint8_t paramID = 0xFF;

    paramID = ICCEData_Service_findCharParamId(pAttr);
    switch(paramID)
    {
    case rnCharact_PARAM_ID:
        valueLen = LOC_OutputValLen;
        break;

    default:
        Log_error0("Attribute was not found.");
        return(ATT_ERR_ATTR_NOT_FOUND);
    }
    if(offset > valueLen)
    {
        Log_error0("An invalid offset was requested.");
        status = ATT_ERR_INVALID_OFFSET;
    }
    else
    {
        *pLen = MIN(maxLen, valueLen - offset);
        memcpy(pValue, pAttr->pValue + offset, *pLen);
    }

    return(status);
}

/*********************************************************************
 * @fn      ICCE_Data_Service_WriteAttrCB
 * @brief   在写操作之前验证属性的数据
 * @param   connHandle - 连接句柄
 * @param   pAttr - 指向属性的指针
 * @param   pValue - 指向待写的数据
 * @param   len - 待写入的数据长度
 * @param   offset - 偏移字节写入
 * @param   method - 写类型
 * @return  SUCCESS, blePending or Failure
 */
static bStatus_t ICCE_Data_Service_WriteAttrCB(uint16_t connHandle,
                                          gattAttribute_t *pAttr,
                                          uint8_t *pValue, uint16_t len,
                                          uint16_t offset,
                                          uint8_t method)
{
    bStatus_t status = SUCCESS;
    uint8_t paramID = 0xFF;
    uint8_t changeParamID = 0xFF;
    uint16_t writeLenMin;
    uint16_t writeLenMax;
    uint8_t *pValueLenVar;

    if(ATT_BT_UUID_SIZE == pAttr->type.len && GATT_CLIENT_CHAR_CFG_UUID == *(uint16_t *)pAttr->type.uuid)
    {
        // Allow notification and indication, but do not check if really allowed per CCCD.
        status = GATTServApp_ProcessCCCWriteReq(
            connHandle, pAttr, pValue, len,
            offset,
            GATT_CLIENT_CFG_NOTIFY | GATT_CLIENT_CFG_INDICATE);
        if(SUCCESS == status && pAppCBs && pAppCBs->pfnCfgChangeCb)
        {
            pAppCBs->pfnCfgChangeCb(connHandle,
                                    ICCEData_Service_findCharParamId(pAttr), len, pValue);
        }

        return(status);
    }

    paramID = ICCEData_Service_findCharParamId(pAttr);
    switch(paramID)
    {
    case wCharact_PARAM_ID:
        writeLenMin = 0;
        writeLenMax = sizeof(LOC_InputVal);
        pValueLenVar = &LOC_InputValLen;

        Log_info5(
            "WriteAttrCB : %s connHandle(%d) len(%d) offset(%d) method(0x%02x)",
            (uintptr_t)"String",
            connHandle,
            len,
            offset,
            method);
        break;

    default:
        Log_error0("Attribute was not found.");
        return(ATT_ERR_ATTR_NOT_FOUND);
    }
    if(offset >= writeLenMax)
    {
        Log_error0("An invalid offset was requested.");
        status = ATT_ERR_INVALID_OFFSET;
    }
    else if(offset + len > writeLenMax)
    {
        Log_error0("Invalid value length was received.");
        status = ATT_ERR_INVALID_VALUE_SIZE;
    }
    else if(offset + len < writeLenMin &&
            (method == ATT_EXECUTE_WRITE_REQ || method == ATT_WRITE_REQ))
    {
        Log_error0("Invalid value length was received.");
        status = ATT_ERR_INVALID_VALUE_SIZE;
    }
    else
    {
        memcpy(pAttr->pValue + offset, pValue, len);
        if(offset + len >= writeLenMin)
        {
            changeParamID = paramID;
            *pValueLenVar = offset + len; // Update data length.
        }
    }
#if 0
    if(changeParamID != 0xFF)
    {
        if(pAppCBs && pAppCBs->pfnChangeCb)
        {
            pAppCBs->pfnChangeCb(connHandle, paramID, len + offset, pValue); // Call app function from stack task context.
        }
    }
#endif
    return(status);
}

extern void ICCE_test(void)
{
    uint8_t value[10] = {0};
    static uint64_t vl = 0;
    vl++;
    memcpy(value, (uint8_t *)&vl, sizeof(vl));
    ICCEDataService_SetParameter(rnCharact_PARAM_ID, 10, value);
}


  • icce_service.h
/*
 * icce_service.h
 *
 *  Created on: 2023年4月15日
 *      Author: 28596xx
 */

#ifndef PROFILES_ICCE_SERVICE_H_
#define PROFILES_ICCE_SERVICE_H_


#ifdef __cplusplus
extern "C"
{
#endif

/*********************************************************************
 * INCLUDES
 */
#include <bcomdef.h>

#define wCharact_PARAM_ID 0x00
#define rnCharact_PARAM_ID 0x01

extern bStatus_t ICCE_DataService_AddService(uint8_t rspTaskId);
extern bStatus_t ICCEDataService_SetParameter(uint8_t param, uint16_t len, void *value);

extern void ICCE_test(void);//用于测试notify是否正常
#endif /* PROFILES_ICCE_SERVICE_H_ */

将上面这两个文件添加到工程,ICCE_test() 用一个定时器每5s执行一次。编译下载到CC2642,运行之后用手机就能发现我们这个服务了。
在这里插入图片描述

color=gray

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

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

相关文章

从Vue2到Vue3的差别学习升级

目录 1 从data,methods到setup 超级NB的写法 2 使用props、emit和context 使用props 使用emit 3 路由变化 4 变量初始化的变化 1 从data,methods到setup <script> export default {components:{NPagination:NPagination,},name: "MyPaging",setup(){//…

Python实现哈里斯鹰优化算法(HHO)优化BP神经网络分类模型(BP神经网络分类算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 2019年Heidari等人提出哈里斯鹰优化算法(Harris Hawk Optimization, HHO)&#xff0c;该算法有较强的全…

Unix、UTC、GPS时间戳及转换

UTC时间 UTC时间的英文全称&#xff1a;Universal Time Coordinated&#xff0c;中文名称&#xff1a;协调世界时。俗的理解为&#xff0c;这个时间是全世界通用的&#xff0c;即全世界都公用的一个时间。可以认为格林威治时间就是时间协调时间&#xff08;GMTUTC&#xff09;&…

聚焦元宇宙赋能产业,打造数字世界,“OFweek2023广州元宇宙产业发展高峰论坛”圆满落幕!

2023年4月12日下午&#xff0c;由广东潮域科技有限公司、OFweek维科网共同主办&#xff0c;OFweek人工智能网承办的“OFweek 2023 广州元宇宙产业发展高峰论坛”在广州保利世贸博览馆1号馆盛大举办。 元宇宙产业相关技术及设备&#xff0c;包括VR&#xff0f;AR、虚拟现实、物联…

PHP 调用百度人脸检测

本文章主要介绍人脸检测API能力、应用场景、请求实例、参数说明。 接口能力 人脸检测&#xff1a;检测图片中的人脸并标记出位置信息。 人脸关键点&#xff1a;展示人脸的核心关键点信息&#xff0c;及150个关键点信息。 人脸属性值&#xff1a;展示人脸属性信息&#xff0c;如…

[MAUI 项目实战] 手势控制音乐播放器(四):圆形进度条

文章目录关于图形绘制创建自定义控件使用控件创建专辑封面项目地址我们将绘制一个圆形的音乐播放控件&#xff0c;它包含一个圆形的进度条、专辑页面和播放按钮。关于图形绘制 使用MAUI的绘制功能&#xff0c;需要Microsoft.Maui.Graphics库。 Microsoft.Maui.Graphics 是一个…

【技术】《Netty》从零开始学netty源码(三十七)之ByteBufAllocator

ByteBufAllocator 在channel的配置类中有一个属性allocator&#xff0c;我们知道netty有自己的缓冲区&#xff0c;可以使用该缓存分配器来分配所需的缓存。在config类中默认使用ByteBufAllocator.DEFAULT&#xff0c;它的初始化过程如下&#xff1a; 在开始分析PooledByteBufA…

基于 QT 开发 FLIPPED :简易且漂亮的跨平台截图贴图软件

文章目录FLIPPED运行预览视频演示截图演示特性快捷键架构思路思路细节打磨实际问题解决方案编译依赖WindowsMacOS / Linux运行效果作者贡献者反馈下载安装包系列地址简 述: 新开发的一款跨平台的截图贴图的软件作品&#xff0c; FLIPPED&#xff1a;Simple and beautiful cross…

基于Web的智慧LNG加气站2D组态控制系统

LNG(Liquefied Natural Gas)&#xff0c;即液化天然气的英文缩写。LNG是通过在常压下气态的天然气&#xff0c;经过预处理&#xff0c;脱除重烃、硫化物、二氧化碳和水等杂质后&#xff0c;冷却至-162℃&#xff0c;使之凝结成液体。LNG无色无毒无味&#xff0c;是天然气经净化…

算法分析与设计—分治法

分治者&#xff0c;分而治之也。概述分治法也称为分解法、分治策略等。分治法算法思想如下&#xff1a;(1) 将一个问题划分为同一类型的若干子问题&#xff0c;子问题最好规模相同。(2) 对这些子问题求解&#xff08;一般使用递归方法&#xff0c;但在问题规模足够小时&#xf…

fileinclude(通过cookie赋值的文件包含)

打开链接&#xff0c;提示flag在flag.php里 内容里还有一个路径/var/www/html/index.php&#xff0c;猜测flag.php应该也是在这个路径下 Ctrlu查看网页源码 代码中发现 include($lan.".php"); 可知这里存在文件包 且包含与lan的值有关 由代码 $lan $_COOKIE[lang…

安装kafka及一些命令

1&#xff0c;先把压缩包放到/opt/install目录下 2&#xff0c;解压&#xff0c;更名 解压&#xff1a;[rootsiwen install]# tar -zxf kafka_2.12-2.8.0.tgz -C ../soft 更名&#xff1a;[rootsiwen soft]# mv kafka_2.12-2.8.0/ kafka212 3&#xff0c;配置文件 cd opt/so…

讯飞 语音唤醒 Android SDK

语音唤醒 Android SDK 文档 | 讯飞开放平台文档中心https://www.xfyun.cn/doc/asr/awaken/Android-SDK.html前往控制台&#xff0c;设置唤醒关键词&#xff1a; 控制台-讯飞开放平台https://console.xfyun.cn/services/awaken注意&#xff0c;可以对唤醒词进行评估&#xff0c…

游戏开发需要学什么

游戏开发可以说是当下最热门的专业了&#xff0c;不过这一行业虽然很火热&#xff0c;但也有一定的竞争压力。这个行业需要大量的人才&#xff0c;而且游戏开发这个行业目前还处于初级阶段&#xff0c;所以发展空间还是很大的。现在有很多人都想进入这个行业&#xff0c;但却不…

ESP32-IDF开发笔记 | 03 - 使用SPI外设驱动ST7789 SPILCD

一、硬件说明 ST7789屏幕引脚ESP32C3F引脚3V33V3GNDGNDMOSIIO_07CLKIO_06DCIO_08RSTIO_04BLIO_05 二、ESP32的SPI外设 1. 外设功能 ESP32-C3具有三个SPI接口(SPI0、SPI1和SPI2)。 SPI0和SPI1只能配置为在SPI内存模式下操作&#xff0c;而SPI2可以配置为在SPI内存和通用SPI模…

2023年MathorCup数学建模C题电商物流网络包裹应急调运与结构优化问题解题全过程

2023年第十三届MathorCup高校数学建模挑战赛 C题 电商物流网络包裹应急调运与结构优化问题 原题再现&#xff1a; 电商物流网络由物流场地&#xff08;接货仓、分拣中心、营业部等&#xff09;和物流场地之间的运输线路组成&#xff0c;如图 1 所示。受节假日和“双十一”、“…

手机应用开发之如何利用蓝牙与HC-05通信?

文章目录0、引言1、创建工程2、准备真机调试3、应用布局4、代码编写5、功能演示0、引言 本文通过AndroidStudio开发手机应用软件&#xff0c;实现蓝牙连接功能&#xff0c;并且能发送消息给HC-05蓝牙&#xff0c;也能接收HC-05回传的消息。本文在【AndroidStudio如何进行手机应…

C++基础学习笔记(八)——提高编程PART3

参考链接&#xff1a;https://www.bilibili.com/video/BV1et411b73Z/p237&spm_id_frompageDriver&vd_sourceb4d9cee68649c8adcb1e266f7147cd5c 4 STL- 函数对象 4.1 函数对象 4.1.1 函数对象概念 概念&#xff1a; 重载函数调用操作符的类&#xff0c;其对象常称为…

STL的并行遍历:for_each(依赖TBB)和omp parallel

文章目录OMP parallelOpenMP安装OpenMP示例1) OMP Hello World2) OMP for 并行3. OMP 官方示例4) map使用OMP遍历TBB的安装和使用Gcc9的安装TBB 安装TBB使用在图像处理等应用中&#xff0c;我们经常需要对矩阵&#xff0c;大数量STL对象进行遍历操作&#xff0c;因此并行化对算…

R语言与作物模型(以DSSAT模型为例)融合应用

随着基于过程的作物生长模型&#xff08;Process-based Crop Growth Simulation Model&#xff09;的发展&#xff0c;R语言在作物生长模型和数据分析、挖掘和可视化中发挥着越来越重要的作用。想要成为一名优秀的作物模型使用者与科研团队不可或缺的人才&#xff0c;除了掌握对…