LVGL学习笔记6 - 输入设备

news2024/11/20 4:27:23

目录

1. 移植文件

2. 移除多余代码

3. 输入设备初始化

4. 输入设备读回调函数

4.1 LV_INDEV_TYPE_POINTER

4.2 LV_INDEV_TYPE_KEYPAD

4.3 LV_INDEV_TYPE_ENCODER

4.4 LV_INDEV_TYPE_BUTTON

5. 事件

6. 实例

7 Group

7.1 创建Group

7.2 与输入设备关联

7.3 添加对象到Group

 7.4 键值与Group的联系

8. GD32450I_EVAL输入设备

 7.1 初始化

7.2 读入键值

7.3 读键处理

7.4 实例

7.4.1 初始化3个Button

7.4.2 初始化Group

7.4.3 初始化输入设备


LVGL 支持 4 种类型的输入设备(这4种类型的设备可以同时存在):

  • Point:触摸屏或鼠标
  • Keypad :键盘
  • Encoder :编码器
  • Button :按键

1. 移植文件

对应的移植文件在lvgl文件夹里面\examples\porting文件夹下的lv_port_indev_template.c和lv_port_indev_template.h。同样将2个文件修改文件名为:lv_port_indev.c和lv_port_indev.h。

打开其中的宏定义

/*Copy this file as "lv_port_indev.c" and set this value to "1" to enable content*/
#if 1
/*Copy this file as "lv_port_indev.h" and set this value to "1" to enable content*/
#if 1

2. 移除多余代码

lv_port_indev.c 中,包含了 5 种设备的 API ,但它们不可能都用到,因此需要裁剪无用的函数和定义。尤其是在初始化函数 lv_port_indev_init() 中,如果不去除无用设备的初始化语句,那么在调用时可能会出现问题。

源码在注释中已经着重强调了不同 API 的分区,只需要根据分区保留需要的代码即可。

这里采用最简单的方法,在lv_port_indev_init中移除多余的初始化代码。

3. 输入设备初始化

对应touchpad_init,mouse_init,keypad_init,encoder_init,button_init。

以Visual Studio模拟器为例,它用到了3个输入设备。初始化部分在lv_win32_init中(没有使用文件lv_port_indev.c和lv_port_indev.h)。

    static lv_indev_drv_t pointer_driver;
    lv_indev_drv_init(&pointer_driver);
    pointer_driver.type = LV_INDEV_TYPE_POINTER;
    pointer_driver.read_cb = lv_win32_pointer_driver_read_callback;
    lv_win32_pointer_device_object = lv_indev_drv_register(&pointer_driver);

    static lv_indev_drv_t keypad_driver;
    lv_indev_drv_init(&keypad_driver);
    keypad_driver.type = LV_INDEV_TYPE_KEYPAD;
    keypad_driver.read_cb = lv_win32_keypad_driver_read_callback;
    lv_win32_keypad_device_object = lv_indev_drv_register(&keypad_driver);

    static lv_indev_drv_t encoder_driver;
    lv_indev_drv_init(&encoder_driver);
    encoder_driver.type = LV_INDEV_TYPE_ENCODER;
    encoder_driver.read_cb = lv_win32_encoder_driver_read_callback;
    lv_win32_encoder_device_object = lv_indev_drv_register(&encoder_driver);

4. 输入设备读回调函数

输入设备的回调函数需要用户根据硬件自己实现,例如上例中的lv_win32_pointer_driver_read_callback、lv_win32_keypad_driver_read_callback和lv_win32_encoder_driver_read_callback。

这个回调函数的原型是:

void (*read_cb)(struct _lv_indev_drv_t * indev_drv, lv_indev_data_t * data);

参数含义:

indev_drv - 输入参数,即输入设备句柄

data - 输出参数,

typedef struct {
    lv_point_t point; /**< For LV_INDEV_TYPE_POINTER the currently pressed point*/
    uint32_t key;     /**< For LV_INDEV_TYPE_KEYPAD the currently pressed key*/
    uint32_t btn_id;  /**< For LV_INDEV_TYPE_BUTTON the currently pressed button*/
    int16_t enc_diff; /**< For LV_INDEV_TYPE_ENCODER number of steps since the previous read*/

    lv_indev_state_t state; /**< LV_INDEV_STATE_REL or LV_INDEV_STATE_PR*/
    bool continue_reading;  /**< If set to true, the read callback is invoked again*/
} lv_indev_data_t;

