IEC60870-5-104通信規約 | 報文解析 | 組織報文與解析報文(C++)

文章目錄

  • 一、IEC60870-5-104通信規約
    • 1.IEC104的報文結構
    • 2.IEC104的報文格式--I/U/S格式
      • 2.1 I幀
      • 2.2 U幀
      • 2.3 S幀
    • 3.應用服務數據單元ASDU
  • 二、IEC60870-5-104規約通信過程報文幀解析
  • 三、組織報文與解析報文(C++)

一、IEC60870-5-104通信規約

??IEC60870-5-104規約,簡稱IEC104,IEC104規約由國際電工委員會制定。IEC104規約把IEC101的應用服務數據單元(ASDU)用網絡規約TCP/IP進行傳輸的標準,該標準為遠動信息的網絡傳輸提供了通信規約依據。采用104規約組合101規約的ASDU的方式后,可很好的保證規約的標準化和通信的可靠性。

TCP端口號為2404,站端為Server 控端為Client,平衡式傳輸

??IEC 104規約是一種電力自動化通信協議,通常采用的是平衡傳輸模式。在平衡傳輸模式下,所有站都可以啟動報文傳輸,即主站和從站都可以獨立地發起通信會話,進行數據的發送和接收。

平衡模式傳輸與非平衡模式傳輸

  • 平衡模式傳輸

    平衡方式傳輸是一種通信傳輸方式,它允許通信雙方在沒有規定誰先發起通信的情況下進行會話。在這種傳輸方式中,通信的兩個實體都具備發起和接收信息的能力,從而實現雙向通信。這種傳輸方式可以提高通信的靈活性和效率,因為它不依賴于固定的通信發起方。

  • 非平衡模式傳輸

    在非平衡傳輸模式下,主站可以順序地召喚各被控站,而被控站只在被查詢時才傳輸數據。這種模式適用于點對點、星形、多點等多種配置

??IEC 60870-5-104 標準中的平衡模式傳輸過程允許通信雙方在沒有規定誰先發起通信的情況下進行會話,即主站和從站都可以獨立地發起通信會話。以下是平衡模式傳輸過程的一般步驟

  1. 建立連接:通信雙方建立一個穩定的通信連接。在 TCP/IP 環境中,這涉及到建立一個 TCP 連接。
  2. 初始化:在開始數據交換之前,雙方可能需要進行初始化過程,以確保它們都處于正確的工作狀態。
  3. 數據傳輸:在平衡模式下,主站和從站都可以獨立地發送數據。數據可以是請求、命令、狀態更新或其他類型的信息。
  4. 確認和響應:接收方收到數據后,會發送確認信息或相應的響應。這確保了數據的可靠傳輸。
  5. 事件觸發:在平衡模式下,任何站都可以獨立地觸發事件,例如,當檢測到特定條件或異常時,從站可以主動向主站發送事件通知。
  6. 周期性更新:被控站可以周期性地向主站發送過程變量的最新值,以保持數據的時效性。
  7. 總召喚:控制站可以使用總召喚命令請求從站發送所有過程變量的實際值,以刷新控制站的數據。
  8. 時鐘同步:為了確保時間一致性,控制站可以發送時鐘同步命令給被控站,以同步它們的時鐘。
  9. 錯誤處理:如果在傳輸過程中出現錯誤,比如數據包丟失或損壞,雙方都可以采取措施進行重傳或錯誤糾正。
  10. 通信結束:當通信雙方完成數據交換后,可以結束通信會話。這通常涉及到關閉 TCP 連接。

1.IEC104的報文結構

??IEC 60870-5-104 協議的報文結構為應用規約數據單元(APDU)。主要包括兩個部分,分別是應用規約控制信息(APCI)、應用服務數據單元(ASDU)。
在這里插入圖片描述

  • APCI應用規約控制信息:它是所有發送、接收的報文頭并且可以單獨發送的。

  • ASDU 應用服務數據單元:

    • ASDU 是報文的有效載荷部分,包含了實際傳輸的數據,如遙信、遙測、遙控等信息。
    • ASDU 由類型標識、可變結構限定詞、傳送原因、ASDU 公共地址、信息對象地址等組成。
    • ASDU 的結構和內容取決于傳輸的數據類型和上下文。
  • APDU 是由 APCI 和 ASDU 組成的完整報文,是 IEC 60870-5-104 協議中傳輸的基本單元。

    APDU 應用規約數據單元(整個數據) = APCI 應用規約控制信息(固定6個字節) + ASDU 應用服務數據單元(長度可變)

  • APDU的長度域定義了APDU體的長度,它包括APCI的四個控制域八位位組和ASDU(ASDU長度加4)。APDU應用規約數據單元長度最大253(255減去啟動符與本身)

  • 控制域定義了確保報文不丟失和重復傳送的控制信息(也就是發送序列號和接收序列號),報文傳輸啟動/停止,以及傳輸連接的監視等。

2.IEC104的報文格式–I/U/S格式

??控制域對應不同類型的格式(I幀、U幀、S幀),意義和格式都不相同。
在這里插入圖片描述

2.1 I幀

I格式(信息傳輸格式類型—Information transmit format):

??I格式報文用于傳遞信息,包含了應用服務數據單元(ASDU)。==I幀為信息幀,用于傳輸具體的通信數據。==遙信、遙測、遙控、遙調、總召、對時等都需要使用I格式傳送。I幀用于傳輸含有信息體的報文和確認對方I格式的信息報文

I幀的基本格式如下:

  • 啟動字符:固定為 68H,用于標識報文的開始。

  • APDU的后續長度:這個長度是變長的

  • 控制域:

    I幀報文控制域格式如下:

    img

    • 包含ASDU

      后續會對應用服務數據單元ASDU進行詳細分析。

2.2 U幀

U格式(不計數的控制功能類型—Unnumbered control function):

??U格式報文用于數據傳輸的過程控制主站實現子站進行數據傳輸(STARTPDT),停止子站的數據傳輸(STOPDT),和TCP鏈路測試(TESTER),它不包含ASDU(應用服務數據單元)。在同一時刻TESTFR、STOPDT、STARTDT中只能有一個功能可以被激活。U幀為控制幀,用于控制啟動、停止和測試U幀是用于傳輸鏈路控制命令的報文

U幀的基本格式如下:

  • 啟動字符:固定為 68H,用于標識報文的開始。

  • APDU的后續長度:這個長度通常是固定的,為4個字節。

  • 控制域:

    U幀報文控制域格式如下:

    img

    控制域1的六種情形:

    U格式報文控制域語義
    68 04 07 00 00 0000000111啟動命令
    68 04 0B 00 00 0000001011啟動確認
    68 04 13 00 00 0000010011停止命令
    68 04 23 00 00 0000100011停止確認
    68 04 43 00 00 0001000011測試命令
    68 04 83 00 00 0010000011測試確認

    控制域2~4為00 00 00

  • 不包含ASDU

U幀的主要用途:

  • 連接的建立和斷開:U幀用于在通信開始前進行握手,建立連接,并在通信結束后進行斷開。它可以用來發送連接請求、確認連接、發送心跳信號、請求斷開連接等

  • 測試鏈路連通性:U幀還用于測試鏈路的連通性。在一段時間內如果沒有數據傳輸,主站或子站可以發送測試U幀(TESTFR)來檢查對方是否仍然在線,并期待收到測試確認U幀(TESTFR ACK)作為響應

  • 維護鏈路活動狀態:當雙方都沒有數據發送時,U幀可以用來維持TCP連接的活動狀態,防止因長時間無活動而導致的連接斷開

U幀報文解析(舉例):

當首次建立通信連接時,需要使用U幀來啟動傳輸

  • 傳輸啟動命令

    啟動字符APDU后續長度控制域
    680407 00 00 00
  • 傳輸啟動確認(響應)

    啟動字符APDU后續長度控制域
    68040B 00 00 00

在數據傳輸過程中,為了確保鏈路的連通性,通信雙方可以周期性地發送測試U幀。或者如果主站超過一定時間沒有下發報文或者RTU也沒有上送任何報文,雙方都可以按頻率發送U幀,測試幀(U幀)

  • 測試命令

    啟動字符APDU后續長度控制域
    680443 00 00 00
  • 測試確認(響應)

    啟動字符APDU后續長度控制域
    680483 00 00 00

當要斷開通信連接時,需要使用U幀來停止傳輸

  • 停止命令

    啟動字符APDU后續長度控制域
    680413 00 00 00
  • 停止確認(響應)

    啟動字符APDU后續長度控制域
    680423 00 00 00

2.3 S幀

S格式(計數的監視功能類型—Numbered supervisory functions):

??S幀用于當沒有I格式報文回應的情況下,回應確認報文的接收,它不包含ASDU(應用服務數據單元)。S幀為監視幀,主要作用是向發送方確認已經成功接收并處理了特定的I幀S幀傳送的沒有具體的信息內容,是用來對站端所發信息報文的確認。S幀的主要作用是對I幀進行確認,它不包含任何實際的數據負載,僅用于確保數據傳輸的可靠性和順序性。通過S幀,接收方可以告知發送方已經成功接收到特定序列號的I幀,從而允許發送方進行流量控制和錯誤恢復。

S幀的基本格式如下:

  • 啟動字符:固定為 68H,用于標識報文的開始。

  • APDU后續長度:這個長度通常是固定的,為4個字節。

  • 控制域:

    S幀報文控制域格式如下:

    img

    控制域1與控制域2:固定為01 00

    控制域3與控制域4 (接收序列號):已接收的I格式報文的發送序列號 + 2

  • 不包含ASDU

S幀的主要用途:

  • 確認接收:接收方通過發送S幀來確認已經成功接收并處理了發送方的I幀。
  • 流量控制:通過調整發送S幀的頻率,接收方可以對發送方的數據流量進行控制,防止因接收方處理能力有限而導致的數據溢出。
  • 錯誤檢測:如果發送方發現接收方的S幀中確認的序列號與預期不符,可以采取重傳等措施。

S幀的使用策略可以根據實際的通信需求和系統設計來定制。例如:

  • 固定頻率確認:無論接收到多少I幀,接收方都按照固定頻率發送S幀。比如接收8幀I幀回答一幀S幀
  • 按需確認:接收方只有在接收到特定數量的I幀后,才發送一個S幀進行確認。
  • 即時確認:接收方在接收到每一個I幀后都立即發送S幀進行確認。

