物联网实战--平台篇之(十三)物模型设备端

本项目的交流QQ群:701889554

物联网实战--入门篇https://blog.csdn.net/ypp240124016/category_12609773.html

物联网实战--驱动篇https://blog.csdn.net/ypp240124016/category_12631333.html

物联网实战--平台篇https://blog.csdn.net/ypp240124016/category_12653350.html

嵌入式文件 https://download.csdn.net/download/ypp240124016/89409505

APP文件 https://download.csdn.net/download/ypp240124016/89409506

一、设备演示

        熟悉的画面,做了点改进。

净化器演示

二、模型要素

        整个平台相当于搭了个窝,目的是为了下蛋,而产品就是我们要下的蛋;对于开发人员来讲,如何定义产品是个很关键的问题,在电信、移动等这些云平台上,他们一般是用一个profile解析插件作为设备模型的,本质上就是json文件,里面描述了产品设备的属性和功能,这种方式的缺点是性能损耗较大,对于功能比较复杂的产品定义和调试都是件很麻烦的事情,需要北向平台人员做配合,沟通成本很高。

        而我们这里对设备模型的定义直接用源代码定义,清晰简洁,产品开发者不需要过多了解平台的底层协议和后台机制,定义好产品本身的数据和类型即可;对于展示界面,美工设计好后对照着样式用前端代码很容易就能实现了。

        对于设备来讲,首先最重要的要素就是设备SN了,它是设备的身份标识,在这里我们的定义是一个4字节的数据,高2字节代表型号,低2字节代码地址码,这个SN是设备生产的时候保存在设备内部的。例如,在演示视频里,我们把之前的净化器进行了完善,并把A101这个型号定义为净化器产品,所以在本系统里,净化器产品的序列号范围是A1010001~A101FFFF,有6万多个,一般场景够用了。

        接下来是要定义产品的属性,比如温湿度、PM2.5和开关状态等这些都算是属性,属性的主要约束内容是范围和精度,比如温度,范围是-40.0~120.0 ℃,精度是0.1℃,这个一般是传感器的物理特性决定的。

        有些设备不仅有属性值,还能进行控制,我们称之为功能定义,比如净化器的开关和风扇速度设置,都属于产品功能。

        有了这些属性和功能之后,还要想办法对它们进行有序地读取和操作,一般我们会定义不同的命令类型去执行不同的操作,比如下图是净化器产品的命令定义,至于每个命令后跟着哪些数据,具体根据产品自己定义即可。,

        总的来讲,我们对设备模型的开发主要就是围绕着SN、属性、功能和命令类型这四个内容展开的;当然,还有一个是通讯密码,因为我们采用的是一型多密原则,所以每个型号的设备都定义了多组密码随机使用。那么,下面我们就以之前的净化器项目为例,做具体的讲解。

       

三、通讯协议

 这里的通讯协议是指建立在MQTT之上的应用层通用协议,类似于modbus协议,我这边已经实现了具体内容,开发者只需要学会配置和调用即可,如果感兴趣可以对照着协议(链接文章第三节内容)和驱动代码看看。物联网实战--平台篇之(二)基础搭建_qt数据库驱动 封装其它数据库-CSDN博客

drv_server.h


#ifndef __DRV_SERVER_H
#define __DRV_SERVER_H

#include "drv_common.h"  


#ifndef				SERVER_PACK_SIZE
#define				SERVER_PACK_SIZE	        256		//数据包最大值
#endif

#ifndef				SERVER_PROTOCOL_VER
#define				SERVER_PROTOCOL_VER	      1   //协议版本
#endif

#ifndef				SERVER_PASSWD_CNTS
#define				SERVER_PASSWD_CNTS	      5   //密码组数
#endif

#ifndef				SERVER_EEPROM_ADDR
#define				SERVER_EEPROM_ADDR         (0x0050)//存储地址
#endif

#ifndef			CONFIG_EEPROM_READ			
#define			CONFIG_EEPROM_READ		EEPROM_Read  //配置读取函数
#endif

#ifndef			CONFIG_EEPROM_WRITE			
#define			CONFIG_EEPROM_WRITE		EEPROM_Write  //配置保存函数
#endif

#define         APP_ID_MIN                       (u32)(123000)

