2015年9月30日 星期三

如何用網卡,讓NuStreams單Port檢測封包數

一般我們板卡都是有一個接收端和一個發送端,實際上,任何一個端口,都可以同時傳送與接收。因此,要實現單一端口同時傳送與接收數值,就需要有一個回彈的設備。這樣一個迴路,我們可以稱作Loopback,或是自環。

這個Loopback的設備,通常我們可以用一台具備Loopback功能的機器做到,更簡便的來說,我們可以利用網卡來做。以下就是介紹如何用網卡設定。


【工具】NICLoopback.exe
【需求】PC端必須安裝Winpcap 4.1.3版本以上
【模擬環境】

【使用說明】

  • 開啟NICLoopback.exe之後,選擇一張想要當作Loopback的網卡

  • 啟動


  • 就可以看到結果了

【傳送端的設定】
這工具需要設定兩個特殊條件:
  1. SA = 00-22-A2-27-XX-XX
  2. Type = 0xFFAB



API會用到的PortMAP設定 - Lock, Group, Global commands

這三種命令,都有PortMap的設定,Map設定三者皆相同,以下說明如何設定Port Map

int membermap[4] - 代表有4個unsigned int值。

每個byte設定一個slot,每個bit代表一個port,多餘的保留不設。因此一個map就可以設定4張板卡(因為4個byte),4個map總共可 以設定4*4=16張板卡。板卡從第二個槽開始,所以:

  • membermap[0]可以設定Slot2(第一個byte)~5(第四個byte)。
  • membermap[1]可以設定Slot6~9
  • membermap[2]可以設定Slot10~13
  • membermap[3]可以設定Slot14~17


再來說明每個membermap的bit設定,以membermap[0]來說:
bit0可以設定(0,2,1),bit1可以設定(0,2,2),bit2可以設定(0,2,3),...,依此類推
如果板卡只有4個port,高位的bit都不用設的。亦即最多為0x0f(0b00001111)。
要設定Port(0,2,1)~Port(0,2,4),就將map設為0x0000000f
           
範例:

假設要lock (0,2,1)~(0,2,4), (0,7,1)~(0,7,4),(0,13,1)~(0,13,2)共3張板卡,10個port,那我們的map長成這樣(對照上表):
membermap[0]=0x00 00 00 0f;//(0,2,1)~(0,2,4)
membermap[1]=0x00 00 0f 00;//(0,7,1)~(0,7,4)
membermap[2]=0x03 00 00 00;//(0,13,1)~(0,13,2)
membermap[2]=0x00 00 00 00;//沒有版卡

如何讓舊的NuStream XM板卡,設定自訂的觸發條件

XM板卡沒有像RM板卡有自動化的XTAG,所以需要自己告 訴板卡要過濾哪些包。

    XTrailer是類似XTAG,在封包裡頭加特定標記,但這標記是用來取裡頭Timestamp欄位用的,不是要達到的功能。在XM板卡上,有一個作法可以達到--Trigger」。就是觸發的意思, 告訴Rx端哪類型的封包, 需要被觸發。說明如下:

 設定Tx端:

DA(6)
SA(6)
Type(2)
IPHeader(20)
Payload
CRC(4)

    假設自定義的欄位裡頭,自行插入4byte標記,以插入「Xtra」這四個字為例,16進制值為「0x58 0x74 0x72 0x61」,    Tx端封包變成類似如此:

DA(6)
SA(6)
Type(2)
IPHeader(20)
58
74
72
61
00
……
CRC(4)

    這四個byte是給Rx端觸發的,所以等 等Rx端也要設定此條件,才搭配的起來。

 設定Rx端:

Rx端必須設定這個function - Nu_XM23L4_StartTriggerCounter(chassis, board, port, triggerSetting)
 使用到「XM23L4_STARTTGRCTR_T這個結構體,裡頭結構很簡單,一組三個欄位,
 告訴
