打造Modbus TCP從站仿真器:從需求到實(shí)測(cè))
做工業(yè)自動(dòng)化開發(fā)Modbus這個(gè)協(xié)議你一定不陌生。PLC、儀表、傳感器、上位機(jī)組態(tài)軟件十幾年來基本靠它溝通。但協(xié)議熟悉歸熟悉真到聯(lián)調(diào)階段設(shè)備沒進(jìn)場(chǎng)、點(diǎn)位沒接好、想模擬一個(gè)設(shè)備掉線或者數(shù)據(jù)跳變的工況往往比寫業(yè)務(wù)代碼還折騰。這篇博文我就用AI原生開發(fā)的思路從零搭一個(gè)Modbus仿真器。簡(jiǎn)單說就是我把“要一個(gè)什么樣的仿真器”描述清楚讓AI承擔(dān)代碼生成的主體工作自己負(fù)責(zé)協(xié)議校驗(yàn)、邊界打磨和實(shí)測(cè)驗(yàn)證。整個(gè)過程里提示詞怎么設(shè)計(jì)、生成后的代碼怎么審查、哪些坑必須手動(dòng)填我都會(huì)拆開講。適合正在做上位機(jī)開發(fā)、協(xié)議測(cè)試或者想試著用AI驅(qū)動(dòng)完整小項(xiàng)目的工程師參考。1. 為什么選擇AI原生開發(fā)方式做這個(gè)仿真器1.1 工業(yè)調(diào)試中的真實(shí)痛點(diǎn)做過程控或物聯(lián)網(wǎng)項(xiàng)目的朋友大概率遇到過這幾類場(chǎng)景。第一類設(shè)備還沒有到貨但上位機(jī)界面、數(shù)據(jù)采集邏輯、告警規(guī)則都得提前開發(fā)和自測(cè)??偛荒茏屨麄€(gè)項(xiàng)目組干等硬件到位。第二類設(shè)備在現(xiàn)場(chǎng)但你不敢隨便拿真實(shí)設(shè)備做破壞性測(cè)試比如強(qiáng)制讓傳感器上報(bào)異常值、讓從站延遲響應(yīng)、或者測(cè)試主站在設(shè)備斷線時(shí)能不能正確告警。第三類網(wǎng)絡(luò)里同時(shí)掛著十幾個(gè)從站每個(gè)站有三四十個(gè)寄存器點(diǎn)位上位機(jī)要按站號(hào)輪詢而你手里根本沒有十幾臺(tái)真實(shí)設(shè)備去組網(wǎng)。這些場(chǎng)景其實(shí)都在指向同一個(gè)剛需你需要的不是一臺(tái)真實(shí)設(shè)備而是一個(gè)“可編程的、行為可控的、能模擬各種異常情況的假設(shè)備”。這就是Modbus仿真器最核心的用處。市面上有不少現(xiàn)成的仿真工具有的能模擬簡(jiǎn)單的寄存器讀寫有的為了兼容通用性把界面做得非常重。但真正到了項(xiàng)目里你要的可能只是“按我定義的寄存器表啟動(dòng)一個(gè)TCP服務(wù)并且能隨時(shí)把某個(gè)站切到離線狀態(tài)”。這種輕量、定制、隨拆隨用的工具反而很少有現(xiàn)成產(chǎn)品能完全滿足。與其浪費(fèi)時(shí)間在通用工具里配置不如自己動(dòng)手做一個(gè)符合自己業(yè)務(wù)習(xí)慣的仿真器。1.2 AI原生開發(fā)與傳統(tǒng)編碼的差異我理解的傳統(tǒng)開發(fā)模式鏈路大概是這樣的先查協(xié)議文檔確認(rèn)報(bào)文格式再設(shè)計(jì)一個(gè)框架然后逐段寫代碼實(shí)現(xiàn)功能最后寫測(cè)試用例不斷修正。整個(gè)過程里電腦只是一個(gè)編輯器核心的腦力勞動(dòng)全部由開發(fā)人員承擔(dān)。而AI原生開發(fā)最大的變化在于你可以用自然語言把需求講清楚讓AI先生成一個(gè)大半成品然后你來做架構(gòu)審查、協(xié)議核對(duì)、邊界補(bǔ)全和驗(yàn)證。這有點(diǎn)像你身邊有一位非常熟悉代碼但不太了解業(yè)務(wù)上下文的老工程師你把需求描述得越準(zhǔn)確他給你的初稿質(zhì)量就越高而你只需要負(fù)責(zé)把最關(guān)鍵的協(xié)議細(xì)節(jié)和項(xiàng)目約束注入進(jìn)去。這種開發(fā)方式的收益不是“少打了幾行字”這么簡(jiǎn)單而是把開發(fā)者的注意力從重復(fù)的輪子搭建轉(zhuǎn)移到更具價(jià)值的“需求定義”和“質(zhì)量驗(yàn)證”上來。仿真器這種項(xiàng)目特別適合AI原生開發(fā)因?yàn)樗鼌f(xié)議標(biāo)準(zhǔn)明確、邊界場(chǎng)景豐富、測(cè)試手段直觀AI生成的代碼能覆蓋大量路徑而人工需要盯住的反而是協(xié)議細(xì)節(jié)、資源管理和異常注入這些“臟活”。1.3 功能取舍與選型思路動(dòng)手前我先給自己定了個(gè)邊界避免做成一個(gè)不倫不類的大雜燴。第一版本只做Modbus TCP從站仿真不做RTU串口模式因?yàn)門CP調(diào)試起來最方便無論是本機(jī)回環(huán)還是局域網(wǎng)訪問都不用關(guān)心串口占用和波特率問題。第二版本只實(shí)現(xiàn)從站側(cè)行為也就是作為被讀取的一方響應(yīng)主站的報(bào)文請(qǐng)求。這樣能覆蓋絕大多數(shù)“驗(yàn)證上位機(jī)邏輯”的需求。第三版本要留出口子方便以后擴(kuò)展RTU模式和主站模式。功能碼方面我要求支持讀線圈、讀保持寄存器、寫單個(gè)線圈、寫單個(gè)寄存器、寫多個(gè)線圈、寫多個(gè)寄存器這幾個(gè)最常用的。寄存器地址范圍不做死通過配置動(dòng)態(tài)分配。異常響應(yīng)必須支持因?yàn)闇y(cè)試上位機(jī)對(duì)異常碼的處理往往是仿真器價(jià)值最大的部分。選型上我決定用Python理由后面詳細(xì)說。整個(gè)項(xiàng)目的核心原則就一句話先做一個(gè)能跑的、行為可控的TCP從站再逐步把邊界和異常場(chǎng)景補(bǔ)上。2. Modbus協(xié)議核心要點(diǎn)與仿真器設(shè)計(jì)拆解2.1 協(xié)議里最容易混淆的幾個(gè)點(diǎn)做仿真器之前協(xié)議細(xì)節(jié)必須摳干凈否則生成出來的代碼表面上能監(jiān)聽端口、返回?cái)?shù)據(jù)實(shí)際上全是錯(cuò)的。Modbus里最容易踩坑的有四件事。第一是字節(jié)序問題。Modbus寄存器是16位寬傳輸時(shí)默認(rèn)使用大端序也就是高字節(jié)在前低字節(jié)在后。假設(shè)你有一個(gè)寄存器值為0x1234上位機(jī)讀到的字節(jié)流必須是0x12 0x34反過來就是錯(cuò)的。這個(gè)細(xì)節(jié)不仔細(xì)核對(duì)很容易出現(xiàn)寄存器值“怎么讀都差好遠(yuǎn)”的問題。第二是CRC校驗(yàn)。串口RTU模式用CRC16校驗(yàn)雖然TCP模式本身有TCP校驗(yàn)和但很多Modbus TCP設(shè)備在報(bào)文層也保留了CRC字段部分工具和代碼庫(kù)也會(huì)做校驗(yàn)。正確計(jì)算CRC的底層邏輯是初始值為0xFFFF按位異或后右移遇到低比特位為1時(shí)與多項(xiàng)式0xA001異或這個(gè)算法沒有捷徑只能把函數(shù)寫對(duì)。第三是數(shù)量限制。讀取線圈或寄存器的單次數(shù)量不能超過0x7D125這是一個(gè)協(xié)議層面的硬性約束很多實(shí)現(xiàn)偷懶不做校驗(yàn)就直接返回?cái)?shù)據(jù)結(jié)果把主站搞蒙。第四是地址對(duì)齊。協(xié)議報(bào)文里的寄存器地址是從0開始計(jì)數(shù)的。比如你配置了一個(gè)保持寄存器地址范圍從0到99那么主站請(qǐng)求報(bào)文中要讀的地址就填0到99但很多PLC里的點(diǎn)位表習(xí)慣從1開始命名配置工具里常做加一減一的轉(zhuǎn)換。仿真器內(nèi)部必須明確以協(xié)議報(bào)文里的地址為真實(shí)下標(biāo)不要讓外部顯示習(xí)慣污染協(xié)議層邏輯。2.2 仿真器的三層內(nèi)部結(jié)構(gòu)我在設(shè)計(jì)這個(gè)仿真器時(shí)刻意把代碼分成三層讓AI生成的代碼也按照這個(gè)結(jié)構(gòu)去組織。最底層是數(shù)據(jù)層負(fù)責(zé)保存所有寄存器、線圈和離散輸入的狀態(tài)??梢院?jiǎn)單用字節(jié)數(shù)組或者Python的list來存儲(chǔ)每個(gè)地址映射一個(gè)值。為了讓仿真器靈活這一層最好做成可配置的比如通過JSON或YAML文件描述寄存器總數(shù)、初始值、每個(gè)寄存器周期變化規(guī)律。第二層是協(xié)議層職責(zé)是解析從socket接收到的原始字節(jié)流提取事務(wù)標(biāo)識(shí)符、協(xié)議標(biāo)識(shí)符、長(zhǎng)度、單元標(biāo)識(shí)符、功能碼和數(shù)據(jù)然后根據(jù)功能碼讀寫數(shù)據(jù)層最后組裝成Modbus TCP響應(yīng)報(bào)文。這一層是整個(gè)仿真器最核心的部分AI生成時(shí)最需要人工核對(duì)的就是這里。第三層是交互層提供啟動(dòng)參數(shù)解析、運(yùn)行時(shí)日志輸出、異常注入開關(guān)。它不參與協(xié)議處理只負(fù)責(zé)跟使用者打交道。三層的價(jià)值在于你后續(xù)無論怎么擴(kuò)展功能改動(dòng)都會(huì)被限制在某一層里。比如要新增一個(gè)功能碼你只需要?jiǎng)訁f(xié)議層要讓某個(gè)寄存器值按照正弦波自動(dòng)變化你只需要?jiǎng)訑?shù)據(jù)層。AI生成代碼時(shí)這種清晰的邊界也能讓生成結(jié)果不跑偏。2.3 為什么異常注入功能很重要普通仿真工具最讓人撓頭的地方就是它只會(huì)正常應(yīng)答不會(huì)給你使絆子??蓪?shí)際測(cè)試?yán)镒钣袃r(jià)值的往往就是那些“異常時(shí)刻”。主站軟件能不能在從站掉線時(shí)彈告警重試幾次才算合理寄存器地址越界時(shí)主站會(huì)不會(huì)正確處理異常碼這些場(chǎng)景需要在仿真階段就暴露出來而不是等到現(xiàn)場(chǎng)設(shè)備出問題才去排查。所以我的仿真器必須支持三類異常注入。第一類無響應(yīng)。也就是主站發(fā)來了請(qǐng)求但從站故意不回答讓主站產(chǎn)生超時(shí)重連的邏輯行為。第二類CRC或格式錯(cuò)誤。往正常響應(yīng)里插入錯(cuò)誤的校驗(yàn)碼或者在報(bào)文中加一段垃圾數(shù)據(jù)模擬鏈路被干擾。第三類合法異常碼。比如請(qǐng)求地址超出配置范圍時(shí)返回0x02非法數(shù)據(jù)地址請(qǐng)求功能碼不支持時(shí)返回0x01非法功能碼從站處理不過來時(shí)返回0x06從站忙。有了這幾個(gè)開關(guān)你的上位機(jī)健壯性測(cè)試就基本覆蓋到位了。3. 實(shí)操過程從提示詞到可運(yùn)行仿真器3.1 開發(fā)語言與運(yùn)行環(huán)境準(zhǔn)備語言我直接選Python理由很現(xiàn)實(shí)。第一第三方庫(kù)和腳本資源豐富AI對(duì)Python代碼生成的準(zhǔn)確率明顯高于其他語言。第二標(biāo)準(zhǔn)庫(kù)socket和struct就能完成Modbus TCP的收發(fā)和字節(jié)拆包不需要引入龐大的依賴。第三后期如果要加界面比如用tkinter或者Web方式Python也都有比較輕的方案。環(huán)境方面我建議使用Python 3.9以上版本。項(xiàng)目除了標(biāo)準(zhǔn)庫(kù)之外如果要做自動(dòng)化驗(yàn)證可以臨時(shí)安裝一個(gè)Modbus客戶端調(diào)試工具比如現(xiàn)成開源的ModbusPoll開源替代品或者直接用Python的pymodbus庫(kù)做回測(cè)。但項(xiàng)目本身的依賴約等于零這對(duì)一個(gè)仿真器來說非常理想。Windows和Linux都可以跑監(jiān)聽端口默認(rèn)用502這是Modbus的標(biāo)準(zhǔn)端口。不過Windows下直接在低端口監(jiān)聽需要管理員權(quán)限簡(jiǎn)單處理方式是允許運(yùn)行參數(shù)里改成用1502或10502這些非特權(quán)端口測(cè)試時(shí)在客戶端里填對(duì)應(yīng)端口就好。3.2 提示詞設(shè)計(jì)把需求講成AI聽得懂的結(jié)構(gòu)化描述AI原生開發(fā)里提示詞就是需求文檔。我發(fā)現(xiàn)一個(gè)很好用的套路把需求拆成“目標(biāo)輸入輸出約定行為細(xì)節(jié)測(cè)試要求”四個(gè)部分。我實(shí)際用的提示詞大概是這個(gè)思路請(qǐng)用Python實(shí)現(xiàn)一個(gè)Modbus TCP從站仿真器監(jiān)聽指定端口。要求 1. 支持功能碼0x01讀線圈、0x03讀保持寄存器、0x05寫單個(gè)線圈、 0x06寫單個(gè)寄存器、0x0F寫多個(gè)線圈、0x10寫多個(gè)寄存器。 2. 使用標(biāo)準(zhǔn)socket庫(kù)監(jiān)聽TCP默認(rèn)端口502。 3. 寄存器空間大小通過啟動(dòng)參數(shù)或者配置文件設(shè)置 初始值在模塊內(nèi)定義。 4. 請(qǐng)求報(bào)文按Modbus TCP格式解析 事務(wù)標(biāo)識(shí)符2字節(jié) 協(xié)議標(biāo)識(shí)符2字節(jié) 長(zhǎng)度2字節(jié) 單元標(biāo)識(shí)符1字節(jié) 功能碼1字節(jié) 數(shù)據(jù)區(qū)。 5. 響應(yīng)報(bào)文必須保持相同的事務(wù)標(biāo)識(shí)符和協(xié)議標(biāo)識(shí)符。 6. 注冊(cè)到日志模塊啟動(dòng)時(shí)輸出監(jiān)聽地址和端口信息。 7. 對(duì)不支持的功能碼返回異常響應(yīng)0x01。注意我沒有讓AI直接寫“全部實(shí)現(xiàn)”而是先讓它把框架和標(biāo)準(zhǔn)功能碼實(shí)現(xiàn)出來。這樣生成的代碼更容易審查也不會(huì)因?yàn)橐淮涡枨筇喽霈F(xiàn)邏輯混亂。如果項(xiàng)目里還要模擬異常注入我會(huì)在拿到第一版可運(yùn)行代碼后再追加需求而不是在第一輪就把異常響應(yīng)、日志等級(jí)、配置文件全塞進(jìn)去。AI原生開發(fā)里小步迭代比一次性完整描述更不容易失控。3.3 核心代碼生成與人工審查AI生成的代碼框架通常長(zhǎng)這樣主線程創(chuàng)建socketbind到端口循環(huán)accept來一個(gè)連接就開一個(gè)線程處理。從直觀性講這個(gè)結(jié)構(gòu)是標(biāo)準(zhǔn)的能跑。但作為開發(fā)人員我不能直接說“可以了”逐行審查是必須的。我建議重點(diǎn)盯三個(gè)位置。第一半包與粘包處理。TCP是一個(gè)字節(jié)流協(xié)議一次recv可能只收到上半截報(bào)文也可能一次性收到了好幾個(gè)請(qǐng)求粘在一起。如果代碼只做單次recv然后立刻解析那么高并發(fā)或者慢速網(wǎng)絡(luò)下必然出問題。我的做法是每個(gè)連接維護(hù)一個(gè)緩沖區(qū)不斷接收并嘗試按MBAP頭部里的長(zhǎng)度字段確定這一幀是不是已經(jīng)完整不完整就繼續(xù)等。第二監(jiān)聽地址。AI很自然地會(huì)用127.0.0.1但要作為仿真器給局域網(wǎng)里的上位機(jī)連接必須綁定0.0.0.0。這是一個(gè)必須在人工審查階段發(fā)現(xiàn)的問題。第三線程序號(hào)增長(zhǎng)。如果用threading處理的連接沒有做異常捕獲或者線程回收一個(gè)客戶端斷開時(shí)可能拋出異常導(dǎo)致整個(gè)服務(wù)崩潰。我通常在異常處理里把線程放在try/except里并記錄客戶端斷開日志而不是讓異常冒泡出來。核心代碼實(shí)現(xiàn)以讀保持寄存器0x03為例AI生成的解析邏輯大體會(huì)是這樣def handle_request(data): transaction_id data[0:2] protocol_id data[2:4] length int.from_bytes(data[4:6], big) unit_id data[6] func_code data[7] if func_code 0x03: start_addr int.from_bytes(data[8:10], big) quantity int.from_bytes(data[10:12], big) if quantity 125: return build_exception(transaction_id, unit_id, func_code, 0x03) if start_addr quantity len(holding_registers): return build_exception(transaction_id, unit_id, func_code, 0x02) values holding_registers[start_addr:start_addr quantity] response struct.pack(H, 1 quantity * 2) bytes([func_code, quantity * 2]) struct.pack( H * quantity, *values) return transaction_id protocol_id struct.pack(H, len(response) 1) bytes([unit_id]) response這里需要注意事務(wù)標(biāo)識(shí)符和協(xié)議標(biāo)識(shí)符必須原樣返回長(zhǎng)度字段計(jì)算的是“單元標(biāo)識(shí)符功能碼數(shù)據(jù)區(qū)”這一段的總長(zhǎng)度一個(gè)字節(jié)都不能多算少算。AI生成時(shí)經(jīng)常把長(zhǎng)度算錯(cuò)我的經(jīng)驗(yàn)是拿到代碼后自己手動(dòng)構(gòu)造幾個(gè)已知報(bào)文逐字節(jié)驗(yàn)證再放行。3.4 測(cè)出來才算數(shù)本地回環(huán)與客戶端回測(cè)代碼能啟動(dòng)之后我堅(jiān)決不只用“啟動(dòng)沒有報(bào)錯(cuò)”來驗(yàn)收。我會(huì)做兩輪測(cè)試。第一輪是構(gòu)造已知報(bào)文做單測(cè)。我直接構(gòu)造一個(gè)讀保持寄存器0x03的報(bào)文請(qǐng)求地址從0開始讀4個(gè)寄存器然后檢查響應(yīng)里的數(shù)據(jù)區(qū)是不是跟配置的初始值一致。比如初始值是[100, 200, 300, 400]那么大端序返回的字節(jié)就該對(duì)應(yīng)0x0064,0x00C8,0x012C,0x0190。第二輪是用現(xiàn)成的Modbus客戶端工具連接仿真器讀取真實(shí)點(diǎn)位寫入寄存器再讀回來。這一輪能驗(yàn)證跨語言、跨實(shí)現(xiàn)的兼容性很多自研仿真器就是栽在這一步——自己跟自己玩沒問題跟別人的客戶端一握手就露餡。如果條件允許把仿真器部署到Linux服務(wù)器上再用Windows上位機(jī)軟件走局域網(wǎng)連接這樣還能順帶驗(yàn)證網(wǎng)絡(luò)環(huán)境、防火墻、端口映射這些實(shí)戰(zhàn)場(chǎng)合的問題。仿真器的好處就在這它不是為了跑通一遍流程而是為了反復(fù)模擬各種上線后的惡劣情況讓問題在進(jìn)機(jī)房之前就暴露出來。4. 常見問題與排查技巧實(shí)錄4.1 協(xié)議實(shí)現(xiàn)上的坑我自己在做的過程中最大的坑是字節(jié)序和長(zhǎng)度字段。AI第一次生成的struct格式用的是小端結(jié)果寄存器值全部高低顛倒讀出來的數(shù)值跟配置完全對(duì)不上。排查方式很簡(jiǎn)單構(gòu)造一個(gè)已知的寄存器值用Wireshark抓包或者手工打印響應(yīng)報(bào)文看字節(jié)順序。第二個(gè)坑是長(zhǎng)度字段多算了一個(gè)字節(jié)因?yàn)橛行┦纠a把單元標(biāo)識(shí)符的長(zhǎng)度漏了導(dǎo)致客戶端一直報(bào)“報(bào)文格式錯(cuò)誤”。這類問題靠肉眼看代碼不容易發(fā)現(xiàn)靠抓包能看到到底多出來的一個(gè)字節(jié)在哪。第三個(gè)坑是邊界讀寄存器的時(shí)候起始地址合法但起始地址加數(shù)量之后的結(jié)束地址越界了。這個(gè)校驗(yàn)條件如果寫成大于等于而不是大于會(huì)把最后一個(gè)合法地址也攔掉導(dǎo)致主站讀取失敗。第四個(gè)坑是半包。之前在局域網(wǎng)上跑沒問題結(jié)果用弱網(wǎng)環(huán)境模擬主站讀取頻繁丟包排查下來是recv只收了一次就著急解析拆包不完整導(dǎo)致的。4.2 AI生成階段的協(xié)作問題用AI生成不等于當(dāng)甩手掌柜?!癆I生成的代碼看起來很有道理”往往是藏雷最多的地方。我遇到過AI聲稱調(diào)用了某個(gè)標(biāo)準(zhǔn)庫(kù)函數(shù)但實(shí)際上Python這個(gè)版本里根本沒有這個(gè)API一運(yùn)行就報(bào)AttributeError。也遇到過AI把協(xié)議層的邏輯寫成了從某個(gè)文件讀取配置的方式雖然也是按我的需求來但顯然跑偏了。還有一次它生成的異常響應(yīng)里功能碼忘了加0x80等于告訴主站“我正常處理了這條不支持的功能碼請(qǐng)求”這在協(xié)議層是完全錯(cuò)誤的行為。所以我給自己定了幾條AI協(xié)作的規(guī)矩。第一要求AI給出“關(guān)鍵算法”的測(cè)試用例比如CRC函數(shù)直接跑幾個(gè)已知結(jié)果校驗(yàn)。第二一次只改一個(gè)需求點(diǎn)不要讓AI同時(shí)處理新增功能碼和重構(gòu)線程模型。第三所有生成代碼必須拿著Modbus協(xié)議規(guī)范過一遍任何“看起來差不多”的函數(shù)都要單獨(dú)驗(yàn)證。AI是很好的初稿生成器但不是權(quán)威裁判。4.3 實(shí)用排查工具與驗(yàn)證技巧做這類協(xié)議仿真器光看代碼和日志還不夠我習(xí)慣搭配三個(gè)工具做多維度驗(yàn)證。第一是抓包工具直接看以太網(wǎng)幀里的原始字節(jié)流確認(rèn)事務(wù)標(biāo)識(shí)符、長(zhǎng)度、數(shù)據(jù)區(qū)都符合預(yù)期。第二是現(xiàn)成Modbus客戶端用一個(gè)與項(xiàng)目毫無關(guān)聯(lián)的第三方實(shí)現(xiàn)來連我的仿真器這種“獨(dú)立視角”的兼容性測(cè)試最有說服力。第三是自動(dòng)化腳本批量構(gòu)造正常請(qǐng)求、異常請(qǐng)求、越界請(qǐng)求和畸形報(bào)文驗(yàn)證仿真器在各種攻擊性輸入下不崩潰。自動(dòng)化腳本里我會(huì)跑一個(gè)循環(huán)連續(xù)寫入一個(gè)寄存器1000次再讀出來檢查數(shù)據(jù)是否一致排除并發(fā)中的狀態(tài)覆蓋問題。這些工具組合起來基本能覆蓋一個(gè)Modbus從站仿真器的全部測(cè)試維度。我個(gè)人最大的體感是測(cè)試比編寫功能本身更費(fèi)時(shí)間。仿真器本身邏輯不復(fù)雜難就難在你要讓它在真實(shí)網(wǎng)絡(luò)環(huán)境下面對(duì)各種不按套路出牌的請(qǐng)求還能穩(wěn)如泰山。5. 一點(diǎn)個(gè)人體會(huì)把整個(gè)仿真器從需求描述到可運(yùn)行這一輪做下來我的感受是AI原生開發(fā)真正改變的不是“寫代碼”這個(gè)動(dòng)作而是把人的精力逼到了更重要的地方——定義清楚邊界、審查協(xié)議細(xì)節(jié)、設(shè)計(jì)驗(yàn)證路徑。仿真器這種協(xié)議型工具恰好是AI生成代碼的高地因?yàn)镸odbus規(guī)范足夠明確網(wǎng)上示例足夠多AI輸出的框架能節(jié)省至少一半時(shí)間。但協(xié)議這東西錯(cuò)一個(gè)字節(jié)就是錯(cuò)算法再聰明也不如實(shí)際報(bào)文驗(yàn)證來得踏實(shí)。如果手頭沒有真實(shí)設(shè)備又想練一練Modbus調(diào)試和上位機(jī)開發(fā)按照這個(gè)思路做一個(gè)仿真器確實(shí)是個(gè)非常合適的方向。我現(xiàn)在還保留了那個(gè)自動(dòng)化測(cè)試腳本后續(xù)再新增功能碼或者調(diào)整異常注入邏輯都用它回歸一遍。下一步我準(zhǔn)備給它加一個(gè)簡(jiǎn)單的Web控制面板把寄存器讀寫、異常注入開關(guān)都做成網(wǎng)頁(yè)上的按鈕這樣現(xiàn)場(chǎng)工程師不用碰命令行也能靈活控制仿真流程。AI原生的開發(fā)節(jié)奏很適合這種持續(xù)增補(bǔ)的小工具每補(bǔ)一個(gè)功能請(qǐng)AI接手初稿我再做協(xié)議層的把關(guān)效率和可控性基本達(dá)到了平衡。