日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當(dāng)前位置:首頁 > > 充電吧
[導(dǎo)讀]本報文格式不能處理粘包問題,因為處理粘包問題的成本太高,會極大的降低服務(wù)端的處理效率以及增加內(nèi)存消耗,如果傳輸速度很高,建議使用UDP,UDP傳輸速度快,并且應(yīng)用層做響應(yīng),增加重傳機制可以有效的保證數(shù)

本報文格式不能處理粘包問題,因為處理粘包問題的成本太高,會極大的降低服務(wù)端的處理效率以及增加內(nèi)存消耗,如果傳輸速度很高,建議使用UDP,UDP傳輸速度快,并且應(yīng)用層做響應(yīng),增加重傳機制可以有效的保證數(shù)據(jù)的可靠性,目前我做的RTU升級等功能都是使用UDP完成,服務(wù)器端開銷小,并且不會粘包。

通訊建議:不要做什么握手機制,直接發(fā)送數(shù)據(jù),服務(wù)器收到了就響應(yīng),收到響應(yīng)后就結(jié)束通訊,握手增加耗電又增加流量消耗,并且在GPRS通訊上是非常浪費資源的,本來網(wǎng)絡(luò)就不可靠,直接發(fā)送數(shù)據(jù),服務(wù)器收到了就響應(yīng)通訊就結(jié)束了,如果弄成握手方式,就是發(fā)送請求,握手成功,發(fā)送數(shù)據(jù),等待響應(yīng),結(jié)束通訊,就增加了1個來回,特別是之前用過MQTT多了幾個來回,太浪費通訊資源了。

?

使用HEX格式的通訊協(xié)議非常高效,不管是發(fā)送打包還是接收拆包,1個結(jié)構(gòu)體就能搞定,無需一個字節(jié)一個字節(jié)解析,如果用字符串json方式,使用單片機作為客戶端就很為難了,一般內(nèi)存不會很多,并且解析json需要的內(nèi)存會非常多。

?

報文全部使用小端模式,便于C語言處理。

注:由于我之前做了一個支持10W個設(shè)備的數(shù)據(jù)解析軟件,其實占用的內(nèi)存非常少,并且對CPU消耗很低,我們使用了一個很低端的服務(wù)器就能做到每秒1K條以上的數(shù)據(jù)處理,并且每條數(shù)據(jù)都在1秒內(nèi)得到響應(yīng),剛開始的時候還好,后面因為設(shè)備越來越多,導(dǎo)致服務(wù)器搜索設(shè)備變得很漫長(我的設(shè)備數(shù)據(jù)都是放到內(nèi)存中,使用了一個指針數(shù)組進行管理),由于設(shè)備會動態(tài)的增加,不能對序列號進行排序,因此查找設(shè)備的索引時間將會不可控,解析一條數(shù)據(jù)非???,存儲是異步的,有1W條的緩存,但是唯獨查找設(shè)備這個看似簡單的過程變得非常復(fù)雜,由于之前的協(xié)議我沒有增加索引,我只能對設(shè)備進行分組,我建了1W個指針數(shù)組,根據(jù)設(shè)備尾號后4位進行分組,這樣會增加幾十兆的內(nèi)存消耗,但是查找卻變得非常迅速。通過這個事情之后,我決定在協(xié)議中直接增加索引,服務(wù)端收到索引后直接取出當(dāng)前設(shè)備的配置與SN進行對比,如果相同就直接解析存儲數(shù)據(jù),如果不相同就進行查找即可,沒有就進行新建,最后建議將設(shè)備分組與協(xié)議中增加索引進行整合,集各自的優(yōu)點,可以最大限度的提高數(shù)據(jù)的解析能力,當(dāng)每個設(shè)備都通訊一次之后,解析任何一個設(shè)備上傳的數(shù)據(jù)的時間都是一樣的,并且是最小的。

禁忌:解析數(shù)據(jù)是千萬不要把配置什么的都放到數(shù)據(jù)庫,數(shù)據(jù)庫相比內(nèi)存實在是慢太多了,而且時間不可控,數(shù)據(jù)庫本身就不是非??煽浚ㄗh將數(shù)據(jù)庫操作做異步處理,中間用FIFO通訊,這樣數(shù)據(jù)解析線程可以以最高效的方式運行(一個線程輕松解析成千上萬條數(shù)據(jù),通過增加一個線程可以提高1倍的處理能力)。