XM-23L4要觸發的「位置,值,和長度」,可以設定6API8組,實際只用到6組)。

 以上面例子來說,我要觸發從第35byteDA+SA+Type+Header用掉34bytes)開始,長度為4bytes
 值為
0x58, 0x74, 0x72, 0x61。那我就這樣設定:
XM23L4_STARTTGRCTR_T trigger;
memset(&trigger, 0, sizeof(XM23L4_STARTTGRCTR_T));
trigger.trigger1_offset = 35;
trigger.trigger1[0] = 0x58;
trigger.trigger1[1] = 0x74;
trigger.trigger1[2] = 0x72;
trigger.trigger1[3] = 0x61;
trigger.trigger1_len = 4;
trigger.trigger_ctrl = 0x1;// 0b11111111, 從右到左分別是trigger1, 2, ..., 8。 設定trigger1, 就設0x1
Nu_XM23L4_StartTriggerCounter(chassis, board, port, trigger);

Rx端在Tx端打包之前,經過 以上設定,那我在counter裡頭就可以看到「RxTriggerN」這些欄位,會有值出現了。

NuTCL-RM的架構

NuStream目前在上層軟體方面,有這三種方式來操控。

先看一下我們一般NuApps系列的軟體,和使用NuTCL軟體的示意圖。黃色部份是我們一般的NuApps系列,呼叫NuAPI-RM的方式。而NuTCL-RM的方式,透過Labview呼叫,一樣可以達到NuApps系列軟體效果。
    

名詞定義:
  • TCL:一種直譯式的程式語言。意思是執行過程中,所下的命令是瀑布式的由上到下執行。
  • DLL:動態執行檔,將一些函式給封裝起來像是黑盒子,透過開放指令讓其他DLL或是AP能夠操作的檔案。
  • NuTCL-RM:一種DLL。以TCL為Base,延伸出Xtramus獨特指令的特殊TCL,對於基本TCL指令亦包含。
  • NuCPP:一種DLL。將NuTCL-RM的參數值整合,並呼叫NuAPI-RM來執行。
  • NuAPI-RM:一種DLL。提供NuStream操作命令的集合檔案。
以軟體分層架構來說,NuTCL-RM就像是 View( UI ) 的部份,負責處理所有TCL輸入指令,有被延展或是原生的TCL指令,才可以被呼叫使用。參數上的正確性判斷也會在這邊完成。

而 NuCPP 就像是 Model 的部份,負責處理資料儲存,以及呼叫NuAPI-RM。基本上所有NuTCL-RM命令,參數,都有一個相對應的NuCPP。名稱應該是取其以C++寫成 的含意。

NuAPI-RM 則像是 Control 的部份,負責所有 NuStream 的指令,NuCPP看狀況選擇性使用部份NuAPI-RM功能。

所以以完整支援性來說,NuAPI-RM > NuCPP > NuTCL-RM。
    
因此NuTCL-RM可以看成是一個完整可以單獨執行的AP,沒有Visual view,可以被Labview等框架給包裝加上 Visual view。

2015年9月21日 星期一

產線監控系統的初步架構

  • 「1」-Database Server + DB。負責存放Database的地方。AP端的Uploader負責寫值,再透過Web Server來取值呈現。我簡易產生一個資料庫,資料表格,以及一些欄位,可以放在有獨立IP的電腦。
  • 「2」-Web Server + Service。負責處理去資料庫擷取資料,以及處理來自網頁的請求,呈現結果至網頁。目前Java在這一塊蓬勃發展,有非常.非常多的框架幫助使用,用意在於強制開發者寫出來的code能維持良好風格我這邊僅透過良好風格的寫法儘量不使用一些框架,再利用一些輔助的工具來達到效果。目前已經可以做到發布在網路上,讓使用者用瀏覽器觀看。
  • 「3」-Uploader。負責將蒐集而來的Log(其實是特定封包結構),透過資料庫Driver,上傳至資料庫。這邊和「4」是利用TCP/IP Socket的方式建立架構,我們要自定義一個封包結構,這邊尚未定義結構內容。
  • 「4」-AP Log Sender。有兩個方式要嘗試,4.1 是一隻獨立的程式,抓取Log後分析上傳。4.2 是內建在ap當中,每當產生Log同時,也去上傳資料。

