:HslCommunication與.NET 4.5兼容實戰(zhàn))
簡介HslCommunication是一款基于.NET Framework 4.5的工業(yè)通信庫組件版本11.3.2面向需要對接PLC、設(shè)備數(shù)據(jù)交互的.NET開發(fā)者或自動化項目集成人員。該封裝包已預(yù)先處理授權(quán)信息使用者無需再調(diào)用Authorization.SetAuthorizationCode激活碼即可直接引用并且不會出現(xiàn)24小時后過期的問題適合快速搭建原型或在正式項目中嵌入通信層。壓縮包共10個文件以HslCommunication.dll為核心包含XML注釋文檔便于IDE提示與查閱API另有HslControls.dll界面控件庫、兩個演示用exe、一個備份dll以及版本忽略說明txt等整體約5.69MB文件分工清晰按需解壓提取對應(yīng)dll即可。該資源已有3716人學(xué)習(xí)下載解壓后可直接將HslCommunication.dll加入工程借助演示程序快速理解連接與讀寫調(diào)用方式同時提供舊版本備份與升級工具可輔助開發(fā)者應(yīng)對版本遷移或調(diào)試排查省去自行處理授權(quán)和找包的步驟。 做上位機的人誰沒被老工控機“教育”過。前陣子我接手了一個老車間數(shù)采改造項目控制室里那臺Windows 7工控機已經(jīng)服役快十年底下掛了三套不同品牌的PLC還有幾塊串口儀表??蛻粢蠛苊鞔_不動系統(tǒng)不換機器把數(shù)據(jù)全部采上來。這種場景下新框架再香也上不了現(xiàn)場我最后把技術(shù)棧壓回了HslCommunication 11.3.2加.NET 4.5的組合。這套組合看著舊但恰恰是存量工業(yè)現(xiàn)場里最不容易翻車的方案。文章不會講太虛的東西就是我這段時間的真實排查記錄、Demo寫法和現(xiàn)場調(diào)試經(jīng)驗給同樣被老環(huán)境綁住手腳的朋友做個參考。1. 拒絕.NET 6之后為什么還是選了11.3.2先說結(jié)論HslCommunication不是那種“新潮”的庫但它在工業(yè)上位機圈子里的地位很穩(wěn)。它解決的是最讓人頭疼的“多品牌設(shè)備互聯(lián)”問題西門子、三菱、歐姆龍、基恩士、Modbus TCP/RTU、AB、松下、臺達還有各種儀表、機器人的通信協(xié)議基本上都能在一個框架里搞定。對于做數(shù)采或者設(shè)備管理系統(tǒng)的團隊來說這意味著一套代碼模式可以通吃現(xiàn)場大部分設(shè)備不用為每臺PLC寫一套獨立的socket解析。版本為什么是11.3.2因為很多存量項目、老工控機上跑的上位機就是基于這個版本開發(fā)的。它對應(yīng)的.NET 4.5目標(biāo)框架對Windows 7 SP1系統(tǒng)兼容性很好網(wǎng)上相關(guān)的demo、報錯案例、二次封裝代碼也最多。真要遇到問題搜索一下基本都能找到答案這在工業(yè)現(xiàn)場調(diào)試時是非常重要的隱性成本。順帶說下.NET 4.5和.NET Framework 4.x運行時的關(guān)系。.NET 4.5以后4.5、4.6、4.7、4.8都共用同一個CLR 4.x.NET 4.5編譯出來的程序集在高版本.NET Framework運行時下是能直接跑的。這就意味著哪怕工控機上最高只裝到.NET 4.6.2也完全不影響你基于.NET 4.5目標(biāo)框架編譯的HslCommunication程序運行。真正限制你的反而是開發(fā)機上的Visual Studio和項目目標(biāo)框架配置。我見過不少團隊一上來就想用.NET 6/8重寫老項目結(jié)果到現(xiàn)場發(fā)現(xiàn)客戶機器的C運行庫、數(shù)據(jù)庫驅(qū)動、加密狗、老式采集卡驅(qū)動全都是十幾年前的重裝系統(tǒng)的代價遠(yuǎn)大于軟件升級的收益。在這種環(huán)境里把目標(biāo)框架定在.NET 4.5不是技術(shù)落后是工程上的必要妥協(xié)。2. Win7裝.NET 4.5失敗的完整排查過程這次項目最折騰我的不是HslCommunication本身而是給一臺干凈的Windows 7工控機裝.NET 4.5。如果只在開發(fā)機上編譯那你裝VS時勾選“.NET Framework 4.5開發(fā)工具”就行但現(xiàn)場部署必須讓目標(biāo)機器具備對應(yīng)的運行時環(huán)境。2.1 癥狀離線安裝包也彈“安裝未成功”我拿著離線安裝包NDP452-KB2901907-x86-x64-allos.exe去現(xiàn)場管理員身份雙擊等了半天彈出“安裝未成功”錯誤碼0x80070643。網(wǎng)上對這個錯的說法很雜有說系統(tǒng)分區(qū)空間不足的有說Windows更新服務(wù)出問題的還有說殺毒軟件攔截的光看錯誤碼很難定位。關(guān)鍵一步是去看安裝日志。.NET Framework安裝器會在C:\Users\用戶名\AppData\Local\Temp下生成DD_NDP452_*.htm文件打開后找到錯誤發(fā)生前最后幾條日志通常能看到是“CBS”相關(guān)還是“Windows Update”相關(guān)。我這個案例日志里反復(fù)出現(xiàn)CBS_E_NOT_APPLICABLE基本判斷是系統(tǒng)缺少某些前置更新導(dǎo)致.NET 4.5安裝器認(rèn)為當(dāng)前系統(tǒng)狀態(tài)不滿足條件。2.2 根因Windows 7 SP1的系統(tǒng)更新底子沒打牢.NET 4.5在Windows 7上最穩(wěn)妥的安裝前提是先打好SP1補丁再確保關(guān)鍵更新在系統(tǒng)里。很多Ghost版Win7或者精簡版Win7會砍掉部分更新組件裝.NET自然會失敗。我當(dāng)時的處理思路是先把Windows Update能打的補丁全打上特別關(guān)注KB2966826和KB2966827這類和.NET Framework安裝直接相關(guān)的前置更新。如果Windows Update本身一直檢查不到補丁可以手動下載這些KB補丁安裝。如果系統(tǒng)里已經(jīng)殘留了安裝一半的.NET組件建議先運行微軟官方的“.NET Framework修復(fù)工具”把半成品清掉再重試。修復(fù)工具會檢測所有.NET版本的注冊表項和文件狀態(tài)對于清理損壞安裝非常有效。2.3 最終能落地的安裝順序我整理一下這次現(xiàn)場驗證過的一套安裝步驟照著走能避開絕大部分坑確認(rèn)系統(tǒng)是Windows 7 SP1不是RTM版。右鍵“計算機”查看版本沒有SP1就先裝SP1補丁重啟。關(guān)閉殺毒軟件和Windows Defender實時防護部分殺軟會把安裝器生成的臨時DLL當(dāng)風(fēng)險文件隔離導(dǎo)致安裝回滾。把離線安裝包復(fù)制到純英文路徑比如C:\dotnet45\不要放桌面或中文目錄下。管理員身份打開CMD運行net stop wuauserv停止Windows Update服務(wù)再把C:\Windows\SoftwareDistribution和C:\Windows\System32\catroot2臨時改名備份。這兩個文件夾緩存著舊的更新數(shù)據(jù)有損壞時會影響新組件注冊。從CMD里直接執(zhí)行NDP452-KB2901907-x86-x64-allos.exe /quiet /norestart安靜模式安裝比雙擊安裝器更少受UAC和交互彈窗干擾。等到CMD返回后查看C:\Windows\Logs\NetFramework\下的安裝日志確認(rèn)沒有error級別記錄。注冊表驗證reg query HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full /v Release如果返回的Release值為379893或以上說明4.5以上運行時已經(jīng)就位。這套順序里最容易被忽略的是第4步。整個SoftwareDistribution目錄如果存在權(quán)限錯亂很多基于Windows Update通道的組件安裝都會莫名其妙失敗不光是.NET Framework后續(xù)裝別的補丁也可能踩同樣的坑。3. 第一個Demo程序用11.3.2讀西門子S7-1500的DB塊環(huán)境準(zhǔn)備好之后開發(fā)這邊就順暢多了。我用的是Visual Studio 2019建的項目是.NET Framework 4.5的WinForms程序通過NuGet引用HslCommunication 11.3.2。如果項目處在離線環(huán)境也可以直接在NuGet緩存目錄里找到對應(yīng)的HslCommunication.dll手動添加到引用效果一樣。3.1 最小可運行代碼讀西門子S7-1500的DB1.DBD0也就是DB塊第0個偏移的32位浮點數(shù)代碼可以短到這樣using System; using HslCommunication; using HslCommunication.Profinet.Siemens; class Program { static void Main(string[] args) { // 1. 創(chuàng)建西門子S7客戶端指定PLC類型和IP SiemensS7Net s7 new SiemensS7Net(SiemensPLCS.S1500, 192.168.0.10); // 2. 如果PLC有多個網(wǎng)卡或需要指定機架號可以在這里設(shè)置 s7.SetSlot(0); // 3. 顯式建立連接便于第一時間發(fā)現(xiàn)網(wǎng)絡(luò)問題 OperateResult connect s7.ConnectServer(); if (!connect.IsSuccess) { Console.WriteLine(連接失敗 connect.Message); return; } // 4. 讀取DB1.DBD0的浮點數(shù)地址寫成 DB1.0 OperateResultfloat result s7.ReadFloat(DB1.0); if (result.IsSuccess) { Console.WriteLine(DB1.DBD0 當(dāng)前值 result.Content); } else { Console.WriteLine(讀取失敗 result.Message); } // 5. 寫入示例把DB1.DBD0寫為80 OperateResult writeResult s7.Write(DB1.0, 80.0f); if (writeResult.IsSuccess) { Console.WriteLine(寫入成功); } // 6. 關(guān)閉連接 s7.ConnectClose(); Console.ReadKey(); } }這段代碼基本體現(xiàn)了HslCommunication的核心使用模式實例化客戶端對象調(diào)用ConnectServer建連用Read/Write系列方法操作數(shù)據(jù)最后關(guān)閉連接。返回值都是OperateResult類型先判斷IsSuccess再看Content這個習(xí)慣我會要求團隊所有人都遵守。因為工業(yè)通信里失敗是常態(tài)不判斷返回值直接往下走遲早會把異常數(shù)據(jù)送進數(shù)據(jù)庫。3.2 關(guān)于連接方式的兩個細(xì)節(jié)第一SiemensS7Net支持隱式連接也就是不調(diào)用ConnectServer直接Read庫內(nèi)部檢測到未連接時會自動建立連接。但在現(xiàn)場調(diào)試階段我強烈建議顯式ConnectServer這樣能更早暴露網(wǎng)絡(luò)不通、IP填錯這些基礎(chǔ)問題。第二很多人不知道SetSlot和SetRack的含義。S7-300/400這類老PLC通常需要考慮機架號和槽號比如CPU在0號機架2號槽就要SetRack(0); SetSlot(2);。而S7-1200/1500走的是優(yōu)化后的訪問機制默認(rèn)0號槽在大多數(shù)場景下能直接工作但如果你發(fā)現(xiàn)連接建立后讀不到數(shù)據(jù)嘗試把Slot改成0之外的值再測一遍。3.3 WinForms程序和線程WinForms里用HslCommunication的坑主要在UI線程。通信回調(diào)或異步方法返回后如果直接操作Label、TextBox會拋出跨線程訪問異常。通常的處理是把數(shù)據(jù)放到一個公共變量或緩存類里再用Timer定時刷新UI或者用Control.BeginInvoke回到UI線程。我個人更喜歡Timer輪詢方案簡單、直觀也方便控制采集頻率。4. 多協(xié)議實戰(zhàn)三菱、歐姆龍、Modbus TCP的編程模型對比項目里不是只有西門子一家設(shè)備。那套老車間里還有一臺三菱Q系列PLC、一個歐姆龍溫控表以及一臺走Modbus TCP協(xié)議的第三方儀表。HslCommunication對它們都有對應(yīng)封裝而且編程模型高度一致這是這個庫在項目化開發(fā)里最有價值的地方。4.1 協(xié)議接入速查表設(shè)備品牌/協(xié)議命名空間典型客戶端類地址示例西門子S7HslCommunication.Profinet.SiemensSiemensS7NetM100, DB1.0, I0.0三菱MC協(xié)議HslCommunication.Profinet.MelsecMcTcpClientD100, M0, X0歐姆龍Fins TCPHslCommunication.Profinet.OmronOmronFinsTcpD100, CIO0, W0Modbus TCPHslCommunication.ModBusModbusTcpClient100, 4x100, 0核心用法上三菱的讀浮點、寫寄存器和西門子幾乎一模一樣只是地址字符串不同。比如三菱Q系列讀D100這個32位浮點數(shù)using HslCommunication.Profinet.Melsec; McTcpClient melsec new McTcpClient(192.168.0.20); OperateResult connect melsec.ConnectServer(); OperateResultfloat val melsec.ReadFloat(D100).Content;Modbus TCP稍微有一點區(qū)別它更偏寄存器地址模型。比如讀儀表保持寄存器40001對應(yīng)的實際地址是0using HslCommunication.ModBus; ModbusTcpClient modbus new ModbusTcpClient(192.168.0.30, 502); OperateResultshort val modbus.ReadInt16(0);如果地址里帶功能碼前綴比如4x0表示讀保持寄存器區(qū)的0號地址庫內(nèi)部會自動換算成Modbus協(xié)議里的功能碼03。這個我在現(xiàn)場交接時經(jīng)常要和電氣工程師確認(rèn)因為不同儀表的寄存器表文檔寫法差異很大。4.2 連接管理和線程安全HslCommunication的多數(shù)客戶端類是線程安全的多線程調(diào)用讀寫方法不會導(dǎo)致協(xié)議錯亂。但線程安全不意味著你可以毫無節(jié)制地并發(fā)請求底層網(wǎng)卡和PLC的響應(yīng)能力是有上限的。我的做法是一個PLC對應(yīng)一個客戶端實例全局共享不讓每個業(yè)務(wù)線程各自new一個連接。這樣既避免頻繁建連斷開也能讓HslCommunication內(nèi)部的連接管理機制發(fā)揮作用。高負(fù)載場景下如果多個線程同時對同一臺PLC寫數(shù)據(jù)可能因為設(shè)備響應(yīng)慢而堆積請求。這種情況下我會引入一個信號量或者隊列把寫請求串行化。實測下來S7-1500和Q系列對單連接連續(xù)請求的吞吐能力足以應(yīng)對車間級數(shù)采瓶頸往往在PLC側(cè)的循環(huán)掃描時間和網(wǎng)絡(luò)質(zhì)量上而不是庫本身的性能。4.3 三分鐘超時與自動重連工業(yè)現(xiàn)場最怕的不是設(shè)備掉線而是掉線后程序進入假死狀態(tài)。HslCommunication的ConnectTimeOut屬性控制連接超時默認(rèn)值是1000毫秒我建議調(diào)大一點比如3000到5000毫秒。對于現(xiàn)場偶發(fā)的網(wǎng)絡(luò)抖動稍微容忍一下比頻繁報錯更符合實際需要。自動重連不能全指望庫本身。HslCommunication在連接斷開后下一次讀寫操作會觸發(fā)重連機制但如果你用了長輪詢方式定時讀取最好在讀取失敗后增加一個延遲重試策略// 偽代碼示意實際請根據(jù)業(yè)務(wù)封裝 if (!readResult.IsSuccess) { // 連續(xù)失敗后先釋放連接避免底層socket半開 plc.ConnectClose(); Thread.Sleep(2000); plc.ConnectServer(); }這里有個現(xiàn)場經(jīng)驗連續(xù)失敗后先把連接顯式關(guān)閉再重連比直接依賴內(nèi)部重連更可靠。半開連接狀態(tài)下直接重連容易一直卡在舊socket上關(guān)掉再建反而干凈利落。5. 從舊版本遷到11.3.2的API差異編譯報錯別慌如果項目之前用的是更老的版本比如8.x或9.x直接替換成11.3.2后大概率會編譯報錯。這不一定是用法寫錯了而是HslCommunication在版本迭代中對方法命名和命名空間做了不少“整理性重構(gòu)”。5.1 浮點數(shù)讀取方式的變化早期版本里讀取浮點數(shù)時對字節(jié)序的處理比較混亂同一個方法在不同設(shè)備上可能得到不同結(jié)果。11.x開始讀浮點拆成了明確大小端的方法比如ReadFloat和ReadFloatL。如果你發(fā)現(xiàn)讀上來的數(shù)值是個天文數(shù)字或者和實際值差了好幾個數(shù)量級第一時間檢查當(dāng)前設(shè)備字節(jié)序和你調(diào)用的方法是否匹配。西門子S7默認(rèn)是高字節(jié)在前Modbus TCP的32位浮點通常是AB CD這個細(xì)節(jié)直接決定數(shù)據(jù)對不對。5.2 命名空間和類名調(diào)整老的HslCommunication.Core里很多擴展方法被重新歸類一些設(shè)備類也從原來的命名空間挪到了更細(xì)分的Profinet目錄下。遇到編譯錯誤時不要逐個類去猜直接把報錯內(nèi)容復(fù)制到搜索引擎里查通常都能找到官方文檔或社區(qū)帖子說明該類遷到了哪里。另外一個版本遷移的技巧不要一上來就全局替換DLL而是先建一個獨立分支讓編譯器把所有報錯列出來然后按“類找不到、方法不存在、返回值類型變化”三類問題分批處理。返回值類型變化是最隱蔽的比如某些方法從直接返回值變成了返回OperateResultT這種改動不報錯但業(yè)務(wù)代碼運行時會拿到不對的對象。所以升級后必須跑一輪完整的單元測試或者儀表對比測試不能只看編譯通過。5.3 開發(fā)工具鏈的兼容如果你用的是VS2022開發(fā).NET 4.5項目需要在安裝器里勾選“.NET Framework 4.5開發(fā)工具”組件否則項目模板和編譯目標(biāo)里看不到4.5。VS2022本身能正常編譯.NET 4.5項目不用因為目標(biāo)框架老而專門安裝VS2015。NuGet還原時HslCommunication 11.3.2會按照項目目標(biāo)框架自動匹配兼容的程序集版本這方面基本透明不用手動處理。6. 現(xiàn)場設(shè)備連不上的三板斧排查順序最后分享一下我在現(xiàn)場調(diào)試設(shè)備通信時固定使用的排查順序。不管用HslCommunication還是別的庫這套方法都能幫你少走很多彎路。第一板斧先ping設(shè)備IP。ping不通就不用往下查了查網(wǎng)線、交換機、IP配置。有些設(shè)備支持ping有些不支持但絕大多數(shù)PLC的以太網(wǎng)口是能ping通的。第二板斧看設(shè)備側(cè)的通信開關(guān)。西門子S7-1200/1500默認(rèn)不開放PUT/GET通信需要在PLC程序里組態(tài)“允許來自遠(yuǎn)程對象的通信”很多第一次調(diào)試西門子的朋友把代碼寫對了也連不上原因就在這。三菱Q系列要確認(rèn)MC協(xié)議在以太網(wǎng)模塊里啟用了TCP端口歐姆龍要確認(rèn)FINS節(jié)點號設(shè)置。這些設(shè)備側(cè)參數(shù)不核對上位機代碼寫得再完美也白搭。第三板斧開一個簡單的協(xié)議測試工具不跑業(yè)務(wù)代碼只做單次讀取。HslCommunication自帶的Demo工具就能干這事填入IP、端口、PLC類型、地址點一下“讀取”能讀到值再回過來檢查自己的代碼。多數(shù)情況下現(xiàn)場問題都是網(wǎng)絡(luò)或設(shè)備配置問題而不是庫本身的問題。作為收尾再分享一個實用習(xí)慣生產(chǎn)環(huán)境上位機一定要有日志記錄每次讀寫操作、連接狀態(tài)變化、錯誤信息都要落盤。HslCommunication的OperateResult里帶著錯誤消息配合日志組件記錄下來現(xiàn)場出問題時能大幅縮短排查時間。我自己就吃過虧沒有日志的上位機一出問題就像黑箱別人根本沒法判斷是網(wǎng)絡(luò)斷了還是PLC程序被人改了。加日志這個動作比優(yōu)化通信性能重要十倍。本文還有配套的精品資源點擊獲取