typedef enum
{
  ENCRYPT_MODE_DISABLE=0,
  ENCRYPT_MODE_TEA,
  ENCRYPT_MODE_AES,
}encryptMode;

typedef enum
{

	SERVER_CMD_UP_DATA=100,
	SERVER_CMD_DOWN_DATA=200,
  
  REG_CMD_REPORT_APP_ID=210,
  REG_CMD_SET_APP_ID,
  
  UPDATE_CMD_IAP=220,//远程升级总命令
  UPDATE_CMD_INTO_BOOT,//使设备进入升级状态
  UPDATE_CMD_KEEP,//保持连接  
  
  SERVER_CMD_LORA=230,//LORA总命令
}ServerCmdType;

typedef struct
{
	u8 head[2];
	u8 version;
	u8 encrypt_index;//密码索引
	u8 crc_h;
	u8 crc_l;
	u8 data_len_h;
	u8 data_len_l;
	u8 app_id[4];
	u8 gw_sn[4];
}ServerHeadStruct;

typedef struct
{
  u32 app_id;//应用ID
  u32 gw_sn;//设备SN
  u16 reserved;
  u16 crcValue;
}ServerSaveStruct;

typedef struct
{	

  u8 passwd_table[SERVER_PASSWD_CNTS][16];//密码表,要跟用户端的模型对应

	int (*fun_send)(u8 *buff, u16 len);
	u16 (*fun_server_cmd_parse)(u8 cmd_type, u8 *in_buff, u16 in_len);	
	u16 (*fun_slave_cmd_parse)(u32 node_sn, u8 *in_buff, u16 in_len);	
   
}ServerWorkStruct;


void drv_server_read(void);
void drv_server_write(void);
void drv_server_init(void);


u16 drv_server_send_msg(u8 cmd_type, u8 *in_buff, u16 in_len);
void drv_server_send_slave_msg(u32 node_sn, u8 *in_buff, u16 in_len);
int drv_server_send_level(u8 *buff, u16 len);
void drv_server_send_register(int (*fun_send)(u8 *buff, u16 len));

u16 drv_server_recv_parse(u8 *buff, u16 len, int (*fun_send)(u8 *buff, u16 len));
u16 drv_server_cmd_parse(u8 cmd_type, u8 *in_buff, u16 in_len);
void drv_server_cmd_parse_register(u16 (*fun_server_cmd_parse)(u8 cmd_type, u8 *in_buff, u16 in_len));

u16 drv_slave_cmd_parse(u32 node_sn, u8 *in_buff, u16 in_len);
void drv_slave_cmd_parse_register(u16 (*fun_slave_cmd_parse)(u32 node_sn, u8 *in_buff, u16 in_len));

void drv_server_set_app_id(u32 app_id);
u32 drv_server_get_app_id(void);
void drv_server_set_gw_sn(u32 gw_sn);
u32 drv_server_get_gw_sn(void);
void drv_server_add_passwd(u8 index, u8 *passwd);

#endif

drv_server.c


#include "drv_server.h"
#include "drv_encrypt.h"


ServerWorkStruct g_sServerWork={0};
ServerSaveStruct g_sServerSave={0};
/*		
================================================================================
描述 :
输入 : 
输出 : 
================================================================================
*/
void drv_server_read(void)
{
  CONFIG_EEPROM_READ(SERVER_EEPROM_ADDR, (u8 *)&g_sServerSave, sizeof(g_sServerSave));
//  printf_hex("read=", (u8 *)&g_sServerSave, sizeof(g_sServerSave));
  
  if(g_sServerSave.crcValue!=drv_crc16((u8*)&g_sServerSave, sizeof(g_sServerSave)-2))
  {
    g_sServerSave.app_id=APP_ID_MIN;
    g_sServerSave.gw_sn=M2M_DEV_TYPE<<16;
    drv_server_write();
    printf("server read new!\n");
  }
//  printf("read app_id=%u, gw_sn=0x%08X\n", g_sServerSave.app_id, g_sServerSave.gw_sn);
}

