物联网技术在智慧广电中的应用

高山发射台智慧广电控制系统

内容提要:本文以某型号发射机为例,介绍现代物联网、大数据等技术在广播电视传输发射行业的具体使用。大致分成基础数据采集、大数据保存服务处理、数据可视化展示以及数据挖掘三部分内容。
广播电视是国家的喉舌和人民群众文化生活的重要组成部分,各级政府每年都投入大量资金到这个行业中,历经多年的发展,广播电视行业也有了巨大的变化,传输覆盖网已经完成了由模拟技术向数字技术的根本转化,通信行业新技术和数字化编码压缩技术彻底的改变了原有的广播电视传输发射通道的格局,但是在传输发射台设备自动化控制领域还有不足,大部分还是停留在眼看仪表手操数码的阶段,尽管近期更新的发射机都带有自动化接口,由于没有合适的配套软件,大多处于闲置状态。本文就利用物联网、大数据等新技术结合发射台的实际情况,构建一套全新的传输发射网自动化管理系统,具体方案见以下图:

系统的框架结构:智慧广电系统拓扑图

在这里插入图片描述
全系统由分成三级智能管理,一级管理层供中央和省局级使用。可以随时掌握所辖全部基层发射台的即时状况和历史数据、自动分析运维管理数据,与信源监控系统配合合理调配播出设备,提前做出安全预警和根据要求随时下达调配指令到基层发射台的值班员桌面,通讯上有最高的优先级权限。二级管理层给各个市局或者二级局使用,及时掌握所辖范围内各个区县发射台的实时状况及历史数据,提供本区域内的安全预警、运维成本管理、运维质量评价和播出规划。 三级管理层运营子系统是提供给基层台站使用,实现基层单位安全播出运行维护管理及计划实施的可控可管和实时巡查和数据自动上报,是全系统的基础和核心,主要包括机房管理、动力保障、门禁安防、天线环境等部分,机房管理可以实现对所有发射设备实时监控、运行告警、数据上报、应急处置和信号质量分析等功能。动力保障可以实时的监控机房供电情况、判断供电质量并及时预警和自动处置切换、监控备用发电设备和供电电瓶的状况,提示维护人员做好日常维护。门禁安防包括安保视频监控、搭配门禁设备,实现人员和环境的日常安保管理。天线环境包括天线测试和区域环境的温湿度风力等自然状况的数据采集和存储上报。此外还可扩展实现基层单位办公管理、党建管理、文化建设等智慧管理功能,三级管理系统共同拥有的部分是:智慧运营数据可视化大屏部分,用来展示系统数据,与之配合的大屏监控控制器、数据存储服务器、外网防火墙、核心路由器和基础网络。不同之处是二三级管理系统增加相应的控制和执行单元(安防设备、门禁图像采集等),特别是三级台站管理系统增加了发射机设备监控、数据上报、动力监控、智慧值班等,是系统的核心所在,特殊的的无线联网方式有效的保证系统在恶劣环境下稳定运行,其核心部件无线模块起到了联网和防雷关键作用。

基础数据采集:

在硬件上,采用无线通讯模块取代有线连接,用组网服务器代替以往的上位机。具体实现的办法如下图二所示:系统的关键点是无线WIFI模块(也可以用其他的NBlot、laola、4G、5G通讯模组),搭配STM32F103C8T6MCU单片机,根据发射机设备的情况差异有两种方案,一种是采用NODEMCU8266为核心的简单方案,针对的设备带有数据通讯端口,不论是RS232\RS485\MODBUS哪种电气标准都行,利用发射机原有的数据采集和控制电路,不改动原来设备,从设备的控制端口连接到模块的通讯端口上,完成硬件连接。另外一种情况是针对没有远程控制功能的老旧发射机,用STM32F103C8T6单片机做关键点的数据采集和控制,通过STM32的串口去控制8266—S01模组,利用STM32单片机的DIO端口,一个模块分配6个A/D转换通道,8个开关量接入通道,2个控制开关出通道,这些模拟量输入、开关量输入输出,基本上满足了一部设备的控制要求。两种模块的硬件参见下图:

在这里插入图片描述
在这里插入图片描述