S幀報文解析(舉例):

  • 已接收的I

    啟動字符APDU后續長度控制域1~2(發送序列號)控制域3~4(接收序列號)ASDU
    68FA6C 6784 00...
  • 確認的S幀

    啟動字符APDU后續長度控制域1~2控制域3~4(接收序列號)
    680401 00 6E 67

    已接收的I格式報文的發送序列號 + 2 = 0x676C+ 2 = 0x676E,表明接收方已經成功接收并處理了發送方的I幀。

3.應用服務數據單元ASDU

??應用服務數據單元(ASDU)是IEC 60870-5-104協議中用于傳輸具體應用數據的單元。ASDU是I幀(Information Frame)的有效載荷部分,包含了實際傳輸的數據,如遙信、遙測、遙控、遙調、總召、對時等信息。

其結構可以簡化為:

類型標識可變結構限定詞傳送原因ASDU公共地址信息體地址數據和品質描述
1個字節1個字節2個字節2個字節3個字節變長
  • 類型標識(1個字節)

    標識出后面的信息體的數據類型,不同的類型標識對應不同的數據結構和意義。

    完整的類型標識與每一種類型標識特點如下:

    類型標識特點

    監視方向過程信息

    • 標度化值與歸一化值占2個字節,短浮點數占4個字節
    • 一般為從站發送給主站

    控制方向過程信息

    • 標度化值與歸一化值占2個字節,短浮點數占4個字節
    • 一般為主站發送給從站

    監視方向系統信息

    • 當廠站(從站)在重新上電、初始化參數、重新分配緩存區等情況下,廠站需要給主站發送該類型,而主站收到該類型的APDU包,主站一般會做一次總召喚;
    • 一般為從站發送給主站

    控制方向的系統信息

    • 一般為主站發送給從站

    常用的類型標識

    image-20240521163411555

    完整的類型標識

    在監視方向的過程信息(上行)

    <1> 0x01:M-SP-NA-1 =單點信息 (總召喚遙信、變位遙信)

    <2> 0x02:M-SP-TA-1 =帶時標單點信息 (SOE事項)

    <3> 0x03:M-DP-TA-1 =雙點信息 (遙信)

    <4> 0x04:M-DP-TA-1 =帶時標雙點信息

    <5> 0x05:M-ST-NA-1 =步位置信息

    <6> 0x06:M-ST-TA-1 =帶時標步位置信息

    <7> 0x07:M-BO-NA-1 =32比特串

    <8> 0x08:M-BO-TA-1 =帶時標32比特串

    <9> 0x09:M-ME-NA-1 =測量值,歸一化值 (遙測)

    <10> 0x0A:M-ME-TA-1 =測量值,帶時標歸一化值

    <11> 0x0B:M-ME-NB-1 =測量值,標度化值 (遙測)

    <12> 0x0C:M-ME-TB-1 =測量值,帶時標標度化值

    <13> 0x0D:M-ME-NC-1 =測量值,短浮點數(遙測)

    <14> 0x0E:M-ME-TC-1 =測量值,帶時標短浮點數

    <15> 0x0F:M-IT-NA-1 =累計量 (電度量)

    <16> 0x10:M-IT-TA-1 =帶時標累計量

    <17> 0x11:M-EP-TA-1 =帶時標繼電保護裝置事件

    <18> 0x12:M-EP-TB-1 =帶時標繼電保護裝置成組啟動事件

    <19> 0x13:M-EP-TC-1 =帶時標繼電保護裝置成組輸出電路信息

    <20> 0x14:M-SP-NA-1 =具有狀態變位檢出的成組單點信息

    <21> 0x15:M-ME-ND-1 =測量值,不帶品質描述的歸一化值 (總召喚遙測量)

    <30> 0x1E:M-SP-TB-1 =帶時標CP56TimE2A的單點信息(遙信帶時標)

    <31> 0x1F:M-DP-TB-1 =帶時標CP56TimE2A的雙點信息(遙信帶時標)

    <32> 0x20:M-ST-TB-1 =帶時標CP56TimE2A的步位信息

    <33> 0x21:M-BO-TB-1 =帶時標CP56TimE2A的32位串

    <34> 0x22:M-ME-TD-1 =帶時標CP56TimE2A的歸一化測量值

    <35> 0x23:M-ME-TE-1 =測量值,帶時標CP56TimE2A的標度化值

    <36> 0x24:M-ME-TF-1 =測量值,帶時標CP56TimE2A的短浮點數

    <37> 0x25:M-IT-TB-1 =帶時標CP56TimE2A的累計值

    <38> 0x26:M-EP-TD-1 =帶時標CP56TimE2A的繼電保護裝置事件

    <39> 0x27:M-EP-TE-1 =帶時標CP56TimE2A的成組繼電保護裝置成組啟動事件

    <40> 0x28:M-EP-TF-1 =帶時標CP56TimE2A的繼電保護裝置成組輸出電路信息

    <41~44> 為將來的兼容定義保留

    在控制方向的過程信息(下行)

    <45> 0x2D:C-SC-NA-1 =單命令 (遙控)

    <46> 0x2E:C-DC-NA-1 =雙命令 (遙控)

    <47> 0x2F:C-RC-NA-1 =升降命令

    <48> 0x30:C-SE-NA-1 =設定值命令,歸一化值 (遙調)

    <49> 0x31:C-SE-NB-1 =設定值命令,標度化值

    <50> 0x32:C-SE-NC-1 =設定值命令,短浮點數

    <51> 0x33:C-BO-NA-1 =32比特串

    <52~57> 為將來的兼容定義保留

    <58> 0x3A:C-SC-TA-1 =帶時標CP56TimE2A的單命令

    <59> 0x3B:C-DC-TA-1 =帶時標CP56TimE2A的雙命令

    <60> 0x3C:C-RC-TA-1 =帶時標CP56TimE2A的升降命令

    <61> 0x3D:C-SE-TA-1 =帶時標CP56TimE2A的設定值命令,歸一化值

    <62> 0x3E:C-SE-TB-1 =帶時標CP56TimE2A的設定值命令,標度化值

    <63> 0x3F:C-SE-TC-1 =帶時標CP56TimE2A的設定值命令,短浮點數

    <64> 0x40:C-BO-TA-1 =帶時標CP56TimE2A的32比特串

    <65~69> 為將來的兼容定義保留

    在監視方向的系統信息(上行)

    <70> 046x:M-EI-NA-1 =初始化結束

    <71~99> 為將來的兼容定義保留

    在控制方向的系統信息(下行)

    <100> 0x64:C-IC-NA-1 =總召喚命令 (總召喚)

    <101> 0x65:C-CI-NA-1 =電能脈沖召喚命令 (召喚電度量)

    <102> 0x66:C-RD-NA-1 =讀單個參數命令 (參數設置)

    <103> 0x67:C-CS-NA-1 =時鐘同步命令 (校時)

    <104>0x68:C-TS-NA-1 =測試命令

    <105> 0x69:C-RP-NA-1 =復位進程命令

    <106> 0x6A:C-CD-NA-1 =延時傳輸命令

    <107> 0x6B:C-TS-TA-1 =帶時標CP56TimE2A的測試命令

    <108~109> 為將來的兼容定義保留

    在控制方向的參數命令(下行)

    <110> 0x6E:P-ME-NA-1 =測量值參數,歸一化值

    <111> 0x6F:P-ME-NB-1 =測量值參數,標度化值

    <112> 0x70:P-ME-NC-1 =測量值參數,短浮點數

    <113> 0x71:P-AC-NA-1 =參數激活

    <114~119> 為將來的兼容定義保留

    文件傳輸

    <120> 0x78:F-FR-NA-1 =文件準備好

    <121> 0x79:F-SR-NA-1 =節已準備好

    <122> 0x7A:F-SC-NA-1 =召喚目錄,選擇文件,召喚文件,召喚節

    <123> 0x7B:F-LS-NA-1 =最后的節,最后的度

    <124> 0x7C:F-AF-NA-1 =確認文件,確認節

    <125> 0x7D:F-SG-NA-1 =段

    <126> 0x7E:F-DR-TA-1 =目錄{空白或×,只在監視(標準)方向有效}

  • 可變結構限定詞(1個字節)

    bit7bit6~bit0
    SQ信息對象數目

    SQ控制信息對象地址是否連續

    • SQ=1表示連續,即當有多個信息對象時,可以依據信息體地址推導出所有信息對象的地址(只有第一個信息對象有地址,其他對象的地址就是累加1)
    • SQ=0表示不連續,即每個信息對象必須給出其地址

    總召喚時,為了壓縮信息傳輸時間SQ=1;而在從站主動上傳變化數據時,因為地址不連續,采用SQ=0

    信息對象數目:如:召喚的遙測、遙信等點位的數目,最多為127個

  • 傳送原因(2個字節)

    bit7bit6bit5~bit0bit7~bit0
    TP/N傳送原因源發地址

    傳輸原因可以是一個或兩個字節,根據需要可以選擇帶或不帶源發地址。

    • TT=0未實驗,T=1實驗(一般T=0)

    • P/NP/N=0肯定確認,P/N=1否定確認(正常為P/N=0;P/N=1 說明該報文無效)

    • 源發地址:用來表明來自哪個主站的召喚,一般情況下不適用。規定源發地址不使用時,應置零。

    • 傳送原因:

      十進制十六進制cause方向
      10X01周期、循環(遙測)上行(從站->主站)
      20x02背景掃描(遙信)(遙測)上行
      30x03突發信息(遙信)(遙測)上行
      40x04初始化上行
      50x05請求、被請求(遙信被請求)(遙測被請求)上行、下行
      60x06激活(遙控、參數設置 控制方向)下行(主站->從站)
      70x07激活確認(遙控、參數設置 監視方向)上行
      80x08停止激活(遙控、參數設置 控制方向)下行
      90x09停止激活確認 (遙控、參數設置 監視方向)上行
      100x0A激活終止(遙控 監視方向)上行
      200x14響應站總召喚(遙信響應總召喚)(遙測響應總召喚)上行
      21-360x15-0x24響應第1組召喚-響應第16組召喚上行
      370x25響應計數量召喚上行
      38-410x26-0x29響應第1組計數量-響應第4組計數量上行
      440x2c未知的類型標識(遙控、參數設置 監視方向)上行
      450x2d未知的傳送原因(遙控、參數設置 監視方向)上行
      460x2e未知的應用服務數據單元公共地址(遙控、參數設置 監視方向)上行
      470x2f未知的信息對象地址(遙控、參數設置 監視方向)上行
      480x30遙控執行軟壓板狀態錯誤上行
      490x31遙控執行時間戳錯誤上行
      500x32遙控執行數字簽名認證錯誤上行
  • ASDU公共地址–廠站地址(2個字節)

    一般為廠站地址,每一個配電終端對應唯一的一個值。規定:高位字節為0x00,低位字節中 ,1-254 為站地址;255為全局地址。

  • 信息體地址(3個字節)

    image-20240522094011637

    • 總召喚,信息地址為00 00 00

      主站向子站發送的總召喚命令幀、子站向總站發送的總召喚確認幀、子站信息傳送完畢向主站發送的總召喚結束幀,這三個幀的信息體地址均為00 00 00。而這個過程中子站向主站發送的全遙測、全遙信等的信息體地址為后續遙測/遙信數據的起始地址,如:01 4C 00。每個遙測/遙信地址在該地址的基礎上依次加1。

    • 時鐘同步,信息體地址為00 00 00

      主站向子站發送的對時報文幀、主站收到來自子站的對時返回幀,這兩個幀的信息體地址均為00 00 00

    • 復位進程,信息體地址為00 00 00

    • 初始化,信息體地址為00 00 00

    • 如果RTU有變化遙測數據時,主動上傳,主站收到的變化遙測報文幀中的信息體地址為變化遙測對應的設備點位地址,如:01 4C 00

  • 數據與品質描述(變長)

    • 數據(變長,數據類型由前面的類型標識所決定)

      舉例信息體的情形:

      連續信息傳輸

      • 帶時標CP56TimE2A的遙測

        image-20240522134534134

      • 不帶時標CP56TimE2A的遙測

        image-20240522134615738

      • 帶時標CP56TimE2A的遙信

        image-20240522134749364

      • 不帶時標CP56TimE2A的遙信

        image-20240522134819124

      非連續信息傳輸型

      • 帶時標CP56TimE2A的遙測

        image-20240522134916095

      • 不帶時標CP56TimE2A的遙測

        image-20240522134944924

      • 帶時標CP56TimE2A的遙信

        image-20240522135031300

      • 不帶時標CP56TimE2A的遙信

        image-20240522135056480

      遙控

      • 單點遙控

        image-20240522135154975

        單點遙控信息;

        image-20240522135225825

        • S/E = 0 遙控執行命令;S/E=1 遙控選擇命令;
        • QU 被控站內部確定遙控輸出方式
          • 1 短脈沖方式輸出
          • 2 長脈沖方式輸出
          • 3 持續脈沖方式輸出
        • RES :保留位
        • SCS : 設置值; 0 = 控開 ;1 = 控合
      • 雙點遙控

        image-20240522135507294

        雙點遙控信息:

        image-20240522135542500

        • S/E = 0 遙控執行命令;S/E=1 遙控選擇命令
        • QU 被控站內部確定遙控輸出方式
          • 1 短脈沖方式輸出
          • 2 長脈沖方式輸出
          • 3 持續脈沖方式輸出
          • 其他值沒有定義
        • DCS 控制
          • 1 控分
          • 2 控合
          • 3 無效控制

      設定值(遙測):

      image-20240522135947934

      QOS:設定命令限定詞

      image-20240522140037246

      • S/E : 0 設定執行;1 設定選擇
      • 設定命令限定詞: 基本就是 0 ,因為其他并沒有定義

      這里需要關注的幾個點:

      • 歸一化值、標度化值、短浮點數對遙測信息體數據的長度的影響

        • 歸一化值(占2個字節)

        • 標度化值(占2個字節)

        • 短浮點數(占4個字節)

      • 遙信數據用一個字節表示

      • 帶時標CP56TimE2A與不帶時標對數據的影響

        image-20240522130931740

        帶時標CP56TimE2A的數據,

        • 如果是遙測,則會在重復信息體數據+品質描述詞,并在最后加7個字節的時標
        • 如果是遙信,則信息體數據與品質描述詞會合在一個字節中,再在最后加7個字節的時標
      • 連續數據上送(SQ=1)與變量數據上送(SQ=0)對數據的影響

        • SQ=1,連續數據上送,如:上送全遙測/全遙信報文,此時的數據部分不包含每個信息對象的地址,只要有前一個字段“信息體地址”就可以。例如:

          image-20240522132430353

        • SQ=0,離散數據上送,如:上送變化遙測/變化遙信報文,此時的數據部分會包含每個信息對象的地址,例如:

          image-20240522132633671

    • 品質描述詞(1個字節)

      分為遙信品質描述詞與遙測品質描述詞

      image-20240522105418789

      • IV(有效標志):IV = 0 狀態有效;IV = 1 狀態無效;

        若值被正確采集就是有效,在采集功能確認了信息源的反常狀態(例如:裝置的啟動過程中或者一些配置錯誤),那么值就是無效的。信息對象的值在這些條件下沒有被定義。標上無效用以提醒使用者,此值不正確而不能使用。

      • NT(刷新標志):NT=0 刷新成功;NT=1 刷新未成功;

        若最近的刷新成功則值就稱為當前值,若一個指定的時間間隔內刷新不成功或者其值不可用,值就稱為非當前值。設備處于調試態或裝置通訊中斷都有可能造成非當前值。、

      • SB(取代標志位):SB=0 未被取代;SB = 1 被取代;

        當信息對象的值由值班員(調度員)輸入(即人工置數)或者由當地自動原因(模擬遙信)所提供時,即為被取代。

      • BL(封鎖標志位):BL=0 未被封鎖;BL=1 封鎖;

        信息對象的值為傳輸而被封鎖,值保持封鎖前被采集的狀態。封鎖和解鎖可以由當地聯鎖機構或當地自動原因啟動。

      • RES(保留位)

      • SPI(遙信狀態位)

        單點遙信,0=開;1=合,占一個bit位。

        雙點遙信,1=開,2=合,0和3為中間狀態,占兩個bit位。

      • OV(溢出標志):OV=0 未溢出;OV=1 遙測超出量程,發生溢出

        信息對象的值超出了預先定義值的范圍(主要適用模擬量值)。僅在遙測品質結構詞中出現。