point - 对应鼠标或触摸屏,包括x,y坐标。

key - 对应键盘,即键值。

btn_id - 对应按钮,即按钮ID

enc_diff - 对应滚轮或旋钮,即旋转值

state - 输入设备的状态

/** States for input devices*/
typedef enum {
    LV_INDEV_STATE_RELEASED = 0,
    LV_INDEV_STATE_PRESSED
} lv_indev_state_t;
#define LV_INDEV_STATE_REL      LV_INDEV_STATE_RELEASED
#define LV_INDEV_STATE_PR       LV_INDEV_STATE_PRESSED

continue_reading - 通知是否需要连续读入设备,设置为true时会再次回调函数。

4.1 LV_INDEV_TYPE_POINTER

触摸屏和鼠标都属于这种类型,鼠标是只支持一种点击类型,即左点击,所以只返回一个点的x, y坐标,以模拟器为例:

更新鼠标的状态,是按下还是释放。

    data->state = (lv_indev_state_t)(
        g_mouse_pressed ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL);

返回x,y坐标值。

    data->point.x = MulDiv(
        GET_X_LPARAM(g_mouse_value),
        USER_DEFAULT_SCREEN_DPI,
        WIN32DRV_MONITOR_ZOOM * g_dpi_value);
    data->point.y = MulDiv(
        GET_Y_LPARAM(g_mouse_value),
        USER_DEFAULT_SCREEN_DPI,
        WIN32DRV_MONITOR_ZOOM * g_dpi_value);

4.2 LV_INDEV_TYPE_KEYPAD

键盘类型,返回的是按键的键值。

以模拟器为例:

更新按键的状态,是按下还是释放。

   data->state = (lv_indev_state_t)(
        g_keyboard_pressed ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL);

返回键值,注意LVGL键值和电脑键值不一样。

WPARAM KeyboardValue = g_keyboard_value;

switch (KeyboardValue)
{
    case VK_UP:
        data->key = LV_KEY_UP;
        break;
    case VK_DOWN:
        data->key = LV_KEY_DOWN;
        break;
    case VK_LEFT:
        data->key = LV_KEY_LEFT;
        break;
    case VK_RIGHT:
        data->key = LV_KEY_RIGHT;
        break;
    ... 
    default:
        if (KeyboardValue >= 'A' && KeyboardValue <= 'Z')
        {
            KeyboardValue += 0x20;
        }

        data->key = (uint32_t)KeyboardValue;

        break;
}

4.3 LV_INDEV_TYPE_ENCODER

鼠标滚轮和旋转编码器属于这种类型,返回的是步进值。

更新编码器的状态,是按下还是释放。

data->state = (lv_indev_state_t)(
    g_mousewheel_pressed ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL);

返回步进值。

data->enc_diff = g_mousewheel_value;

4.4 LV_INDEV_TYPE_BUTTON

按键类型的输入设备和键盘类型的输入设备区别是按键是对应屏幕上的位置,当这个物理按键被压下时就相当于一个触摸屏点击。

在模拟器中添加一个BUTTON的输入设备,通过键盘模拟实现。

在win32drv.c中添加

EXTERN_C lv_indev_t* lv_win32_button_device_object = NULL;

在win32drv.h中添加

EXTERN_C lv_indev_t* lv_win32_button_device_object;

在win32drv.c中添加初始化代码

    static lv_indev_drv_t button_driver;
    lv_indev_drv_init(&button_driver);
    button_driver.type = LV_INDEV_TYPE_BUTTON;
    button_driver.read_cb = lv_win32_button_driver_read_callback;
    lv_win32_button_device_object = lv_indev_drv_register(&button_driver);

添加button对应屏幕坐标组

    static const lv_point_t btn_points[3] = {
        {30, 30},       /*Button 0 -> x:30; y:30*/
        {230, 30},      /*Button 1 -> x:230; y:30*/
        {430, 30},      /*Button 2 -> x:430; y:30*/
    };
    lv_indev_set_button_points(lv_win32_button_device_object, btn_points);

这里对应3个button,分别用键盘的F1,F2,F3控制,即F1按下时即(30, 30)被点击。数组btn_points里面的每一个元素的位置即是Button的ID号。

 增加回调函数:

static void lv_win32_button_driver_read_callback(
    lv_indev_drv_t* indev_drv,
    lv_indev_data_t* data)
{
    UNREFERENCED_PARAMETER(indev_drv);

    data->state = (lv_indev_state_t)(
        g_keyboard_pressed ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL);

    WPARAM KeyboardValue = g_keyboard_value;

    switch (KeyboardValue)
    {
        case VK_F1:
            data->btn_id = 0;
            break;
        case VK_F2:
            data->btn_id = 1;
            break;
        case VK_F3:
            data->btn_id = 2;
            break;
        default:
            data->state = LV_INDEV_STATE_REL;
            break;
    }
}

注意这里btn_id不能赋值非法值, 即不能越界数组btn_points,否则会运行错误。

5. 事件

通过lv_obj_add_event_cb给对象添加事件处理程序函数。一个对象可以通过多次调用这个函数添加多个事件处理程序,它们将按添加时的相同顺序调用。

struct _lv_event_dsc_t * lv_obj_add_event_cb(struct _lv_obj_t * obj, lv_event_cb_t event_cb, lv_event_code_t filter, void * user_data);

参数含义:

obj - 对象句柄。

event_cb - 事件处理回调函数。

typedef void (*lv_event_cb_t)(lv_event_t * e);

filter - 事件代码,例如LV_EVENT_CLICKED表示点击事件,具体可以看lv_event.h中的枚举lv_event_code_t。

user_data - 事件处理回调函数的参数数据

6. 实例

3个方框,分别按F1,F2,F3键,对应的方框内显示字符串“Pressed”,松开后显示“Released”。

 初始化方框:

    lv_obj_t* rect1 = lv_obj_create(lv_scr_act());
    lv_obj_t* rect2 = lv_obj_create(lv_scr_act());
    lv_obj_t* rect3 = lv_obj_create(lv_scr_act());

    lv_obj_set_size(rect1, LV_PCT(20), LV_PCT(20));
    lv_obj_align(rect1, LV_ALIGN_TOP_LEFT, 20, 20);
    lv_obj_add_event_cb(rect1, event_press_cb1, LV_EVENT_PRESSED, NULL);
    lv_obj_add_event_cb(rect1, event_release_cb1, LV_EVENT_RELEASED, NULL);

    lv_obj_set_size(rect2, LV_PCT(20), LV_PCT(20));
    lv_obj_align(rect2, LV_ALIGN_TOP_LEFT, 220, 20);
    lv_obj_add_event_cb(rect2, event_press_cb2, LV_EVENT_PRESSED, NULL);
    lv_obj_add_event_cb(rect2, event_release_cb2, LV_EVENT_RELEASED, NULL);

    lv_obj_set_size(rect3, LV_PCT(20), LV_PCT(20));
    lv_obj_align(rect3, LV_ALIGN_TOP_LEFT, 420, 20);
    lv_obj_add_event_cb(rect3, event_press_cb3, LV_EVENT_PRESSED, NULL);
    lv_obj_add_event_cb(rect3, event_release_cb3, LV_EVENT_RELEASED, NULL);

每个方框设置事件Pressed和Released的回调函数。

在每个方框内显示字符串:

    tst_input_label1 = lv_label_create(rect1);
    lv_obj_set_size(tst_input_label1, LV_PCT(50), LV_PCT(20));
    lv_obj_align(tst_input_label1, LV_ALIGN_CENTER, 0, 0);
    lv_label_set_text(tst_input_label1, "Released");

    tst_input_label2 = lv_label_create(rect2);
    lv_obj_set_size(tst_input_label2, LV_PCT(50), LV_PCT(20));
    lv_obj_align(tst_input_label2, LV_ALIGN_CENTER, 0, 0);
    lv_label_set_text(tst_input_label2, "Released");

    tst_input_label3 = lv_label_create(rect3);
    lv_obj_set_size(tst_input_label3, LV_PCT(50), LV_PCT(20));
    lv_obj_align(tst_input_label3, LV_ALIGN_CENTER, 0, 0);
    lv_label_set_text(tst_input_label3, "Released");

回调函数更新对应的label显示字符串:

static void event_press_cb1(lv_event_t* e)
{
    lv_label_set_text(tst_input_label1, "Pressed");
}

static void event_release_cb1(lv_event_t* e)
{
    lv_label_set_text(tst_input_label1, "Released");
}

static void event_press_cb2(lv_event_t* e)
{
    lv_label_set_text(tst_input_label2, "Pressed");
}

static void event_release_cb2(lv_event_t* e)
{
    lv_label_set_text(tst_input_label2, "Released");
}