/*		
================================================================================
描述 :
输入 : 
输出 : 
================================================================================
*/
void drv_server_write(void)
{
  g_sServerSave.crcValue=drv_crc16((u8*)&g_sServerSave, sizeof(g_sServerSave)-2);
  CONFIG_EEPROM_WRITE(SERVER_EEPROM_ADDR, (u8 *)&g_sServerSave, sizeof(g_sServerSave));
//  printf_hex("write=", (u8 *)&g_sServerSave, sizeof(g_sServerSave));
}

/*		
================================================================================
描述 :
输入 : 
输出 : 
================================================================================
*/
void drv_server_init(void)
{
  drv_server_read();
}


/*		
================================================================================
描述 :组合发送报文
输入 : 
输出 : 组合后的数据长度
================================================================================
*/
u16 drv_server_send_msg(u8 cmd_type, u8 *in_buff, u16 in_len)
{
	static u8 pack_num=0;
	static u8 data_buff[SERVER_PACK_SIZE]={0}, make_buff[SERVER_PACK_SIZE];
	static u8 to_server_pwd[16]={0};//密码
 	static u8 encrypt_mode=ENCRYPT_MODE_TEA;//加密模式--对于设备端来讲是固定的
	static u32 gw_sn=0, app_id=0;
	u16 data_len=0,union_len,remain_len,make_len,crcValue;
	ServerHeadStruct *pHead=(ServerHeadStruct *)make_buff;
	u8 *pData=&make_buff[16];//加密区起始地址
	int out_len;
	if(in_len+32>SERVER_PACK_SIZE)
	{
		printf("in len too long!\n");
		return 0;
	}
	
	if(gw_sn==0)
	{
		gw_sn=g_sServerSave.gw_sn;
	}
	
	if(app_id==0)
	{
		app_id=g_sServerSave.app_id;
	}
	
	pack_num++;
	

	memset(data_buff, 0, SERVER_PACK_SIZE);
	data_len=0;
	union_len=in_len+4;//数据单元长度
	data_buff[data_len++]=union_len>>8;//从此处开始加密
	data_buff[data_len++]=union_len;		
	data_buff[data_len++]=pack_num;
	data_buff[data_len++]=cmd_type;
	memcpy(&data_buff[data_len], in_buff, in_len);
	data_len+=in_len;
	crcValue=drv_crc16(data_buff, union_len);//数据单元校验
	data_buff[data_len++]=crcValue>>8;
	data_buff[data_len++]=crcValue;		
	remain_len=data_len%8;
	if(remain_len>0)
		data_len+=(8-remain_len);//8字节对齐,便于TEA加密 
	
  pHead->encrypt_index=drv_get_sec_counter()%SERVER_PASSWD_CNTS;//根据时间随机获取密码
  memcpy(to_server_pwd, g_sServerWork.passwd_table[pHead->encrypt_index], 16);//根据索引复制密码
	switch(encrypt_mode)
	{
		case ENCRYPT_MODE_DISABLE:
		{
			memcpy(pData, data_buff, data_len);
			out_len=data_len;
			break;
		}
		
		case ENCRYPT_MODE_TEA:
		{
			out_len=tea_encrypt_buff(data_buff, data_len, (u32*)to_server_pwd);
			if(out_len==data_len)
			{
				memcpy(pData, data_buff, data_len);
			}			
			else
			{
				printf("server tea error!\n");
				return 0;
			}
			break;
		}
#ifdef    USE_AES    //是否启用AES算法
		case ENCRYPT_MODE_AES:
		{
			out_len=aes_encrypt_buff(data_buff, data_len, pData, SERVER_PACK_SIZE-16, to_server_pwd);//aes加密
			if(out_len<16)
			{
				printf("server aes error!\n");
				return 0;
			}
			break;
		}
#endif    
		default:
			return 0;
	}
	data_len=out_len+8;//加上app_id和gw_sn的长度
	crcValue=drv_crc16(&make_buff[8], data_len);//总校验
	pHead->head[0]=0xAA;
	pHead->head[1]=0x55;
	pHead->version=SERVER_PROTOCOL_VER;
	pHead->crc_h=crcValue>>8;
	pHead->crc_l=crcValue;
	pHead->data_len_h=data_len>>8;
	pHead->data_len_l=data_len;
	pHead->app_id[0]=app_id>>24;
	pHead->app_id[1]=app_id>>16;
	pHead->app_id[2]=app_id>>8;
	pHead->app_id[3]=app_id;
	pHead->gw_sn[0]=gw_sn>>24;
	pHead->gw_sn[1]=gw_sn>>16;
	pHead->gw_sn[2]=gw_sn>>8;
	pHead->gw_sn[3]=gw_sn;	
	
	make_len=data_len+8;
	drv_server_send_level(make_buff, make_len);//发送
	return make_len;
}