二、IEC60870-5-104規約通信過程報文幀解析

??要想理解IEC60870-5-104規約通信過程,需要先了解多種時間間隔,這些時間間隔用于確保數據的完整性、系統的同步以及通信鏈路的監控。

  • 總召喚周期(遙測、遙信)

    控制站(主站)向所有受控站(從站)發送總召喚命令的周期,總召喚可以確保控制站獲得所有相關設備的當前狀態信息。

  • 站對時周期

    站對時周期是指控制站與受控站之間進行時鐘同步的頻率。這個周期確保了系統中所有設備的時間同步。

  • 電度召喚時間間隔

    召喚電度周期是指控制站請求受控站發送電度量(如電能表讀數)信息的時間間隔。

  • 測試間隔

    測試間隔是指控制站或受控站發送測試幀(如TESTFR U幀)以檢測和維護通信鏈路狀態的時間間隔。如果在指定的測試間隔內沒有收到對方的響應,可能表明通信鏈路存在問題。

主從站間的通信過程

傳輸啟動過程

  • 主站向從站發送啟動命令(STARTDT U幀),來啟動數據傳輸

    報文幀:68 04 07 00 00 00

    解析:68(啟動符) 04(長度4) 07(控制域0000 0111) 00 00 00

  • 主站收到來自從站的啟動確認

    報文幀:68 04 0B 00 00 00

    解析:68(啟動符) 04(長度4)0B(控制域0000 1011) 00 00 00

總召喚過程:總召喚功能是在初始化以后進行,或者是定期進行總召喚

  • 主站向從站發送的總召喚命令幀,會設置總召喚周期如:5分鐘,來定時發送總召喚命令幀。總召喚的內容包括子站的遙測、遙信等。

    報文幀:68 0E 00 00 00 00 64 01 06 00 01 00 00 00 00 14

    解析:68(啟動符) 0E(長度14) 00 00(發送序號) 00 00(接受序號) 64(類型標示召喚全部數據) 01(可變結構限定詞 sq=0:離散的信息報告) 06 00 (傳輸原因 0006:激活)01 00 (公共地址即RTU地址)00 00 00(信息體地址) 14(區分是總召喚還是分組召喚,02年修改后的規約中沒有分組召喚)

  • 子站收到后,如果確認,則主站會收到來自子站的總召喚確認幀;如果否認,則子站會送否定確認

    報文幀:68 0E 00 00 02 00 64 01 07 00 01 00 00 00 00 14

    解析:68(啟動符) 0E(長度14) 00 00(發送序號) 02 00(接受序號) 64(類型標示召喚全部數據) 01(可變結構限定詞 sq=0:離散的信息報告)07 00(傳輸原因 0007:激活確認)01 00(公共地址即RTU地址) 00 00 00 (信息體地址) 14(區分是總召喚還是分組召喚,02年修改后的規約中沒有分組召喚)

  • 子站連續地向主站傳送數據,包括遙測 、遙信等

    主站收到的全遙測報文

    報文幀:68 F3 02 00 02 00 0D AE 14 00 01 00 01 4C 00 第1點遙測的4字節浮點數值 第1點遙測的品質描述 第2點遙測的4字節浮點數值 第2點遙測的品質描述 ……

    解析:68(啟動符) F3(長度243)02 00(發送序號)02 00 (接受序號)0D (帶品質描述的浮點值,每個遙測值占5個字節)AE(可變結構限定詞10101110 SQ=1:表示順序的信息報文;101110=46:表示后面有46個遙測) 14 00(0014:響應總召喚) 01 00 (公共地址即RTU地址)01 4C 00 (3字節的第1點遙測信息體地址,每個遙測地址在該地址的基礎上依次加1)

    9A 99 41 41 00 (yc1)

    34 33 97 41 00 (yc2)

    67 66 08 C2 00 (yc3)

    33 33 03 42 00 (yc4)

    2E 33 23 41 00 (yc5)

    67 66 92 C1 00 (yc6)

    66 66 AA C1 00(yc7)

    9A 99 19 B6 00(yc8)

    9A 99 11 C1 00(yc9)

    36 33 29 42 00(yc10)

    9A 19 47 43 00 (yc40)

    CE 4C 78 C3 00 (yc41)

    00 00 C9 42 00(yc42)

    35 33 7D C3 00 (yc43)

    9A 99 AE 42 00 (yc44)

    CA 2C 4B 44 00(yc45)

    CD CC 8C 36 00 (yc46)

    主站收到的全遙信報文

    報文幀:68 71 04 00 02 00 01 E4 14 00 01 00 01 00 00 01 00 00 01 00 00 01 00 00 00 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 01 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 01 00 00 01 00 00 00 01 00 00 00 00 00 01 00 00 00 00 00 00 00 0100 00 00 00 00 00 00 00 00 01

    解析:68(啟動符) F3(長度243) 04 00(發送序號) 02 00(接受序號) 01(不帶時標的單點遙信) E4可變結構限定詞11100100 SQ=1:表示順序的信息報文;1100100=100:表示后面有100個遙信 14 00(0014:相應總召喚) 01 00(公共地址即RTU地址) 01 00 00(3字節的第1點遙信信息體地址,每個遙信地址在該地址的基礎上依次加1)

    01 (yx1)

    00 (yx2)

    00 01 00 00 01 00 00 00 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 01 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 01 00 00 01 00 00 00 01 00 00 00 00 00 01 00 00 00 00 00 00 00 0100 00 00 00 00 00 00 00 00 01

  • 子站信息傳送完畢后,發送總召喚結束幀

    報文幀:68 0E 06 00 02 00 64 01 0A 00 01 00 00 00 00 14

    解析:68(啟動符) 0E(長度14) 0C 00 (發送序號)02 00 (接受序號)64 (類型標示召喚全部數據)01(可變結構限定詞 sq=0:離散的信息報告) 0A 00 (傳輸原因 000A:激活結束)01 00(公共地址即RTU地址) 00 00 00 (信息體地址)14(區分是總召喚還是分組召喚,02年修改后的規約中沒有分組召喚)