?

存儲設(shè)備實時信息方式

我存儲設(shè)備相關(guān)信息的方式是,每個設(shè)備有一個自己的統(tǒng)一的結(jié)構(gòu)體,比如我最大支持10000個設(shè)備,我就會先申請1個指針數(shù)組,大小為10000,占用40000B,也就不到40KB內(nèi)存。然后每添加一個設(shè)備,就給這個指針申請一個內(nèi)存,存放設(shè)備的配置信息,同時會使用托管的線程池異步向數(shù)據(jù)庫中增加一個設(shè)備。

但是如果有幾千上萬個設(shè)備的時候,尋找這個設(shè)備的配置就變得很吃力了(相對來說,內(nèi)存中遍歷都會比數(shù)據(jù)庫快),這個時候我就會對設(shè)備進行分組,比如分100組,最好的情況下每個組有100個設(shè)備,但是不排除有一種可能,一個組中有超過100個設(shè)備的呀,我就要做一個二維指針數(shù)組,分100個數(shù)組,每個數(shù)組中1000個設(shè)備指針,這樣內(nèi)存占用就會是100*1000*4,也還好400K,但是如果一個組超過了1000個就不好辦了,同樣如果是10W個設(shè)備的時候,這個分組就很吃內(nèi)存了,我的做法是每個分組用個變量進行記錄,每個組給50個設(shè)備指針,當(dāng)這個組超過50個之后,我重新對這個指針進行申請內(nèi)存,大小為上一次的+50個,然后把之前的數(shù)據(jù)拷貝過來,釋放掉之前的數(shù)據(jù),這樣就可以動態(tài)的進行分組控制,實現(xiàn)效率的提升與內(nèi)存的最小消耗。


使用分組提高查找的一個例子

	//查找指定SN的設(shè)備索引
	//返回索引;-1:沒有找到
	//2017-12-27?:?會使用分組進行快速查找索引,只能在單線程中使用,不要跨線程搜索
	int?FindSN(char?pSN[16])
	{
		DWORD?Hash;
		BYTE?temp[3];
		int?GroupIndex;
		ONE_DEVICE_DATA_TYPE?*pDeviceData;

		if?(pSN?==?nullptr)?return?-1;
		pSN[15]?=?0;
		if(strlen(pSN)?!=?15)?return-1;
		//檢查最后3位是否為000-999
		temp[0]?=?pSN[12]?-?'0';	//字符轉(zhuǎn)換為數(shù)字
		temp[1]?=?pSN[13]?-?'0';	//字符轉(zhuǎn)換為數(shù)字
		temp[2]?=?pSN[14]?-?'0';	//字符轉(zhuǎn)換為數(shù)字
		if?(temp[0]?>?9?||?temp[1]?>?9?||?temp[2]?>?9)
		{
			return?-1;
		}
		GroupIndex?=?temp[0]?*?100?+?temp[1]?*?10?+?temp[2];//計算當(dāng)前設(shè)備的分組索引
		if?(GroupIndex?>=?DEVICE_GROUP_CNT)?return?-1;		//分組索引無效
		Hash?=?USER_LIB.BKDRHash(pSN);		//計算哈希結(jié)果
		//在指定的分組內(nèi)去搜索當(dāng)前設(shè)備
		for?(DWORD?i?=?0;?i?<?this->GroupDeviceCnt[GroupIndex];?i++)
		{
			pDeviceData?=?(ONE_DEVICE_DATA_TYPE?*)this->pDeviceDataGroupPointerBuff[GroupIndex][i];	//遍歷當(dāng)前分組
			if?(pDeviceData->Config.Hash?==?Hash)		//先判斷哈希結(jié)果是否正確
			{
				if?(strcmp(pSN,?pDeviceData->Config.SN)?==?0)?return?pDeviceData->Index;			//找到了,返回索引
			}
		}

		return?-1;
	}


添加設(shè)備的例子,先添加到全局的配置數(shù)組中,然后再把指針存儲一份到分組索引中