static void event_press_cb3(lv_event_t* e)
{
    lv_label_set_text(tst_input_label3, "Pressed");
}

static void event_release_cb3(lv_event_t* e)
{
    lv_label_set_text(tst_input_label3, "Released");
}

7 Group

当使用键盘或者编码器进行左右选择时,LVGL是通过Group的方式来获知下一个被选中的对象。所以将多个对象放入同一个Group内可以更方便的实现输入设备的控制。

7.1 创建Group

lv_group_t * group = lv_group_create();

7.2 与输入设备关联

lv_indev_set_group(indev, group);

其中indev是输入设备初始化时调用lv_indev_drv_register返回的值。

7.3 添加对象到Group

lv_group_add_obj(group, obj);

注意并不是所有的对象都可以加入Group,例如基础对象lv_obj_t就不行。加入后按左右按键并没有反应。 

 7.4 键值与Group的联系

一些特殊的键值会和Group产生直接的联系,最常见的键是 LV_KEY_NEXT/PREVLV_KEY_ENTER 和 LV_KEY_UP/DOWN/LEFT/RIGHT,因为大多数对象就可以用它们完全控制。

8. GD32450I_EVAL输入设备

GD32450I_EVAL有3个按键,

将Wakeup作为LEFT键,Tamper作为RIGHT键,USER作为ENTER键。 

 7.1 初始化

static void keypad_init(void)
{
    /*Your code comes here*/
    gpio_mode_set(IO_KEY_WAKEUP, GPIO_MODE_INPUT, GPIO_PUPD_NONE, ((uint32_t)1 << PIN_KEY_WAKEUP)); 
    gpio_mode_set(IO_KEY_TAMPER, GPIO_MODE_INPUT, GPIO_PUPD_NONE, ((uint32_t)1 << PIN_KEY_TAMPER));
    gpio_mode_set(IO_KEY_USER, GPIO_MODE_INPUT, GPIO_PUPD_NONE, ((uint32_t)1 << PIN_KEY_USER));
}

初始化按键对应IO为输入。 

7.2 读入键值

static uint32_t keypad_get_key(void)
{
    /*Your code comes here*/
    if(RESET == gpio_input_bit_get(IO_KEY_WAKEUP, (uint32_t)1 << PIN_KEY_WAKEUP))
    {
        return 1;
    }
    else if(RESET == gpio_input_bit_get(IO_KEY_TAMPER, (uint32_t)1 << PIN_KEY_TAMPER))
    {
        return 2;
    }
    else if(RESET == gpio_input_bit_get(IO_KEY_USER, (uint32_t)1 << PIN_KEY_USER))
    {
        return 3;
    }
    return 0;
}

7.3 读键处理

static void keypad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
    static uint32_t last_key = 0;
    /*Get whether the a key is pressed and save the pressed key*/
    uint32_t act_key = keypad_get_key();
    if(act_key != 0) {
        data->state = LV_INDEV_STATE_PR;

        /*Translate the keys to LVGL control characters according to your key definitions*/
        switch(act_key) {
            case 1:
                act_key = LV_KEY_PREV;
                break;
            case 2:
                act_key = LV_KEY_NEXT;
                break;
            case 3:
                act_key = LV_KEY_ENTER;
                break;
        }
        last_key = act_key;
    }
    else {
        data->state = LV_INDEV_STATE_REL;
    }
    data->key = last_key;
}

这里键值1,2对应按键PREV和NEXT,不是LEFT和RIGHT。这样才能左右选择Group的对象。 

7.4 实例

7.4.1 初始化3个Button

static lv_obj_t *rect1, *rect2, *rect3; //全局变量

    rect1 = lv_btn_create(lv_scr_act());
    lv_obj_set_size(rect1, LV_PCT(20), LV_PCT(20));
    lv_obj_align(rect1, LV_ALIGN_TOP_LEFT, 10, 20);

    rect2 = lv_btn_create(lv_scr_act());
    lv_obj_set_size(rect2, LV_PCT(20), LV_PCT(20));
    lv_obj_align(rect2, LV_ALIGN_TOP_LEFT, 150, 20);

    rect3 = lv_btn_create(lv_scr_act());
    lv_obj_set_size(rect3, LV_PCT(20), LV_PCT(20));
    lv_obj_align(rect3, LV_ALIGN_TOP_LEFT, 290, 20);

7.4.2 初始化Group

将这3个button加入到group中。

