OpenHarmony-3.HDF框架(2)

  • OpenHarmony HDF 平台驱动

1.平台驱动概述

  系统平台驱动框架是系统驱动框架的重要组成部分,它基于HDF驱动框架、操作系统适配层(OSAL, operating system abstraction layer)以及驱动配置管理机制,为各类平台设备驱动的实现提供标准模型。

  系统平台驱动(PlatformDriver),即平台设备驱动,它用于驱动平台设备(PlatformDevice),为系统及外设驱动提供访接口。这里的平台设备,泛指I2C/UART等总线、以及GPIO/RTC等SOC片内硬件资源。

  系统平台驱动框架为外设提供了标准的平台设备访问接口,使其不必关注具体硬件及OS平台;同时为平台设备驱动提供统一的适配接口,使其只关注自身硬件的控制。

  为实现这个目标,OpenHarmony系统平台驱动框架满足如下特性:

  • 统一的平台设备访问接口:对平台设备操作接口进行统一封装,屏蔽不同SOC平台硬件差异以及不同OS形态差异。
  • 统一的平台驱动适配接口:为平台设备驱动提供统一的适配接口,使其只关注自身硬件的控制,而不必关注设备管理及公共业务流程。
  • 提供设备注册、管理、访问控制等与SOC无关的公共能力。

2.平台驱动框架介绍

  OpenHarmony系统平台驱动框架主要由平台接口层、平台核心层以及平台适配层三个部分组成。
在这里插入图片描述

  • 平台接口层 以API的形式提供标准的平台设备访问接口。

    平台接口层以设备句柄加配套API的形式对外提供统一的、标准的访问接口。

    设备句柄是DevHandle类型的实例,通过不同设备模块提供的Open/Close方法进行获取、释放。成功获取设备句柄后,即可使用相应的API执行设备操作。例如通过I2cTransfer完成一次I2C数据传输。

    这是一种代理模式,即接口层API不直接引用实际设备对象,而是通过DevHandle作为代理,间接访问设备;而所有来自外设驱动的访问,都建议走接口层,以获得最佳的稳定性。

  • 平台核心层 提供平台设备模型及公共业务框架。
    提供统一适配接口:定义了标准的设备对象模型,驱动程序仅需关注标准对象模型的适配。

    抽取公共业务框架:将不同设备模块的公共流程、算法加以抽取,使得具体设备驱动更加轻薄。

    设备管理:设备注册、注销、设备查找、访问控制。

  • 平台适配层 提供特定平台设备的适配驱动,并遵守核心层约束。

    驱动具体平台设备硬件,并创建对应的设备模型对象,注册到核心层纳入统一管理。

2.1.平台接口层分析

  接口层用DevHandle类型的设备句柄表示一个平台设备对象,然后针对不同类型设备提供一套标准的API方法用于设备访问。那么设备句柄和真实的设备对象如何关联呢?

 drivers_hdf_core/framework/include/platform/platform_if.h:39 /**40  * @brief Defines the common device handle of the platform driver.41  *42  * The handle is associated with a specific platform device and is used as the43  * first input parameter for all APIs of the platform driver.44  *45  * @since 1.046  */47 typedef void* DevHandle;

  在内核态,这个指针可以直接指向实际设备对象,但是对于某些类型的平台设备,需要在用户态提供同样的DevHandle类型及配套API,而实际设备对象在内核空间,导致无法直接获取和使用内核空间的地址。

  解决办法:将平台设备对象实现为一个HDF设备服务,这样借助HDF DeviceManager的设备服务机制,可以在用户态、内核态同时获取到设备服务,而用户态同内核态通信的问题交由HDF DeviceManager处理。此时,DevHandle只需要关联到这个设备服务即可,而void*类型保证了足够的灵活性。

  根据DevHandle和设备对象关联方式的不同,接口层的设计有三种模式:

2.1.1.独立服务模式

  典型实践是UART模块。