召喚電度量(電能脈沖)過程

  • 主站向從站發送電度召喚命令,會設置電度召喚間隔,如:10分鐘。召喚電度量的內容為電能脈沖數據

    報文幀:68 0E 02 00 00 00 65 01 06 00 01 00 00 00 00 05

    解析:68(啟動符) 0E(長度14) 02 00(發送序號) 00 00(接受序號) 65(類型標識:召喚電度量) 01(可變結構限定詞 sq=0:離散的信息報告) 06 00(傳輸原因 0006:激活) 01 00(公共地址即RTU地址) 00 00 00(信息體地址) 05

  • 子站收到后,如果確認,則主站會收到來自子站的召喚電度量確認幀;如果否認,則子站會送否定確認

    報文幀:68 0E 08 00 04 00 65 01 07 00 01 00 00 00 00 05

    解析:68(啟動符) 0E(長度14) 08 00(發送序號) 04 00(接受序號) 65(類型標識:召喚電度量) 01(可變結構限定詞 sq=0:離散的信息報告) 07 00(傳輸原因 0007:激活確認) 01 00(公共地址即RTU地址) 00 00 00(信息體地址) 05

  • 子站連續地向主站傳送電能量數據

    報文幀:68 FD 0A 00 04 00 0F B0 25 00 01 00 01 64 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 02 00 00 00 00 03 00 00 00 00 04 00 00 00 00 05 00 00 00 00 06 00 00 00 00 07 00 00 00 00 08 00 00 00 00 09 00 00 00 00 0A 00 00 00 00 0B 00 00 00 00 0C 00 00 00 00 0D 00 00 00 00 0E 00 00 00 00 0F 00 00 00 00 10 00 00 00 00 11 00 00 00 00 12 00 00 00 00 13 00 00 00 00 14 00 00 00 00 15 00 00 00 00 16 00 00 00 00 17 00 00 00 00 18 00 00 00 00 19 00 00 00 00 1A 00 00 00 00 1B 00 00 00 00 1C 00 00 00 00 1D 00 00 00 00 1E 00 00 00 00 1F 00 00 00 00 20 00 00 00 00 21 00 00 00 00 22 00 00 00 00 23 00 00 00 00 24 00 00 00 00 25 00 00 00 00 26 00 00 00 00 27 00 00 00 00 28 00 00 00 00 29 00 00 00 00 2A 00 00 00 00 2B 00 00 00 00 2C 00 00 00 00 2D 00 00 00 00 2E 00 00 00 00 2F

    解析:68(啟動符) FD(長度253) 0A 00(發送序號) 04 00(接受序號) 0F(累計量–電度量,每個占5個字節) B0(可變結構限定詞10110000 SQ=1:表示順序的信息報文;110000=48:表示后面有48個電度量) 25 00(0025:響應計數量召喚) 01 00(公共地址即RTU地址) 01 64 00(3字節的第1點電度量信息體地址,每個電度量地址在該地址的基礎上依次加1)

    00 00 00 00 00 00 00 00 00 01 00 00 00 00 02 00 00 00 00 03 00 00 00 00 04 00 00 00 00 05 00 00 00 00 06 00 00 00 00 07 00 00 00 00 08 00 00 00 00 09 00 00 00 00 0A 00 00 00 00 0B 00 00 00 00 0C 00 00 00 00 0D 00 00 00 00 0E 00 00 00 00 0F 00 00 00 00 10 00 00 00 00 11 00 00 00 00 12 00 00 00 00 13 00 00 00 00 14 00 00 00 00 15 00 00 00 00 16 00 00 00 00 17 00 00 00 00 18 00 00 00 00 19 00 00 00 00 1A 00 00 00 00 1B 00 00 00 00 1C 00 00 00 00 1D 00 00 00 00 1E 00 00 00 00 1F 00 00 00 00 20 00 00 00 00 21 00 00 00 00 22 00 00 00 00 23 00 00 00 00 24 00 00 00 00 25 00 00 00 00 26 00 00 00 00 27 00 00 00 00 28 00 00 00 00 29 00 00 00 00 2A 00 00 00 00 2B 00 00 00 00 2C 00 00 00 00 2D 00 00 00 00 2E 00 00 00 00 2F

  • 子站信息傳送完畢后,發送召喚電度量結束幀

    報文幀:68 0E 10 00 04 00 65 01 0A 00 01 00 00 00 00 05

    解析:68(啟動符) 0E(長度14) 10 00(發送序號) 04 00(接受序號) 65(類型標識:召喚電度量) 01(可變結構限定詞 sq=0:離散的信息報告) 0A 00(傳輸原因 000A:激活結束) 01 00(公共地址即RTU地址) 00 00 00(信息體地址) 05

站對時過程

  • 主站向從站發送對時報文,會設置站對時間隔,如:20分鐘。

    報文幀:68 14 02 00 0E 00 67 01 06 00 01 00 00 00 00 8E 6D 2C 0B 2F 0B 0A

    解析:68(啟動符)14(長度20) 02 00 (發送序號)0E 00 (接受序號)67 (類型標示:67時鐘同步)01 (可變結構限定詞 sq=0:離散的信息報告)06 00 (傳輸原因 0006:激活)01 00(公共地址即RTU地址)

    00 00 00 (信息體地址)

    8E(毫秒低位) 6D(毫秒高位)

    2C(分鐘)

    0B(時)

    2F(日與星期)

    0B (月)

    0A(年)

  • 主站收到來自從站的對時返回

    報文幀:68 14 0E 00 04 00 67 01 07 00 01 00 00 00 00 8E 6D 2C 0B 2F 0B 0A

    解析:68 (啟動符)14 (長度20) 0E 00(發送序號) 04 00 (接受序號)67(類型標示:67時鐘同步) 01 (可變結構限定詞 sq=0:離散的信息報告)07 00(傳輸原因 0007:激活確認)01 00(公共地址即RTU地址)

    00 00 00(信息體地址)

    8E(毫秒低位)

    6D(毫秒高位) 6D8E = 28046/1000 = 28秒 28046%1000 = 46毫秒

    2C(分鐘) 0010 1100 取 0-5 01100 = 12分

    0B(時) 0000 1011 取 0-4 01011 = 11時

    2F(日與星期) = 101111 取0-4 01111 = 15日

    0B (月)= 0000 1011 取0-3 1011 = 11月

    0A(年)= 0000 1010 取0-6 001010 = 10年