/*		
================================================================================
描述 : 转发从机消息
输入 : 
输出 : 
================================================================================
*/
void drv_server_send_slave_msg(u32 node_sn, u8 *in_buff, u16 in_len)
{
  u8 make_buff[100]={0};
	u16 make_len=0;
  if(in_len+20>sizeof(make_buff))
  {
    return;
  }
	make_buff[make_len++]=node_sn>>24;
	make_buff[make_len++]=node_sn>>16;
	make_buff[make_len++]=node_sn>>8;
	make_buff[make_len++]=node_sn;
  u8 *pUnion=&make_buff[make_len];
  make_len+=2;//单元长度  
  memcpy(&make_buff[make_len], in_buff, in_len);
  make_len+=in_len;
  u16 union_len=make_len-4;//单元长度  
	pUnion[0]=union_len>>8;
	pUnion[1]=union_len;
  u16 crcValue=drv_crc16(pUnion, union_len);
	make_buff[make_len++]=crcValue>>8;  
	make_buff[make_len++]=crcValue;  
//  printf_hex("slave msg=", make_buff, make_len);
	drv_server_send_msg(SERVER_CMD_UP_DATA, make_buff, make_len);  //转发  
}

/*		
================================================================================
描述 : 服务端数据接收解析
输入 : 
输出 : 
================================================================================
*/
u16 drv_server_recv_parse(u8 *buff, u16 len, int (*fun_send)(u8 *buff, u16 len))
{
	static u8 head[2]={0xAA, 0x55}, out_buff[800]={0}, recv_pack_num=109;
	u8 to_server_pwd[16]={0}, encrypt_mode=0;//加密模式
	u8 *pBuff=buff, *pData=NULL, pack_num, cmd_type;
	u16 data_len=0, out_len=0, union_len=0, crcValue;
	static u32 local_app_id=0, local_gw_sn=0;
	u32 recv_gw_sn, recv_app_id;
	
	if(local_gw_sn==0)
	{
		local_gw_sn=g_sServerSave.gw_sn;
	}
	
	if(local_app_id==0)
	{
		local_app_id=g_sServerSave.app_id;
	}
	
//	printf_hex("drv_server_recv_parse: ", buff, len);
	
	if( (pBuff=memstr(buff, len, head, 2))!=NULL )
	{
		ServerHeadStruct *pHead = (ServerHeadStruct *)pBuff;
		data_len=pHead->data_len_h<<8|pHead->data_len_l;
		crcValue=pHead->crc_h<<8|pHead->crc_l;
		recv_app_id=pHead->app_id[0]<<24|pHead->app_id[1]<<16|pHead->app_id[2]<<8|pHead->app_id[3];
		recv_gw_sn=pHead->gw_sn[0]<<24|pHead->gw_sn[1]<<16|pHead->gw_sn[2]<<8|pHead->gw_sn[3];
		
    if(recv_app_id==0)
    {
      return 0;
    }
		if(recv_gw_sn!=local_gw_sn)
		{
			printf("drv_server_parse_recv error: recv_gw_sn(%u)!=local_gw_sn(%u)!", recv_gw_sn, local_gw_sn);
			return 0;
		}
		if(data_len<12 || data_len+20>sizeof(out_buff))
		{
			printf("drv_server_parse_recv error: data_len<12 || data_len+20>sizeof(out_buff)\n");
			return 0;
		}
    if(pHead->encrypt_index>=SERVER_PASSWD_CNTS)
    {
      return 0;
    }
    memcpy(to_server_pwd, g_sServerWork.passwd_table[pHead->encrypt_index], 16);//根据索引复制密码
    encrypt_mode=ENCRYPT_MODE_TEA;

		pData=pBuff+8;
		if(crcValue==drv_crc16(pData, data_len))
		{
			pData+=8;//app_id和gw_sn不加密
			data_len-=8;
			//解密
			switch(encrypt_mode)
			{
				case ENCRYPT_MODE_DISABLE:
				{
					memcpy(out_buff, pData, data_len);
					out_len=data_len;
					break;
				}
				
				case ENCRYPT_MODE_TEA:
				{
					out_len=tea_decrypt_buff(pData, data_len, (u32*)to_server_pwd);
					if(out_len==data_len)
					{
						memcpy(out_buff , pData, data_len);
					}			
					else
					{
						printf("server tea error!\n");
						return 0;
					}
					break;
				}
#ifdef    USE_AES    //是否启用AES算法
        case ENCRYPT_MODE_AES:
        {
          out_len=aes_decrypt_buff(pData, data_len, out_buff, sizeof(out_buff)-16, to_server_pwd);//aes
          if(out_len<16)
          {
            printf("server aes error!\n");
            return 0;
          }
          break;
        }
#endif 
				default:
					return 0;			
			}
		}
		else
		{
			printf("drv_server_parse_recv crc error 000!\n");
			return 0;
		}
		
		pData=out_buff;
		union_len=pData[0]<<8|pData[1];
		pData+=2;
		pack_num=pData[0];
		pData+=1;
		cmd_type=pData[0];
		pData+=1;
//		printf("union_len=%d, pack_num=%d, cmd_type=%d\n", union_len, pack_num, cmd_type);
		if(recv_pack_num==pack_num)//过滤相同的包序号
		{
			printf("recv_pack_num==pack_num\n");
			return 0;
		}
		recv_pack_num=pack_num;//更新包序号
		if(union_len<4 || union_len>sizeof(out_buff))
		{
			printf("drv_server_parse_recv error: union_len<4 || union_len>sizeof(out_buff)\n");
			return 0;		
		}
		crcValue=out_buff[union_len]<<8|out_buff[union_len+1];
		if(crcValue==drv_crc16(out_buff, union_len))//解密后校验
		{
			union_len-=4;
//			printf("cmd_type=%d\n", cmd_type);
			switch(cmd_type)
			{ 
				case SERVER_CMD_DOWN_DATA://数据转发
				{
					u32 recv_node_sn=pData[0]<<24|pData[1]<<16|pData[2]<<8|pData[3];//目标节点序列号
          drv_slave_cmd_parse(recv_node_sn, pData, union_len);//从机解析
					break;
				}
				case REG_CMD_SET_APP_ID://设置APP ID
				{
					u32 new_app_id=pData[0]<<24|pData[1]<<16|pData[2]<<8|pData[3];//新的应用ID
          pData+=4;
          if(new_app_id>=APP_ID_MIN)
          {
            drv_server_set_app_id(new_app_id);
            delay_os(100);
            drv_system_reset();//复位系统
          }
					break;
				}        
				default://其余命令交给应用层处理
				  drv_server_cmd_parse(cmd_type, pData, union_len);
			}		
			return 1;
		}
		else
		{
			printf("drv_server_parse_recv crc error 111!\n");
			return 0;
		}		
	}
	return 0;
}