在这里插入图片描述
  使用范围:在用户态和内核态同时提供API的设备类型,DevHandle同设备对象的关联方式为:

  • 用户态:关联到平台设备对应的设备服务;

  • 内核态:关联到实际平台设备对象或其设备服务(在内核态两者可互相转换)

  每一个设备对象,会独立发布一个设备服务,来处理外部访问,服务收到API的访问请求之后,通过提取请求参数,并调用实际设备对象的相应内部方法。

  优点:管理比较简单,因为它借助了HDF DeviceManager的服务管理能力;
  缺点:需要为每一个设备对象配置设备节点,以便发布设备服务。

2.1.2.统一服务模式

  典型实践是I2C模块。
在这里插入图片描述
  同一类型的设备对象可能会很多,例如I2C模块,可能同时有十几个控制器。如果采用独立服务的模式,每一个控制器,作为一个平台设备,为其创建一个设备服务,那么将会有十几个服务被创建,不光要配置很多设备节点,而且这些服务还会占用内存资源。

  因此为一类设备对象,创建一个平台设备管理器(PlatformManager)对象,并同时对外发布一个管理器服务,由这个管理器服务来统一处理外部访问。当用户需要打开某个设备时,先通过HDF DeviceManager获取到管理器服务,然后管理器服务根据用户指定参数查找到指定设备,并返回一个设备描述符,而这个描述符仍然可以由DevHandle类型表示。

2.1.3.无服务模式
在这里插入图片描述  用于不需要在用户态提供API的设备类型或者没有用户态、内核区分的OS系统,其关联方式是DevHandle直接指向设备对象内核态地址。而PlatformManager的实现比较自由,它不需要实现设备服务,只需做好某种类型的设备管理即可,甚至在C语言中,由于无法进行OOP编程,很多模块直接将这个功能面向过程化了,使得没有一个具体的结构体与之对应。

2.2.平台核心层

  平台核心层的作用是承上启下,其主要内容包括:

  • 提供适配接口:为具体的平台设备驱动提供统一的适配接口

    平台驱动框架为不同设备类型,定义了标准的设备对象模型,具体设备驱动只需要关注标准设备对象的适配即可

  • 提供设备管理:提供设备的注册、注销、查找等功能、访问控制等能力

    核心层会提供一系列内部方法,用于设备的注册、注销,设备对象的查找、获取、释放,以及处理多线程访问。例如当向核心层注册一个I2C控制器对象时,使用I2cCntlrAdd;当希望获取一个I2C控制器对象时,通过I2cCntlrGet并指定控制器编号;当不再使用这个对象时,还需要通过I2cCntlrPut释放。这样做的好处是将每一个具体的操作步骤高度抽象化,减小同平台接口层及平台适配层的耦合面,便于业务解耦、演进。由于业务需求需要对I2cCntlr对象进行引用计数,那么只需要修改I2cCntlrGet/Put这对方法的实现即可,并不会影响平台接口层和平台适配层。

  • 公共业务实现:抽取公共的业务流程、算法
    凡是跟特定硬件无关的业务逻辑,都会被抽取到核心层,例如RTC时钟的时间格式转换算法,GPIO模块的线程中断实现等等。

2.3.平台适配层实现

  适配层提供具体平台硬件设备的驱动,按照核心层定义的模型创建设备对象,并完成对象的初始化(包括必要的成员变量初始化以及钩子方法挂接,以及相关的硬件初始化操作),最后使用核心层提供的注册方法将设备对象注册到核心层纳入统一管理。

3.OpenHarmony系统平台驱动适配

3.1.UART模块适配

  UART模块适配的核心环节,是UartHost对象的创建、初始化及注册。UART模块采用的是独立服务模式,要求每一个UartHost对象关联一个HDF设备服务。

1).device_info.hcs: 为每一个UART控制器配置一个HDF设备节点

 vendor/hihope/rk3568/hdf_config/khdf/device_info/device_info.hcs:71             device_uart :: device {72                 device0 :: deviceNode {73                     policy = 2;74                     priority = 40;75                     permission = 0644;76                     moduleName = "HDF_PLATFORM_UART";77                     serviceName = "HDF_PLATFORM_UART_0";78                     deviceMatchAttr = "rockchip_rk3568_uart_0";79                 }80                 device1 :: deviceNode {81                     policy = 2;82                     permission = 0644;83                     priority = 40;84                     moduleName = "HDF_PLATFORM_UART";85                     serviceName = "HDF_PLATFORM_UART_1";86                     deviceMatchAttr = "rockchip_rk3568_uart_1";87                 }88                 device2 :: deviceNode {89                     policy = 2;90                     permission = 0644;91                     priority = 40;92                     moduleName = "HDF_PLATFORM_UART";93                     serviceName = "HDF_PLATFORM_UART_3";94                     deviceMatchAttr = "rockchip_rk3568_uart_3";95                 }96             }