這邊方式是會產生幾個Demo用的AP,開啟後不斷產生資料,然後送交給Uploader,模擬多個AP測試完成後產生Log的情況。

目前「1」「2」雛型已經完成了,透過手動去更新「1」的方式,網頁的數值就會自動(每5秒update一次)跟著變動。等待Evans將「3」「4」完成並接軌到DB之後,整個雛型就完成了。

【實作】

  • 按先前规划讨论的架构,拟定一个雏型,底下展示的是这个雏型具体化,以一条简单的MAP为例:
    • 其中写到IP的部份都是表示透过IP来连接,因此不限於Local端
    • 目前整套放在非固定IP端,因此展示在一台机器上,也可以透过同一区域的另一台机器连上观看

  • Server端(Uploader):汇整來自AP的资料,重复的部份用Update的,不重复的部份用新增的。
    • 开启後自动打开开放的Port,和Client协定好的
    • 等待来自Client(AP)的资料
    • Update之後,先去DB捞资料比对,判断是否重复,重复则更新「Pass」/「Fail」栏位。不重复则新增
    • 1.4 图示:

  • Client端(AP):模拟执行完AP之後,要上传的资料。
    • 连结至Server(Uploader所在位置)
    • 变更好资料後上传
    • 图示:

  • DB端:纪录汇整资料,透过Server(Uploader)上传资料更新。
    • 不执行任何操作,都是由Server(Uploader)负责处理内容
    • 图示:

  • Web Service端:透过Java Service去调用DB中资料,单纯做捞值,不写值
    • 透过「jdbc driver」连结到资料库,利用绑定的帐号密码
    • 将资料表全部取出,储存在Vector里头後结束
    • 图示:无,都是Class
  • Browser端:使用者透过浏览器直接看结果
    • 透过JSP连结Web Service的Class动作
    • 利用Struts2替换网页别名,避免直接引用档名
    • 用「显示网页」去包装「读取并呈现DB资料网页」,利用JQuery自动更新「读取并呈现DB资料网页」,避免画面闪动
    • 图示:



NuStream API Group 和 Global 命令差異

關於Global command以及Group command的差異,這邊都先忽略counter等其他動作,單純就設定打包。我這邊用圖來說明:

Group Command for XM module

Global Command for RM module
這邊簡單說明一下內容:

  1. 設定FillTxpkt
    • 這邊換成以「RMModule_FILLTXPKT_ADDENTRY_T」來對每個Port做設定,因此會多了(chassis, board, port) 需要去設定
  2. 設定StartTx
    • 在「RMModule_STARTTXPKT_T」裡頭有個欄位「start_tx」,設定成0,表示需要等待Global Command觸發
  3. 設定Global Command
    • 這邊要稍微說明一下,因為Global command是叫XM-2S8G(NuStream 2000i機箱)或是XM-2S10(NuStream 600i機箱)這兩著控制卡來操控,因此是對這兩張板卡下Gloabl命令,以2000i的例子來說,就是去呼叫「Nu_XM2S8G_GlobalCmd_StartTx」這個function而裡頭的參數portmap可以這樣設定:
      • globaltx_map += power(2, (port.boardid-2)*4+(port.portid-1));
    • 意思是說,bit0代表(機箱插槽2的第1個port),bit1代表(機箱插槽2的第2個port),…,bit10代表(機箱插槽4的第2個port),……,bit63(機箱插槽17的第4個port)
這樣設定就可以達到Global Start Tx的效果了

NuStreams API 如何設定一次多條Streams