變化數據主動上傳過程:如果RTU有變化數據則主動上傳(從站向主站主動上傳變化數據)

  • 主站收到的變化遙測報文

    報文幀:68 EA 04 00 00 00 0D 1C 03 00 01 00

    變化遙測的3字節信息體地址 第1個變化遙測的4字節浮點數值 第1個變化遙測的品質描述

    變化遙測的3字節信息體地址 第2個變化遙測的4字節浮點數值 第2個變化遙測的品質描述

    ……

    解析:68(啟動符) EA(長度234) 04 00 (發送序號)00 00(接受序號) 0D (帶品質描述的浮點值,每個遙測值占5個字節)1C (可變結構限定詞(00011100 sq=0: 離散的信息報告; 11100 = 28:表示有28個遙測值;) 03 00 (傳輸原因 0003:突發)01 00 (公共地址即RTU地址)

    01 4C 00 (信息體地址)CE CC 64 41 00 ——1

    02 4C 00 (信息體地址)CE CC B8 C1 00 ——2

    04 4C 00 (信息體地址)33 33 03 C2 00 ——3

    4C 00 67 (信息體地址) 66 92 C1 00 07 ——4

    00 32 33 (信息體地址) 63 41 00 08 4C ——5

    00 97 99 (信息體地址) 01 41 00 0A 4C——6

    CE 8C C6 (信息體地址)C3 00 26 4C 00——24

    00 E0 0E (信息體地址) 44 00 29 4C 00 ——25

    34 F3 B8 (信息體地址)C3 00 2A 4C 00 ——26

    9B 99 FC (信息體地址)C3 00 2C 4C 00 ——27

    6C 66 B0(信息體地址) C2 00 4C 06 41——28

  • 主站收到的SOE報文

    SOE是為廠站端RTU采集的現場遙信變位信息,它包含了遙信變位的準確發生時間(精細到毫秒),RTU將其打包成SOE報文發送給主站系統,主站系統將規約報文解析成SOE報表

    報文幀:68 20 12 00 04 00 1E 02 03 00 01 00

    03 00 00 00 99 AF 3A 13 1E 03 00

    03 00 01 00 99 AF 3A 13 1E 03 00

    解析:68(啟動符)20 (長度32)12 00(發送序號)04 00 (接受序號)1E (類型標示:帶時標的遙信)02 (可變結構限定詞 sq=0:離散的信息報告,2個SOE)03 00 (傳輸原因 0003:突發)01 00 (公共地址即RTU地址)

    03 00 00 (信息體地址)

    00 (分)99 AF(毫秒) 3A(分鐘)13(時) 1E(日與星期) 03(月)00 (年)03 00 01 (信息體地址)

    00 (分)99 AF(毫秒) 3A (分鐘)13 (時)1E(日與星期) 03 (月)00(年)

補充

  • 在總召喚時間間隔內,會用測試幀(TESTDT幀,U幀)來檢測與維護通信鏈路狀態,如:

    24-05-22 14:22:16 : 104子站–U幀:測試
    68 04 43 00 00 00
    24-05-22 14:22:16 : 104主站–U幀:測試確認
    68 04 83 00 00 00
    24-05-22 14:22:56 : 104子站–U幀:測試
    68 04 43 00 00 00
    24-05-22 14:22:56 : 104主站–U幀:測試確認

  • 主站會發送S幀,告訴從站已經成功接收并處理了特定的I幀(主站已接收并處理的I幀,S幀)。

    24-05-22 14:21:56 : 104主站–S幀
    68 04 01 00 06 00
    24-05-22 14:21:56 : 104主站–S幀
    68 04 01 00 0C 00
    24-05-22 14:21:56 : 104主站–S幀
    68 04 01 00 12 00

三、組織報文與解析報文(C++)

  • 首先,從數據庫表中獲取規約的編號,如:ptl_104_cj

  • 然后,加載規約動態庫,得到該規約對應的組織數據與解析數據的函數

  • 通道通信發送數據處理

    • 調用規約報文組織函數組織報文

      m_pFuncbuildData(m_SPTLRecvSenddata, dynamic_cast<CUnitBase*>(m_pCurrentUnit));  //調用規約報文組織函數組織報文
      
      PTL_API void buildData(S_RecvSendData &sRecvSendData, CUnitBase* pUnit)  //組織報文
      {S_PtlVariant sVariant;memset(&sVariant, 0, sizeof(S_PtlVariant));if(pUnit != NULL){sVariant.pUnit = pUnit;sVariant.pBySendBuffer = sRecvSendData.pSendBuf;sVariant.uwSendDataLen = sRecvSendData.uiSendLen;sVariant.bMainChannel = sRecvSendData.bMainChannel;sVariant.byChannelMode = sRecvSendData.byChannelMode;sVariant.byChannelIndex = sRecvSendData.byChannelIndex;sVariant.pPtlFlag = (S_PtlFlag*)(pUnit->getTUFlagBuffer());  // 獲取規約標志位使用的緩沖區sendFrame(sVariant); //組織數據sRecvSendData.uiSendLen = sVariant.uwSendDataLen;}
      }
      

      基于IEC60870-5-104的通信過程來組織報文

      • 發送STARTDT(U幀)是在規約初始化完成后、重新建立連接后、三分鐘內沒有通信默認通道是斷的時。發送TESTDT(U幀)是在15秒內無數據通信時

        調用buildU()接口來組織U幀

      • 發送S幀是在主站解析完來自從站的I幀報文后

        調用buildS()接口來組織S幀

      • 針對遙控與遙調類型,首先這些數據的來源是站端解析來自云端的MQTT報文得到的

        遙控

        • 單點遙控,調用buildYKDataASDU45()接口
        • 雙點遙控,調用buildYKDataASDU46()接口

        遙調:

        • 遙調模式-歸一化值,調用buildYTCmdASDU48()接口
        • 遙調模式-標度化值,調用buildYTCmdASDU49()接口
        • 遙調模式-短浮點數,調用buildYTCmdASDU50()接口
      • 超過總召時間間隔,調用buildCallAllASDU100()接口組織發送總召命令

      • 超過遙脈總召時間間隔,調用buildDDASDU101()接口組織發送遙脈總召時間間隔

      • 超過校時時間間隔,調用buildSyncTimeASDU103()接口組織發送校時報文

      void sendFrame(S_PtlVariant &sVariant)  //組織數據
      {sVariant.uwMaxSendLen = MAX_FRAME_LEN;if(!sVariant.pPtlFlag->bPtlInitOK){if(initPtl(sVariant)){sVariant.pPtlFlag->bPtlInitOK = true;qDebug() << "initptl";}else{return;}}if(sVariant.pUnit->isNeedRestartLink()){sVariant.pPtlFlag->bPtlInitOK = false;sVariant.pPtlFlag->bLinkOK = 0;sVariant.pUnit->setRestartLink(false);return;}S_DateTime nowTime = qGetCurrentTime();//輪詢周期quint32 uiScancYC = sVariant.pPtlFlag->pSPtl104->uiScancYCLen;uiScancYC = 0 ? 5: uiScancYC;//規約初始化if(!sVariant.pPtlFlag->bPtlInitOK){sVariant.pPtlFlag->bPtlInitOK = true;}//規約初始化完成if(!sVariant.pPtlFlag->bLinkOK){sVariant.pPtlFlag->bLinkOK = true;sVariant.pPtlFlag->byUStart_V = 1;}S_DateTime time = qGetCurrentTime();sVariant.pPtlFlag->tLastRecvTime = time;    //上次接收數據時間//作為數據接收方,要控制鏈路if(sVariant.pPtlFlag->bLinkOK){if(time - sVariant.pPtlFlag->tLastRecvTime > NODATA_LMT){//三分鐘沒收到數據,通道是斷的,發啟動幀sVariant.pPtlFlag->bLinkOK = 0;sVariant.pPtlFlag->byUStart_V = 1; }//15秒無數據發送測試幀else if(time - sVariant.pPtlFlag->tLastRecvTime > TESTDATA_LMT){//測試ASDU超時if(time - sVariant.pPtlFlag->tLastSendTime > T1){sVariant.pPtlFlag->byTest_V = 1;}}}else//重新建立連接{sVariant.pPtlFlag->uwRecvSN = 0;sVariant.pPtlFlag->uwSendSN = 0;if(time - sVariant.pPtlFlag->tLastSendTime >T0){sVariant.pPtlFlag->byUStart_V = 1;}}//判斷鏈路連接成功,是否發送總召if((sVariant.pPtlFlag->bLinkOK)&& (sVariant.pPtlFlag->bSendCallAll)&& sVariant.pPtlFlag->byDowmFrame == FRAME_NULL){sVariant.pPtlFlag->byDowmFrame = ASDU_100;  //站總召命令}//鏈路成功,設置時鐘if((sVariant.pPtlFlag->bLinkOK) && (sVariant.pPtlFlag->bSendTimeSynchro)&& sVariant.pPtlFlag->byDowmFrame == FRAME_NULL){sVariant.pPtlFlag->byDowmFrame = ASDU_103;  //時鐘同步命令}if ((sVariant.pPtlFlag->bLinkOK) && (sVariant.pPtlFlag->bSendDDCall)&& sVariant.pPtlFlag->byDowmFrame == FRAME_NULL){sVariant.pPtlFlag->byDowmFrame = ASDU_101;  //電能量召喚命令}if(uiScancYC > 0){if(sVariant.pPtlFlag->bSendSFrame || (sVariant.pPtlFlag->bSendSFrame && nowTime - sVariant.pPtlFlag->tLastSFrameTime >= 1000)){buildS(sVariant);}if(sVariant.pPtlFlag->byUStart_V){sVariant.pPtlFlag->byDowmFrame = UFRAME_TYPE_STARTDT_V; //啟動生效}if(sVariant.pPtlFlag->byUStart_C){sVariant.pPtlFlag->byDowmFrame = UFRAME_TYPE_STARTDT_C;}if(sVariant.pPtlFlag->byStop_V){sVariant.pPtlFlag->byDowmFrame = UFRAME_TYPE_STOPDT_V;}if(sVariant.pPtlFlag->byStop_C){sVariant.pPtlFlag->byDowmFrame = UFRAME_TYPE_STOPDT_C;}if(sVariant.pPtlFlag->byTest_V){sVariant.pPtlFlag->byDowmFrame = UFRAME_TYPE_TESTFR_V;}if(sVariant.pPtlFlag->byTest_C){sVariant.pPtlFlag->byDowmFrame = UFRAME_TYPE_TESTFR_C;}if(sVariant.pPtlFlag->byDowmFrame != FRAME_NULL){switch (sVariant.pPtlFlag->byDowmFrame) //根據啟動控制信息,組織不同形式的幀{case UFRAME_TYPE_STARTDT_V:buildU(sVariant,UFRAME_TYPE_STARTDT_V);sVariant.pPtlFlag->byUStart_V = 0;sVariant.pPtlFlag->byDowmFrame = FRAME_NULL;break;case UFRAME_TYPE_STARTDT_C:buildU(sVariant,UFRAME_TYPE_STARTDT_C);sVariant.pPtlFlag->byUStart_C = 0;sVariant.pPtlFlag->byDowmFrame = FRAME_NULL;break;case UFRAME_TYPE_STOPDT_V:buildU(sVariant,UFRAME_TYPE_STOPDT_V);sVariant.pPtlFlag->byStop_V = 0;sVariant.pPtlFlag->byDowmFrame = FRAME_NULL;break;case UFRAME_TYPE_STOPDT_C:buildU(sVariant,UFRAME_TYPE_STOPDT_C);sVariant.pPtlFlag->byStop_C = 0;sVariant.pPtlFlag->byDowmFrame = FRAME_NULL;break;case UFRAME_TYPE_TESTFR_V:buildU(sVariant,UFRAME_TYPE_TESTFR_V);sVariant.pPtlFlag->byTest_V = 0;sVariant.pPtlFlag->byDowmFrame = FRAME_NULL;break;case UFRAME_TYPE_TESTFR_C:buildU(sVariant,UFRAME_TYPE_TESTFR_C);sVariant.pPtlFlag->byTest_C = 0;sVariant.pPtlFlag->byDowmFrame = FRAME_NULL;break;default:break;}}if(sVariant.pPtlFlag->bLinkOK){if(sVariant.pUnit->getHaveCmd()&&!sVariant.pUnit->isCmdProcing()){sVariant.pUnit->setCmdProcing(true);switch (sVariant.pUnit->getCmdType()) //采集裝置:命令類型;轉發裝置:命令響應的類型{case CMD_TYPE_YK:  //遙控{S_YKCmd* pYKCmd = static_cast<S_YKCmd*>(sVariant.pUnit->getCmd(CMD_TYPE_YK));quint8 byType = 0;sVariant.pUnit->getValue(S_DataID(TABLE_PREYK, pYKCmd->uiZFYKID, COL_PREYK_YKTYPE), &byType);  //遙控類型:單點遙信與雙點遙信if(byType == EYKT_SP){sVariant.pPtlFlag->byDowmFrame = ASDU_45;}else if (byType == EYKT_DP){sVariant.pPtlFlag->byDowmFrame = ASDU_46;}}break;case CMD_TYPE_YT:  //遙調{switch(sVariant.pPtlFlag->pSPtl104->byYTMode){case YT104_MODE_ASDU48:  //104遙調模式-歸一化值buildYTCmdASDU48(sVariant);break;case YT104_MODE_ASDU49:  //遙調模式-標度化值buildYTCmdASDU49(sVariant);break;case YT104_MODE_ASDU50:  //遙調模式-短浮點數buildYTCmdASDU50(sVariant);break;default:break;}}break;case CMD_TYPE_SET_TIME:  //時鐘同步命令sVariant.pPtlFlag->byDowmFrame = ASDU_103;break;case CMD_TYPE_CALL_DATA:  //站總召命令sVariant.pPtlFlag->byDowmFrame = ASDU_100;break;default:break;}}}if(nowTime - sVariant.pPtlFlag->tLastSendCallAllTime >= sVariant.pPtlFlag->pSPtl104->uwAllCallInterval){buildCallAllASDU100(sVariant);}if(nowTime - sVariant.pPtlFlag->tLastSendCallDDTime >= sVariant.pPtlFlag->pSPtl104->uwYMInterval){
      //            sVariant.pPtlFlag->bSendDDCall = 1;buildDDASDU101(sVariant);}if(nowTime - sVariant.pPtlFlag->tLastCheckTime >= sVariant.pPtlFlag->pSPtl104->uwCheckTimeInterval){buildSyncTimeASDU103(sVariant);}switch (sVariant.pPtlFlag->byDowmFrame){case S_FRAME:buildS(sVariant);break;case ASDU_100:buildCallAllASDU100(sVariant);break;case ASDU_103:buildSyncTimeASDU103(sVariant);break;case ASDU_101:buildDDASDU101(sVariant);break;case UFRAME_TYPE_TESTFR_V:buildU(sVariant,UFRAME_TYPE_TESTFR_V);sVariant.pPtlFlag->byTest_V = 0;sVariant.pPtlFlag->byDowmFrame = FRAME_NULL;break;case UFRAME_TYPE_TESTFR_C:buildU(sVariant,UFRAME_TYPE_TESTFR_C);sVariant.pPtlFlag->byTest_C = 0;sVariant.pPtlFlag->byDowmFrame = FRAME_NULL;break;case ASDU_45:buildYKDataASDU45(sVariant);break;case ASDU_46:buildYKDataASDU46(sVariant);break;default:break;}}sVariant.pPtlFlag->byDowmFrame = FRAME_NULL;putSendData(sVariant);   //把發送數據放入RTU發送緩沖區中
      }
      

      以組織總召喚命令為例:

      void buildCallAllASDU100(S_PtlVariant &sVariant)
      {if(sVariant.uwMaxSendLen - sVariant.uwSendDataLen < 16){return;}S_IframeHead *pIframeHead = (S_IframeHead*)(sVariant.pBySendBuffer + sVariant.uwSendDataLen);S_ASDUHead *pASDUHead = (S_ASDUHead *)(sVariant.pBySendBuffer + sVariant.uwSendDataLen+6);pIframeHead->byStart = 0x68;pIframeHead->byLen = 0x0E;pIframeHead->uwDoublens = sVariant.pPtlFlag->uwSendSN;pIframeHead->uwDoublens = qBigLittleEndianConvert(pIframeHead->uwDoublens);pIframeHead->uwDoublenr = sVariant.pPtlFlag->uwRecvSN;pIframeHead->uwDoublenr = qBigLittleEndianConvert(pIframeHead->uwDoublenr);pASDUHead->byType = ASDU_100;pASDUHead->bSq = 0;pASDUHead->bNum = 1;pASDUHead->uwReason = 6;pASDUHead->uwReason = qBigLittleEndianConvert(pASDUHead->uwReason);pASDUHead->uwCa = sVariant.pUnit->getTUAddress();pASDUHead->uwCa = qBigLittleEndianConvert(pASDUHead->uwCa);memset(sVariant.pBySendBuffer + sVariant.uwSendDataLen+12,0,3);sVariant.pBySendBuffer[sVariant.uwSendDataLen + 15] = 20;sVariant.uwSendDataLen += 16;sVariant.pPtlFlag->uwSendSN += 2;sVariant.pPtlFlag->bSendCallAll = 0;S_DateTime time = qGetCurrentTime();sVariant.pPtlFlag->tLastSendCallAllTime = time;sVariant.pPtlFlag->tLastSendTime = time;//sVariant.pPtlFlag->tLastSendCallDDTime = time;sVariant.pPtlFlag->bSendDDCall = 1;}
      

      組織好報文后,基于通道通訊類型,分別調用串口或網絡的發送數據接口

      switch(m_pChannel->byChannelCOMType)
      {case CHANNEL_TYPE_TCP_CLIENT:case CHANNEL_TYPE_TCP_SERVER:case CHANNEL_TYPE_UDP_CLIENT:case CHANNEL_TYPE_UDP_SERVER:m_SocketMutex.lock();m_SPTLRecvSenddata.iSendedLen = m_pNetComm->sendData(m_pSocketHandle, m_SPTLRecvSenddata.pSendBuf, m_SPTLRecvSenddata.uiSendLen);m_SocketMutex.unlock();if(m_SPTLRecvSenddata.iSendedLen > 0){if(m_qsPtlLibName.contains("_zf")){//ljx debugqMSleep(50);}else{//                qMSleep(30);}}break;case CHANNEL_TYPE_COM_485:case CHANNEL_TYPE_COM_232:{m_SPTLRecvSenddata.iSendedLen = m_pSerial->sendData(m_SPTLRecvSenddata.pSendBuf,static_cast<int>(m_SPTLRecvSenddata.uiSendLen));}break;case CHANNEL_TYPE_CAN:break;default:break;
      }
      
  • 通道通信接收數據處理

    基于通道類型,采用不同的處理方法將接收到的數據存放到接收緩存區中

    switch(m_pChannel->byChannelCOMType)   //通道類型
    {case CHANNEL_TYPE_TCP_CLIENT:case CHANNEL_TYPE_TCP_SERVER:case CHANNEL_TYPE_UDP_CLIENT:case CHANNEL_TYPE_UDP_SERVER:m_NewRecvedDataMutex.lock();while(m_sNewRecvedDataList.size() > 0){S_NewRecvedData& sNewRecvedData = m_sNewRecvedDataList.first();quint32 uiFreeRecvBufLen = MAX_BUF_SIZE - m_SPTLRecvSenddata.uiRecvedLen;   //剩余的接收緩沖區長度if(uiFreeRecvBufLen >= sNewRecvedData.uiRecvedLen)  //若剩余的緩沖區長度大于新接收的緩沖區長度,則新接收到的數據全部復制 {memcpy(m_SPTLRecvSenddata.pRecvBuf + m_SPTLRecvSenddata.uiRecvedLen, sNewRecvedData.pRecvBuf, sNewRecvedData.uiRecvedLen);m_SPTLRecvSenddata.uiRecvedLen += sNewRecvedData.uiRecvedLen;m_sNewRecvedDataList.removeFirst();}else if(uiFreeRecvBufLen > 0)  //若剩余的接收緩沖區長度大于0,則只復制剩余的接收緩沖區長度{memcpy(m_SPTLRecvSenddata.pRecvBuf + m_SPTLRecvSenddata.uiRecvedLen, sNewRecvedData.pRecvBuf, uiFreeRecvBufLen);m_SPTLRecvSenddata.uiRecvedLen += uiFreeRecvBufLen;sNewRecvedData.uiRecvedLen -= uiFreeRecvBufLen;memmove(sNewRecvedData.pRecvBuf, sNewRecvedData.pRecvBuf + uiFreeRecvBufLen, sNewRecvedData.uiRecvedLen);break;}else{break;}}m_NewRecvedDataMutex.unlock();break;case CHANNEL_TYPE_COM_485:case CHANNEL_TYPE_COM_232:m_uiMaxRecvLen = MAX_BUF_SIZE - m_SPTLRecvSenddata.uiRecvedLen;if(m_uiMaxRecvLen > 0){m_SPTLRecvSenddata.iRecvedLen = m_pSerial->recvData(m_SPTLRecvSenddata.pRecvBuf + m_SPTLRecvSenddata.uiRecvedLen,static_cast<int>(m_uiMaxRecvLen));  //從串口中接收數據if(m_SPTLRecvSenddata.iRecvedLen > 0){m_tRecvedTime = time(NULL);addCommMsg(true, static_cast<quint16>(m_SPTLRecvSenddata.iRecvedLen), m_SPTLRecvSenddata.pRecvBuf + m_SPTLRecvSenddata.uiRecvedLen);  //添加通信報文setUpCommFault(false);setDownCommFault(false);m_SPTLRecvSenddata.uiRecvedLen += static_cast<quint32>(m_SPTLRecvSenddata.iRecvedLen);}else if(m_SPTLRecvSenddata.iRecvedLen < 0){setCommState(COMM_ERROR);}}break;case CHANNEL_TYPE_CAN:break;default:break;
    }
    

    調用規約報文解析函數來解析報文

    m_pFuncParseData(m_SPTLRecvSenddata, dynamic_cast<CUnitBase*>(m_pCurrentUnit)
    
    PTL_API bool parseData(S_RecvSendData &sRecvSendData, CUnitBase* pUnit) //解析報文
    {S_PtlVariant sVariant;memset(&sVariant, 0, sizeof(S_PtlVariant));if(pUnit != NULL){sVariant.pUnit = pUnit;sVariant.pByReadBuffer = sRecvSendData.pRecvBuf;sVariant.uwRecvDataLen = sRecvSendData.uiRecvedLen;sVariant.bMainChannel = sRecvSendData.bMainChannel;sVariant.byChannelMode = sRecvSendData.byChannelMode;sVariant.byChannelIndex = sRecvSendData.byChannelIndex;memset(sVariant.pParsedCommMsg, 0, MAX_BUF_SIZE);sVariant.uwParsedCommMsgLen = 0;sVariant.pPtlFlag = (S_PtlFlag*)(pUnit->getTUFlagBuffer());if(readFrame(sVariant)) //解析數據{sRecvSendData.uiRecvedLen = sVariant.uwRecvDataLen;return true;}}sRecvSendData.uiRecvedLen = sVariant.uwRecvDataLen;return false;
    }
    

    解析報文

    • 首先,判斷當前主站接收到的報文幀是U幀S幀還是I幀,并設置對應的幀狀態與幀類型。若幀的第一個字符不是啟動字符,則偏移緩沖區中 的數據

    • 接著,根據幀類型調用通道報文添加函數,將報文轉發給上位機。此時幀狀態為接收幀已同步

    • 最后,根據幀類型調用不同的報文解析函數

      U幀S幀:調用parseUOrSFrame()接口

      I幀:調用parseIFrame()接口

    bool readFrame(S_PtlVariant &sVariant) //解析數據
    {bool bResult = false;while(sVariant.uwRecvDataLen >= sizeof (S_Uframe)){S_IframeHead sIFrame;memcpy(&sIFrame, sVariant.pByReadBuffer, sizeof (S_IframeHead));if(FRAME_STATE_NO == sVariant.byCurrentRecvedFrameState)    //當前接收幀的狀態為無有效幀{quint16 uwAddr;S_ASDUHead sASDUHead;uwAddr = sVariant.pUnit->getTUAddress();int i;for(i = 0;i <= sVariant.uwRecvDataLen - 6; i++){//找到啟動符68 和第二個字長度為04,確定為短幀:U幀或S幀if(0x68 == sVariant.pByReadBuffer[i] && 0x04 == sVariant.pByReadBuffer[i+1]&& 0x00 == sVariant.pByReadBuffer[i+3] && 0x00 == sVariant.pByReadBuffer[i+5])  //短幀{if(sVariant.uwRecvDataLen >= i+6){//為u或s幀sVariant.byCurrentRecvedFrameState = FRAME_STATE_SYNING;sVariant.byCurrentRecvedFrameType = FRAME_TYPE_UORS;break;}else{if(i > 0){offsetRecvBuffer(sVariant,static_cast<quint16>(i));}return bResult;}}else    //長幀:I幀{if(0x68 == sVariant.pByReadBuffer[i]){if(sVariant.uwRecvDataLen > i + 10 && uwAddr == sVariant.pByReadBuffer[i + 10]){sVariant.byCurrentRecvedFrameState = FRAME_STATE_SYNING;sVariant.byCurrentRecvedFrameType = FRAME_TYPE_I;break;}else{if(i > 0){offsetRecvBuffer(sVariant,static_cast<quint16>(i));}return bResult;}}}}if(i > 0){offsetRecvBuffer(sVariant,static_cast<quint16>(i));}}if(FRAME_STATE_SYNING == sVariant.byCurrentRecvedFrameState){if(FRAME_TYPE_UORS == sVariant.byCurrentRecvedFrameType){if(sVariant.uwRecvDataLen >= 6){sVariant.byCurrentRecvedFrameState = FRAME_STATE_SYN;sVariant.uwCurrentRecvedFrameLen = 6;sVariant.pUnit->addCommMsg(true, sVariant.uwCurrentRecvedFrameLen, sVariant.pByReadBuffer, sVariant.byChannelMode, sVariant.byChannelIndex, sVariant.bMainChannel);// qDebug()<< "裝置通信短幀" <<sVariant.uwCurrentRecvedFrameLen << (quint8*)pCommMsg->chCommMsg;}}if(FRAME_TYPE_I == sVariant.byCurrentRecvedFrameType){if(sVariant.uwRecvDataLen >= sVariant.pByReadBuffer[1]){sVariant.byCurrentRecvedFrameState = FRAME_STATE_SYN;sVariant.uwCurrentRecvedFrameLen = sVariant.pByReadBuffer[1] + 2;sVariant.pUnit->addCommMsg(true, sVariant.uwCurrentRecvedFrameLen, sVariant.pByReadBuffer, sVariant.byChannelMode, sVariant.byChannelIndex, sVariant.bMainChannel);  //添加通信報文到隊列(包括轉發給上位機的隊列,以及要保存到本地的隊列)//qDebug()<< "裝置通信長幀" <<sVariant.uwCurrentRecvedFrameLen << (quint8*)pCommMsg->chCommMsg;}}}if(FRAME_STATE_SYN != sVariant.byCurrentRecvedFrameState){return bResult;}S_DateTime time = qGetCurrentTime();sVariant.pPtlFlag->tLastRecvTime = time;switch (sVariant.byCurrentRecvedFrameType){case FRAME_TYPE_UORS:parseUOrSFrame(sVariant); //解析U幀或S幀break;case FRAME_TYPE_I:parseIFrame(sVariant); // 解析I幀數據sVariant.pPtlFlag->uwRecvSN = sIFrame.uwDoublens;sVariant.pPtlFlag->uwRecvSN += 2;sVariant.pPtlFlag->uwRecvSN = qBigLittleEndianConvert(sVariant.pPtlFlag->uwRecvSN);break;default:break;}sVariant.pPtlFlag->bWaitDataReturn = 0;sVariant.byCurrentRecvedFrameState = FRAME_STATE_NO;offsetRecvBuffer(sVariant,sVariant.uwCurrentRecvedFrameLen);sVariant.uwCurrentRecvedFrameLen = 0;bResult = true;}return bResult
    

    解析U幀、S幀、I幀的函數接口分別為:

    void parseUFrame(S_PtlVariant &sVariant)
    {S_Uframe* pSUFrame = (S_Uframe *)sVariant.pByReadBuffer;//主站發送→激活傳輸啟動if(pSUFrame ->byCtrlOctets1 == 0x07){sVariant.pPtlFlag->uwRecvSN = 0;       //接收計數sVariant.pPtlFlag->uwSendSN = 0;       //發送計數sVariant.pPtlFlag->bLinkOK = true;     //鏈路狀態}else if(pSUFrame ->byCtrlOctets1 == 0x43){sVariant.pPtlFlag->byTest_C = 1;}//從站發送→確認激活傳輸啟動else if(pSUFrame ->byCtrlOctets1 == 0x0B){sVariant.pPtlFlag->uwRecvSN = 0;       //接收計數sVariant.pPtlFlag->uwSendSN = 0;       //發送計數sVariant.pPtlFlag->bLinkOK = 1;     //鏈路狀態sVariant.pPtlFlag->bSendCallAll = 1;//總召狀態}//主站發送→停止鏈路else if(pSUFrame ->byCtrlOctets1 == 0x13){sVariant.pPtlFlag->bLinkOK = 0;     //鏈路狀態}//從站發送→確認停止鏈路else if(pSUFrame ->byCtrlOctets1 == 0x23){sVariant.pPtlFlag->bLinkOK = 0;     //鏈路狀態}//重新同步sVariant.byCurrentRecvedFrameState = FRAME_STATE_NO;
    }void parseSFrame(S_PtlVariant &sVariant)
    {quint16 uwRecv = 0;memcpy(&uwRecv,&sVariant.pByReadBuffer[4],sizeof(quint16));sVariant.pPtlFlag->uwConfirmRecvSn = uwRecv;sVariant.pPtlFlag->byRecvSFrameSendFlag = 1;S_DateTime time = qGetCurrentTime();sVariant.pPtlFlag->tLastRecvSFrame = time;//重新同步sVariant.byCurrentRecvedFrameState = FRAME_STATE_NO;}
    void parseIFrame(S_PtlVariant &sVariant)
    {S_ASDUHead sASDUHead;memcpy(&sASDUHead,sVariant.pByReadBuffer + sizeof (S_IframeHead),sizeof(S_ASDUHead));//判斷公共地址sASDUHead.uwCa = qBigLittleEndianConvert(sASDUHead.uwCa);if(sASDUHead.uwCa != sVariant.pUnit->getTUAddress()){return;}switch (sASDUHead.byType){case ASDU_01:   //單點信息(遙信)parseYXDataASDU1(sVariant);break;case ASDU_03:   //雙點信息(遙信)parseYXDataASDU3(sVariant);break;case ASDU_09:   //解析遙測歸一化值parseYCDataASDU9(sVariant);break;case ASDU_21:   //解析遙測歸一化值  測試 debug 白parseYCDataASDU21(sVariant);break;case ASDU_11:   //解析遙測標度化值parseYCDataASDU11(sVariant);break;case ASDU_13:   //解析遙測短浮點數parseYCDataASDU13(sVariant);break;case ASDU_30:   //解析帶時標單點遙信parseYXDataASDU30(sVariant);break;case ASDU_31:   //解析帶時標單點遙信parseYXDataASDU31(sVariant);break;case ASDU_45:   //解析遙控單命令if(sVariant.pByReadBuffer[1] != 0x0E){return;}parseYKDataASDU45(sVariant);break;case ASDU_46:   //解析遙控單命令if(sVariant.pByReadBuffer[1] != 0x0E){return;}parseYKDataASDU46(sVariant);break;case ASDU_48:if(sVariant.pByReadBuffer[1] != 16){return;}parseYTCmdResultASDU48(sVariant);break;case ASDU_49:if(sVariant.pByReadBuffer[1] != 16){return;}parseYTCmdResultASDU49(sVariant);break;case ASDU_50:if(sVariant.pByReadBuffer[1] != 18){return;}parseYTCmdResultASDU50(sVariant);break;case ASDU_100:  //解析總召喚數據if(sVariant.pByReadBuffer[1] != 0x0E){return;}parseZHDataASDU100(sVariant);break;case ASDU_15:  //解析遙脈值parseYMDataASDU206(sVariant);break;case ASDU_206:  //解析遙脈值parseYMDataASDU206(sVariant);break;case ASDU_207:  //解析遙脈值parseYMDataASDU207(sVariant);break;case ASDU_103:  //解析時鐘同步parseTimeSynASDU103(sVariant);default:break;}sVariant.pPtlFlag->bSendSFrame = 1;//debug白11/26
    }
    

    在解析I幀中,以解析遙測歸一化值為例

    void parseYCDataASDU9(S_PtlVariant &sVariant)
    {S_ASDUHead sASDUHead;S_Addr sAddr;S_ASDU9YC sYCValue;quint16 uwIndex;bool bIsCalc = false;memcpy(&sASDUHead,sVariant.pByReadBuffer + sizeof (S_IframeHead),sizeof(S_ASDUHead));sASDUHead.uwReason = qBigLittleEndianConvert(sASDUHead.uwReason);//類型不為9,原因不為1/2/3/5/20
    //    if(    sASDUHead.uwReason != 1 &&sASDUHead.uwReason != 2 &&sASDUHead.uwReason != 3
    //           && sASDUHead.uwReason != 5 &&sASDUHead.uwReason != 20)
    //    {
    //        return;
    //    }if(sASDUHead.bSq)//連續{memcpy(&sAddr,&sVariant.pByReadBuffer[12],sizeof (sAddr));sAddr.uwAddrOrder = qBigLittleEndianConvert(sAddr.uwAddrOrder);//地址合法性if((sAddr.uwAddrOrder > sVariant.pPtlFlag->pSPtl104->uwYCEndAddr)||(sAddr.uwAddrOrder < sVariant.pPtlFlag->pSPtl104->uwYCStartAddr)){return;}uwIndex = sAddr.uwAddrOrder - sVariant.pPtlFlag->pSPtl104->uwYCStartAddr;for(quint8 i = 0;i < sASDUHead.bNum; i++){memcpy(&sYCValue,&sVariant.pByReadBuffer[15 + i*2],sizeof (S_ASDU9YC));sYCValue.wValue = qBigLittleEndianConvert(sYCValue.wValue);double dValue = (double)sYCValue.wValue;sVariant.pUnit->getValue(S_DataIndex(TABLE_PREYC, uwIndex + i, COL_PREYC_ISCALC), &bIsCalc);if(!bIsCalc){sVariant.pUnit->setValue(S_DataIndex(TABLE_PREYC,uwIndex + i,COL_PREYC_YCVALUE) ,&dValue);}}}else //不連續{for(quint8 i = 0;i < sASDUHead.bNum; i++){memcpy(&sAddr,&sVariant.pByReadBuffer[12 + i*6],sizeof (sAddr));sAddr.uwAddrOrder = qBigLittleEndianConvert(sAddr.uwAddrOrder);//合法性檢查if((sAddr.uwAddrOrder > sVariant.pPtlFlag->pSPtl104->uwYCEndAddr)||sAddr.uwAddrOrder < sVariant.pPtlFlag->pSPtl104->uwYCStartAddr){return;}uwIndex = sAddr.uwAddrOrder - sVariant.pPtlFlag->pSPtl104->uwYCStartAddr;memcpy(&sYCValue,&sVariant.pByReadBuffer[15 + i*6],sizeof (sYCValue));sYCValue.wValue = qBigLittleEndianConvert(sYCValue.wValue);double dValue = (double)sYCValue.wValue;sVariant.pUnit->getValue(S_DataIndex(TABLE_PREYC, uwIndex, COL_PREYC_ISCALC), &bIsCalc);if(!bIsCalc){sVariant.pUnit->setValue(S_DataIndex(TABLE_PREYC, uwIndex, COL_PREYC_YCVALUE) ,&dValue);}}}}
    

    解析報文后,依據連續數據點位與非連續數據點位來討論分別設置數據庫表中對應點位的值。


參考文獻:

  • IEC60870協議是什么?IEC60870協議介紹

  • 從零開始理解IEC104協議之一——104規約幀格式

  • IEC60870開源庫

  • IEC 60870-5-104 詳細解讀

  • IEC104規約(一)協議結構闡述

  • IEC104/101 主站/客戶端 模擬器

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

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

相關文章

golang 守護進程管理

添加守護進程 vim /etc/systemd/system/xxx.service [Unit] DescriptionGo Socket Service Afternetwork.target[Service] Typesimple ExecStart/data/quwan/quwan_ws WorkingDirectory/data/quwan # 停止前發送信號 ExecStop/bin/kill -SIGTERM $MAINPID # 如果超過20s 進程…

筆記-Python lambda

在學習python的過程中&#xff0c;lambda的語法時常會使人感到困惑&#xff0c;lambda是什么&#xff0c;為什么要使用lambda&#xff0c;是不是必須使用lambda&#xff1f; 下面就上面的問題進行一下解答。 1、lambda是什么&#xff1f; 看個例子&#xff1a; 1 g lambda…

什么是GPT-4o,推薦GPT-4o的獲取使用方法,使用GPT4o模型的最新方法教程(2024年5月16更新)

2024年5月最新GPT-4o模型使用教程和簡介 2024年5月最新GPT-4o模型使用教程和簡介 2024 年 5 月 13 日&#xff0c;openai 發布了最新的模型 GPT4o。 很多同學還不知道如何訪問GPT-4、GPT-4 Turbo和GPT-4o等模型&#xff0c;這篇文章介紹如何在ChatGPT中訪問GPT-4o&#xff0…

milvus索引

Milvus是一個開源的向量數據庫引擎&#xff0c;旨在支持大規模向量相似度搜索和分析。索引在Milvus中扮演著非常重要的角色&#xff0c;它們用于加速向量數據的檢索。下面詳細介紹一下Milvus中的索引&#xff1a; 1. 索引類型 Milvus支持多種索引類型&#xff0c;每種類型都適…

無人機偵察:雷達系統概述

一、雷達基本原理 無人機偵察中的雷達系統主要基于無線電波的傳播和反射原理。雷達發射機產生特定頻率的電磁波&#xff0c;并通過天線以定向波束形式向空間發射。當這些電磁波遇到目標時&#xff0c;部分能量會被反射回來&#xff0c;被雷達接收機捕獲。通過測量發射和接收電…

基于SpringBoot+Vue+Redis+Mybatis的商城購物系統 【系統實現+系統源碼+答辯PPT】

前言 該系統采用SpringBootVue前后端分離開發&#xff0c;前端是一個單獨的項目&#xff0c;后端是一個單獨的項目。 ??技術棧&#xff1a;SpringBootVueMybatisRedisMysql ??開發工具&#xff1a;IDEA、Vscode ??瀏覽器&#xff1a;Chrome ??開發環境&#xff1a;JDK1…

Pytorch 筆記

執行下面這段代碼后&#xff0c;為什么返回的是 2 &#xff1f; vector torch.tensor([7, 7]) vector.shape為什么返回的是 torch.Size([2])&#xff1f; 當你創建一個PyTorch張量時&#xff0c;它會記住張量中元素的數量和每個維度的大小。在你的代碼中&#xff0c;torch.t…

通過 js 調起微信官方的微信支付api

通過 js 調起微信官方的微信支付api function onBridgeReady() {WeixinJSBridge.invoke(getBrandWCPayRequest, { "appId": "wx2421b1c4370ec43b", // 公眾號ID&#xff0c;由商戶傳入 "timeStamp": "1395712654", // 時間戳&quo…

動態插入HTML內容有哪些常見用法

動態插入HTML內容的常見用法包括但不限于以下幾種情況&#xff1a; 用戶交互反饋&#xff1a;當用戶在網頁上進行某些操作時&#xff08;如點擊按鈕、提交表單等&#xff09;&#xff0c;可以使用JavaScript動態插入HTML內容來提供即時的反饋或結果。例如&#xff0c;當用戶點…

vue3第三十五節(TS 之 泛型)

本節介紹 ts 中泛型的常用情景 1 什么是泛型 泛型的本質是參數化類型&#xff0c;也就是說所操作的數據類型被指定為一個參數。這種參數類型可以用在類、接口和方法的創建中&#xff0c;分別稱為泛型類、泛型接口、泛型方法。 泛型使用<T>來定義類型&#xff0c;<T…

使用canarytokens進行入侵檢測

canarytokens 基本概念 canarytokens是一種用于識別網絡入侵的工具。它們是一種虛擬的“蜜罐”&#xff0c;可以在網絡上放置&#xff0c;當有人嘗試訪問它們時&#xff0c;可以立即觸發警報&#xff0c;以便及時發現潛在的安全威脅。這些token可以是各種形式&#xff0c;可以…

項目管理基礎知識

項目管理基礎知識 導航 文章目錄 項目管理基礎知識導航一、項目相關概念二、時間管理三、人員管理四、風險管理 一、項目相關概念 項目定義的三層意思 一定的資源約束:時間資源、經費資源、人力資源一定的目標一次性任務 里程碑 是項目中的重要時點或事件持續時間為零&…

深度神經網絡——什么是遷移學習?

1.概述 在練習機器學習時&#xff0c;訓練模型可能需要很長時間。從頭開始創建模型架構、訓練模型&#xff0c;然后調整模型需要大量的時間和精力。訓練機器學習模型的一種更有效的方法是使用已經定義的架構&#xff0c;可能具有已經計算出的權重。這是背后的主要思想 遷移學習…

makefile一些特殊且常用的符號

$^&#xff1a;表示所有的依賴文件列表&#xff0c;多個文件以空格分隔。 $&#xff1a;表示目標文件的名稱。 $<&#xff1a;表示第一個依賴文件的名稱。 $*&#xff1a;表示目標文件的主文件名&#xff08;不包括擴展名&#xff09;。 $?&#xff1a;表示所有比目標文件更…

前端面試題日常練-day26 【面試題】

題目 希望這些選擇題能夠幫助您進行前端面試的準備&#xff0c;答案在文末。 1. Vue中&#xff0c;以下哪個選項可以用于在組件之間傳遞數據&#xff1f; a) props b) emit c) model d) data 2. 在Vue中&#xff0c;以下哪個指令可以用于條件性地渲染一個元素&#xff1f; …

【Python設計模式10】外觀模式

外觀模式&#xff08;Facade Pattern&#xff09;是一種結構型設計模式&#xff0c;它通過提供一個統一的接口&#xff0c;來簡化客戶端與復雜系統之間的交互。外觀模式為子系統中的一組接口提供一個高層接口&#xff0c;使得子系統更容易使用。 外觀模式的結構 外觀模式主要…

【學習心得】超簡單的加載模型和保存模型的方法

方法一&#xff1a;pickle庫 這是Python的標準序列化模塊&#xff0c;可以將幾乎任何Python對象轉化為字節流&#xff08;即序列化&#xff09;&#xff0c;然后可以將其存儲到文件中或通過網絡發送。之后&#xff0c;可以使用pickle再次加載這個字節流&#xff0c;恢復原始對象…

Linux shell命令

cat 文件名 查看文件內容&#xff0c; tac文件名 倒著顯示。 more 文件名 顯示內容 less文件名 和more的功能一樣&#xff0c;按上下左右鍵&#xff0c;按Q鍵結束。 head文件名&#xff0c;只顯示前10行內容。 ln是一個默認創建硬鏈接的命令 ln 文件名 ls -i文件名…

全棧:Web 用戶登錄過程實例與Cookie管理

用戶創建與使用cookie全過程 1.用戶訪問網站 當用戶使用瀏覽器訪問一個網站時&#xff0c;瀏覽器會向服務器發送一個HTTP請求。 2. 服務器響應請求 服務器接收到HTTP請求后&#xff0c;會處理請求并準備響應。如果服務器需要設置Cookie&#xff0c;它會在HTTP響應頭中包含一…

SpringBoot整合RabbitMQ的快速使用教程

目錄 一、引入依賴 二、配置rabbitmq的連接信息等 1、生產者配置 2、消費者配置 三、設置消息轉換器 四、生產者代碼示例 1、配置交換機和隊列信息 2、生產消息代碼 五、消費者代碼示例 1、消費層代碼 2、業務層代碼 在分布式系統中&#xff0c;消息隊列是一種重要…