说明:

  • policy大于等于1(如需对用户态可见为2,仅内核态可见为1);

  • moduleName需要与驱动Entry中moduleName 保持一致;

  • serviceName必须要按照HDF_PLATFORM_UART_X的格式,X为UART控制器编号;

  • deviceMatchAttr用于配置控制器私有数据,要与uart_config.hcs中对应控制器保持一致,如不需要则忽略。

2). uart_config.hcs:每一个UART控制器配置私有数据

  如果控制器需要配置一些私有数据,例如寄存器基地址,初始化波特率等等,可以在uart_config.hcs中配置,该文件将在产品配置目录的hdf.hcs中导入,具体路径可由产品自由配置。

 vendor/hihope/rk3568/hdf_config/khdf/platform/rk3568_uart_config.hcs:1 root {2     platform {3         uart_config {4             template uart_device {5                 serviceName = "";6                 match_attr = "";7                 driver_name = "ttyS";8                 num = 0;9             }1011             device_uart_0x0000 :: uart_device {12                 match_attr = "rockchip_rk3568_uart_0";13             }14             device_uart_0x0001 :: uart_device {15                 num = 1;16                 match_attr = "rockchip_rk3568_uart_1";17             }18             device_uart_0x0003 :: uart_device {19                 num = 3;20                 match_attr = "rockchip_rk3568_uart_3";21             }22         }23     }24 }hihope/rk3568/hdf_config/khdf/hdf.hcs:1 #include "device_info/device_info.hcs"2 #include "platform/adc_config_linux.hcs"3 #include "platform/pwm_config.hcs"4 #include "platform/rk3568_watchdog_config.hcs"5 #include "platform/rk3568_uart_config.hcs"6 #include "platform/sdio_config.hcs"7 #include "platform/emmc_config.hcs"8 #include "platform/rk3568_spi_config.hcs"9 #include "input/input_config.hcs"10 #include "wifi/wlan_platform.hcs"11 #include "wifi/wlan_chip_ap6275s.hcs"12 #include "camera/camera_config.hcs"13 #include "sensor/sensor_config.hcs"14 #include "audio/audio_config.hcs"15 #include "audio/codec_config.hcs"16 #include "audio/dai_config.hcs"17 #include "audio/dma_config.hcs"18 #include "audio/dsp_config.hcs"19 #include "audio/analog_headset_config.hcs"20 #include "light/light_config.hcs"21 #include "vibrator/vibrator_config.hcs"22 #include "vibrator/linear_vibrator_config.hcs"23 #include "vibrator/drv2605l_linear_vibrator_config.hcs"24 #include "lcd/lcd_config.hcs"

3).驱动的Entry结构

先执行Bind方法绑定服务,后执行Init 初始化。

drivers_hdf_core\framework\sample\platform\uart\src\uart_sample.c
struct HdfDriverEntry g_sampleUartDriverEntry = {.moduleVersion = 1,.moduleName = "UART_SAMPLE",.Bind = SampleUartDriverBind,.Init = SampleUartDriverInit,.Release = SampleUartDriverRelease,
};// Initialize HdfDriverEntry
HDF_INIT(g_sampleUartDriverEntry);
  • Bind方法调用UartHostCreate创建 UartHost 控制器对象并完成服务绑定。