先解釋一下我們版卡多條stream的概念。
首先,我們版卡每條stream稱作一個entry槽,每一個entry槽代表可以設定一種封包,裡頭可以設定長度,Gap,內容Payload等等,而其中還有另外兩個欄位,是這樣的架構的:
兩者都是數字,Entry代表目前的槽編號,Next Entry代表下一個Entry槽編號,因此透過這樣架構,可以連結組成,很像鍊結串列,只是最後一個槽的next entry必須返回最初的,形成一個迴路:
觀念上是如圖說明,而實際上,我們也按照這樣設定,在每次設定封包時,填上這兩個值,板卡收到之後會自行串連,就可以達到目的。以下是範例,假設我要讓一個port同時打出4種不同size的封包,那這樣設定:

RMModule_FILLTXPKT_ADDENTRY_T tmpEntry;
memset(&tmpEntry, 0xFF, sizeof(RMModule_FILLTXPKT_ADDENTRY_T));
//......
tmpEntry.packet_len = 64;
tmpEntry.entry_index = 0;
tmpEntry.next_index = 1;
tmpEntry.stream_id = 123;
Nu_RMModule_FillTxPkt_AddEntry(chassis, board, port, tmpEntry);
tmpEntry.packet_len = 256;
tmpEntry.entry_index = 1;
tmpEntry.next_index = 2;
tmpEntry.stream_id = 124;
Nu_RMModule_FillTxPkt_AddEntry(chassis, board, port, tmpEntry);
tmpEntry.packet_len = 512;
tmpEntry.entry_index = 2;
tmpEntry.next_index = 3;
tmpEntry.stream_id = 125;
Nu_RMModule_FillTxPkt_AddEntry(chassis, board, port, tmpEntry);
tmpEntry.packet_len = 1500;
tmpEntry.entry_index = 3;
tmpEntry.next_index = 0;
tmpEntry.stream_id = 126;
Nu_RMModule_FillTxPkt_AddEntry(chassis, board, port, tmpEntry);

最後要返回第0個entry。透過這種設定方式,再送出傳送命令之後,就可以叫Port打出FrameSize=64,256,512,1500的封包了。

TCL運算得到錯誤數值

這問題發生原因,可能在於32位元和64位元電腦的處理方式。
假設有這樣運算如下:

  • $param*1000/125000000*($length+12+$frame_gap/8) 

第一個變數假設是10000000,那運算結果會是:
$param*1000 = 10000000*1000 = 10000000000
換算成16進制來看會得到「0x2540BE400」


帶入運算值 > 32位元最大值。這個值都會預設裁切成1410065408,以符合32位元,把這個值再去做後面的運算,得到結果就是2924了。 最後得到完全錯誤結果,因此在數值運算時還是得考慮到這一點。

NuStreams 2000 如何收發封包

首先先說明標準的NuStreams環境架構圖











User操作的流程很簡單,
1. 透過Software執行功能
2. Software透過API將相對應的command送出給NuStreams
3. NuStreams根據命令去通知該打包的板卡收送包給DUT
4. NuStreams隨時將結果以Report的方式回傳給API
5.上層Software顯示結果。

而打包的內容校驗方面,可以分成軟硬體,以及收送包共四個區塊,統整如下:

















  • XTAG – NuStreams特有的TAG,在封包中,且不影響內容的特定位置插入,主要用來識別NuSteams打出的封包
  • SDFR - NuStreams特有的Filter技術,可以識別Destination MAC, Source MAC, VLAN ID, QinQ, MPLS, Source IP, Destination IP, Source IPv6, Destination IPv6, Source Port, Destination Port, XTG, user define, ICMP, IGMP等封包

RFC2544 Latency

Method:


  • Cut-Through switch:只看CRC有沒有錯,直接轉出 - 較快。
  • Store-and-Forward switch:將封包整個收完檢查,處理後再轉出 - 較慢。

Latency:

  • Cut-Throughput機器是FIFO(First-in, First-out)











  • Store-and-Forward機器則是屬於LIFO(Last-in, First-out)











