02 FreeRTOS 任务
1、创建任务函数
1.1 动态内存的使用
在之前我们如果要创建一个与学生有关的任务,我们会定义:
//打印50个学生的信息
char name[50][100];
int age[50];
int sex[50]; //1表示男,0表示女
int score[50];
如果之后要对其进行修改会非常麻烦,因此我们要引入面对对象的编程思想,这个对象就是student,那么我们就定义一个相关的结构体来表示一个学生:
struct Student{char name[100];int age;int sex;int score;struct Student *next; //这里定义一个指针,可以使用链表把学生管理起来
};
通过这种编程思想,我们在FressRTOS中对任务也要构造出一个结构体,之前我们是通过xTaskCreate函数动态创建了任务,xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1),同时在创建时指定了栈的大小为100*4个字节,这个TaskHandle_t * const则是指向了typedef struct tskTaskControlBlock * TaskHandle_t,也就是TCB_t *这个结构体,所以说我们创建任务时所返回的handle,就只是这个TCB_t 的指针,另外取了一个名字。
在xTaskCreate中有一个TCB_t *结构体,同时可以看出这里用了malloc从动态内存,也就是从堆里面来做分配
1.2 静态创建任务
使用xTaskCreateStatic函数静态创建任务,要事先分配好TCB结构体,栈。
如果要使用这个函数,还要在FreeRTOSconfig.h中定义"configSUPPORT_STATIC_ALLOCATION""为1,并且实现vApplicationGetldleTaskMemory函数。
//关键代码
void Task1Function( void * param)
{while(1){printf("1");}
}void Task2Function( void * param)
{while(1){printf("2");}
}void Task3Function( void * param)
{while(1){printf("3");}
}/*-----------------------------------------------------------*/
StackType_t xTask3Stack[100];StaticTask_t xTask3TCB;StackType_t xIdleTaskStack[100]; //定义空闲任务栈
StaticTask_t xIdleTask3TCB; //定义空闲任务TCB//申请获得空闲任务内存
//要提供空闲任务的TCBBuffer,空闲任务的栈Buffer,空闲任务的栈大小
void vApplicationGetIdleTaskMemory(StaticTask_t ** ppxIdleTaskTCBBuffer, StackType_t ** ppxIdleTaskStackBuffer,uint32_t * pulIdleTaskStackSize)
{*ppxIdleTaskTCBBuffer = &xIdleTask3TCB;*ppxIdleTaskStackBuffer = xIdleTaskStack;*pulIdleTaskStackSize = 100;
}//main.c
xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1); //动态创建
xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL); //动态创建
xTaskCreateStatic(Task3Function, "Task3", 100, NULL, 1, xTask3Stack, &xTask3TCB); //静态创建
1.3 进一步实验
1.3.1 优先级实验
在FreeRTOS中,优先级的取值范围是:0~(configMAX_PRIORITIES – 1),数值越大优先级越高。
在之前的代码中添加修改:
static int task1flagrun = 0;
static int task2flagrun = 0;
static int task3flagrun = 0;void Task1Function( void * param)
{while(1){task1flagrun = 1; task2flagrun = 0;task3flagrun = 0;printf("1");}
}void Task2Function( void * param)
{while(1){task1flagrun = 0; task2flagrun = 1;task3flagrun = 0;printf("2");}
}void Task3Function( void * param)
{while(1){task1flagrun = 0; task2flagrun = 0;task3flagrun = 1;printf("3");}
}
然后再main函数中打断点,打开调试,运行到断点,然后将task1flagrun、task2flagrun、task3flagrun这三个变量添加到逻辑分析仪中去运行一段时间,我们会发现,同优先级的3个任务,不会同时进行。
如果我们将Task1Function的优先级设置为2,其它仍为1,运行时候我们就会发现这时候,只打印1,没有执行另外两个任务。由此可以看出,对于FreeRTOS来说,在默认的调度下面,高优先级的任务先执行,如果高优先级的任务没有主动放弃运行,其它低优先级的任务无法执行。
1.3.2 删除任务
我们在创建任务的时候传入了一个handle,以后想要引用这个任务就必须要通过这个handle执行。如果
void Task2Function( void * param)
{int i = 0;while(1){task1flagrun = 0; task2flagrun = 1;task3flagrun = 0;printf("2");//删除任务一if(i++ == 100){vTaskDelete(xHandleTask1);}//自杀if(i == 200){vTaskDelete(NULL);}}
}
通过观察实验结果可以发现,经过修改的程序,在运行之初会打印1、2、3,运行一会儿后只打印2、3,再之后就只打印3了。
使用vTaskDelete既可以删除动态创建的任务,也可以删掉静态创建的任务,但是要想删除任务,必须要记录创建任务时的返回值handle。
FreeRTOS在xTaskCreate中分配TCB和栈,但是并不是在vTaskDelete中释放TCB和栈,而是在空闲任务中进行这些清理工作,如果连续不断的调用xTaskCreate和vTaskDelete,最终会导致内存耗光。
1.3.3 使用同一个任务函数创建多个任务
void TaskGenericFunction( void * param)
{int val = (int)param;while(1){ printf("%d", val);}
}xTaskCreate(TaskGenericFunction, "Task4", 100, (void *)4, 1, NULL);
xTaskCreate(TaskGenericFunction, "Task5", 100, (void *)5, 1, NULL);
通过结果发现4和5都成功打印出来了,证明可以在同一个任务函数中创建多个任务。
使用同一个任务会产生不同的效果,是因为它们的栈是不一样的,传入的参数都保存在不同的栈里面,运行的时候互不影响。
1.3.4 栈大小实验
void Task1Function( void * param)
{//在创建任务时,申请了100*4的空间,这里故意定义500,耗尽栈的空间,从高地址向下增长,破坏下面的空间//这样程序运行的时候是完全不可控的,程序会崩溃volatile char buf[500]; //使用关键字,不允许没有使用的空间被优化掉int i;while(1){task1flagrun = 1; task2flagrun = 0;task3flagrun = 0;printf("1");for(i = 0; i < 500; i++){buf[i] = 0;}}
}
程序按上面修改之后运行,直接崩溃了,在申请的栈空间以及TCB空间的前面会有一个头来存储有关的信息,便于程序返回等操作,一旦栈的空间使用不当,冲破了空间限制,把前面头部的信息更改了,程序就会发生不可控的问题。
2、任务状态
2.1 任务切换的基础:tick中断
在FreeRTOS系统中有个定时器,这个定时器每隔一段时间会产生一个中断,这个间隔就称为tick,发生中断时,它会将发生中断的次数记录下来,初始值为0,每发生一次中断就累加1,这个中断值就成为tick count,这将是RTOS的时钟基准。
在发生中断时,tick中断处理函数被调用,在这个函数中,它会判断是否要切换任务,如果要切换任务就会去切换任务。而运行任务的基准时间(周期),可以在源码的FreeRTOSConfig.h中去配置,我们也可以指定每个任务每次执行几个tick:
#define configTICK_RATE_HZ ((TickType_t)1000)
在使用keil模拟器中的逻辑分析仪时,如果时间不准确,要确认代码中设置时钟时用的频率和Options中Xtal的频率相同。
在main函数中创建多个任务时,为什么后面创建的任务反而先运行?因为后面的任务插入链表时,pxCurrentTCB先执行它。创建任务时代码如下,后创建的最高优先级任务先执行:
2.2 有哪些任务状态?状态切换图
正在运行的任务:Running状态
可以随时运行但是现在还没轮到:Ready状态
阻塞,等待某些事情发生才能继续执行:Blocked状态
暂停,主动/被动休息:Suspended状态
状态转换图:
2.3 怎么管理不同状态的任务:放在不同的链表里
将不同状态的任务放在不同的链表里,当有需要执行时,就从对应链表中挑出一个来执行。
2.4 阻塞状态(Blocked)举例:vTaskDelay函数
使用vTaskDelay时,如果想要延时若干毫秒,那么可以自己把毫秒换算成Tick数,或者使用宏把毫秒换算成Tick数。有一个宏:pdMS_TO_TICKS(ms),可以把毫秒转换成Tick数。
void Task2Function( void * param)
{int i = 0;while(1){task1flagrun = 0; task2flagrun = 1;task3flagrun = 0;printf("2");//让任务2进入阻塞状态vTaskDelay(10);}
}
2.5 暂停状态(Suspended)举例:vTaskSuspend/vTaskResume
void Task1Function( void * param)
{//获得TickCount的值TickType_t tStart = xTaskGetTickCount();TickType_t t;int flag = 0;while(1){t = xTaskGetTickCount(); //在运行的时候获取当前时间task1flagrun = 1; task2flagrun = 0;task3flagrun = 0;printf("1");//命令任务3进入暂停状态,如果想让自己主动休息,可以传入NULL//对于进入暂停状态的任务必须由别人来唤醒if(!flag && t > tStart + 10){vTaskSuspend(xHandleTask3);flag = 1; //设置一个标志位,不要让重复让任务3进入暂停状态}if(t > tStart + 30){//命令任务3恢复运行状态vTaskResume(xHandleTask3);}}
}
3、实现周期性的任务
3.1 vTaskDelay
至少等待指定个数的Tick Interrupt才能变成就绪状态。
static int rands[] = {2, 18, 64, 121, 9};void Task1Function( void * param)
{//获得TickCount的值TickType_t tStart = xTaskGetTickCount();int i =0;int j =0;while(1){task1flagrun = 1; task2flagrun = 0;task3flagrun = 0;for(i = 0; i < rands[j]; i++){printf("1"); }j++;if(j == 5){j = 0;}vTaskDelay(20); //延时20个tick}
}
通过结果图可以看出,延迟的时间的一致的。
3.2 vTaskDelayUntil
等待到指定的绝对时刻,才能变为就绪状态。
vTaskDelayUntil是老版本,它没有返回值,在新版本中可以用xTaskDelayUntil函数,二者传入的参数是一样的,只是新的有返回值。
static int rands[] = {2, 18, 64, 121, 9};void Task1Function( void * param)
{//获得TickCount的值TickType_t tStart = xTaskGetTickCount();int i =0;int j =0;while(1){task1flagrun = 1; task2flagrun = 0;task3flagrun = 0;for(i = 0; i < rands[j]; i++){printf("1"); }j++;if(j == 5){j = 0;}//设置个开关
#if 0vTaskDelay(20); //延时20个tick
#elsevTaskDelayUntil(&tStart, 20); //延时到(tStart+20tick)时刻,同时更新tStart=tStart+20tick
#endif}
}
通过结果图可以看出,每两次开始执行之间的时间是一致的。
4、空闲任务及其钩子函数
4.1 空闲任务
在xTaskCreate中分配TCB和栈,但是并不一定是在vTaskDelete中释放TCB和栈,对于自杀的任务,由空闲任务来清理内存,对于他杀的任务,由凶手来清理。
我们在任务1中创建任务2,并且将任务一的优先级设置为1,任务二的优先级设置为2,在任务二中打印语句之后自杀。这样我们在程序中会有3个任务,任务一、任务二和空闲任务,空闲任务的优先级最低,为0。这样执行之后,程序很快就崩溃了,因为可分配的内存不够了,堆不够了。
void Task2Function( void * param);/*-----------------------------------------------------------*/void Task1Function( void * param)
{TaskHandle_t xHandleTask2;BaseType_t xReturn;while(1){printf("1");xReturn = xTaskCreate(Task2Function, "Task2", 1024, NULL, 2, &xHandleTask2);if(xReturn != pdPASS){printf("xTaskCreate err\r\n");}}
}void Task2Function( void * param)
{while(1){printf("2");//vTaskDelay(2);vTaskDelete(NULL);}
}
如果是在任务一中杀死任务二,那么程序现象会一直执行。
void Task2Function( void * param);/*-----------------------------------------------------------*/void Task1Function( void * param)
{TaskHandle_t xHandleTask2;BaseType_t xReturn;while(1){printf("1");xReturn = xTaskCreate(Task2Function, "Task2", 1024, NULL, 2, &xHandleTask2);if(xReturn != pdPASS){printf("xTaskCreate err\r\n");}vTaskDelete(xHandleTask2);}
}void Task2Function( void * param)
{while(1){printf("2");vTaskDelay(2);}
}
4.2 钩子函数
使用空闲任务不仅可以帮我们清理自杀的任务,还可以执行一些低优先级、后台的、需要连续执行的函数、测量系统的空闲时间、让系统进入省电模式等。如果要做到这些,我们可以通过修改空闲任务的函数来实现,但是如果直接修改会破坏FreeRTOS的核心文件,因此提供了一个钩子函数,可以先配置规定的宏,来告诉程序你要使用这个钩子函数。
对钩子函数,空闲任务对其也会有一些限制:不能导致空闲任务进入阻塞状态、暂停状态;如果你会使用vTaskDelete()来删除任务,那么钩子函数要非常高效地执行。如果空闲任务一直卡在钩子函数里的话,它就无法释放内存。
使用钩子函数的前提:
在FreeRTOSConfig.h中把这个宏定义为1:configUSE_IDLE_HOOK
实现vApplicationIdleHook函数
void Task2Function( void * param);/*-----------------------------------------------------------*/
static int task1flagrun = 0;
static int task2flagrun = 0;
static int taskidleflagrun = 0;void Task1Function( void * param)
{TaskHandle_t xHandleTask2;BaseType_t xReturn;while(1){task1flagrun = 1; task2flagrun = 0;taskidleflagrun = 0;printf("1");xReturn = xTaskCreate(Task2Function, "Task2", 1024, NULL, 2, &xHandleTask2);if(xReturn != pdPASS){printf("xTaskCreate err\r\n");}vTaskDelete(xHandleTask2);}
}void Task2Function( void * param)
{while(1){task1flagrun = 0; task2flagrun = 1;taskidleflagrun = 0;printf("2");vTaskDelay(2);}
}void vApplicationIdleHook(void)
{task1flagrun = 0; task2flagrun = 0;taskidleflagrun = 1;printf("0");
}//main函数里
xTaskCreate(Task1Function, "Task1", 100, NULL, 0, &xHandleTask1);
在这个程序中,任务一先执行,在任务一中创建任务二,此时任务二的优先级最高,因此任务二先执行,之后杀掉任务二,任务一和空闲任务的优先级相同,二者交替执行,在执行空闲任务时会用到钩子函数。
5、任务调度算法
5.1 状态与事件
正在运行的任务,被称为"正在使用处理器",它处于运行状态。在单处理器系统中,任何时间里只能有一个任务处于运行状态。
非运行状态的任务,它处于这3种状态之一:
阻塞(Blocked)
暂停(Suspended)
就绪(Ready)
就绪态的任务,可以被调度器挑选出来切换为运行状态,调度器永远都是挑选最高优先级的就绪态任务并让它进入运行状态。
阻塞状态的任务,它在等待"事件",当事件发生时任务就会进入就绪状态。
事件分为两类:
时间相关的事件:就是设置超时时间,在指定时间内阻塞,时间到了就进入就绪状态。使用时间相关的事件,可以实现周期性的功能、可以实现超时功能。
同步事件:同步事件就是某个任务在等待某些信息,别的任务或者中断服务程序会给它发送信息。怎么"发送信息"的方法有很多,比如任务通知(task notification)、队列(queue)、事件组(event group)、信号量(semaphoe)、互斥量(mutex)等。这些方法用来发送同步信息,比如表示某个外设得到了数据。
5.2 调度策略
5.2.1 可否抢占?
高优先级的任务能否优先执行(配置项: configUSE_PREEMPTION)
如果可以:被称作"可抢占调度"(Pre-emptive),高优先级的就绪任务马上执行,下面再细化。
如果不可以:不能抢就只能协商了,被称作"合作调度模式"(Co-operative Scheduling)。当前任务执行时,更高优先级的任务就绪了也不能马上运行,只能等待当前任务主动让出CPU资源。其他同优先级的任务也只能等待:更高优先级的任务都不能抢占,平级的更应该老实点
2.2.2 可抢占的前提下,同优先级的任务是否轮流执行(配置项: configUSE_TIME_SLICING)?
轮流执行:被称为"时间片轮转"(Time Slicing),同优先级的任务轮流执行,你执行一个时间片、我再执行一个时间片
不轮流执行:英文为"without Time Slicing",当前任务会一直执行,直到主动放弃、或者被高优先级任务抢占。
5.2.3 允许抢占、允许时间片轮转时,空闲任务是否让步?
在"可抢占"+"时间片轮转"的前提下,进一步细化:空闲任务是否让步于用户任务(配置项:configlDLE_SHOULD_YIELD)。如果让步,则空闲任务低人一等,每执行一次循环,就看看是否主动让位给用户任务。如果不让步,空闲任务跟用户任务一样,大家轮流执行,没有谁更特殊。
相关文章:

02 FreeRTOS 任务
1、创建任务函数 1.1 动态内存的使用 在之前我们如果要创建一个与学生有关的任务,我们会定义: //打印50个学生的信息 char name[50][100]; int age[50]; int sex[50]; //1表示男,0表示女 int score[50]; 如果之后要对其进行修改会非常麻烦&…...

NSS题目练习4
[LitCTF 2023]1zjs 打开后是一个游戏,用dirsearch扫描,什么都没发现 查看源代码搜索flag,发现没有什么用 搜索php,访问 出现一堆符号,看样子像是jother编码 解码得到flag,要删掉[] [LitCTF 2023]Http pro …...
【算法】合并k个已排序的链表
✨题目链接: NC51 合并k个已排序的链表 ✨题目描述 合并 k 个升序的链表并将结果作为一个升序的链表返回其头节点。 数据范围:节点总数 0≤𝑛≤50000≤n≤5000,每个节点的val满足 ∣𝑣𝑎𝑙∣&…...
【Muduo】三大核心之EventLoop
Muduo网络库的EventLoop模块是网络编程框架中的核心组件,负责事件循环的驱动和管理。以下是对EventLoop模块的详细介绍: 作用与功能: EventLoop是网络服务器中负责循环的重要模块,它持续地监听、获取和处理各种事件,…...
ubuntu安装完桌面后如何启动
ubuntu安装完桌面后如何启动 在Ubuntu服务器上安装桌面环境后,您可以使用以下命令启动图形界面: sudo systemctl start gdm3如果您使用的是Ubuntu 20.04或更新版本,可能需要使用gdm3作为显示管理器。在早期的Ubuntu版本中,可能使…...

知识融合概述
文章目录 知识融合知识融合过程研究现状技术发展趋势 知识融合 知识融合的概念最早出现在1983年发表的文献中,并在20世纪九十年代得到研究者的广泛关注。而另一种知识融合的定义是指对来自多源的不同概念、上下文和不同表达等信息进行融合的过程认为知识融合的目标是…...
LIO-EKF: High Frequency LiDAR-Inertial Odometry using Extended Kalman Filters
一、论文摘要 里程计估计是每个需要在未知环境中导航的自主系统的关键要素。在现代移动机器人中,3D LiDAR 惯性系统通常用于执行此任务。通过融合 LiDAR 扫描和 IMU 测量,这些系统可以减少因顺序注册各个 LiDAR 扫描而引起的累积漂移,并提供稳…...

Shell脚本学习笔记(更新中...)
一、什么是shell shell的作用是: 解释执行用户输入的命令程序等。 用户输入一条命令,shell就解释一条。 键盘输入命令,LInux给与响应的方式,称之为交互式。 shell是一块包裹着系统核心的壳,处于操作系统的最外层&a…...
leetcode 210.课程表II
思路:拓补排序 其实就是对于第一个题的问题变了一个问法,上一个题本质上是求有没有环,这道题本质上就是让你求出来符合没有环的路径输出而已,本质上没有什么区别。 不同就在于这里需要你额外开一个数组用来存储你遍历这个有向图…...
SpringBootTest测试框架五
示例 package com.xxx;import com.xxx.ut.AbstractBasicTest; import com.xxx.ut.uttool.TestModel; import...

赛事|基于SprinBoot+vue的CSGO赛事管理系统(源码+数据库+文档)
CSGO赛事管理系统 目录 基于SprinBootvue的CSGO赛事管理系统 一、前言 二、系统设计 三、系统功能设计 1系统功能模块 2管理员功能模块 3参赛战队功能模块 4合作方功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&…...
线性化技巧:绝对值变量的线性化
文章目录 1. 问题2. 线性化3. 缺少 x i x i − 0 x_i^ \times x_i^- 0 xixi−0 有什么问题4. 延伸思考5. 参考文献 1. 问题 以方述诚老师课件中的案例为例: m a x 3 x 1 − 2 x 2 − 4 ∣ x 3 ∣ s . t . − x 1 2 x 2 ≤ − 5 3 x 2 − x 3 ≥ 6 2 x 1 …...

List基本使用(C++)
目录 1.list的介绍 2.list的使用 list的构造 list的size() 和 max_size() list遍历操作 list元素修改操作 assign()函数 push_front(),push_back 头插,尾插 pop_front() pop_back 头删尾删 insert()函数 swap()函数 resize()函数 clear()函数 list类数…...

ELK 日志监控平台(一)- 快速搭建
文章目录 ELK 日志监控平台(一)- 快速搭建1.ELK 简介2.Elasticsearch安装部署3.Logstash安装部署4.Kibana安装部署5.日志收集DEMO5.1.创建SpringBoot应用依赖导入日志配置文件 logback.xml启动类目录结构启动项目 5.2.创建Logstash配置文件5.3.重新启动L…...

工作中写单片机代码,与学校里有什么不同?
来聊聊我的经历,提供几个提升方向,亲测有效,希望能让你少走几年弯路。 10几年前,还没参加工作的时候,主要是玩玩开发板,也接触不到实际产品的代码,很好奇那些产品级的代码是怎样的。 第一份工作…...

Unity LayerMask避坑笔记
今天使用Physics2D.OverlapAreaNonAlloc进行物理检测时候,通过LayerMask.NameToLayer传入了int值的LayerMask,结果一直识别不到,经过Debug才找到问题,竟是LayerMask的“值”传输有问题,记录一下。 直接贴代码输出结果&…...

(原创)从右到左排列RecycleView的数据
问题的提出 当我们写一个Recycleview时,默认的效果大概是这样的: 当然,我们也可以用表格布局管理器GridLayoutManager做成这样: 可以看到,默认的绘制方向是: 从左到右,从上到下 那么问题来了…...

【C语言】数据指针地址的取值、赋值、自增操作避坑
【C语言】数据指针的取值、赋值、自增操作避坑 文章目录 指针地址指针自增指针取值、赋值附录:压缩字符串、大小端格式转换压缩字符串浮点数压缩Packed-ASCII字符串 大小端转换什么是大端和小端数据传输中的大小端总结大小端转换函数 指针地址 请看下列代码&#…...

Java进阶-SpringCloud使用BeanUtil工具类简化对象之间的属性复制和操作
在Java编程中,BeanUtil工具类是一种强大且便捷的工具,用于简化对象之间的属性复制和操作。本文将介绍BeanUtil的基本功能,通过详细的代码示例展示其应用,并与其他类似工具进行对比。本文还将探讨BeanUtil在实际开发中的优势和使用…...

【ES6】ECMAS6新特性概览(一):变量声明let与const、箭头函数、模板字面量全面解析
🔥 个人主页:空白诗 🔥 热门专栏:【JavaScript】 文章目录 🌿 引言一、 let 和 const - 变量声明的新方式 🌟📌 var的问题回顾📌 let的革新📌 const的不变之美 二、 Arro…...

铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件
在选煤厂、化工厂、钢铁厂等过程生产型企业,其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进,需提前预防假检、错检、漏检,推动智慧生产运维系统数据的流动和现场赋能应用。同时,…...

dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...