drivers_hdf_coreframework\support\platform\include\uart\uart_core.h:
struct UartHost {struct IDeviceIoService service;struct HdfDeviceObject *device;uint32_t num;OsalAtomic atom;void *priv;struct UartHostMethod *method;
};drivers_hdf_core\framework\sample\platform\uart\src\uart_sample.c
static int32_t SampleUartDriverBind(struct HdfDeviceObject *device)
{struct UartHost *uartHost = NULL;HDF_LOGD("%s: Enter", __func__);uartHost = UartHostCreate(device);uartHost->service.Dispatch = SampleDispatch;return HDF_SUCCESS;
}drivers_hdf_core/framework/support/platform/src/uart/uart_core.c79 struct UartHost *UartHostCreate(struct HdfDeviceObject *device)80 {81     struct UartHost *host = NULL;8288     host = (struct UartHost *)OsalMemCalloc(sizeof(*host));9394     host->device = device;95     device->service = &(host->service);96     host->device->service->Dispatch = UartIoDispatch;97     OsalAtomicSet(&host->atom, 0);98     host->priv = NULL;99     host->method = NULL;
100     return host;
101 }

  该方法中将UartHost对象同HdfDeviceObject进行关联:

  • 为HdfDeviceObject的service成员进行赋值,使其指向UartHost的IDeviceIoService类型的成员对象;
  • 同时为service成员的Dispatch方法赋值。
    UartHostCreate函数设置host->device->service->Dispatch = UartIoDispatch; 最后SampleUartDriverBind函数使用uartHost->service.Dispatch = SampleDispatch; 进行覆盖UartIoDispatch。

  这样做的结果:

  • 为HdfDeviceObject对象绑定IDeviceIoService类型的服务对象

  • UartHost和其IDeviceIoService类型的成员对象service可以相互转换

  • 通过UartHost对象即可获取HdfDeviceObject对象

  • 通过HdfDeviceObject对象即可间接获取UartHost对象(先获取service再转为host)

4). Init方法

  完成UartHost对象的初始化。代码调用流程:

SampleUartDriverInit->AttachUartDevice->AddUartDevice->AddRemoveUartDev->register_driver->host->method = &g_sampleUartHostMethod;

  代码分析:

drivers_hdf_core\framework\sample\platform\uart\src\uart_sample.c
static int32_t SampleUartDriverInit(struct HdfDeviceObject *device)
{int32_t ret;struct UartHost *host = NULL;HDF_LOGD("%s: Enter", __func__);host = UartHostFromDevice(device);ret = AttachUartDevice(host, device);host->method = &g_sampleUartHostMethod;return ret;
}struct UartHostMethod g_sampleUartHostMethod = {.Init = SampleUartHostInit,.Deinit = SampleUartHostDeinit,.Read = NULL,.Write = SampleUartHostWrite,.SetBaud = SampleUartHostSetBaud,.GetBaud = SampleUartHostGetBaud,.SetAttribute = NULL,.GetAttribute = NULL,.SetTransMode = NULL,
};

  通过UartHostFromDevice从HdfDeviceObject对象获取之前关联的UartHost对象,然后调用 AttachUartDevice方法完成host对象的初始化,最后为host对象挂接钩子方法g_sampleUartHostMethod。

  小结:

  UART适配关键是在驱动Entry的Bind方法中创建UartHost对象,而且是使用UartHostCreate创建。这个创建动作同时也是注册的动作,因为它将UartHost以HDF设备服务的形式同HdfDeviceObject进行绑定,这样就完成了服务的发布,HDF Manager对设备服务的管理也就是对UartHost的管理,核心层可以通过HDF提供的服务获取接口来访问UartHost。

  UART适配采用独立服务模式,每一个UartHost对象同时也是一个设备服务,其优点是可以直接利用HDF Manager进行管理;缺点是需要在device_info.hcs为每一个UartHost对象定义设备节点。

3.2.UART模块主要接口

  • drivers/hdf_core/framework/include/platform/uart_if.h
DevHandle UartOpen(uint32_t port)	UART获取设备句柄
void UartClose(DevHandle handle)	UART释放设备句柄
int32_t UartRead(DevHandle handle, uint8_t *data, uint32_t size)	从UART设备中读取指定长度的数据
int32_t UartWrite(DevHandle handle, uint8_t *data, uint32_t size)	向UART设备中写入指定长度的数据
int32_t UartGetBaud(DevHandle handle, uint32_t *baudRate)	UART获取波特率
int32_t UartSetBaud(DevHandle handle, uint32_t baudRate)	UART设置波特率
int32_t UartGetAttribute(DevHandle handle, struct UartAttribute *attribute)	UART获取设备属性
int32_t UartSetAttribute(DevHandle handle, struct UartAttribute *attribute)	UART设置设备属性
int32_t UartSetTransMode(DevHandle handle, enum UartTransMode mode)	UART设置传输模式