/*		
================================================================================
描述 :
输入 : 
输出 : 
================================================================================
*/
int drv_server_send_level(u8 *buff, u16 len)
{
  if(g_sServerWork.fun_send != NULL)
  {
    return g_sServerWork.fun_send(buff, len);
  }  

	return 0;
}

/*		
================================================================================
描述 :
输入 : 
输出 : 
================================================================================
*/
void drv_server_send_register(int (*fun_send)(u8 *buff, u16 len))
{
  g_sServerWork.fun_send=fun_send;
}

/*		
================================================================================
描述 : 服务端解析 
输入 : 
输出 : 
================================================================================
*/
u16 drv_server_cmd_parse(u8 cmd_type, u8 *in_buff, u16 in_len)
{
	u16 make_len=0; 
	if(g_sServerWork.fun_server_cmd_parse != NULL)
	{
		make_len=g_sServerWork.fun_server_cmd_parse(cmd_type, in_buff, in_len);
	}
	 
	return make_len;
}


/*		
================================================================================
描述 : 服务端解析函数注册
输入 : 
输出 : 
================================================================================
*/
void drv_server_cmd_parse_register(u16 (*fun_server_cmd_parse)(u8 cmd_type, u8 *in_buff, u16 in_len))
{
	g_sServerWork.fun_server_cmd_parse=fun_server_cmd_parse;
}