STM32和ESP8266程序主要完成采集模拟量、开关量等数据,定时向服务器上报,同时对服务器下发的控制指令进行响应,完成对被控端的控制。

/*-------------------------------------------------*/
/*            实现MQTT协议功能的源文件             */
/*采用RET6温控系统的硬件,实现了上云、控温、数码管 */
/*显示,可以远程app控制,部分代码借鉴      例程    */                                    
/*仅供学习交流,程序中有电压温度转化算法可以借鉴   */
/*程序使用了flash标准函数库记录设定温度值,防止掉电*/
/*后最大温度值重新设定。                           */ 
/*版本:0.1,程序没有优化2021年9月2日             */
/*-------------------------------------------------*/
#include "stm32f10x_flash.h"
#include "oled.h"
/****************************************************************************/
int main(void) 
{	
	hardwareinit();			//硬件初始化
	
	WiFi_ResetIO_Init();   	//初始化WiFi的复位IO	
	display_TAB(12,13);			//显示"CD"
	IoT_Parameter_Init();  //初始化云IoT平台MQTT服务器的参数	
	display_TAB(14,15);			//显示"EF"


	while(1)
	{		
		// connectFlag=1同服务器建立了连接 
			
		if(connectFlag == 1) 
		{
			alarmFlag=BEEP;
		
/***********************************************************************************/
			
			Led_Status[0] = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4);//读取LED0的状态
			Led_Status[1] = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5);//读取LED1的状态
			Led_Status[2] = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6);//读取LED2的状态
			Led_Status[3] = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_7);//读取LED3的状态
			Led_Status[4] = GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_4);//读取LED4的状态
			Led_Status[5] = GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_5);//读取LED5的状态
			Led_Status[6] = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0);//读取LED6的状态
			Led_Status[7] = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1);//读取LED7的状态
	
			