//添加一個設(shè)備到配置緩沖區(qū),返回索引,DeviceCnt?>=?DEVICE_MAX_CNT)							//設(shè)備數(shù)量超出范圍
		{
			*pError?=?"設(shè)備配置超出范圍了";
			return?-1;	
		}
		pConfig->SN[15]?=?0;
		if?(strlen(pConfig->SN)?!=?15)
		{
			*pError?=?"設(shè)備序列號必須為15位";
			return?-1;
		}
		//檢查最后3位是否為000-999
		temp[0]?=?pConfig->SN[12]?-?'0';	//字符轉(zhuǎn)換為數(shù)字
		temp[1]?=?pConfig->SN[13]?-?'0';	//字符轉(zhuǎn)換為數(shù)字
		temp[2]?=?pConfig->SN[14]?-?'0';	//字符轉(zhuǎn)換為數(shù)字
		if?(temp[0]?>?9?||?temp[1]?>?9?||?temp[2]?>?9)
		{
			return?-1;
		}
		GroupIndex?=?temp[0]?*?100?+?temp[1]?*?10?+?temp[2];			//計算當(dāng)前設(shè)備的分組索引
		if?(GroupIndex?>=?DEVICE_GROUP_CNT)
		{
			*pError?=?"無效的分組索引";
			return?-1;
		}

		if?(FindSN(pConfig->SN)?>=?0)									//尋找指定地址的設(shè)備是否存在
		{
			*pError?=?"當(dāng)前設(shè)備地址已經(jīng)存在";
			return?-1;
		}
		p?=?new?ONE_DEVICE_DATA_TYPE;									//申請內(nèi)存
		memset(p,?0,?sizeof(ONE_DEVICE_DATA_TYPE));						//清控配置區(qū)域
		memcpy(&p->Config,?pConfig,?sizeof(DEVICE_CONFIG_TYPE));		//拷貝數(shù)據(jù)
		p->Config.Hash?=?USER_LIB.BKDRHash(p->Config.SN);				//生成SN的哈希值
		p->DbStatus.ReadHistCongifCnt?=?p->DbStatus.ReadHistStatusCnt?=?p->DbStatus.WriteHistCongifCnt?=?-1;		//將歷史記錄條數(shù)置為-1無效值

		p->Index?=?this->DeviceCnt;										//記錄當(dāng)前設(shè)備的索引
		//將當(dāng)前設(shè)備編號進行分組
		if?(this->AddDeviceToGroup(GroupIndex,?(DWORD)p)?==?false)
		{
			*pError?=?"添加設(shè)備到分組失?。?;
			return?-1;
		}
		U32_DeviceDataPointerBuff[this->DeviceCnt]?=?(DWORD)p;			//存放指針
		pDeviceDataPointerBuff[this->DeviceCnt++]?=?p;					//存儲指針
		

		return?(this->DeviceCnt?-?1);									//返回當(dāng)前的索引
	}


添加設(shè)備到分組中,如果分組滿了將會進行擴充

//添加一個設(shè)備到指定分組中,如果分組慢,將會申請擴容
	//不會進行編號等檢查,只能在AddDevice中被調(diào)用
	bool?AddDeviceToGroup(int?GroupIndex,?DWORD?DeviceDataPointer)
	{
		if?(GroupIndex?<?0?||?GroupIndex?>=?DEVICE_GROUP_CNT)?return?false;
		if?(this->GroupSize[GroupIndex]?>=?DEVICE_MAX_CNT)?return?false;		//分組大小不能超過總設(shè)備限制
		if?(this->GroupDeviceCnt[GroupIndex]?>?this->GroupSize[GroupIndex])?return?false;
		if?(this->GroupDeviceCnt[GroupIndex]?==?this->GroupSize[GroupIndex])	//需要擴容
		{
			if?(DeviceGroupExpansion(GroupIndex)?==?false)						//擴容失敗,返回
			{
				SYS_LOG.Write(__FILE__?+?__LINE__?+?"分組擴容失敗,分組索引"?+?GroupIndex+"rn");
				return?false;
			}
			SYS_LOG.Write("分組"?+?GroupIndex?+?"擴容成功rn");
		}
		this->pDeviceDataGroupPointerBuff[GroupIndex][this->GroupDeviceCnt[GroupIndex]]?=?DeviceDataPointer;	//保存當(dāng)前設(shè)備指針
		this->GroupDeviceCnt[GroupIndex]?++;									//分組內(nèi)的設(shè)備數(shù)量增加

		return?true;
	}
	//為指定的分組進行擴容,會先復(fù)制緩沖區(qū),然后重新申請,再釋放之前的緩沖區(qū)(注意:使用分組索引只能在一個線程中使用)
	//必須分組已經(jīng)滿了再調(diào)用
	bool?DeviceGroupExpansion(int?GroupIndex)
	{
		if?(GroupIndex?<?0?||?GroupIndex?>=?DEVICE_GROUP_CNT)?return?false;
		if?(this->GroupSize[GroupIndex]?>=?DEVICE_MAX_CNT)?return?false;	//分組大小不能超過總設(shè)備限制
		DWORD?*p?=?this->pDeviceDataGroupPointerBuff[GroupIndex];			//先復(fù)制之前的指針
		DWORD?Size?=?this->GroupSize[GroupIndex];							//記錄之前分組容量
		this->GroupSize[GroupIndex]?+=?GROUP_DEVICE_INC;					//當(dāng)前分組容量增加
		this->pDeviceDataGroupPointerBuff[GroupIndex]?=?new?DWORD[this->GroupSize[GroupIndex]];	//重新為當(dāng)前分組申請內(nèi)存
		memcpy(this->pDeviceDataGroupPointerBuff[GroupIndex],?p,?Size*sizeof(DWORD));			//將之前的分組設(shè)備信息拷貝到新緩沖區(qū)中
		USER_DELTE(p);														//釋放掉之前的舊緩沖區(qū)

		return?true;
	}


