PICTURE 外部函式呼叫功能介紹

回覆文章
福州包
一等士官長
一等士官長
文章: 298
註冊時間: 2017-04-07, 17:57

PICTURE 外部函式呼叫功能介紹

文章 福州包 »

PICTURE於11.1版本新增了PANEL iH Pro外部函式呼叫功能,也於11.3版開始對應PANEL iH。

可透過自製動態連結檔案(DLL)調用外部函式庫,再由PICTURE呼叫該DLL即可實現外部函式的間接呼叫機制。

如下圖所示
概要.png
概要.png (5.67 KiB) 已瀏覽 248 次
此功能可取代以往需透過 iPC 使用 FOCAS2 函式庫進行 CNC 資料讀取與寫入的方式,現在可直接由 PANEL iH 呼叫 FOCAS2 函式達成相同目的。

此外,亦可利用 PANEL iH 的 WinCE 系統所提供的 API,實現 TCP/IP 資料傳輸功能。

範例概圖如下:

應用範例1. 藉由 FOCAS2 函式庫寫入刀具補正
cnc_wrtofs.png
cnc_wrtofs.png (86.47 KiB) 已瀏覽 248 次
應用範例2. 藉由 WinCE Winsock 函式庫進行 TCP/IP 傳輸
TCP_IP.png
TCP_IP.png (31.38 KiB) 已瀏覽 248 次
下方提供說明文件與專案範本,詳細內容請參考說明文件,有問題也歡迎提出討論~

說明文件
PICTURE 外部函式呼叫功能.pdf
(546.22 KiB) 已下載 53 次
專案範本
Project_Sample.zip
(13.18 MiB) 已下載 22 次
福州包
一等士官長
一等士官長
文章: 298
註冊時間: 2017-04-07, 17:57

Re: PICTURE 外部函式呼叫功能介紹

文章 福州包 »

TCP/IP應用於RFID設備範例 ( Pepperl+Fuchs IC-KP2-2HB17-2V1D )

Pepperl+Fuchs IC-KP2-2HB17-2V1D為內建 TCP Server 的設備,特性如下:
• 運作模式:設備以 TCP Server 角色待命接收指令,在此範例中CNC為Client端。
• 通訊埠:固定為 TCP Port 10000。
• 傳輸格式:以 byte 為單位的二進位資料流(包含封包長度與命令碼等欄位)。
• 連線方式:用戶端(Client)需建立 socket 並連線至設備 IP 與 port 10000。

指令格式(Command Frame)
位元組位置 說明
Byte 0 長度高位((N+1)/256)
Byte 1 長度低位((N+1)%256)
Byte 2 命令代碼(Command Code)
Byte 3 通道/旗標(Channel + toggle bit)
Byte 4~5 命令參數(依命令定義而定)
Byte 6~N 寫入資料(如有)
回應格式(Response Frame)
位元組位置 說明
Byte 0~1 回傳封包長度
Byte 2 命令碼(回應)
Byte 3 通道/旗標(回應)
Byte 4 狀態(0 = 成功;其他 = 錯誤碼)
Byte 5 回應計數器(Reply Counter)
Byte 6~N 回傳資料

上述表格定義了與設備通訊所使用的標準資料格式。要與裝置正確溝通,必須依照協定格式傳送對應的命令代碼(Command Code)與參數。以下分別說明 寫入指令 及 讀取指令 的範例格式。

寫入指令格式(Client → Server)
位元組 說明
Byte 0~1 長度 0x00 0A
Byte 2 命令碼(SW) 0x40
Byte 3 通道/字數 0x21
Byte 4~5 寫入位址 0x00 00
Byte 6~7 寫入資料 (高/低) 0x12 34 56 78

代碼: 選擇全部

00 0A 40 21 00 00 12 34 56 78
🔹 表示向 Channel 1, Address 0 寫入 2 word(資料為 0x12345678)


裝置回應(Response)(Server → Client)
位元組 說明
Byte 0~1 長度 0x00 06
Byte 2 命令碼(Echo) 0x40
Byte 3 通道/字數 0x21
Byte 4 狀態(成功為 0) 0x00
Byte 5 回應計數器 0x03

代碼: 選擇全部

00 06 40 21 00 03
🔹 回傳 Channel 1, Address 0 寫入 1 word(資料為 0x1234)成功


讀取指令格式(Client → Server)
位元組 說明
Byte 0 長度高位 0x00
Byte 1 長度低位 0x06
Byte 2 命令碼(SR) 0x10
Byte 3 通道/字數 0x12
Byte 4 讀取位址高位 0x00
Byte 5 讀取位址低位 0x00

代碼: 選擇全部

00 06 10 12 00 00
🔹 表示從 Channel 1 的 Tag 位置 0,讀取 1 個字(Word)(即 4 bytes)


裝置回應(Response)(Server → Client)
位元組 說明
Byte 0~1 長度 0x00 0E (14)
Byte 2 命令碼(Echo) 0x10
Byte 3 Channel/字數 0x12
Byte 4 狀態(成功=0) 0x00
Byte 5 回應計數器 0x02
Byte 6~13 資料(4 bytes) 31 32 33 34 (ASCII "1234")

代碼: 選擇全部

00 0E 10 22 00 02 31 32 33 34
🔹 回傳從 Channel 1 的 Tag 位置 0,讀取 1 個字(Word)(即 4 bytes)成功