/***********************************************************************************/			
			adcx1=Get_Adc_Average(ADC_Channel_10,8);//读取PC0管脚输入的ADC转换的8次算术平均滤波结果
			adcx2=Get_Adc_Average(ADC_Channel_11,8);//读取PC0管脚输入的ADC转换的8此算术平均滤波结果
			if(adcx1>adcx2)
				adcx=adcx1;
			else
				adcx=adcx2;
			if(ttt<min) HEAT2_ON;//判断温度过低并加热
			if(ttt>max) LED0=!LED0;//判断温度过高并报警

				if((MQTT_TxDataOutPtr[2] == 0x10)||((MQTT_TxDataOutPtr[2] == 0x82)&&(connectPackFlag == 1))||(subcribePackFlag == 1)) 
				{    
					if (MQTT_TxDataOutPtr[2] == 0x30) 
					{	
						u1_printf("发送数据:0x%x,单片机数据推送至服务器\r\n", MQTT_TxDataOutPtr[2]);	
						u1_printf("\r\n");
					}
					else 
					{
						u1_printf("发送数据:0x%x\r\n", MQTT_TxDataOutPtr[2]);//串口提示信息
					}
					
					MQTT_TxData(MQTT_TxDataOutPtr);							//发送数据
					MQTT_TxDataOutPtr += TBUFF_UNIT;						//指针下移
					if(MQTT_TxDataOutPtr == MQTT_TxDataEndPtr)				//如果指针到缓冲区尾部了
					{ 
						MQTT_TxDataOutPtr = MQTT_TxDataBuf[0];				//指针归位到缓冲区开头
					}	
				} 				
			}
			/*-------------------------------------------------------------*/
			/*                     处理接收缓冲区数据                      */
			/*-------------------------------------------------------------*/
			if(MQTT_RxDataOutPtr != MQTT_RxDataInPtr) //if成立的话,说明接收缓冲区有数据了	
			{												
				u1_printf("接收到数据:");
				/*-----------------------------------------------------*/
				/*                    处理CONNACK报文                  */
				/*-----------------------------------------------------*/				
				//if判断,如果第一个字节是0x20,表示收到的是CONNACK报文
				//接着我们要判断第4个字节,看看CONNECT报文是否成功
				if(MQTT_RxDataOutPtr[2] == 0x20)
				{             			
					switch(MQTT_RxDataOutPtr[5])
					{					   
						case 0x00 : u1_printf("CONNECT报文成功\r\n");							//串口输出信息	
										connectPackFlag = 1;									//CONNECT报文成功,订阅报文可发
									break;														//跳出分支case 0x00                                              
						case 0x01 : u1_printf("连接已拒绝,不支持的协议版本,准备重启\r\n");    //串口输出信息
									connectFlag = 0;											//connectFlag置零,重启连接
									break;														//跳出分支case 0x01   
						case 0x02 : u1_printf("连接已拒绝,不合格的客户端标识符,准备重启\r\n");//串口输出信息
									connectFlag = 0;                                            //connectFlag置零,重启连接
									break; 														//跳出分支case 0x02 
						case 0x03 : u1_printf("连接已拒绝,服务端不可用,准备重启\r\n");	    //串口输出信息
									connectFlag = 0;											//connectFlag置零,重启连接
									break;														//跳出分支case 0x03
						case 0x04 : u1_printf("连接已拒绝,无效的用户名或密码,准备重启\r\n");	//串口输出信息
									connectFlag = 0;											//connectFlag置零,重启连接						
									break;														//跳出分支case 0x04
						case 0x05 : u1_printf("连接已拒绝,未授权,准备重启\r\n");				//串口输出信息
									connectFlag = 0;											//connectFlag置零,重启连接						
									break;														//跳出分支case 0x05 		
						default   : u1_printf("连接已拒绝,未知状态,准备重启\r\n");		    //串口输出信息 
									connectFlag = 0;											//connectFlag置零,重启连接					
									break;														//跳出分支case default 								
					}				
			    }			
				//if判断,第一个字节是0x90,表示收到的是SUBACK报文
				//接着我们要判断订阅回复,看看是不是成功
				else if(MQTT_RxDataOutPtr[2] == 0x90)
				{ 
					switch(MQTT_RxDataOutPtr[6])
					{					
						case 0x00 :
						case 0x01 : 
									u1_printf("订阅成功\r\n");			//串口输出信息
									subcribePackFlag = 1;				//subcribePackFlag置1,表示订阅报文成功,其他报文可发送
									pingFlag = 0;						//pingFlag清零
									TIM3_ENABLE_30S();					//启动30s的PING定时器	
									Send_Data();
									TIM2_ENABLE_10S();					//10秒一次发布数据
									break;								//跳出分支                                             
						default: 
									u1_printf("订阅失败,准备重启\r\n");//串口输出信息 
									connectFlag = 0;					//connectFlag置零,重启连接
									break;								//跳出分支 								
						}					
			    }
				//if判断,第一个字节是0xD0,表示收到的是PINGRESP报文
				else if(MQTT_RxDataOutPtr[2] == 0xD0)
				{ 
					u1_printf("PING报文回复\r\n");						//串口输出信息 
					if(pingFlag == 1)
					{                   						        //如果pingFlag=1,表示第一次发送
						 pingFlag = 0;    				       			//要清除pingFlag标志
					}
					else if(pingFlag > 1)	
					{ 				 									//如果pingFlag>1,表示是多次发送了,而且是2s间隔的快速发送
						pingFlag = 0;     				      			//要清除pingFlag标志
						TIM3_ENABLE_30S(); 				      			//PING定时器重回30s的时间
					}				
				}	
				//if判断,如果第一个字节是0x30,表示收到的是服务器发来的推送数据
				//我们要提取控制命令
				else if(MQTT_RxDataOutPtr[2] == 0x30)
					{ 
						u1_printf("服务器等级0推送\r\n"); 		   	//串口输出信息 
						MQTT_DealPushdata_Qs0(MQTT_RxDataOutPtr);   //处理等级0推送数据
					}									
					MQTT_RxDataOutPtr += RBUFF_UNIT;                //指针下移
					if(MQTT_RxDataOutPtr == MQTT_RxDataEndPtr)      //如果指针到缓冲区尾部了
					{
						MQTT_RxDataOutPtr = MQTT_RxDataBuf[0];      //指针归位到缓冲区开头              
					}		          
			}//处理接收缓冲区数据的else if分支结尾
						
			/*---------------------------------------------------------------------------------------------------------------------*/
			/*                    							 处理命令缓冲区数据                   		          			         */
			/*---------------------------------------------------------------------------------------------------------------------*/
			if(MQTT_CMDOutPtr != MQTT_CMDInPtr)							  //if成立的话,说明命令缓冲区有数据了	
			{                             		       
				u1_printf("命令:%s\r\n",&MQTT_CMDOutPtr[2]);              //串口输出信息
				
				if(!memcmp(&MQTT_CMDOutPtr[2],cmdLED0_On,strlen(cmdLED0_On)))//判断指令,如果是CMD1 
				{                                            
					ledFlag = "LED0ON";  								             
					LED0_On();	//主板上的大发光LED								  		   //LED开启
				}
				else if(!memcmp(&MQTT_CMDOutPtr[2],cmdLED0_Off,strlen(cmdLED0_Off))) //判断指令,如果是CMD1
				{ 
					ledFlag = "LED0OFF";                                              
					LED0_Off(); 								                //LED0关闭
				}
				else if(!memcmp(&MQTT_CMDOutPtr[2],cmdLED1_On,strlen(cmdLED1_On)))//判断指令,如果是CMD1 
				{                                            
					ledFlag = "LED1ON";  								             
					LED1_On();	//主板上的大发光LED								  		   //LED开启
				}
				else if(!memcmp(&MQTT_CMDOutPtr[2],cmdLED1_Off,strlen(cmdLED1_Off))) //判断指令,如果是CMD1
				{ 
					ledFlag = "LED1OFF";                                              
					LED1_Off(); 								                //LED1关闭
				}
				else if(!memcmp(&MQTT_CMDOutPtr[2],cmdLED2_On,strlen(cmdLED2_On)))//判断指令,如果是CMD1 
				{                                            
					ledFlag = "LED2ON";  								             
					LED2_On();	//主板上的大发光LED								  		   //LED开启
				}
				else if(!memcmp(&MQTT_CMDOutPtr[2],cmdLED2_Off,strlen(cmdLED2_Off))) //判断指令,如果是CMD1
				{ 
					ledFlag = "LED2OFF";                                              
					LED2_Off(); 								                //LED2关闭
				}
				else if(!memcmp(&MQTT_CMDOutPtr[2],cmdLED3_On,strlen(cmdLED3_On)))//判断指令,如果是CMD1 
				{                                            
					ledFlag = "LED3ON";  								             
					LED3_On();	//主板上的大发光LED								  		   //LED开启
				}
				else if(!memcmp(&MQTT_CMDOutPtr[2],cmdLED3_Off,strlen(cmdLED3_Off))) //判断指令,如果是CMD1
				{ 
					ledFlag = "LED3OFF";                                              
					LED3_Off(); 								                //LED3关闭
				}
				
				
				
				else if(!memcmp(&MQTT_CMDOutPtr[2],cmdDHT11_On,strlen(cmdDHT11_On)))//判断指令,如果是CMD3
				{      
					dhtFlag = 1;                 //dataFlag置1,表示处于采集状态了
					TIM2_ENABLE_10S();           //定时器2,,10s间隔采集温湿度   
				}
				else if(!memcmp(&MQTT_CMDOutPtr[2],cmdDHT11_Off,strlen(cmdDHT11_Off)))//判断指令,如果是CMD3
				{     
					dhtFlag = 0;                 //dataFlag置0,表示处于停止状态了
					TIM_Cmd(TIM2,DISABLE);		 //判断2路开关状态和采集状态,并发布给服务器
				}								
				//不做处理,后面会发送状态
				else u1_printf("未知指令\r\n");				//串口输出信息	
			
				MQTT_CMDOutPtr += CBUFF_UNIT;				//指针下移
				if(MQTT_CMDOutPtr == MQTT_CMDEndPtr)	    //如果指针到缓冲区尾部了
				MQTT_CMDOutPtr = MQTT_CMDBuf[0];			//指针归位到缓冲区开头	
				
				Send_Data();//发送控制数据
				//处理命令缓冲区数据的else if分支结尾					
			}	//connectFlag=1的if分支的结尾
			
		}
	/*--------------------------------------------------------------------*/
	/*      connectFlag=0同服务器断开了连接,我们要重启连接服务器          */
	/*--------------------------------------------------------------------*/
		else
		{ 
			u1_printf("需要连接服务器\r\n");               //串口输出信息
			LED8_Off();									//Mini板指示灯灭!
			TIM_Cmd(TIM4, DISABLE);                        //关闭TIM4 
			TIM_Cmd(TIM3, DISABLE);                        //关闭TIM3  
			WiFi_RxCounter = 0;                            //WiFi接收数据量变量清零                        
			memset(WiFi_RX_BUF, 0, WiFi_RXBUFF_SIZE);      //清空WiFi接收缓冲区 
			if(WiFi_Connect_IoTServer() == 0)			   //如果WiFi连接云服务器函数返回0,表示正确,进入if
			{   

				u1_printf("建立TCP连接成功\r\n");           //串口输出信息
				LED8_On();								   //点亮板载指示灯
				connectFlag = 1;                           //connectFlag置1,表示连接成功	
				WiFi_RxCounter = 0;                        //WiFi接收数据量变量清零                        
				memset(WiFi_RX_BUF, 0, WiFi_RXBUFF_SIZE);  //清空WiFi接收缓冲区 
				MQTT_Buff_Init();                          //初始化发送缓冲区                    
			}				
		}
	}
}