static lv_group_t *groupRect; //全局变量

    groupRect = lv_group_create();
    extern lv_indev_t * indev_keypad;
    lv_indev_set_group(indev_keypad, groupRect);
    lv_group_add_obj(groupRect, rect1);
    lv_group_add_obj(groupRect, rect2);
    lv_group_add_obj(groupRect, rect3);

7.4.3 初始化输入设备

在合适的位置调用lv_port_indev_init()。

    lv_init();
    lv_port_disp_init();
    lv_port_indev_init();

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

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

相关文章

低频功率放大器参考电路图解大全

功率放大器一般也被我们称为电压放大器&#xff0c;主要是把微弱信号进行电压放大&#xff0c;其输入输出的电压电流一般很小&#xff0c;不能够直接驱动功率较大的仪器。为了满足使用需求&#xff0c;需要在放大器末级增加功率放大器。而功率放大器主要就是放大信号功率&#…

供应商绩效管理对企业采购组织的重要价值

供应商绩效管理是供应商管理中的重要组成部分&#xff0c;它在企业整个采购管理生命周期中起到重要的作用&#xff0c;作为管理好供应商的一个重要手段&#xff0c;现代企业几乎都会对供应商实施绩效考核。供应商绩效管理是对公司供应商的可靠性、质量和性能的监视和分析。它能…

sec2-GObject

1 类和实例 GObject实例用函数g_object_new创建。GObject不仅仅有实例&#xff0c;也有类。 一个GObject类在第一次访问g_object_new时候创建&#xff0c;只有有一个GObject类存在。GObject实例在任何时候访问g_object_new都会被创建&#xff0c;所以就会创建更多GObject实例…

【剧前爆米花--爪哇岛寻宝】String类型构造,修改的底层逻辑与StringBuilder和StringBuffer的关系

作者&#xff1a;困了电视剧 专栏&#xff1a;《JavaSE语法与底层详解》 文章分布&#xff1a;这是一篇关于String类型及其底层构造的文章&#xff0c;如有疏漏&#xff0c;欢迎大佬指正&#xff01; String对象的创建 字符串的用法比较多&#xff0c;所以String类提供的构造方…

算法复杂度 O(1),O(n),O(logn),O(nlogn)的区别

算法复杂度分为时间复杂度和空间复杂度 时间复杂度是指执行这个算法所需要的计算工作量空间复杂度是指这个算法所需要的内存空间 1.对于一个循环&#xff0c;假设循环体的时间复杂度为O(n),循环次数为n&#xff0c;则这个循环的时间复杂度为O(n*1)。 void aFunc(int n) {for…

嵌入式终端的以太网系统简析

一 初识以太网电路 从硬件的角度看&#xff0c;以太网接口电路主要由 MAC&#xff08;Media Access Control&#xff09;控制器和物理层接口 PHY&#xff08;Physical Layer&#xff09;两大部分构成&#xff0c;一般一个嵌入式终端系统的以太网硬件抽象 如下&#xff1a; 1 网…

数字孪生教学楼3d可视化系统功能介绍

目前&#xff0c;校园教学楼运维管理阶段面临的主要难题有&#xff1a;数据采集不全、数据各自为阵的数据孤岛现象严重&#xff1b;系统的集成度低&#xff0c;缺乏统一、有效的运行维护处理中心平台&#xff1b;能耗巨大&#xff0c;不符合绿色建筑要求&#xff0c;未形成有效…

做了多年的Android开发,自己是否有擅长领域?(Framework 篇)

前言 如今Android 开发行业的招聘需求可谓是越来越高了&#xff0c;如果你想入大厂工作&#xff0c;学历还只是他们的基础入门的门槛&#xff0c;他们不仅要看学历还得看你是否在某块技术领域有着过硬的实力。比如&#xff1a;APP性能优化、Framework底层原理、音视频、APP架构…

数字验证学习笔记——SystemVerilog芯片验证17 ——数组约束

一、数组约束 1.1 数组的属性约束 多数情况下&#xff0c;数组的大小应该给定范围&#xff0c;防止生成过大体积的数组或者空数组此外还可以在约束中结合数组的其他方法sum&#xff08;&#xff09;&#xff0c;product&#xff08;&#xff09;&#xff0c;and&#xff08;&a…

Ansys Zemax | 如何在 OpticStudio 中模拟人眼