/*		
================================================================================
描述 : 从机端解析 
输入 : 
输出 : 
================================================================================
*/
u16 drv_slave_cmd_parse(u32 node_sn, u8 *in_buff, u16 in_len)
{
	u16 make_len=0; 
	if(g_sServerWork.fun_slave_cmd_parse != NULL)
	{
		make_len=g_sServerWork.fun_slave_cmd_parse(node_sn, in_buff, in_len);
	}
	 
	return make_len;
}


/*		
================================================================================
描述 : 从机端解析函数注册
输入 : 
输出 : 
================================================================================
*/
void drv_slave_cmd_parse_register(u16 (*fun_slave_cmd_parse)(u32 node_sn, u8 *in_buff, u16 in_len))
{
	g_sServerWork.fun_slave_cmd_parse=fun_slave_cmd_parse;
}


/*		
================================================================================
描述 : 
输入 : 
输出 : 
================================================================================
*/
void drv_server_set_app_id(u32 app_id)
{
  g_sServerSave.app_id=app_id;
  drv_server_write();
  printf("set app_id=%u\n", app_id);
}

/*		
================================================================================
描述 : 
输入 : 
输出 : 
================================================================================
*/
u32 drv_server_get_app_id(void)
{
  return g_sServerSave.app_id;
}

/*		
================================================================================
描述 : 
输入 : 
输出 : 
================================================================================
*/
void drv_server_set_gw_sn(u32 gw_sn)
{
  g_sServerSave.gw_sn=gw_sn;
  drv_server_write(); 
  printf("set gw_sn=%08X\n", gw_sn);
}

/*		
================================================================================
描述 : 
输入 : 
输出 : 
================================================================================
*/
u32 drv_server_get_gw_sn(void)
{
  return g_sServerSave.gw_sn;
}

/*		
================================================================================
描述 : 
输入 : 
输出 : 
================================================================================
*/
void drv_server_add_passwd(u8 index, u8 *passwd)
{
  if(index<SERVER_PASSWD_CNTS)
  {
    memcpy(g_sServerWork.passwd_table[index], passwd, 16);
  }
}
 四、密码表配置

          在这个与平台服务器对接的通讯文件里,不仅实现了发送加密和接收解密的功能,还保存了app_id和设备SN,即gw_sn (gw是网关GateWay的缩写,意味着是跟服务器直接网络对接的角色)。还有一个是密码表,这个表实际使用时最好动态混淆填充,这样才能提升密码获取难度,这里我是直接定义的,二进制文件打开是可以直接检索到的,并不安全,暂时图个方便。密码表是QT那边随机生成复制过来的。

五、收发函数注册

         drv_server.c文件里的收发函数都是在应用层注册的,因为我们用了mqtt,所以这个过程在app_mqtt.c文件内完成的,具体如下所示,整个mqtt的配置跟原来差不多,就是订阅话题做了些改动。

        收发函数:

六、设备定义

        从设备端开始,首先定义产品型号值是A101,这个值是自己分配定义的,你要定义成A102也行,核心就是不同产品类型值不能重复就行了,在规划上也要有长远的打算,不要乱定义、浪费数字资源。

        净化器的属性包含了温度、湿度、PM2.5、风速等级和开关状态,如下图所示,至于数据为什么要乘以10在加1000这种操作,我在之前的文章里有详细解释了,可以回头看看。物联网实战--入门篇之(八)嵌入式-空气净化器-CSDN博客

        上图是解析下发的指令,即功能定义,简单讲就是根据不同命令类型执行不同的操作了。     

        对于净化器本身的功能,跟原来入门篇是一样的,这里只是把发送和解析函数做了改动,接入现有的通讯协议系统。

        对于产品开发者来讲,通讯层的驱动文件都是定义好的,无需改动,只要专心完成app_ap01.c里的功能就行了,这样就可以把开发者从繁杂的底层通讯中解放出来,专注于产品本身的功能实现,尽可能优化,提升用户体验,这才是最为关键的。这也是我开发这个平台的根本原因所在。    

       

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

