-
-
Notifications
You must be signed in to change notification settings - Fork 117
Open
Description
I’d like to ask: is it possible to turn the PC into an adapter? What I’m trying to do is make my PC act as an adapter so that the PLC can continuously read the values I want to respond with. I’ve already imported the corresponding EDS file, but it still doesn’t work. I’m stuck at the part where I need to set up the server. Below is my code. mycode is from copliot.
import threading
import time
import logging
# 匯入 cpppo 的 EIP Server 和數據模型
from cpppo.server.enip import server
from cpppo.server.enip.poll import poll
from cpppo.server.enip.get_attribute import attribute_name
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# --- 參數設定 ---
PC_IP_ADDRESS = '172.16.0.201' # 替換為您的 PC 實際 IP 地址
# PLC (Scanner) 期望讀寫的數據模型 (Assembly Object)
# 使用 DINT 是因為 INT (16位) 在許多 EIP 實現中容易被忽略,DINT (32位) 更通用。
TAG_NAME = "My_EIP_Data"
DATA_TYPE = "DINT"
ARRAY_LENGTH = 10
DINT_SIZE = 4 # 每個 DINT 佔 4 bytes
# 總 I/O 數據長度: 10 * 4 = 40 bytes
# --- 核心邏輯 ---
def run_eip_adapter():
# 1. 定義 Server 的數據字典 (這是 PLC 會讀寫的 I/O 記憶體)
parameters = {
TAG_NAME: [0] * ARRAY_LENGTH # 初始值
}
logging.info(f"🟢 EIP Adapter 啟動中...")
logging.info(f" - PC IP 地址: {PC_IP_ADDRESS}")
logging.info(f" - 數據模型: {TAG_NAME}={DATA_TYPE}[{ARRAY_LENGTH}] ({ARRAY_LENGTH * DINT_SIZE} bytes)")
logging.info(f" - 監聽 TCP/44818 (等待 PLC 的 Forward_Open 請求)...")
# 2. 啟動 EIP Server
try:
# 創建 EIP Server 實例
srv = server.server(
bindings=[PC_IP_ADDRESS], # 綁定到特定 IP,確保監聽正確
parameters=parameters,
timeout=0.1, # 短暫超時,讓主迴圈可以更新數據
workers=1 # 使用單一工作執行緒
)
logging.info("✨ EIP Adapter 成功運行!請切換到 PLC 配置。")
counter = 0
while True:
# 讓 Server 運行一小段時間 (處理連線請求和數據交換)
srv.loop_iter()
# --- 模擬數據更新 (用於測試 PLC 是否成功讀到變化) ---
# 讓第一個值不斷變化
parameters[TAG_NAME][0] = counter
# 檢查是否有來自 PLC 的寫入 (如果 PLC 配置為寫入,值會變動)
plc_write_value = parameters[TAG_NAME][1]
counter += 1
if counter > 65535:
counter = 0
# 顯示當前狀態和 PLC 寫入的值
print(f"\r[Server 狀態]: Counter={parameters[TAG_NAME][0]} | PLC寫入值: {plc_write_value} | 等待連線...", end='', flush=True)
time.sleep(0.5)
except KeyboardInterrupt:
logging.info("\n程式被使用者中斷,正在停止 EIP Adapter...")
except Exception as e:
logging.error(f"❌ 發生致命錯誤: {e}")
finally:
if 'srv' in locals() and srv:
srv.stop()
logging.info("Adapter 已停止。")
if __name__ == "__main__":
run_eip_adapter()
Right now, I’m stuck at the line from cpppo.server.enip import server
. It says “cannot import name 'server' from 'cpppo.server.enip'.” How should I resolve this?
Here is the content of my EDS file.
$ EZ-EDS Version 3.36.1.20241010 Generated Electronic Data Sheet
[File]
DescText = "EDS file for Simple IO Device";
CreateDate = 09-24-2025;
CreateTime = 12:00:00;
ModDate = 09-24-2025;
ModTime = 12:00:00;
Revision = 1.00;
HomeURL = "https://github.com/";
[Device]
VendCode = 1234;
VendName = "test";
ProdType = 43;
ProdTypeStr = "Generic Device";
ProdCode = 1;
MajRev = 1;
MinRev = 1;
ProdName = "Simple IO Device";
Catalog = "SimpleIO";
[Device Classification]
Class1 = EtherNetIP;
[Params]
Param1 =
0, $ reserved, shall equal 0
,, $ Link Path Size, Link Path
0x0000, $ Descriptor
0xC6, $ Data Type (USINT)
1, $ Data Size in bytes
"Write Byte", $ name
"", $ units
"Write a single byte to the device.", $ help string
0,255,0, $ min, max, default data values
,,,, $ mult, div, base, offset scaling
,,,, $ mult, div, base, offset links
; $ decimal places
Param2 =
0, $ reserved, shall equal 0
,, $ Link Path Size, Link Path
0x0030, $ Descriptor
0xC6, $ Data Type (USINT)
1, $ Data Size in bytes
"Read Byte", $ name
"", $ units
"Read a single byte from the device.", $ help string
0,255,0, $ min, max, default data values
,,,, $ mult, div, base, offset scaling
,,,, $ mult, div, base, offset links
; $ decimal places
Param10 =
0, $ reserved, shall equal 0
,, $ Link Path Size, Link Path
0x0000, $ Descriptor
0xC8, $ Data Type
4, $ Data Size in bytes
"RPI", $ name
"us", $ units
"Sets the RPI.", $ help string
1000,3200000,10000, $ min, max, default data values
,,,, $ mult, div, base, offset scaling
,,,, $ mult, div, base, offset links
; $ decimal places
[Assembly]
Revision = 2;
Object_Name = "Assembly Object";
Object_Class_Code = 0x04;
MaxInst = 2;
Number_Of_Static_Instances = 2;
Max_Number_Of_Dynamic_Instances = 0;
Assem1 =
"Output (O->T)",
"20 04 24 01 30 03",
,
0x0001,
,,
8,Param1;
Assem2 =
"Input (T->O)",
"20 04 24 01 30 03",
,
0x0000,
,,
8,Param2;
[Connection Manager]
Object_Name = "Connection Manager Object";
Object_Class_Code = 0x06;
Connection1 =
0x04010002, $ Connection type flags
0x44640455, $ Connection flags
Param10,,Assem1, $ O->T RPI, size, format
Param10,,Assem2, $ T->O RPI, size, format
,, $ proxy config size, format
,, $ target config size, format
"Exclusive Owner", $ Connection Name
"", $ help string
"20 04 2C 01 2C 02"; $ Path
Connection2 =
0x01010002, $ Connection type flags
0x44640355, $ Connection flags
Param10,0,, $ O->T RPI, size, format
Param10,,Assem2, $ T->O RPI, size, format
,, $ proxy config size, format
,, $ target config size, format
"Listen Only", $ Connection Name
"", $ help string
"20 04 2C C6 2C 02"; $ Path
Connection3 =
0x02010002, $ Connection type flags
0x44640355, $ Connection flags
Param10,0,, $ O->T RPI, size, format
Param10,,Assem2, $ T->O RPI, size, format
,, $ proxy config size, format
,, $ target config size, format
"Input Only", $ Connection Name
"", $ help string
"20 04 2C C5 2C 02"; $ Path
[Capacity]
MaxIOConnections = 2;
MaxMsgConnections = 6;
MaxConsumersPerMcast = 2;
TSpec1 = TxRx, 8, 2000;
[DLR Class]
Revision = 3;
Object_Name = "Device Level Ring Object";
Object_Class_Code = 0x47;
MaxInst = 1;
Number_Of_Static_Instances = 1;
Max_Number_Of_Dynamic_Instances = 0;
Ring_Supervisor_Capable = No;
[TCP/IP Interface Class]
Revision = 4;
Object_Name = "TCP/IP Interface Object";
Object_Class_Code = 0xF5;
MaxInst = 1;
Number_Of_Static_Instances = 1;
Max_Number_Of_Dynamic_Instances = 0;
[Ethernet Link Class]
Revision = 3;
Object_Name = "Ethernet Link Object";
Object_Class_Code = 0xF6;
MaxInst = 1;
Number_Of_Static_Instances = 1;
Max_Number_Of_Dynamic_Instances = 0;
InterfaceLabel1 = "Ethernet Port 1";
InterfaceType1 = 2;
[Identity Class]
Revision = 2;
Object_Name = "Identity Object";
Object_Class_Code = 0x01;
MaxInst = 1;
Number_Of_Static_Instances = 1;
Max_Number_Of_Dynamic_Instances = 0;
[QoS Class]
Revision = 1;
Object_Name = "QoS Object";
Object_Class_Code = 0x48;
MaxInst = 1;
Number_Of_Static_Instances = 1;
Max_Number_Of_Dynamic_Instances = 0;
Metadata
Metadata
Assignees
Labels
No labels