本文是人眼模型的一个案例研究&#xff0c;并提供了更高级的序列模式建模技术的演示。我们将在OpticStudio中使用Liou & Brennan 1997眼睛模型创建人眼模型。在OpticStudio中成功生成这个眼睛模型后&#xff0c;我们将使用它来设计一个自由形式的渐进眼镜镜片。 下载 联系…

正大国际期货:2022年各行业顶级富豪身价大洗牌

2022年各行业顶级富豪身价大洗牌 富豪身价较2021年年底变动幅度涨跌幅 行业&#xff1a;加密货币&#xff0c;币安创始人&#xff1a;赵长鹏816%&#xff0c;身价174亿美元 行业&#xff1a;基建、大宗商品&#xff0c;阿达尼集团创始人&#xff1a;高塔姆阿达尼210%&#x…

Linux下Python脚本的编写(二)

今天试着把两个shell小脚本转化成Python版本 一. 脚本1 判断所接的nvme 在哪个CPU上 #!/bin/bash lscpu |grep -i node for nvme in nvme list|sed 1,2d|awk {print $1}|awk -F "/" {print $NF} doecho $nvmebusid$(readlink -f /sys/block/$nvme |cut -d / -f 6)ec…

【解决】VMware虚拟机中ubuntu系统联网问题——以桥接模式解决

问题描述 由于需要通过笔记本的蓝牙与外接设备进行通信&#xff0c;我使用的是无线网。所以需要使用VMware中提供的桥接模式&#xff0c;借助笔记本的无线网卡进行联网&#xff0c;之前设置过一次&#xff0c;能够正常运行&#xff0c;但是关机后&#xff0c;可能加载的快照不同…

维视智造成为苏州市人工智能行业协会首届会员单位

近日&#xff0c;以“智者相偕聚势赋能”为主题的苏州市人工智能行业协会一届一次会员大会暨成立大会在苏州举行&#xff0c;来自全市200多家人工智能相关单位的代表以及人工智能领域的知名学者、产业专家、企业家参加了会议。维视智造作为协会的首届会员&#xff0c;与苏州市工…

2022/12/28总结

今天AC了一道题&#xff08;后面的题目对我来说好难&#xff0c;刷不动了&#xff09; P2895 [USACO08FEB]Meteor Shower S P2895 [USACO08FEB]Meteor Shower S_lxh0113的博客-CSDN博客 学的新知识&#xff1a; dijkstra算法 dijkstra算法是求最短路径的算法。相比较于flo…

【真干货】Activiti7工作流如何使用?看这里

一. 前言 近日文哥有个毕业学员在公司开发时遇到了工作流的相关业务场景。在这里&#xff0c;文哥给大家精心准备了以Activiti为代表的工作流简单使用教程&#xff0c;希望能给有需要的小伙伴们一些帮助。下面我们就来开始介绍Activiti工作流的基本使用情况。 二. Activiti工…

利器 | Java 接口自动化测试首选方案:REST Assured 实践 (一)

在 REST Assured 的官方 GitHub 上有这样一句简短的描述&#xff1a; Java DSL for easy testing of REST services 简约的 REST 服务测试 Java DSL REST Assured 官方的 README 第一句话对进行了一个优点的概述&#xff0c;总的意思表达的就是简单好用。那么 REST Assured 有…

dolphinscheduler 调用shell脚本执行sql

1. 资源中心--创建文件 脚本&#xff1a; #!/bin/bash hive <<EOF alter table app.app_bi_test drop partition (dayid$1); insert overwrite table app.app_bi_test partition(dayid) select a.Ccode,a.inCcode ,a.Cname ,$1,COALESCE(b.num,0) ,COALESCE(c.c_num…

以前的任何一个行业只要与互联网技术产生联系,便焕发生机与活力

事实上&#xff0c;以往&#xff0c;我们所经历的那个互联网玩家频出的年代&#xff0c;其实就是一个以互联网技术为主导的年代。在那样一个年代里&#xff0c;互联网技术几乎是解决一切痛点和难题的万能解药&#xff0c;几乎是破解一切行业痛点和难题的杀手锏。任何一个行业&a…

XXE渗透与攻防(一)

目录 前言 XML基础知识 XML用途 XML内容 XML格式要求 XML格式校验 不同语言支持的协议 完整的XML内容 什么是XXE 案列演示 盲打-DNSLog XXE 防御 前言 现在许多不同的客户端技术都可以使用XMl向业务应用程序发送消息&#xff0c;为了使应用程序使用自定义的XML消…