存在d盘里的数据突然没有了?别担心,恢复方案在此

在数字化时代&#xff0c;数据存储的重要性不言而喻。然而&#xff0c;有时候我们会遭遇一些意想不到的困扰&#xff0c;比如存储在D盘的数据突然消失。这不仅可能导致重要文件的丢失&#xff0c;还可能影响我们的工作和生活。本文将探讨D盘数据消失的可能原因&#xff0c;提供…

【机器学习】与【数据挖掘】技术下【C++】驱动的【嵌入式】智能系统优化

目录 一、嵌入式系统简介 二、C在嵌入式系统中的优势 三、机器学习在嵌入式系统中的挑战 四、C实现机器学习模型的基本步骤 五、实例分析&#xff1a;使用C在嵌入式系统中实现手写数字识别 1. 数据准备 2. 模型训练与压缩 3. 模型部署 六、优化与分析 1. 模型优化 模…

OpenAI新研究破解GPT-4大脑,分解1600万个特征打开“黑匣子”,Ilya 、Jan Leike也参与了!

6月7日凌晨&#xff0c;OpenAI在官网发布了一个新的研究成果&#xff0c;首次破解GPT-4的神经网络活动。通过改进大规模训练稀疏自动编码器将GPT-4的内部表示分解为 1600 万个特征。而且&#xff0c;前段时间离职的Ilya Sutskever、Jan Leike也是作者之一&#xff01; 这不是破…

【CS.SE】浅谈: 程序员的职业素养与成长之路

文章目录 1 引言2 持续学习与自我提升2.1 永无止境的学习之路2.2 真实案例&#xff1a;自学Python 3 团队合作与沟通能力3.1 高效沟通是团队成功的基石 4 责任心与职业道德4.1 责任心&#xff1a;代码背后的承诺4.2 真实案例&#xff1a;修复紧急Bug 5 适应变化与快速反应5.1 适…

pytorch 自定义学习率更新 Poly

Poly 学习率调整策略需要继承_LRScheduler类&#xff0c;该类包含三个重要属性和两个重要方法 学习率与batch-size的关系 一般来说&#xff0c;batch-size的大小一般与学习率的大小成正比。batch-size越大一般意味着算法收敛方向的置信度越大&#xff0c;也可以选择较大的学…

2024年【起重机械指挥】考试及起重机械指挥新版试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 起重机械指挥考试考前必练&#xff01;安全生产模拟考试一点通每个月更新起重机械指挥新版试题题目及答案&#xff01;多做几遍&#xff0c;其实通过起重机械指挥试题及解析很简单。 1、【多选题】《中华人民共和国特…

校验参数个数工具类

项目中有个需求&#xff1a;前后端参数一致性校验&#xff0c;在某业务场景下后端代码需要校验参数个数&#xff0c;因此设计了1个工具类方便大伙使用&#xff0c;特此简单记录下。 校验参数个数工具类 一、校验工具类CheckNumInsideParamters二、单元测试ParameterSizeTest三…

Nginx 配置:gzip动态压缩、静态压缩

