CAT1模塊 EC800M HTTP 使用后續記錄

記錄一下 CAT1 模塊EC800 HTTP 使用后續遇到的問題       by  矜辰所致

目錄

  • 前言
  • 一、一些功能的完善
    • 1.1 新的交互指令添加
    • 1.2 連不上網絡處理
  • 二、問題出現
  • 三、分析及解決
    • 3.1 定位問題
    • 3.2 問題分析與解決
      • 3.2.1 查看變量在內存中的位置
    • 3.3 數據類型說明
      • 3.3.1 常用格式化輸出符號
      • 3.3.2 不同平臺數據類型定義
  • 結語

前言

此前我寫過一篇文章 CAT1模塊 EC800M HTTP使用總結記錄 詳細講述了,如何使用 EC800M HTTP協議 的應用。

在后續的過程中,根據客戶的不同需求,應用有一定的改變,所以進行了一些定制的修改,同時也發現了以前留下的一個 bug , 覺得還是有必要來記錄一下,這不是也太久沒寫文章了,都要生銹了。

所以本文就繼續來說明一下CAT1模塊 EC800M HTTP 應用的后續問題吧。

我是矜辰所致,全網同名,盡量用心寫好每一系列文章,不浮夸,不將就,認真對待學知識的我們,矜辰所致,金石為開!

目錄

  • 前言
  • 一、一些功能的完善
    • 1.1 新的交互指令添加
    • 1.2 連不上網絡處理
  • 二、問題出現
  • 三、分析及解決
    • 3.1 定位問題
    • 3.2 問題分析與解決
      • 3.2.1 查看變量在內存中的位置
    • 3.3 數據類型說明
      • 3.3.1 常用格式化輸出符號
      • 3.3.2 不同平臺數據類型定義
  • 結語

一、一些功能的完善

在前面的使用文章中,我們主要的目的在于介紹 EC800M HTTP 的使用,對于正式使用的產品來說,有一些問題還是需要考慮到的。

1.1 新的交互指令添加

使用 HTTP 協議,客戶端想要和服務器交換數據,也只能等到 HTTP POST 以后通過服務器返回的響應來進行交互。這個其實在上一篇文章已經講到過,所以呢,如果是后續有和服務器數據交互的需求,也只能在 HTTP POST 以后來實現,那對于我們來說,都是在讀取 HTTP 響應之后,通過 strstr 函數找到我們需要的數據進行處理,比如下面一段代碼:

...
else if(!strcmp(cmd,"AT+QHTTPREAD=2\r\n")){printf("%s\r\n", EC800_RX_BUF);   //打印出響應用來參考char* position = strstr((char*)EC800_RX_BUF, "\"expectGear\":");//先找到expectGear 的位置if (position != NULL) {// Use sscanf to extract the integer value after "expectGear"sscanf(position + strlen("\"expectGear\":"), "%hd", &Http_set_mode);if((Http_set_mode > 0)&&(Http_set_mode < 5)&&(Http_set_mode != Value_Mode)) need_change = TRUE;// printf("Http_set_mode is %d\r\n",Http_set_mode);} else {printf("not found expectGear!!\r\n");}position = strstr((char*)EC800_RX_BUF, "\"voice\":");//先找到 voice 的位置if (position != NULL) {// Use sscanf to extract the integer value after "expectGear"sscanf(position + strlen("\"voice\":"), "%hhu", &Http_set_voice);// printf("Http_set_voice is %d\r\n",Http_set_voice);  if(Http_set_voice)//這里要關閉聲音{Voice_close_state = TRUE;Voice_close_count = 0;}} else {}CLEAR_EC800_Buffer(EC800_RX_Data);return 0;}
...

這個問題說明一下即可,沒什么難點,可能需要注意的點就是 HTTP 的響應數據量會稍微有點多,達到幾百個字節,需要注意自己的串口緩存區的大小,這個在上一篇文章也有說明。但是這一塊的代碼有個問題確實也是本文要說明的地方,上看的代碼已經是修改過的版本。

1.2 連不上網絡處理

對于有些網絡產品,它具備本地功能,所以在實際工作的時候要根據需求來判斷它如果連不上網絡是不停聯網還是保持本地功能完善。
如果是必須要連上網絡才能運行,很簡單,可以開啟看門口,然后在配網的過程中如果失敗重試的時間長一點,那么就能夠不停的自動復位。
如果是需要保證沒有網絡也要使得本地功能正常,然后設備自動定時連接網絡,那么就需要考慮好配網失敗重試的時間,超時返回標志位,不能等到看門狗自動復位。再在程序中根據配網成功或者失敗的標志位進行判斷需不需要再次連接網絡。比如本次我們就是要修改成這種狀態。

所以我們需要在配網操作的時候做一個超時,同時還需要返回狀態值,所以我們把配網聯網的狀態定義一個結構體:

typedef struct
{uint8		Ec800_init_state;	uint8		Ec800_pdp_prepare_state;	uint8		Http_set_url_state;	
} cat1_state_struct;extern char IMEI[15];
extern cat1_state_struct My_4g_state;

上面的 3個狀態分別對應 ec800_initec800_pdp_preparehttp_set_url 3個函數的狀態,以前函數沒有返回值,現在我們需要加上返回值:

uint8 ec800_init();
int Iot_SendCmd(const char* cmd, char* reply, int wait);
void Iot_SendNOCheck(char* cmd);uint8 ec800_pdp_prepare();
uint8 http_set_url(char *url);
void http_post_message(const char *message); 

這里使用 ec800_init 作為示例看一下超時的實現:

uint8 ec800_init()
{u16 cat1_timeout = 0;while(Iot_SendCmd(AT,"OK", 200)){HAL_Delay(1);cat1_timeout ++;if(cat1_timeout >= 2000){printf("uart false\r\n");return false;   }}cat1_timeout = 0;
...

上面這個原始的邏輯是有點問題的, cat1_timeout 應該是一個重試的次數,每次嘗試會等待 200ms ,然后再等待 1ms,再次嘗試,一共要嘗試 2000 次,那么不考慮程序運行指令的時間來算,都需要 201* 2000 ms 才會超時,除了時間太久了,失敗后1ms 就重試也不是那么合理。所以我們簡單修改一下:

  while(Iot_SendCmd(AT,"OK", 200)){HAL_Delay(100);cat1_timeout ++;if(cat1_timeout >= 30){printf("uart false\r\n");return false;   }}cat1_timeout = 0;printf("\r\nuart ok\r\n");while(Iot_SendCmd(CPIN,"READY", 200)){HAL_Delay(100);cat1_timeout ++;if(cat1_timeout >= 30){return false;   }}

具體的可以根據自己看門狗的設定時間來確定每次 AT 指令的重試次數。

然后我們在具體使用的時候,先定義一個結構體變量,保存我們的聯網狀態,然后在開機的時候進行配網聯網操作,不管成功與否,我們可以得到對應的狀態,示例如下:

cat1_state_struct My_4g_state;...
//省略
...
int main(void)
{
//初始化省略MX_IWDG_Init();My_4g_state.Ec800_init_state = ec800_init();HAL_IWDG_Refresh(&hiwdg);printf("Ec800_init_state:%d\r\n",My_4g_state.Ec800_init_state);My_4g_state.Ec800_pdp_prepare_state = ec800_pdp_prepare();HAL_IWDG_Refresh(&hiwdg);printf("Ec800_pdp_prepare_state:%d\r\n",My_4g_state.Ec800_pdp_prepare_state);My_4g_state.Http_set_url_state = http_set_url(url);HAL_IWDG_Refresh(&hiwdg);printf("Http_set_url_state:%d\r\n",My_4g_state.Http_set_url_state);...//省略...//我這里使用了初始化狀態確定是否需要 postif(My_4g_state.Ec800_init_state) http_post_message(message);while(1){...//定時發送,如果網絡狀態異常,就重新連接網絡,再嘗試發送if(send_count >= 180){send_count = 0;snprintf(message, sizeof(message), pm_message, IMEI,PM10_Data,PM25_Data,PM1_Data,Value_Mode);if(My_4g_state.Ec800_init_state) {http_post_message(message);}else {  //重新連接My_4g_state.Ec800_init_state = ec800_init();      HAL_IWDG_Refresh(&hiwdg);My_4g_state.Ec800_pdp_prepare_state = ec800_pdp_prepare();HAL_IWDG_Refresh(&hiwdg);My_4g_state.Http_set_url_state = http_set_url(url); HAL_IWDG_Refresh(&hiwdg);      if(My_4g_state.Ec800_init_state) http_post_message(message);}}...}
}

上面是設備初始化上電后的聯網配置示例,上電先配網,然后讀取一次數據進行上傳,在主函數的 while 循環中,根據自己的需要的周期,定時的進行數據上傳,或者根據 My_4g_state.Ec800_init_state 狀態,進行重新配置網絡操作。

整體來說就是這么一個流程,不復雜,但是用起來卻出現了一個小 bug 。

二、問題出現

如果我們 SIM 卡正常,網絡信號正常,我們會經歷正常的初始化了,得到的My_4g_state3個成員變量都為1,然后他可以正常的進行 POST 操作,但是我測試的時候發現,設備還是會定期聯網,但是奇怪的是,它能夠正常的進行 POST 操作。

確定的問題是只要運行過了http_post_message函數 ,My_4g_state.Ec800_init_state 就會變成 0 ,如下圖:

在這里插入圖片描述

簡單一想,在實際代碼中,http_post_message函數里根本沒有改變這個變量的值, 那么出這種問題極大概率的就是數據溢出,最開始想的是不是串口緩存數據溢出,嘗試過加大串口緩沖區,沒有用。測試下來發現這個 bug 只會改變 1個字節,所以期間采用了一個辦法,就是在結構體成員變量上加上一個預留位置,如下圖:

typedef struct
{uint8       Reserved_state;uint8		Ec800_init_state;	uint8		Ec800_pdp_prepare_state;	uint8		Http_set_url_state;	
} cat1_state_struct;

倒是能夠解決,程序邏輯正常的跑,但是這是治標不治本的方式,還是存在 bug 。

三、分析及解決

3.1 定位問題

還是得進一步的分析問題,于是進一步的修改了一下 http_post_message 函數,在每次操作后把My_4g_state.Ec800_init_state 值打印出來,如下 :

void http_post_message(const char *message) {int length = strlen(message);char at_post[32];u16 cat1_timeout = 0;snprintf(at_post, sizeof(at_post), "AT+QHTTPPOST=%d,%d,%d\r\n", length, 5, 10);printf("five:%d\r\n",My_4g_state.Ec800_init_state);while(Iot_SendCmd(at_post,"CONNECT", 500)){HAL_Delay(100);cat1_timeout ++;if(cat1_timeout >= 10){return;   }}cat1_timeout = 0; printf("\r\nready to send post message!\r\n%s\r\n", message);printf("six:%d\r\n",My_4g_state.Ec800_init_state);while(Iot_SendCmd(message,"+QHTTPPOST:", 5000)){  // HAL_Delay(100);cat1_timeout ++;if(cat1_timeout >= 3){printf("http post wrong\r\n");return;   }}cat1_timeout = 0;printf("\r\nhttp post OK\r\n");printf("seven:%d\r\n",My_4g_state.Ec800_init_state);HAL_IWDG_Refresh(&hiwdg);while(Iot_SendCmd("AT+QHTTPREAD=2\r\n","+QHTTPREAD:", 2000)){HAL_Delay(100);cat1_timeout ++;if(cat1_timeout >= 5){printf("read wrong\r\n");return;   }}cat1_timeout = 0;printf("\r\nHTTPREAD OK\r\n");printf("eight:%d\r\n",My_4g_state.Ec800_init_state); 
}

測試結果如下:

在這里插入圖片描述

通過上面測試,已經可以直接定位到云運行過while(Iot_SendCmd("AT+QHTTPREAD=2\r\n","+QHTTPREAD:", 2000)) 后,My_4g_state.Ec800_init_state 的值就變成了 0 ,我們看看上面這條語句會做什么工作,我只把 HTTPREAD 相關的部分代碼截取出來:

int Iot_SendCmd(const char* cmd, char* reply, int wait)
{u8 i=0;char* rss_str;int rssi,res;CLEAR_EC800_Buffer(EC800_RX_Data);Uart3_sendBuffer((u8*)cmd,strlen(cmd));/*此處串口回的不止是一幀數據,所以使用 IDLE 中斷不合適*/if ((!strcmp(reply,"+QHTTPREAD:"))||(!strcmp(reply,"+QHTTPPOST:"))){//讀取和發送的處理,直接等一段時間HAL_Delay(1000);// 500 600 800 1000 一直加大   }/*另外的設置指令大多都是等待一個 OK 返回,屬于一幀數據所以可以用 IDLE 中斷*/else{...}EC800ReceiveState = false;if (!strcmp(reply,"+CSQ")){...}else if (strstr((char*)EC800_RX_BUF, reply)){  if (!strcmp(cmd,"AT+CGSN\r\n")){}else if(!strcmp(cmd,"AT+QHTTPREAD=2\r\n")){printf("%s\r\n", EC800_RX_BUF);   //打印出響應用來參考char* position = strstr((char*)EC800_RX_BUF, "\"expectGear\":");//先找到expectGear 的位置if (position != NULL) {// Use sscanf to extract the integer value after "expectGear"sscanf(position + strlen("\"expectGear\":"), "%d", &Http_set_mode);if((Http_set_mode > 0)&&(Http_set_mode < 5)&&(Http_set_mode != Value_Mode)) need_change = TRUE;} else {printf("not found expectGear!!\r\n");}CLEAR_EC800_Buffer(EC800_RX_Data);return 0;}CLEAR_EC800_Buffer(EC800_RX_Data);return 0;}return 1;  
}

從上面可以看到,在這個操作中我們只會改變變量 Http_set_modeneed_change 的值,但是 need_change 并不是每次都改變,所以基本上可以判斷是 Http_set_mode 的值變化使得My_4g_state.Ec800_init_state 變化了。

3.2 問題分析與解決

那我們前文也說過,此類問題極大概率的就是數據溢出問題,這兩個數據在內存中存放的地址應該是靠在一起的才會出現這種問題,為了更加直觀的說明這個問題,我們可以查看一下他們在內存中存放的位置。

3.2.1 查看變量在內存中的位置

如何查看變量的存放位置?那就是查看編譯過后的 .map 文件!

我們打開 .map 文件 搜索一下我們的變量,如下圖:

在這里插入圖片描述

果然他們是緊靠著的,他們在內存中如下圖分部:

在這里插入圖片描述

那我們回去看一下問題,Http_set_mode 我們定義的為一個字節的數據,怎么影響到了后面的數據,我們在出問題的地方有一條語句,注意看:

sscanf(position + strlen("\"expectGear\":"), "%d", &Http_set_mode);

expectGear 字符位置后面的數值,放到變量Http_set_mode 的地址,也就是 0x2000046d 位置處,放的數據類型為 %d 類型 !!!

%d 類型是幾個字節? 在 32 位系統中,%d 通常表示 4 字節(32 位)的有符號整數(int 類型)。

所以問題我們已經確定了,就是因為這個sscanf函數中的 %d導致的,比如我們收到的是一個 2 ,我們知道 STM32 為小端模式,那么操作過后我們的內存中的數據會變成如下這樣:

在這里插入圖片描述

到這里,問題已經很明了了,我們沒有注意數據類型,導致我們改變了存放在地址 0x20000470 處的My_4g_state.Ec800_init_state 變量的值。

對于這個問題,我們只需要把 %d改成 %hh , 就解決了這個問題了!如下:

sscanf(position + strlen("\"expectGear\":"), "%hhu", &Http_set_mode);

3.3 數據類型說明

那既然遇到了這個問題,在解決的過程中,我也發現有一些值得說明的地方,所以接下來就順帶做做一個筆記說明把。

3.3.1 常用格式化輸出符號

首先,先來看一看我們常用的格式化輸出符號,這里只需要記住這張表格就好了:

格式符含義對應數據類型位數(典型情況)
%d有符號十進制整數int32位(4字節)
%u無符號十進制整數unsigned int32位(4字節)
%hd有符號短整數short16位(2字節)
%hu無符號短整數unsigned short16位(2字節)
%ld有符號長整數long32/64位(平臺相關)
%lu無符號長整數unsigned long32/64位(平臺相關)
%lld有符號長長整數long long64位(8字節)
%llu無符號長長整數unsigned long long64位(8字節)
%hhd有符號單字節整數char8位(1字節)
%hhu無符號單字節整數unsigned char8位(1字節)
%f十進制浮點數float32位(4字節)
%lf十進制雙精度浮點數double64位(8字節)
%c單個字符char8位(1字節)
%s字符串char[](以\0結尾)動態長度
%p指針地址任意指針類型32/64位(平臺相關)

3.3.2 不同平臺數據類型定義

對于本文遇到的問題,除了上面的解決辦法,我們其實還可以修改一下Http_set_mode 的數據類型,如下:

在這里插入圖片描述

這樣處理的話其實也能夠解決問題。但是我這里要說的一個問題是,我在修改的過程中,有把 Http_set_mode 定義為 u16 的數據類型,但是呢他還是占用 4 個字節,實際上這里就讓我發現了另外一個問題,按理來說u16 類型我本意是定義為 16位 的數據。

于是查看了一下u16 的定義:

在這里插入圖片描述

我第一印象是,確實是 4 個字節的,為什么會這么定義? 想了一下,這個頭文件是在上次 51 單片機項目中復制過來的……

在 8 位的單片機中:

unsigned int 通常是16 位(2字節)
unsigned long 為 32 位
unsigned char 8位

所以這里我忽略了平臺的變換,直接使用 8 位單片機上的數據類型定義,如果要在 32 位單片機上定義 16位數據類型 ,建議使用uint16_t(無符號) 和 int16_t(有符號),因為這是在標準 C 語言庫文件 <stdint.h> 中定義的,在所有平臺上都明確表示16位,可以避免因編譯器差異導致的問題。

其實,即便我們真的記不得某一個數據類型到底占多少個字節,我們可以直接通過 sizeof 來判斷,示例如下:

printf("Size: %d bytes\n", (int)sizeof(uint16_t)); 

在這里插入圖片描述

結語

本文我們講到的問題,是數據類型處理不當的問題,對數據類型的應用不夠熟練嚴謹導致的數據覆蓋。

我們通過問題,復習了一遍數據類型的一些基礎知識,也說明了如何通過 .map 文件檢查數據溢出或者覆蓋問題。希望對大家以后的產品開發有一定的幫助。

好了,本文就到這里,謝謝大家!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/74519.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/74519.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/74519.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

單純形法之大M法

1. 問題背景與標準化 在求解某些線性規劃問題時&#xff0c;往往難以直接找到初始的基本可行解。特別是當約束中存在等式或 “≥” 類型的不等式時&#xff0c;我們需要引入人工變量來構造一個初始可行解。 考慮如下標準形式問題&#xff08;假設為最大化問題&#xff09;&am…

Springboot集成Debezium監聽postgresql變更

1.創建springboot項目引入pom <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>io.debezium</groupI…

報錯 standard_init_linux.go:228: exec user process caused: exec format error

docker logs 容器名 報錯&#xff1a; standard_init_linux.go:228: exec user process caused: exec format error 或者 standard_init_linux.go:228: exec user process caused: input/output error 排查思路 1、檢查源鏡像的框架是否正確&#xff0c;是否amd64&#x…

Go 代理爬蟲

現在注冊&#xff0c;還送15美金注冊獎勵金 --- 亮數據-網絡IP代理及全網數據一站式服務商 使用代理服務器&#xff0c;通過 Colly、Goquery、Selenium 進行網絡爬蟲的基礎示例程序 本倉庫包含兩個分支&#xff1a; basic 分支包含供 Go Proxy Servers 這篇文章改動的基礎代碼…

STM32實現智能溫控系統(暖手寶):PID 算法 + DS18B20+OLED 顯示,[學習 PID 優質項目]

一、項目概述 本文基于 STM32F103C8T6 單片機&#xff0c;設計了一個高精度溫度控制系統。通過 DS18B20 采集溫度&#xff0c;采用位置型 PID 算法控制 PWM 輸出驅動 MOS 管加熱Pi膜&#xff0c;配合 OLED 實時顯示溫度數據。系統可穩定將 PI 膜加熱至 40℃&#xff0c;適用于…

neo4j知識圖譜常用命令

1. 查看所有節點和關系 如果你想查看圖數據庫中的所有節點和關系&#xff0c;可以使用以下查詢&#xff1a; Cypher 深色版本 MATCH (n)-[r]->(m) RETURN n, r, m n 和 m 表示節點。r 表示兩個節點之間的關系。這條命令會返回所有節點及其直接相連的關系。 2. 查看所有節…

從零開始:使用Luatools工具高效燒錄Air780EPM核心板項目的完整指南

本文將深入講解如何使用Luatools工具燒錄一個具體的項目到Air780EPM開發板中。如何使用官方推薦的Luatools工具&#xff08;一款跨平臺、命令行驅動的燒錄利器&#xff09;&#xff0c;通過“環境配置→硬件連接→參數設置→一鍵燒錄”四大步驟&#xff0c;幫助用戶實現Air780E…

2024年認證杯SPSSPRO杯數學建模C題(第二階段)云中的海鹽全過程文檔及程序

2024年認證杯SPSSPRO杯數學建模 C題 云中的海鹽 原題再現&#xff1a; 巴黎氣候協定提出的目標是&#xff1a;在2100年前&#xff0c;把全球平均氣溫相對于工業革命以前的氣溫升幅控制在不超過2攝氏度的水平&#xff0c;并為1.5攝氏度而努力。但事實上&#xff0c;許多之前的…

大疆上云api介紹

概述 目前對于 DJI 無人機接入第三方云平臺,主要是基于 MSDK 開發定制 App,然后自己定義私有上云通信協議連接到云平臺中。這樣對于核心業務是開發云平臺,無人機只是其中一個接入硬件設備的開發者來說,重新基于 MSDK 開發 App 工作量大、成本高,同時還需要花很多精力在無人…

云原生之開源遙測框架OpenTelemetry(在 Gin 框架中使用 OpenTelemetry 進行分布式追蹤和監控)

文章目錄 云原生之開源遙測框架OpenTelemetry背景什么是可觀測性&#xff1f; 什么是 OpenTelemetry&#xff1f;Opentelemetry的主要優勢有以下幾點&#xff1a;理解分布式鏈路日志Spans分布式鏈路 在 Gin 框架中使用 OpenTelemetry 進行分布式追蹤和監控0. 整體思路1. 初始化…

【藍橋杯速成】| 11.回溯 之 子集問題

題目一&#xff1a;子集 問題描述 78. 子集 - 力扣&#xff08;LeetCode&#xff09; 給你一個整數數組 nums &#xff0c;數組中的元素 互不相同 。返回該數組所有可能的子集&#xff08;冪集&#xff09;。 解集 不能 包含重復的子集。你可以按 任意順序 返回解集。 示例…

Nginx目錄結構

Nginx目錄結構 ? Nginx 的安裝目錄結構可能會因安裝方式&#xff08;如使用包管理器、源碼編譯等&#xff09;和操作系統的不同而有所差異。以下是通過在線安裝時&#xff0c;Nginx 默認的目錄結構&#xff0c;以及各目錄和文件的作用。 yum install nginx查詢nginx [rootRo…

2.(vue3.x+vite)使用vue-router

前端技術社區總目錄(訂閱之前請先查看該博客) 效果預覽 路由配置的“/”與“helloWorld”都可以訪問到以下內容 http://10.11.0.87:4000/#/ http://10.11.0.87:4000/#/helloWorld 1:安裝vue-router npm i vue-router 2:創建router文件 在src的目錄下創建router文件夾…

后端返回了 xlsx 文件流,前端怎么下載處理

當后端返回一個 .xlsx 文件流時&#xff0c;前端可以通過 JavaScript 處理這個文件流并觸發瀏覽器下載。 實現步驟 發送請求獲取文件流&#xff1a; 使用 fetch 或 axios 等工具向后端發送請求&#xff0c;確保響應類型設置為 blob&#xff08;二進制數據流&#xff09;。 創建…

HTML5拖拽功能教程

HTML5拖拽功能教程 簡介 HTML5引入了原生拖放(Drag and Drop)API&#xff0c;使開發者能夠輕松實現網頁中的拖拽功能&#xff0c;無需依賴第三方庫。拖拽功能可以大大提升用戶體驗&#xff0c;適用于文件上傳、列表排序、看板系統等多種交互場景。本教程將帶您全面了解HTML拖…

VUE3 路由配置

1.下載 VueRouter 模塊 在命令行中輸入 yarn add vue-router 2.導?相關函數 在自己創建的router/index.js 文件中 import { createRouter, createWebHashHistory } from vue-router 3.創建路由實例 在自己創建的router/index.js 文件中 const theFirstRouter ()>{return…

歷史序列影像 Esri的World Imagery Wayback簡介

Esri的World Imagery Wayback是一個專注于提供歷史衛星影像的在線平臺&#xff0c;由全球領先的地理信息系統&#xff08;GIS&#xff09;技術提供商Esri開發。該平臺整合了多源衛星影像數據&#xff0c;允許用戶回溯特定區域在不同時間點的影像變化&#xff0c;支持時間序列分…

golang結構體與指針類型

結構體與指針類型 指針類型字段 具名字段 舉例 package struct_knowledgeimport "fmt"//結構體字段為指針類型 func StructWithPoint(){type Student struct{name *string}var lisa Studentfmt.Printf("賦值前,Student的實例的值%#v\n",lisa)//錯誤的賦…

NetMizer-日志管理系統-遠程命令執行漏洞挖掘

漏洞描述&#xff1a;NetMizer 日志管理系統 cmd.php中存在遠程命令執行漏洞&#xff0c;攻擊者通過傳入 cmd參數即可命令執行 1.fofa搜素語句 title"NetMizer 日志管理系統" 2.漏洞驗證 網站頁面 驗證POC /data/manage/cmd.php?cmdid

Contactile三軸觸覺傳感器:多維力感賦能機器人抓取

在非結構化環境中&#xff0c;機器人對物體的精準抓取與操作始終面臨巨大挑戰。傳統傳感器因無法全面感知觸覺參數&#xff08;如三維力、位移、摩擦&#xff09;&#xff0c;難以適應復雜多變的場景。Contactile推出的三軸觸覺力傳感器&#xff0c;通過仿生設計與創新光學技術…