本文涉及的UART所有接口,支持内核态及用户态使用。

3.2.1.UartOpen

UartOpen->UartGetObjGetByBusNum(port);-> DevSvcManagerClntGetService(name)  -> UartHostRequest((struct UartHost *)handle);
  • UartGetObjGetByBusNum 调用DevSvcManagerClntGetService,根据name=“HDF_PLATFORM_UART_port” (framework/sample/config/device_info/device_info.hcs) 获取到handle 即host。
  • UartHostRequest 调用host->method->Init,即UartHostDevInit(uart_asr.c)

3.2.2.UartWrite

int32_t UartWrite(struct DevHandle *handle, uint8_t *data, uint32_t size)
{int ret;struct HdfIoService *service = NULL;struct HdfSBuf *sBuf = HdfSbufObtainDefaultSize();if (!HdfSbufWriteBuffer(sBuf, data, size)) {HDF_LOGE("Failed to write sbuf");HdfSbufRecycle(sBuf);return HDF_FAILURE;}service = (struct HdfIoService *)handle->object;ret = service->dispatcher->Dispatch(&service->object, UART_WRITE, sBuf, NULL);HdfSbufRecycle(sBuf);return ret;
}
  • 调用Dispatch接口进行写操作,即SampleDispatch,通过传输cmd 为UART_WRITE调用SampleDispatchWrite,然后调用UartPl011Write调用底层代码进行传输数据。
drivers_hdf_core\framework\sample\platform\uart\src\uart_dispatch_sample.c
int32_t SampleDispatch(struct HdfDeviceIoClient *client, int cmdId, struct HdfSBuf *data, struct HdfSBuf *reply)
{int32_t result = HDF_FAILURE;(void)reply;struct UartHost *uartHost = (struct UartHost *)client->device->service;struct UartDevice *uartDevice = (struct UartDevice *)uartHost->priv;switch (cmdId) {case UART_WRITE: {result = SampleDispatchWrite(uartDevice, data);break;}default:break;}return result;
}static int32_t SampleDispatchWrite(struct UartDevice *device, struct HdfSBuf *txBuf)
{uint32_t idx;uint32_t dataSize = 0;const uint8_t *data = NULL;struct UartRegisterMap *regMap = (struct UartRegisterMap *)device->resource.physBase;if (!HdfSbufReadBuffer(txBuf, (const void **)&data, &dataSize)) {HDF_LOGE("%s: Failed to read sbuf", __func__);return HDF_FAILURE;}regMap = (struct UartRegisterMap *)device->resource.physBase;for (idx = 0; idx < dataSize; idx++) {UartPl011Write(regMap, data[idx]);}return HDF_SUCCESS;
}drivers_hdf_core\framework\sample\platform\uart\include\uart_pl011_sample.h
static inline void UartPl011Write(struct UartRegisterMap *regMap, uint8_t byte)
{while (UartPl011IsBusy(regMap)) {}regMap->dr = byte;
}

3.3.用户层测试代码

  • 通过/dev/uartdev-%d 节点访问
drivers_hdf_core\framework\sample\platform\uart\dev\hello_uart_dev.c
int main(void)
{int ret;int fd;const char info[INFO_SIZE] = {" HELLO UART! "};fd = open("/dev/uartdev-5", O_RDWR);if (fd < 0) {HDF_LOGE("uartdev-5 open failed %d", fd);return -1;}ret = write(fd, info, INFO_SIZE);if (ret != 0) {HDF_LOGE("write uartdev-5 ret is %d", ret);}ret = close(fd);if (ret != 0) {HDF_LOGE("uartdev-5 close failed %d", fd);return -1;}return ret;
}
  • 使用UartOpen 接口进行方法