由於 PICTURE 中的 mRuby 腳本在處理資料型態上較為困難,因此在與 IC-KP2-2HB17-2V1D 裝置進行 TCP 通訊時,建議透過以下方式進行整體架構設計:

資料傳送
 mRuby 腳本僅負責以 字串形式 組裝並傳遞待發送的資料內容。
 該字串(通常為 HEX 格式)會作為參數傳入 PICTURE 所呼叫的動態連結檔案(C++ DLL)。

資料處理(DLL)
 在 DLL 端,C++ 程式負責:
 - 將 HEX 字串轉換為對應的 Byte 陣列(Binary 格式)。
 - 透過 TCP Socket 傳送封包給設備端。
 - 接收來自設備的回應資料(Binary 格式)。
 - 將回應資料轉換為 HEX 字串格式。

資料回傳
 經過處理後的 HEX 字串,作為 DLL 函式的回傳值,提供給 mRuby 腳本使用。
 mRuby 腳本再以字串操作方式解析與應用回應資料(例如擷取命令回應碼、狀態碼、資料內容等)。

透過此架構,mRuby 僅需操作字串,將複雜的資料型態轉換與 TCP 封包組解工作交由 C++ DLL 處理,可大幅簡化腳本邏輯並提高穩定性與可維護性。

Ruby程式碼範例如下

代碼: 選擇全部

# TCP_Write
		keyin_data = rdstr(400, 0, 0, 40, 99, 0)  # 從系統變數讀取輸入資料(例:畫面鍵入)
		keyin_hex = "000A40120000" + keyin_data.to_s # 前面為寫入用固定指令,keyin_data為要寫入的資料
		TcpSendHexString($P_sock, keyin_hex, keyin_hex.length)
		recv1 = TcpRecvHexString($P_sock)
		sleep(100)
		recv2 = TcpRecvHexString($P_sock)
		$P_recvdata1 = recv1.to_s
		$P_recvdata2 = recv2.to_s

代碼: 選擇全部

# TCP_Read
		keyin_hex = "000610120000" # 讀取用固定指令
		TcpSendHexString($P_sock, keyin_hex, keyin_hex.length)
		recv1 = TcpRecvHexString($P_sock)
		sleep(100)
		recv2 = TcpRecvHexString($P_sock)
		$P_recvdata1 = recv1.to_s
		$P_recvdata2 = recv2.to_s
動態連結檔案(C++)程式碼範例如下

代碼: 選擇全部

__declspec(dllexport) int TcpSendHexString(int sock, const char* hexstr, int hexlen) {
    char buffer[1024] = {0};
    int len = 0;

    // 檢查長度是否為偶數
    if (hexlen % 2 != 0) {
        return -2;
    }

    // 檢查是否為合法 HEX 字元
    for (int i = 0; i < hexlen; ++i) {
        char c = hexstr[i];
        if (!((c >= '0' && c <= '9') ||
              (c >= 'A' && c <= 'F') ||
              (c >= 'a' && c <= 'f'))) {
            return -3;
        }
    }

    // 將字串轉換為 byte buffer
    for (int i = 0; i < hexlen; i += 2) {
        unsigned char high = hexCharToByte(hexstr[i]);
        unsigned char low  = hexCharToByte(hexstr[i + 1]);
        buffer[len++] = (unsigned char)((high << 4) | low);
    }

    int sent = send((SOCKET)sock, buffer, len, 0);
    if (sent <= 0) {
        //MessageBoxA(NULL, "send() 傳送失敗", "Socket 錯誤", MB_OK);
        return -1;
    }

    return sent;
}

代碼: 選擇全部

// 接收資料並回傳 HEX 字串
__declspec(dllexport) const char* TcpRecvHexString(int sock)
{
    memset(g_recvRaw, 0, sizeof(g_recvRaw));
    memset(g_hexBuffer, 0, sizeof(g_hexBuffer));

    int total = 0;

    // 第一步:先讀取封包長度的前兩個位元組(高位 + 低位)
    while (total < 2) {
        int ret = recv((SOCKET)sock, g_recvRaw + total, 2 - total, 0);
        if (ret <= 0) return "";  // 連線中斷或錯誤
        total += ret;
    }

    // 第二步:解析封包總長度(Byte[0] = 高位, Byte[1] = 低位)
    int packet_len = ((unsigned char)g_recvRaw[0] << 8) | (unsigned char)g_recvRaw[1];

    // 安全檢查,避免溢出
    if (packet_len > sizeof(g_recvRaw)) return "";

    // 第三步:讀取剩餘封包內容
    while (total < packet_len) {
        int ret = recv((SOCKET)sock, g_recvRaw + total, packet_len - total, 0);
        if (ret <= 0) return "";
        total += ret;
    }

    // 第四步:將整包資料轉為十六進位格式的字串
    char* p = g_hexBuffer;
    for (int i = 0; i < total; ++i) {
		#ifdef _WIN32_WCE
			sprintf(p, "%02X ", (unsigned char)g_recvRaw[i]); //WinCE
		#else
			wsprintfA(p, "%02X ", (unsigned char)g_recvRaw[i]); //CNC GUIDE, iH PRO
		#endif
        p += 3;
    }

    return g_hexBuffer;
}
回覆文章