动态压缩 动态压缩开启的现象 Nginx配置 http {# 启用 gzip 压缩gzip on;# 设置 gzip 压缩级别&#xff0c;范围是1-9&#xff0c;数字越大压缩率越高但CPU消耗也越大gzip_comp_level 5;# 设置最低压缩的文件大小&#xff08;大于1KB的文件才进行压缩&#xff09;gzip_min_le…

Stable Diffusion WebUI 各操作系统安装教程

最近几天在 2 台 Mac、2 台 PC、一台云无 GPU 的 Linux 安装了 Stable Diffusion WebUI&#xff0c;这里记录下如何安装&#xff0c;以及一些注意点和坑。 以下内容针对 Windows&#xff08;N 卡&#xff09;、MacOS&#xff08;m 系列芯片&#xff09;、Linux&#xff08;Ubu…

指针在C/C++中的魔力:一级指针与二级指针

什么是指针&#xff1f; 指针是一个变量&#xff0c;它的值是另一个变量的地址。在C/C中&#xff0c;指针是一个强大的工具&#xff0c;可以让我们直接操作内存地址。指针的主要用途包括动态内存分配、数组和字符串处理、函数参数传递等。 一级指针 一级指针&#xff08;也称为…

数据库-列的完整性约束-概述

引言 我们都知道人以群分 &#xff0c;但分为 若按照 人类的皮肤分类 黄种人&#xff08;其实是西方人定义&#xff09;我们虽然不承认也不否定 &#xff0c;黑皮肤 &#xff0c;棕色人种&#xff08;在南太平洋和西太&#xff09;白种人 排名你懂的 这好像是枚举类型 emm 尴尬…

Sql-labs的第一关

前言 我们在使用Sql-libs靶场进行Sql注入实验的时候&#xff0c;前提要求我们对mysql数据库结构要有一个大概的了解&#xff0c;因为mysql5.0以上的版本都会自带一个名为information_schema的数据库&#xff0c;这个数据库下面会有columns和tables两个表。 tables这个表的table…

异步复位和同步释放

文章目录 前言一、为什么需要复位呢&#xff1f;二、同步复位1. 同步复位定义2. 同步复位的实现3. 同步复位的优点和缺点同步复位优点同步复位缺点 三、异步复位1. 异步复位定义2. 异步复位的实现3. 异步复位的优点和缺点异步复位优点异步复位缺点 四、异步复位同步释放1. reco…

Type-C转音频(C/3.5mm接口USB2.0数据传输)带PD充电低成本解决方案

LDR6500&#xff1a;领先市场的USB-C DRP接口USB PD通信芯片 产品介绍 LDR6500&#xff0c;由乐得瑞科技精心研发&#xff0c;是一款针对USB Type-C标准中Bridge设备而优化的USB-C DRP&#xff08;Dual Role Port&#xff0c;双角色端口&#xff09;接口USB PD&#xff08;Po…

Questflow借助MongoDB Atlas以AI重新定义未来工作方式

MongoDB客户案例导读 Questflow借助MongoDB Atlas赋能AI员工&#xff0c;助力中小型初创企业自动化工作流程&#xff0c;简化数据分析&#xff0c;提升客户体验&#xff0c;推动AI与员工的协作&#xff0c;重新定义未来工作方式。 协作式AI自动化平台 无需编码即可拥有自己的…

[word] word表格如何设置外框线和内框线 #媒体#笔记

word表格如何设置外框线和内框线 点击表格的左上角按钮从而选中表格 点击边框按钮边上的下拉箭头&#xff0c;选择边框和底纹 点击颜色边上的下拉箭头&#xff0c;选择红色 点击取消掉中间的边框&#xff0c;只保留外围边框 点击颜色边上的下拉箭头&#xff0c;选择另外一个颜…

使用2个手机文件恢复工具,轻松找回文件

在这个智能手机横行的时代&#xff0c;手机已经成为我们生活中不可或缺的一部分。然而&#xff0c;就像生活中的一切事物一样&#xff0c;手机也有可能出现意外&#xff0c;比如文件丢失。这就像是你在超市购物&#xff0c;结果发现钱包不见了&#xff0c;那种感觉真是让人抓狂…

【Linux】信号(二)

上一章节我们进行了信号产生的讲解。 本节将围绕信号保存展开&#xff0c;并会将处理部分开一个头。 目录 信号保存&#xff1a;信号的一些概念&#xff1a;关于信号保存的接口&#xff1a;sigset_t的解释&#xff1a;对应的操作接口&#xff1a;sigprocmask&#xff1a;sigp…

【Git】Git 的基本操作 -- 详解

一、创建 Git 本地仓库 要提前说的是&#xff0c;仓库是进行版本控制的一个文件目录。我们要想对文件进行版本控制&#xff0c;就必须先创建一个仓库出来。 创建⼀个 Git 本地仓库对应的命令为 git init &#xff0c;注意命令要在文件目录下执行&#xff0c;例如&#xff1a;…

力扣hot100:155. 最小栈(栈,辅助栈存储相关信息)

LeetCode&#xff1a;155. 最小栈 1、尝试单调栈 看到这题说&#xff0c;要常数时间内检索最小元素的栈&#xff0c;想到了单调栈&#xff0c;递增单调栈确实能维护最小值&#xff0c;但是这个最小值是存在一定意义的&#xff0c;即如果后面出现了最小值&#xff0c;那么前面…