因此根據以上說明,有兩種情況:

  1. 對不同裝置打包 –以上圖來說,S&F我們定義為ST0和ST1,CT我們定義為CT0和CT1。起始相同(CT0=ST0),結束時間(ST1 >> CT1)ST1遠遠大於CT1,因此S&F數值較大,是可以理解的。
  2. 對相同裝置打包 – cut-throughput多了封包長度,因此T1-T0後,得到數值比較大,也是可以理解的。

這問題在於,我們不知道待測物是哪種裝置,因此同時列出兩種數值呈現。用CT的機器來測試,就看CT的數值,而用SF的機器來測試,就看SF數值了。

Linux载入自建的*.so失败

我是按照這步驟做的(1-3步可以不用做):

  1. 先確保所有軟體是最新版本,至少GCC和G++是4.9以上
    • $ sudo apt-get update
    • $ sudo apt-get install g++-4.9
    • $ sudo apt-get install gcc-4.9
  2. 檢查系統內的gcc版本
    • $ gcc –version
  3. 如果不是4.9而是其他版本,但又不確定電腦中是否有安裝4.9以上,列出所有安裝版本
    • $ ls /usr/bin/gcc*
  4. 幫系統建立替換的項目,可以建立多組,並給定Index。如果只建立一組,系統就會用指定的。我隨便給個Index=50,另外可以幫gcc-4.8建立一個其他index也可以,這樣就可以在多組內切換。如果沒有要切換,建立這兩個就好
    • $ sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.9 50
    • $ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.9 50
  5. 嗯這樣就完成了,如果有指定多組,再利用以下命令切換。
    • $ sudo update-alternatives --config gcc
    • 這樣會列出清單,然後再按數字鍵選擇

參考這篇的:http://www.linuxidc.com/Linux/2012-10/72284.htm

如何將TCL包到C的方式

【建立TCL Library】
要將TCL Library成功build,按照底下幾個步驟,以Windows為例:
  1. 準備好TCL 8.6.2 32bit的source code
  2. 執行.\tcl8.6.2\win底下的「vsvars32.bat」,確保環境為32bit
  3. 再利用console切換到「.\tcl8.6.2\win」底下,執行
    • nmake -f makefile.vc
    • 這邊主要是看有哪些選項設定可以用的,可以跳過
  4. 然後輸入底下就可以
    • 產生Release - nmake -f makefile.vc release
    • 產生Debug - nmake -f makefile.vc release OPT=symbols
  5. 關於使用,以Release來說,他產生的library名稱為「tcl86t.lib」,直接在VC++環境裡頭Link就好了
【使用TCL Library,並寫自己的TCL】
要將TCL包進C/C++裡頭,寫一個TCL的延伸,按照底下幾個步驟:

  1. 開一個DLL專案,將剛剛的library給Link進來
  2. 因為是DLL所以這樣宣告主要的function載入點:
    • EXTERN int Hello_Init(Tcl_Interp * interp);
    • 這個EXTERN是TCL內建的define,後面是你自己的名稱
  3. 為這個函式加入TCL元素,這個意思是,先Initial TCL環境,再確認版本,最後加入TCL command
    • 這邊加入的是"SayHello",意思是說,當進入TCL,畫面上輸入"SayHello"時,會去呼叫Hello_Cmd做出反應
    • if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL)
                                       return TCL_ERROR;
      if (Tcl_PkgProvide(interp, "hello", VERSION) != TCL_OK)
                                      return TCL_ERROR;
                            Tcl_CreateObjCommand(interp, "SayHello", Hello_Cmd, NULL, NULL);
      return TCL_OK;
  4. 然後我們去寫Hello_Cmd function,這個Hello_Cmd必須遵照TCL的語法,這裡意思是說去顯示Hello, World在螢幕上:
    • static int Hello_Cmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
              {
                       Tcl_SetObjResult(interp, Tcl_NewStringObj("Hello, World!", -1));
                       return TCL_OK;
              }
  5. 我們的NuTCL-RM就是這樣擴充下去,分出很多命令,底下又帶子命令,完全是自己定義的。