drivers_hdf_core\framework\sample\platform\uart\dispatch\hello_uart_dispatch.c
int main(void)
{const char *info = " HELLO UART! ";struct DevHandle *handle = UartOpen(UART_PORT);int ret = UartWrite(handle, (uint8_t *)info, strlen(info));UartClose(handle);return ret;
}

refer to

  • git clone https://gitee.com/openharmony/drivers_hdf_core.git
  • https://blog.csdn.net/HarmonyOS_666/article/details/140824175
  • https://blog.csdn.net/maniuT/article/details/141064333
  • https://blog.csdn.net/zxc95279527q/article/details/143062695

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

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

相关文章

BT1120接口自学笔记

一、技术简介 1.1名词解释 BT.1120协议是一种广泛应用的高清数字视频传输协议,能够把取样结构为4:4:4和4:4:2的视频数据编码成内嵌同步定时基准码的视频数据流进行传输。也可以用于ITU-R BT.709建议书和ITU-R BT.2100建议书规定的像素阵列为1 920*1080视屏数据传输。 经常听…

pdf转word/markdown等格式——MinerU的部署:2024最新的智能数据提取工具

一、简介 MinerU是开源、高质量的数据提取工具&#xff0c;支持多源数据、深度挖掘、自定义规则、快速提取等。含数据采集、处理、存储模块及用户界面&#xff0c;适用于学术、商业、金融、法律等多领域&#xff0c;提高数据获取效率。一站式、开源、高质量的数据提取工具&…

探索前端世界的无限可能:玩转Excel文件

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

MySQL两阶段提交目的

阶段提交的过程 事务执行阶段&#xff1a;事务开始执行&#xff0c;InnoDB执行SQL语句的具体操作&#xff0c;如数据修改、删除等&#xff0c;并将这些操作记录在内存中。写入Redo Log&#xff08;准备阶段&#xff09;&#xff1a;事务即将提交时&#xff0c;首先将事务相关的…

前端项目使用gitlab-cicd+docker实现自动化部署

GitLab CI/CD 是一个强大的工具&#xff0c;可以实现项目的自动化部署流程&#xff0c;从代码提交到部署只需几个步骤。本文将带你配置 GitLab CI/CD 完成一个前端项目的自动化部署。 前言 为什么使用cicddocker&#xff1f; 目前我们公司开发环境使用的shell脚本部署&#…

easyexcel 导出日期格式化

1.旧版本 在新的版本中formate已经被打上废弃标记。那么不推荐使用这种方式。 2.推荐方式 推荐使用另外一种方式【 Converter 】代码如下&#xff0c;例如需要格式化到毫秒【yyyy-MM-dd HH:mm:ss SSS】级别 创建一个公共Converter import com.alibaba.excel.converters.Conv…

DApp开发前端框架选择:React还是Vue?

在区块链DApp开发中&#xff0c;前端框架的选择对用户体验和开发效率至关重要。React和Vue作为两大主流前端框架&#xff0c;各自拥有广泛的开发者基础和丰富的生态支持。那么在DApp开发中&#xff0c;该如何选择适合自己的框架呢&#xff1f;下面我们来比较一下&#xff0c;看…

6. 一分钟读懂“抽象工厂模式”

6.1 模式介绍 书接上文&#xff0c;工厂方法模式只能搞定单一产品族&#xff0c;遇到需要生产多个产品族时就歇菜了。于是&#xff0c;在需求的“花式鞭策”下&#xff0c;程序员们再次绷紧脑细胞&#xff0c;创造出了更强大的抽象工厂模式&#xff0c;让工厂一次性打包多个产品…

gulp应该怎么用,前端批量自动化替换文件

背景 最近公司准备把所有项目中用到的国际化相关的key规范化&#xff0c;原因是: 一直以来公司的app和web端 在针对相同的需求以及相同的国际化语言&#xff0c;需要设置不同的两份国际化文件&#xff0c;难以维护旧版的国际化文件中&#xff0c;存在的大量值重复&#xff0c…

UML箭线图的理解和实践