后面我會擴展使用這個協(xié)議的下位機與服務(wù)端框架,并提供相應(yīng)的測試例子。

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: 驅(qū)動電源

在工業(yè)自動化蓬勃發(fā)展的當(dāng)下,工業(yè)電機作為核心動力設(shè)備,其驅(qū)動電源的性能直接關(guān)系到整個系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動勢抑制與過流保護是驅(qū)動電源設(shè)計中至關(guān)重要的兩個環(huán)節(jié),集成化方案的設(shè)計成為提升電機驅(qū)動性能的關(guān)鍵。

關(guān)鍵字: 工業(yè)電機 驅(qū)動電源

LED 驅(qū)動電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個照明設(shè)備的使用壽命。然而,在實際應(yīng)用中,LED 驅(qū)動電源易損壞的問題卻十分常見,不僅增加了維護成本,還影響了用戶體驗。要解決這一問題,需從設(shè)計、生...

關(guān)鍵字: 驅(qū)動電源 照明系統(tǒng) 散熱

根據(jù)LED驅(qū)動電源的公式,電感內(nèi)電流波動大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關(guān)鍵字: LED 設(shè)計 驅(qū)動電源

電動汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產(chǎn)業(yè)的重要發(fā)展方向。電動汽車的核心技術(shù)之一是電機驅(qū)動控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機驅(qū)動系統(tǒng)中的關(guān)鍵元件,其性能直接影響到電動汽車的動力性能和...

關(guān)鍵字: 電動汽車 新能源 驅(qū)動電源

在現(xiàn)代城市建設(shè)中,街道及停車場照明作為基礎(chǔ)設(shè)施的重要組成部分,其質(zhì)量和效率直接關(guān)系到城市的公共安全、居民生活質(zhì)量和能源利用效率。隨著科技的進步,高亮度白光發(fā)光二極管(LED)因其獨特的優(yōu)勢逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關(guān)鍵字: 發(fā)光二極管 驅(qū)動電源 LED

LED通用照明設(shè)計工程師會遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關(guān)鍵字: LED 驅(qū)動電源 功率因數(shù)校正

在LED照明技術(shù)日益普及的今天,LED驅(qū)動電源的電磁干擾(EMI)問題成為了一個不可忽視的挑戰(zhàn)。電磁干擾不僅會影響LED燈具的正常工作,還可能對周圍電子設(shè)備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來解決L...

關(guān)鍵字: LED照明技術(shù) 電磁干擾 驅(qū)動電源

開關(guān)電源具有效率高的特性,而且開關(guān)電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機重量也有所下降,所以,現(xiàn)在的LED驅(qū)動電源

關(guān)鍵字: LED 驅(qū)動電源 開關(guān)電源

LED驅(qū)動電源是把電源供應(yīng)轉(zhuǎn)換為特定的電壓電流以驅(qū)動LED發(fā)光的電壓轉(zhuǎn)換器,通常情況下:LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: LED 隧道燈 驅(qū)動電源
關(guān)閉