/***********************************************************************/

发布数据采用MQTT通信协议,使用EMQx服务器、TDengine数据库服务程序和Grafana数据展示软件,现分别说明如下:

大数据保存服务处理:

1.安装 EMQ X:
采用EMQ 开源程序服务器,对数据点的数据验证、传输数据的加密、数据点MCU的工作状态等信息数据及时处理。使用 ubuntu-18.04.5-desktop-amd64.iso 系统平台,打开终端,执行以下命令:

$ git clone https://gitee.com/isfive/linux-apt-get-aliyun.git
$ sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
$ cp /etc/apt/sources.list /etc/apt/sources.list.bak
$ cd linux-apt-get-aliyun
$ cd ./18.04
$ sudo cp ./sources.list /etc/apt/sources.list
$ sudo apt-get update

更改镜像源。然后安装 EMQ X服务器程序:`

$ sudo apt update && sudo apt install -y \
  apt-transport-https \
  ca-certificates \
  curl \
  gnupg-agent \
  software-properties-common
$ curl -fsSL https://repos.emqx.io/gpg.pub | sudo apt-key add -
$ sudo apt-key fingerprint 3E640D53
$ sudo add-apt-repository \
  "deb [arch=amd64] https://repos.emqx.io/emqx-ce/deb/ubuntu/ \
  ./$(lsb_release -cs) \
  stable"
$ sudo apt update
$ sudo apt install emqx=4.1.1
$ emqx start
若要停止 EMQ X Broker:$ emqx stop
若要卸载 EMQ X Broker:$ sudo apt remove emqx

以上是安装EMQx服务器程序的过程,接下来安装TDengine和Grafana程序。
2.安装 Tdengine:进入网址https://www.taosdata.com/cn/getting-started/#%E5%BF%AB%E6%8D%B7%E5%AE%89%E8%A3%85
下载 TDengine-server-2.0.5.1-Linux-x64.deb (2.7M),直接安装

$ systemctl start taosd
查看 TDengine状态:$ systemctl status taosd
若要停止 TDengine:$ systemctl stop taosd

3.安装 Grafana:

$ sudo apt-get install -y adduser libfontconfig1
$ wget https://dl.grafana.com/oss/release/grafana_7.2.1_amd64.deb
$ sudo dpkg -i grafana_7.2.1_amd64.deb
$ sudo service grafana-server start
若要停止 Grafana:$ sudo service grafana-server stop

4.为Grafana添加TDengine数据源插件:

$ git clone https://github.com/taosdata/TDengine.git
$ sudo cp -r /TDengine/minidevops/grafana/tdengine /var/lib/grafana/plugins/
$ sudo service grafana-server restart

以上4步完成基础软件的搭配,接下来介绍创建数据库和数据存储挖掘展示。

数据可视化展示以及数据挖掘:

一、TDengine 创建数据库与数据表,进入TDengine 服务程序.建立test数据库和相应的表:

create database test;
create table test.onoff_data (
 ts timestamp, onoff nchar(20), clientid nchar(50),  devid nchar(50),  usename nchar(50) );
 
CREATE TABLE test.cdr1000 ( time TIMESTAMP,  PwrFwd FLOAT,PwrRfl FLOAT, Temp FLOAT ,Vadj  FLOAT,Vdrv  FLOAT,Vamp  FLOAT,Idrv  FLOAT,I1  FLOAT,I2 FLOAT,I3  FLOAT,InLevel  FLOAT,Status1  FLOAT,Status2 FLOAT ,WiFi_riss FLOAT );

以上是用tdengine建立一个数据库test和表onoff_data、cdr1000,表onoff_data记录客户端上、下线情况,表中包括ts,onoff,clinentid,devid ,username五个字段,表cdr1000记录发射机运行参数等多个记录。

四、配置 EMQ X 规则引擎
1.打开 EMQ X Dashboared( http://服务器IP:18083 )使用 admin public 默认用户名密码完成初次登录,在设置中切换为中文
2.进入 规则引擎 -> 规则 页面,点击 新建 按钮进入创建页面
3.填写 规则SQL,其作用是将上报数据保存到TDengine数据库表cdr1000中。

SELECT
  payload
FROM
 "/pub/8266"

4.添加 响应动作,“动作”选择发送数据到Web服务
5.在“关键资源”新建资源,资源类型“WebHook”,资源名称“Tdengine Local”,请求URL“http://127.0.0.1:6041/rest/sql”,请求方法“POST”,请求头的键“Authorization”、的值“Basic cm9vdDp0YW9zZGF0YQ==”,点击新建按钮
6.关键资源选择新建的“Tdengine Local”,消息内容模板为

添加动作:
INSERT INTO test.cdr1000 VALUES( 
	now, 
	${payload.PwrFwd}, 
	${payload.PwrRfl}, 
	${payload.Temp}, 
	${payload.Vadj}, 
	${payload.Vdrv}, 
	${payload.Vamp}, 
	${payload.Idrv}, 
	${payload.I1}, 
	${payload.I2}, 
	${payload.I3}, 
	${payload.InLevel}, 
	${payload.Status1}, 
	${payload.Status2},
	${payload.Wifi_Rssi}	
	)

保存规则引擎后,正常情况下应该看到发射机运行参数已经实时的保存到数据库表cdr1000中。接下来我们进行可视化配置:
五。可视化配置:
1.打开 Grafana 可视化面板( http://127.0.0.1:3000 )使用 admin admin 默认用户名密码完成初次登录,登录后按照提示修改密码使用新密码登录进入主界面
2.在 Configuration->Data Sources 中添加数据源(Add data source),选取 TDengine 类型数据源,输入连接参数进行配置,默认情况下,Host=“http://127.0.0.1:6041”,User=“root”,Password=“taosdata”,点击 Save & Test,正常情况下,显示 TDengine Data source is working
在这里插入图片描述

3.在 Create->Dashboard 中 Add new panel,INPUT SQL填写如下查询指令来查询各项数值的平均值

select avg(PwrFwd), avg(PwrRfl), avg(Temp), avg(Vadj), avg(Vdrv), avg(Vamp), avg(Idrv), avg(I1) from test.cdr1000 interval($interval)

4.保存仪表盘,拖拽调整每个数据面板大小、位置,最终得到一个视觉效果较好的数据仪表盘。
在这里插入图片描述
经过以上的操作,就可以把发射机数据实时展示在屏幕上了。接下来,讲讲历史数据挖掘和机器人值班等技术的应用

标题数据挖掘和机器人值班的展望

以上是将发射机数据采集处理存储展示的全套过程,如法炮制,可以将一个发射台的所有设备都实现自动化管理,积累数据,10年保存,利用Python和 相关的数据库工具增删改查,配合行走式机器人实时监控设备的状态,做出故障预警,完成值机工作。
相关的所有资料随后将发布到github上,需要详细资料也可以QQ:1953455898联系。链接:https://codechina.csdn.net/qq_45852940/mqtt/-/starrers

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

姚晋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值