通信實(shí)戰(zhàn):MC協(xié)議與SLMP幀解析)
簡(jiǎn)介本資源面向工業(yè)自動(dòng)化與上位機(jī)開發(fā)人員提供一套基于C#與WinForm實(shí)現(xiàn)三菱FX5U PLC以太網(wǎng)通信的完整示例工程重點(diǎn)解決3E幀報(bào)文構(gòu)造、連接建立與數(shù)據(jù)讀寫等核心問(wèn)題適用于自動(dòng)化立體倉(cāng)庫(kù)等場(chǎng)景下的上位機(jī)開發(fā)學(xué)習(xí)。壓縮包共35個(gè)文件約79KB以cs源碼為主輔以config配置、resx與resources資源、csproj與sln工程文件及exe可執(zhí)行程序結(jié)構(gòu)完整可直接在Visual Studio中打開運(yùn)行。目前已有465人學(xué)習(xí)下載。讀者可從中獲取3E幀報(bào)文封裝、網(wǎng)絡(luò)接口篩選、指令收發(fā)與錯(cuò)誤重試等關(guān)鍵實(shí)現(xiàn)思路并參考WinForm界面與業(yè)務(wù)邏輯結(jié)合方式快速搭建可復(fù)用的PLC通信框架減少?gòu)牧阏{(diào)試通信協(xié)議的時(shí)間成本。1. 三菱FX5U上位機(jī)以太網(wǎng)通信從一根網(wǎng)線到穩(wěn)定讀寫車間里一臺(tái) FX5U 已經(jīng)跑了半年P(guān)LC 程序沒人動(dòng)但上位機(jī)那邊天天喊“讀不到數(shù)”。到現(xiàn)場(chǎng)一看網(wǎng)線插在 CPU 本體自帶的以太網(wǎng)口上上位機(jī)用 MC 協(xié)議按 Q 系列的地址格式去讀 D 區(qū)結(jié)果全是超時(shí)。把地址換成 FX5U 的軟元件編碼、端口確認(rèn)是 5000 之后數(shù)據(jù)立刻就上來(lái)了。這類問(wèn)題在 FX5U 上位機(jī)以太網(wǎng)通信里非常典型硬件沒問(wèn)題、網(wǎng)絡(luò)通、ping 也通卡在協(xié)議細(xì)節(jié)和地址映射上。這篇筆記講的就是三菱 FX5U 通過(guò)內(nèi)置以太網(wǎng)口和上位機(jī)做數(shù)據(jù)交互這件事。它解決的是“上位機(jī)怎么穩(wěn)定讀寫 FX5U 的 D/M/X/Y 軟元件”適合做設(shè)備數(shù)據(jù)采集、產(chǎn)線看板、C#/Qt 上位機(jī)開發(fā)、以及用 Python 做快速驗(yàn)證的工程師。下面按“協(xié)議怎么選 → 報(bào)文怎么拼 → 代碼怎么寫 → 坑在哪 → 怎么驗(yàn)證”的順序展開每一步都給到能直接抄的參數(shù)和命令。2. 先定協(xié)議再動(dòng)手MC 協(xié)議、SLMP 與 FX5U 的以太網(wǎng)口2.1 為什么 FX5U 上位機(jī)通信優(yōu)先選 MC 協(xié)議而不是 Modbus TCPFX5U 本體自帶以太網(wǎng)口支持兩種主流上位機(jī)通信方式一是三菱自家的 MC 協(xié)議也叫 SLMPSeamless Message Protocol二是通過(guò)擴(kuò)展模塊或內(nèi)置功能做 Modbus TCP。很多人一上來(lái)就想用 Modbus TCP理由是“通用、庫(kù)多”但在 FX5U 這個(gè)場(chǎng)景里MC 協(xié)議才是第一選擇。原因很直接。FX5U 的 D、M、X、Y、T、C 這些軟元件在 MC 協(xié)議里是原生尋址的你寫 D100 就是 D100不需要像 Modbus 那樣先查一張“軟元件到保持寄存器”的映射表。Modbus TCP 在 FX5U 上要么依賴擴(kuò)展模塊要么用內(nèi)置的 Modbus 功能做映射地址偏移和數(shù)據(jù)類型轉(zhuǎn)換會(huì)額外引入一層心智負(fù)擔(dān)。對(duì)于只做數(shù)據(jù)采集和少量寫入的上位機(jī)MC 協(xié)議報(bào)文結(jié)構(gòu)固定、響應(yīng)快、沒有映射歧義。代價(jià)是 MC 協(xié)議是三菱私有協(xié)議你得自己拼二進(jìn)制報(bào)文或者用現(xiàn)成的庫(kù)。常見做法是 C# 用 HslCommunication、Python 用 pymcprotocol 或自己按 SLMP 幀格式封裝。選型上我的建議是如果上位機(jī)只跟三菱設(shè)備打交道直接上 MC 協(xié)議如果現(xiàn)場(chǎng)還有西門子、匯川混在一起再考慮用 Modbus TCP 統(tǒng)一但 FX5U 這邊要接受映射帶來(lái)的額外配置。2.2 FX5U 以太網(wǎng)參數(shù)IP、端口和二進(jìn)制/ASCII 的選擇動(dòng)手前先把 FX5U 側(cè)的網(wǎng)絡(luò)參數(shù)確認(rèn)清楚。用 GX Works3 打開工程在“模塊參數(shù) → 以太網(wǎng)端口設(shè)置”里看這幾項(xiàng)參數(shù)典型值說(shuō)明IP 地址192.168.1.10與上位機(jī)同網(wǎng)段避免跨網(wǎng)段子網(wǎng)掩碼255.255.255.0常規(guī)默認(rèn)網(wǎng)關(guān)按現(xiàn)場(chǎng)同網(wǎng)段通信可不填通信數(shù)據(jù)代碼二進(jìn)制強(qiáng)烈建議報(bào)文更短端口號(hào)5000MC 協(xié)議常用可改但要一致打開方式MC 協(xié)議對(duì)應(yīng) SLMP這里有兩個(gè)容易翻車的點(diǎn)。第一是“通信數(shù)據(jù)代碼”如果設(shè)成 ASCII報(bào)文里所有數(shù)值都是 ASCII 字符長(zhǎng)度翻倍解析邏輯完全不同新手經(jīng)常拿二進(jìn)制示例去連 ASCII 端口怎么都對(duì)不上。第二是端口號(hào)FX5U 默認(rèn) MC 協(xié)議端口常見是 5000但有些工程被改成 5001 或別的值上位機(jī)不改就一直連不上。確認(rèn)完這些再確認(rèn)上位機(jī) IP 和 FX5U 在同一網(wǎng)段用ping 192.168.1.10先通一下。2.3 最小連通性驗(yàn)證先 ping 再 telnet 端口不要一上來(lái)就寫代碼。先用系統(tǒng)自帶工具確認(rèn)網(wǎng)絡(luò)層和傳輸層都通# 1. 確認(rèn)網(wǎng)絡(luò)可達(dá) ping 192.168.1.10 # 2. 確認(rèn) MC 協(xié)議端口開放Windows 可用 Test-NetConnection # PowerShell: Test-NetConnection 192.168.1.10 -Port 5000 # Linux/macOS: nc -vz 192.168.1.10 5000ping通只說(shuō)明 IP 層沒問(wèn)題不代表 5000 端口在監(jiān)聽。Test-NetConnection或nc返回成功才說(shuō)明 FX5U 的 MC 協(xié)議服務(wù)已經(jīng)起來(lái)。如果端口不通回到 GX Works3 檢查以太網(wǎng)端口設(shè)置是否下載進(jìn) PLC、PLC 是否處于 RUN 狀態(tài)、有沒有別的設(shè)備占用了這個(gè)端口。這一步花兩分鐘能省掉后面半小時(shí)的“代碼明明沒錯(cuò)”的自我懷疑。3. 把報(bào)文拼對(duì)SLMP 幀結(jié)構(gòu)、軟元件編碼與批量讀取3.1 SLMP 二進(jìn)制幀的組成從副頭部到結(jié)束代碼MC 協(xié)議SLMP的二進(jìn)制請(qǐng)求幀結(jié)構(gòu)是固定的以 3E 幀QnA 兼容 3E為例一次批量讀 D 區(qū)的請(qǐng)求大致長(zhǎng)這樣字段長(zhǎng)度典型值含義副頭部2 字節(jié)0x50 0x003E 幀固定網(wǎng)絡(luò)號(hào)1 字節(jié)0x00站內(nèi)PLC 號(hào)1 字節(jié)0xFF固定請(qǐng)求目標(biāo)模塊 IO2 字節(jié)0xFF 0x03上位機(jī)訪問(wèn)請(qǐng)求目標(biāo)模塊站號(hào)1 字節(jié)0x00站內(nèi)請(qǐng)求數(shù)據(jù)長(zhǎng)度2 字節(jié)變長(zhǎng)后續(xù)字節(jié)數(shù)監(jiān)視定時(shí)器2 字節(jié)0x0010單位 250ms指令2 字節(jié)0x0401批量讀子指令2 字節(jié)0x0000字單位首軟元件編號(hào)3 字節(jié)變長(zhǎng)小端軟元件代碼1 字節(jié)0xA8D 區(qū)軟元件點(diǎn)數(shù)2 字節(jié)變長(zhǎng)讀取個(gè)數(shù)響應(yīng)幀在請(qǐng)求基礎(chǔ)上把“指令/子指令”之后換成“結(jié)束代碼 數(shù)據(jù)”。結(jié)束代碼 0x0000 表示成功非零就是出錯(cuò)比如 0xC059 是軟元件指定錯(cuò)誤0xC056 是點(diǎn)數(shù)超限。理解這張表你就能看懂任何一份 SLMP 抓包也能在庫(kù)不好用的時(shí)候自己拼。3.2 FX5U 軟元件代碼對(duì)照D/M/X/Y 分別對(duì)應(yīng)哪個(gè)字節(jié)FX5U 的軟元件代碼和 Q 系列基本一致但地址范圍不同。常用對(duì)照如下軟元件代碼說(shuō)明讀寫D0xA8數(shù)據(jù)寄存器16 位讀寫M0x90內(nèi)部繼電器位讀寫X0x9C輸入位只讀Y0x9D輸出位讀寫T0xC2定時(shí)器當(dāng)前值讀C0xC5計(jì)數(shù)器當(dāng)前值讀位軟元件M/X/Y按位讀時(shí)子指令用 0x0001返回的是按位打包的數(shù)據(jù)按字讀時(shí)用 0x0000一次讀 16 位。新手最容易錯(cuò)的是把 X/Y 當(dāng)成字來(lái)讀結(jié)果數(shù)據(jù)錯(cuò)位。另一個(gè)坑是軟元件編號(hào)的字節(jié)序SLMP 里首軟元件編號(hào)是 3 字節(jié)小端比如 D100 要寫成0x64 0x00 0x00不是0x00 0x00 0x64。3.3 用 Python 跑通一次批量讀 D 區(qū)先用腳本驗(yàn)證協(xié)議比直接上 C# 工程快得多。裝pymcprotocolpip install pymcprotocol然后寫最小讀取腳本import pymcprotocol # 參數(shù)說(shuō)明 # type3e 對(duì)應(yīng) SLMP 3E 幀F(xiàn)X5U 內(nèi)置以太網(wǎng)口常用 # host/port 必須與 GX Works3 里的以太網(wǎng)設(shè)置一致 plc pymcprotocol.Type3E() plc.connect(192.168.1.10, 5000) # 批量讀 D100 開始的 10 個(gè)字 # 返回是一個(gè)列表每個(gè)元素是 0~65535 的整數(shù) values plc.batchread_wordunits(headdeviceD100, readsize10) print(values) # 批量讀 M0 開始的 16 個(gè)位 bits plc.batchread_bitunits(headdeviceM0, readsize16) print(bits) plc.close()邏輯說(shuō)明Type3E封裝的就是 3E 幀batchread_wordunits對(duì)應(yīng)子指令 0x0000batchread_bitunits對(duì)應(yīng) 0x0001。headdevice直接寫D100、M0這種軟元件名庫(kù)內(nèi)部會(huì)轉(zhuǎn)成軟元件代碼和 3 字節(jié)編號(hào)。參數(shù)上readsize不要超過(guò) FX5U 單次允許的點(diǎn)數(shù)字單位一般一次別超過(guò) 960 個(gè)位單位別超過(guò) 7168 個(gè)超了會(huì)返回 0xC056。如果連接報(bào)錯(cuò)先看 IP/端口再看type是不是選錯(cuò)有的老設(shè)備用 1E 幀。3.4 寫入 D 區(qū)和 M 點(diǎn)子指令與數(shù)據(jù)格式寫入和讀取結(jié)構(gòu)類似指令換成 0x1401批量寫。用同一個(gè)庫(kù)import pymcprotocol plc pymcprotocol.Type3E() plc.connect(192.168.1.10, 5000) # 寫 D200 開始的 3 個(gè)字值必須在 0~65535 plc.batchwrite_wordunits(headdeviceD200, values[1234, 5678, 90]) # 寫 M10 開始的 4 個(gè)位值用 0/1 plc.batchwrite_bitunits(headdeviceM10, values[1, 0, 1, 1]) plc.close()寫入時(shí)要注意數(shù)據(jù)范圍字單位是 16 位無(wú)符號(hào)寫負(fù)數(shù)要先轉(zhuǎn)成補(bǔ)碼比如 -1 寫成 65535。位單位只接受 0 和 1。如果寫入后 PLC 里沒變化先確認(rèn)這些軟元件沒有被 PLC 程序里的其他邏輯覆蓋尤其是 M 點(diǎn)經(jīng)常被程序當(dāng)中間變量反復(fù)刷新。D 區(qū)如果被用作系統(tǒng)區(qū)域或掉電保持區(qū)也要確認(rèn)寫入權(quán)限。4. 上位機(jī)工程化C# 與 Qt 的通信封裝和線程處理4.1 C# 上位機(jī)用 HslCommunication 連接 FX5UC# 上位機(jī)開發(fā)是熱詞里出現(xiàn)頻率很高的方向現(xiàn)場(chǎng)也確實(shí)是 C# 居多。用 HslCommunication 連 FX5U 的 MC 協(xié)議using HslCommunication; using HslCommunication.Profinet.Melsec; // 參數(shù)說(shuō)明 // ip/port 與 PLC 以太網(wǎng)設(shè)置一致 // 構(gòu)造函數(shù)最后一個(gè)參數(shù)指定數(shù)據(jù)格式二進(jìn)制用 DataFormat.Binary var plc new MelsecMcNet(192.168.1.10, 5000); plc.ConnectTimeOut 3000; // 連接超時(shí)毫秒 plc.ReceiveTimeOut 3000; // 接收超時(shí)毫秒 OperateResult connect plc.Connect(); if (!connect.IsSuccess) { Console.WriteLine(連接失敗: connect.Message); return; } // 讀 D100 開始 10 個(gè)字 OperateResultshort[] read plc.ReadInt16(D100, 10); if (read.IsSuccess) { foreach (var v in read.Content) Console.WriteLine(v); } // 寫 D200 plc.Write(D200, new short[] { 1234, 5678 }); plc.ConnectClose();邏輯說(shuō)明MelsecMcNet默認(rèn)走 3E 幀二進(jìn)制ReadInt16按有符號(hào) 16 位讀如果數(shù)據(jù)是無(wú)符號(hào)的用ReadUInt16。ConnectTimeOut和ReceiveTimeOut一定要設(shè)默認(rèn)值偏長(zhǎng)網(wǎng)絡(luò)抖動(dòng)時(shí)界面會(huì)卡死。參數(shù)上ReadInt16(D100, 10)的第二個(gè)參數(shù)是點(diǎn)數(shù)不是結(jié)束地址。寫入時(shí)Write會(huì)自動(dòng)判斷軟元件類型D 區(qū)按字寫M 區(qū)按位寫。4.2 Qt 上位機(jī)通信把 MC 協(xié)議讀寫放到獨(dú)立線程Qt 上位機(jī)通信的典型問(wèn)題是把 socket 讀寫放在 UI 線程網(wǎng)絡(luò)一慢界面就假死。正確做法是把通信封裝成一個(gè) QObjectmoveToThread 到工作線程// 通信類頭文件片段 class PlcWorker : public QObject { Q_OBJECT public slots: void doConnect(const QString ip, quint16 port); void doReadD(int start, int count); signals: void connected(); void dValuesReady(QVectorquint16 values); void errorOccurred(QString msg); private: QTcpSocket *socket nullptr; };// 連接與讀取實(shí)現(xiàn)片段 void PlcWorker::doConnect(const QString ip, quint16 port) { socket new QTcpSocket(this); connect(socket, QTcpSocket::connected, this, PlcWorker::connected); connect(socket, QTcpSocket::errorOccurred, this, [this](QAbstractSocket::SocketError){ emit errorOccurred(socket-errorString()); }); socket-connectToHost(ip, port); // 連接超時(shí)用 QTimer 單獨(dú)控制避免阻塞 } void PlcWorker::doReadD(int start, int count) { if (!socket || socket-state() ! QAbstractSocket::ConnectedState) { emit errorOccurred(未連接); return; } QByteArray req buildSlmpReadD(start, count); // 按 3.1 幀結(jié)構(gòu)拼 socket-write(req); socket-waitForReadyRead(3000); QByteArray resp socket-readAll(); QVectorquint16 vals parseSlmpWordResponse(resp); emit dValuesReady(vals); }邏輯說(shuō)明buildSlmpReadD和parseSlmpWordResponse需要自己按 SLMP 幀結(jié)構(gòu)實(shí)現(xiàn)或者引入第三方庫(kù)。關(guān)鍵點(diǎn)是 socket 的waitForReadyRead只在工作線程里調(diào)用UI 線程通過(guò)信號(hào)槽拿結(jié)果。參數(shù)上waitForReadyRead的超時(shí)要和 PLC 的監(jiān)視定時(shí)器匹配PLC 側(cè)設(shè) 4 秒0x0010 × 250ms上位機(jī)側(cè)給 3 秒比較穩(wěn)。如果現(xiàn)場(chǎng)網(wǎng)絡(luò)質(zhì)量差把超時(shí)放大到 5 秒同時(shí)加斷線重連。4.3 輪詢周期與批量策略別一個(gè)點(diǎn)一個(gè)請(qǐng)求上位機(jī)采集最常見的性能問(wèn)題是“一個(gè)變量發(fā)一次請(qǐng)求”。FX5U 的 MC 協(xié)議單次請(qǐng)求有開銷100 個(gè) D 點(diǎn)如果分 100 次讀周期會(huì)拉到幾百毫秒甚至更久。正確做法是按連續(xù)地址批量讀把要采集的 D 點(diǎn)按地址排序合并成連續(xù)區(qū)間比如 D100~D199 一次讀 100 個(gè)。不連續(xù)的區(qū)間分開請(qǐng)求但盡量控制在 3~5 個(gè)請(qǐng)求以內(nèi)。輪詢周期根據(jù)工藝定一般 100~500ms太快沒意義PLC 掃描周期本身也有延遲。位軟元件和字軟元件分開批量讀不要混在一個(gè)請(qǐng)求里。如果上位機(jī)還要顯示波形熱詞里有人問(wèn)“有沒有什么能給上位機(jī)顯示波形好用點(diǎn)的庫(kù)”C# 可以用 ScottPlot、Qt 可以用 QCustomPlot數(shù)據(jù)源就是批量讀回來(lái)的數(shù)組不要在繪圖庫(kù)里直接發(fā)通信請(qǐng)求。5. 避坑與排查FX5U 以太網(wǎng)通信最常見的 5 個(gè)翻車點(diǎn)5.1 現(xiàn)象ping 通但連接超時(shí)原因FX5U 的以太網(wǎng)端口設(shè)置沒下載進(jìn) PLC或者端口號(hào)不是 5000或者 PLC 處于 STOP 狀態(tài)。還有一種情況是上位機(jī)用了 1E 幀去連只支持 3E 的端口。解決GX Works3 里重新確認(rèn)“以太網(wǎng)端口設(shè)置”下載后斷電重啟一次用Test-NetConnection確認(rèn)端口把幀類型統(tǒng)一成 3E 二進(jìn)制。5.2 現(xiàn)象讀回來(lái)的數(shù)據(jù)全是 0 或明顯錯(cuò)位原因軟元件代碼寫錯(cuò)比如把 D 區(qū)代碼 0xA8 寫成 0xA9或者首軟元件編號(hào)字節(jié)序搞反或者位/字單位用錯(cuò)。解決對(duì)照 3.2 的軟元件代碼表逐項(xiàng)核對(duì)用抓包工具看請(qǐng)求幀的軟元件代碼和編號(hào)位軟元件確認(rèn)子指令是 0x0001。5.3 現(xiàn)象寫入成功但 PLC 里值馬上變回去原因該軟元件被 PLC 程序里的邏輯覆蓋M 點(diǎn)尤其常見或者寫的是掉電保持區(qū)PLC 上電時(shí)被初始值覆蓋。解決在 GX Works3 里監(jiān)控該軟元件看是不是被其他指令驅(qū)動(dòng)換一個(gè)沒被程序占用的 D 區(qū)做測(cè)試確認(rèn)掉電保持范圍。5.4 現(xiàn)象運(yùn)行一段時(shí)間后連接斷開重連才能恢復(fù)原因FX5U 的 MC 協(xié)議連接有超時(shí)機(jī)制上位機(jī)長(zhǎng)時(shí)間不發(fā)請(qǐng)求或請(qǐng)求間隔過(guò)大PLC 側(cè)會(huì)主動(dòng)斷開也可能是網(wǎng)絡(luò)交換機(jī)或網(wǎng)線質(zhì)量導(dǎo)致丟包。解決上位機(jī)加心跳每隔幾秒發(fā)一次讀請(qǐng)求加斷線重連邏輯檢測(cè)到 socket 斷開后自動(dòng)重連換屏蔽網(wǎng)線避開變頻器干擾。5.5 現(xiàn)象批量讀點(diǎn)數(shù)多時(shí)報(bào) 0xC056原因單次請(qǐng)求點(diǎn)數(shù)超過(guò) FX5U 允許上限字單位一般 960 點(diǎn)、位單位 7168 點(diǎn)。解決把大區(qū)間拆成多次請(qǐng)求每次控制在 500 點(diǎn)以內(nèi)更穩(wěn)或者優(yōu)化采集點(diǎn)表只讀需要的地址。6. 進(jìn)階驗(yàn)證用抓包和壓力測(cè)試確認(rèn)通信真的穩(wěn)協(xié)議調(diào)通只是第一步現(xiàn)場(chǎng)要的是“連續(xù)跑一周不出問(wèn)題”。我一般會(huì)做兩件事抓包和壓力測(cè)試。抓包用 Wireshark過(guò)濾tcp.port 5000看請(qǐng)求和響應(yīng)的時(shí)序。重點(diǎn)看三件事一是響應(yīng)時(shí)間是否穩(wěn)定正常應(yīng)該在幾毫秒到幾十毫秒二是結(jié)束代碼是否始終為 0x0000三是有沒有 TCP 重傳。如果看到大量重傳說(shuō)明網(wǎng)絡(luò)鏈路有問(wèn)題先換網(wǎng)線或交換機(jī)別急著改代碼。壓力測(cè)試寫一個(gè)循環(huán)腳本按實(shí)際輪詢周期連續(xù)讀寫 24 小時(shí)import time import pymcprotocol plc pymcprotocol.Type3E() plc.connect(192.168.1.10, 5000) fail 0 start time.time() while time.time() - start 86400: # 跑 24 小時(shí) try: vals plc.batchread_wordunits(headdeviceD100, readsize100) plc.batchwrite_wordunits(headdeviceD300, values[int(time.time()) % 65535]) except Exception as e: fail 1 print(異常:, e) try: plc.close() plc.connect(192.168.1.10, 5000) except Exception: pass time.sleep(0.2) # 200ms 輪詢 print(總失敗次數(shù):, fail) plc.close()參數(shù)說(shuō)明readsize100模擬實(shí)際采集規(guī)模sleep(0.2)對(duì)應(yīng) 200ms 輪詢周期fail統(tǒng)計(jì)異常次數(shù)。跑完之后看失敗次數(shù)如果是個(gè)位數(shù)且都能重連恢復(fù)說(shuō)明通信基本穩(wěn)定如果失敗頻繁回到第 5 章逐條排查。這個(gè)腳本我一般會(huì)在項(xiàng)目上線前跑一遍比在界面上點(diǎn)一天靠譜得多。最后說(shuō)個(gè)習(xí)慣每次改完 PLC 以太網(wǎng)參數(shù)我一定會(huì)在上位機(jī)側(cè)重新抓一次包確認(rèn)幀結(jié)構(gòu)和端口都對(duì)得上再繼續(xù)。血淚經(jīng)驗(yàn)是很多“代碼 bug”其實(shí)是 PLC 側(cè)參數(shù)被人改過(guò)。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取