在软件开发的世界里&#xff0c;UML&#xff08;统一建模语言&#xff09;作为一种标准化的建模语言&#xff0c;扮演着举足轻重的角色。UML类图更是软件开发设计和架构过程中的核心工具&#xff0c;它不仅能帮助开发者明确系统中的类及其关系&#xff0c;还能为后续的代码实现…

hive 行转列

行转列的常规做法是&#xff0c;group bysum(if())【或count(if())】 建表: CREATE TABLE table2 (year INT,month INT,amount DOUBLE );INSERT INTO table2 (year, month, amount) VALUES(1991, 2, 1.2),(1991, 3, 1.3),(1991, 4, 1.4),(1992, 1, 2.1),(1992, 2, 2.2),(1992…

【NoSQL数据库】Hbase基本操作——数据库表的增删改查

目录 一、Hbase原理 二、HBase数据库操作 三、遇到的问题和解决方法 一、Hbase原理 HBase的数据模型&#xff1a; 行键 时间戳 列族&#xff1a;contents 列族&#xff1a;anchor 列族&#xff1a;mime “com.cnn.www” T9 Achor:cnnsi.com”CNN” T8 Achor:…

【ETCD】ETCD用户密码认证

目录 概述 特殊用户和角色 root用户 root角色 用户操作 角色操作 启用身份验证 使用etcdctl进行身份验证 使用TLS通用名称 概述 etcd 2.1中增加了身份验证功能。etcd v3 API对身份验证功能的API和用户界面进行了轻微修改&#xff0c;以更好地适应新的数据模型。本指南…

王道考研编程题总结

我还在完善中&#xff0c;边复习边完善&#xff08;这个只是根据我自身总结的&#xff09; 一、 线性表 1. 结构体 #define MaxSize 40 typedef struct{ElemType data[MaxSize]&#xff1b;int length; }SqList 2. 编程题 1. 删除最小值 题意 &#xff1a;从顺序表中删除…

Vue3技术开发,使用纯CSS3动手制作一个3D环绕的相册展示效果,支持传入任意图片.3D轮播相册的组件

主要讲述封装一个3D轮播相册的组件&#xff0c;效果图如下&#xff0c;仅仅传入一个图片的数组即可&#xff0c;效果如下&#xff1a; 使用Vue3技术开发&#xff0c;支持传入任意张数的图片。 使用方法 <template><Swiper :list"list" /> </templat…

本地运行打包好的dist

首先输入打包命令 每个人设置不一样 一般人 是npm run build如果不知道可以去package.json里去看。 打包好文件如下 命令行输入 :npm i -g http-server 进入到dist目录下输入 命令cmd 输入 http-server 成功

通过华为鲲鹏认证的软件产品如何助力信创产业

软件通过华为鲲鹏认证与信创产业有着密切的联系。鲲鹏认证是华为推动信创产业发展的一项重要举措&#xff0c;通过该认证&#xff0c;软件可以在华为的生态系统中实现更好的兼容性和性能优化&#xff0c;从而推动信创产业的全面发展和国产化替代。 鲲鹏认证的定义和重要性 鲲…

RabbitMQ介绍及安装

文章目录 一. MQ二. RabbitMQ三. RabbitMQ作用四. MQ产品对比五. 安装RabbitMQ1. 安装erlang2. 安装rabbitMQ3. 安装RabbitMQ管理界⾯4. 启动服务5. 访问界面6. 添加管理员用户7. 重新登录 一. MQ MQ( Message queue ), 从字⾯意思上看, 本质是个队列, FIFO 先⼊先出&#xff…

Vue生成类似于打卡页面

数据表格 <el-table :data"tableData" border height"calc(100vh - 240px)" :cell-style"cellFun"><el-table-column label"姓名" show-overflow-tooltip prop"name" align"center"/><el-table-co…

vscode上传本地文件到服务器

vscode上传本地文件到服务器 首先下载插件SFTP&#xff0c;我们通过ftp进行文件传输 VScode打开要传输的文件 使用快捷键 ctrlshiftP 打开搜索窗口&#xff0c;搜索SFTP 点击之后vscode文件夹下会生成对应json文件 我们编辑json信息根据远程的服务器情况填写&#xff0c;比如…